2010/03/24

ACER AO751h安裝Windows7

嗯,剛剛打電話給客服人員,得到以下的回應
Q. 安裝win7後,原有的還原磁區還能用嗎?
A. 不行。

Q. 不能再用還原磁區,那怎麼辦?
A. 建議你直接做成還原光碟,需要還原時只要用那張光碟就可以了。

Q. 還原是連備份磁區一起還原嗎?
A. 不是,只有單一磁區。

Q. 開機時的ALT + F10功能鍵可以用BIOS手動關閉嗎?
A. BIOS沒有這個項目。

Q. 751h有支援XP-mode嗎?
A. 沒有。

以上,ACER的工程師真的…話很簡潔,可能是夠專業吧。

那個還原磁區好像只要重灌後就不能用了,很雞肋。
從以上解答可以知道,AO751h的VT功能確定是被關起來的!

嗯...真的要好好考慮換系統的事情了…

[轉載]用OpenOffice Writer製作一本包含封面、目錄、內容及頁數的書

資料來源:用OpenOffice Writer製作一本包含封面、目錄、內容及頁數的書

如何讓OpenOffice Writer自動加入目錄,並製作成包含封面、目錄、內容及頁數的書,是一個讓我困擾已久的問題。以下的方法,是我今天在製作講義時才摸索出來,希望能對各位OpenOffice的愛用者有幫助,如果文件有問題或是有更方便的方法也歡迎提出。

我的目標如下:
1. 封面:不加頁數
2. 目錄:有章節編號及名稱,自動
加上頁數
3. 內容:分章、節、小節、subsubsection,從1開始編頁碼

首先附上我製作的例子:odt pdf
以下的說明請參考這兩個例子。

1. 設定頁面樣式

為了讓不同的樣面採用不同型式的頁碼,我們必須先插入不同樣式的頁面。首先開啟一個新的文字文件,一開始預設的頁面樣式為標準。從上方工具列中選取插入=>手動換行,會出現如下圖的視窗。



選取換頁、樣式為標準變更頁碼為1,再用同一的方法新增一頁,但這次選擇樣式為目錄。但是這樣我們仍然缺少封面,因此再從下圖中紅線框起的部分,按右方的三角形下拉選單,再選擇更多,會出現如下下圖的視窗,雙擊首頁即可將所在的頁面樣式轉換為首頁。






2. 插入頁尾及頁數
從上方工具列中選取插入=>頁尾,選擇標準目錄就好了,因為我們不想在首頁(封面)顯示頁數。在頁尾的方框中,自上方工具列中選取插入=>欄位指令=>頁碼,即可在除了首頁以外的每一頁最底下顯示頁碼。如果想要讓目錄的頁碼使用別的格式,請在頁碼上按滑鼠右鍵選擇欄位(有時要多按幾次,不容易按到),會出現如下圖的視窗,在這裡面就可以選擇想要的頁碼格式。



3. 變更段落格式
我們在此要先變更一下段落樣式,這些格式是為了給chapter、section、subsection及subsubsection用的(我不知道subsubsection中文要怎麼翻,所以乾脆用英文)。從上方工具列選取工具=>章節編號,會出現如下圖的視窗。我是將標題1設定為第1級的格式,並且在分隔符之前加上、在之後加上,這樣顯示出來的結果就會是第x章。相同地,我再將標題2設定給第2級使用,不過這次就不加上文字,而是讓它顯示為x.x。我只有設定到標題4,因為我通常不會用到subsubsection以下的編號。



4. 輸入章節標題
再來就到頁面樣式為標準的頁面中輸入內容,可以先打好章的標題之後再將滑鼠游標移到所在的那行,再從步驟2中第二張圖所標示的地方改變段落樣式。如果是的標題,則改為標題1的標題則改為標題2,依此類推。


5. 插入目錄
最後到頁面樣式為目錄的頁面中,從上方工具列中選取插入=>目錄=>目錄,會出現如下圖的視窗。在第一個分頁中可以決定標題及要顯示到多少級,我將它設定為3級,也就是不顯示subsubsection。另外,我通常會將不允許手動變更去掉,這樣等一下出了任何狀況才可以手動修改。



在第二個分頁中,可以決定顯示的格式。我通常將E#E之間加入10個空格,讓章節編號和標題間有點距離。




到此為此算是大功告成了。再來我們可以在檔案=>屬性中加入一些此文件的相關資訊(如下圖)。



之後再從檔案=>匯出成PDF檔,將文件轉成pdf檔。在按下匯出之後,會出現如下圖的視窗,我們可以在此決定這個pdf檔相關的性質。





最後,用pdf reader開啟剛才輸出的檔案來看看。因為有加入章節編號的關係,所以pdf檔中已經建立好書籤了,看起來就很專業吧!

2010/03/23

Win7執行BCB6時,發生問題的解決辦法

BCB6可以安裝在Win7上面,不過在開啟BCB6時,會告訴你說ooxx的檔案無法開啟,而導致Project開不了。

由於Win7的架構與Vista幾乎是一樣的,所以解決方式應該大同小異

所以參考了
BCB6:
在Vista執行BCB6

Delphi:

測試過之後,真的可以用耶!
呵呵呵!

附上執行結果圖:
那麼…就再繼續用BCB6吧
不過畫面還是鳥鳥的…等存夠錢再來升級一下吧 ^ ^

2010/03/22

放棄…也算是勇於面對自我吧?

準備領隊導遊的考試也有了一段時間,其間也對觀光資源概要做了很多很多的研讀
但不知怎麼的,每次的考古題都可以考出很多從沒看過的題目

平均一直都在3x附近排徊…而實務一、實務二也因為長時間沒複習而只有約5x分的水平。

這樣的情況,叫我怎麼去應考呢!!!

猜答案都沒猜對過的經驗來看,放棄應該是明智的選擇吧。

傳說明年度及格分數會調高到75分,看來這張證照離我真是越來越遙遠了呀~~~~~

2010/03/16

[轉載] C++ Builder: Use a mutex to achieve synchronization

在網路上有看到一篇:C++ Builder中引用API函数禁止应用程序多次启动
看得不是很明白
於是找到另一篇,C++ Builder Developer's Journal / Use a mutex to achieve synchronization
但編排不是很好,所以我有稍微做了一下程式碼的排版,其它內容一字不變地轉載:
January 1998
Use a mutex to achieve synchronization
by Sam Azer

Over the past few months, a few people on the CPB-Thread listserv have asked how to prevent more than one instance of an application from being started. (To subscribe to CPB-Thread, visit www.cobb.com/cpb.) Several people have worked very hard on a variety of answers to this question. It seems, though, that the simplest solution is one suggested by members of Borland's TeamB on their news server: a named mutex. As you know, Windows 95 provides a number of built-in functions for running multiple tasks and threads within a task. Accordingly, a group of Synchronization functions is available to ensure that your tasks and threads are able to work together effectively.

One of the more common problems in a multitasking environment is resource sharing. If you're using a resource--for example, a serial port--you don't want somebody else to start using it before you're finished. The standard solution to this problem is for you to get a mutual exclusion lock (mutex) on the resource. If you're able to get the lock, you can use the resource. If, while trying to lock the resource, you find that somebody else has it--you must wait.

In this article, we'll build a small and very simple function to return true if an instance of a program is already running. To do this, we'll use a simple named mutex. (You can download the code from this article at www.cobb.com/cpb.) Let's start by looking at how you can use a mutex.


Using a named mutex
A mutex is a very simple object. It has a name, access rights, and a state. The name must be unique among all mutex, semaphore, and file-mapping objects. In most cases, the access rights will be MUTEX_ALL_ACCESS. The state can be Owned or Not Owned. If you create a resource that can be used by only one caller at a time, you'll probably also create a mutex for it. In this case, your callers try to open the mutex to see if the resource exists. Once they have a valid handle to the mutex, they use wait functions to gain ownership of the resource and start using it. Wait functions sleep until a mutex isn't owned, then take ownership of it and use the resource. (You can optionally place a time limit on a wait function.) Eventually, the caller will release the mutex, marking it not owned and available to the next caller. In this way, all callers wait to take ownership of the resource when they need it and release it when they're finished. If a caller has no further need for a resource, the mutex handle can be closed.

Once a mutex exists, any tasks trying to create it again will get a handle to it and an error already exists error. If a task ends, all the mutex handles that it owns are closed. When all tasks have closed their handles to a mutex, it's destroyed. Therefore, a single call to the CreateMutex() function is all you need to find out if an instance of an application is already running!

Using CreateMutex()

The CreateMutex() function takes only three parameters: a pointer to a security descriptor, a flag to request immediate ownership, and the name of the mutex to create. It returns a HANDLE. It ignores the pointer to the security descriptor under Windows 95. Under Windows NT, you can get default security settings by passing a NULL pointer. The mutex name is simply any unique null-terminated string (up to MAXPATH characters in length). If the returned HANDLE is null, something undesirable happened. In any case, GetLastError() should return zero to indicate that the mutex was created. A value of ERROR_ALREADY_EXISTS indicates that the mutex already exists. Use SysErrorString() to translate the other error codes into English.

By now you've probably realized that there really isn't much to it--you can very easily detect an existing instance of an application using this method. Listing A shows the source code for one way to do it.

Listing A: Detecting another instance of an application

//--------------------------------------------
#include
#pragma hdrstop
//--------------------------------------------
USEFORM("OneOnlyForm.cpp", Form1);
USERES("OneOnly.res");
//--------------------------------------------
// the name of the mutex for this program
const char *MutexName = "OneOnlyDemo";
//--------------------------------------------
HANDLE CheckInstance( const char *Name )
{
// 1st: create mutex. Request ownership.
HANDLE Mutex = CreateMutex(NULL,true,Name);
// Next, error result - should be zero
int r = GetLastError();
// if r != 0, probably ERROR_ALREADY_EXISTS
if ( r != 0 )
return 0; // disaster or another instance
return Mutex; // else, return the handle.
}
//--------------------------------------------
WINAPI WinMain (HINSTANCE,HINSTANCE,LPSTR, int)
{
HANDLE Mutex = CheckInstance( MutexName );
if ( !Mutex )
{
MessageBox(HInstance, "Another Instance is running!", "Sorry", MB_OK );
ReleaseMutex( Mutex );
return 1;
};

try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1),&Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
//--------------------------------------------

Wrap up
Do a keyword search for Synchronization in the Win32 (Platform) SDK Help files for detailed information on the functions available for synchronizing--they're quite handy. In many cases the code presented in this article is enough to prevent problems from occurring between two instances of a simple database program. However, for MDI applications you'll probably want to send a message to the original instance. Watch for an article on inter-process communications in a future issue of C++Builder Developer's Journal.

哼…TDataSetProvider…真是被你打敗了呀

不知道是不是BCB6過於老舊,對ClientDataSet做Edit工作(SQL's Update)
一直很不順利,老是出現:
"Table unkown 'TableName'"

於是丟了個SQLMonitor去除錯
結果從SQLMonitor查到的語法是這樣的:
update "TableName"
set 'Field' = ?
where
set 'Field' = ?

[轉載]Delphi ListView基本用法大全

作者:不明,取自CSDN轉載
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//增加項或列(字段)
ListView1.Clear;
ListView1.Columns.Clear;
ListView1.Columns.Add;
ListView1.Columns.Add;
ListView1.Columns.Add;
ListView1.Columns.Items[0].Caption := 'id';
ListView1.Columns.Items[1].Caption := 'type';
ListView1.Columns.Items[2].Caption := 'title';
ListView1.Columns.Items[2].Width := 300;
Listview1.ViewStyle := vsreport;
Listview1.GridLines := true;
  //注:此處代碼也可以直接在可視化編輯器中完成,也可寫成以下這樣
 
begin
  with listview1 do
  begin
    Columns.Add;
    Columns.Add;
    Columns.Add;
    ViewStyle := vsreport;
    GridLines := true;
    columns.items[0].caption := ' 進程名';
    columns.items[1].caption := '進程ID';
    columns.items[2].caption := ' 進程文件路徑';
    Columns.Items[0].Width := 100;
    Columns.Items[1].Width := 100;
    Columns.Items[2].Width := 150;
  end
end;
 
//增加記錄
with listview1.items.add do
begin
  caption := '1212';
  subitems.add('hh1');
  subitems.add('hh2');
end;
 
//刪除
listview1.items.delete(0);
 
//從數據庫表裡讀取數據寫入Listview
var
  Titem: Tlistitem; //此處一定要預定義臨時記錄存儲變量.
begin
  ListView1.Items.Clear;
  with adoquery1 do
  begin
    close;
    sql.Clear;
    sql.Add('select spmc,jg,sl from kcxs');
    Open;
    ListView1.Items.Clear;
    while not eof do
    begin
      Titem := ListView1.Items.add;
      Titem.Caption := FieldByName('spmc').Value;
      Titem.SubItems.Add(FieldByName('sl').Value);
      Titem.SubItems.Add(FieldByName('jg').Value);
      next;
    end;
 
    //刪除
    ListView1.DeleteSelected;
  end;
end;
 
//如何取得ListView中選中行的某一列的值
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage(ListView1.Selected.SubItems.Strings[1]); //返回選中行第三列中的值
end;
 
showMessage(listView1.Selected.Caption); //返回選中行第一列的值.
 
//第1列的值: - - > > > 
  ListView1.Selected.Caption
//第i列的值(i > 1): - - > > > 
  ListView1.Selected.SubItems.Strings[i]
 
ListView1.Items.Item[1].SubItems.GetText); //取得listview某行某列的值
 
Edit2.Text := listview1.Items[i].SubItems.strings[0]; //讀第i行第2列
 
//返回選中行所有子列值.是以回車符分開的,你還要從中剝離出來你要的子列的值。
 
showMessage(ListView1.Selected.SubItems.GetText);
 
//ListView 簡單排序的實現
 
//ListView 排序
 
//怎樣實現單擊一下按升序,再單擊一下按降序。
 
function CustomSortProc(Item1, Item2: TListItem; ColumnIndex: integer): integer;
  stdcall;
begin
  if ColumnIndex = 0 then
    Result := CompareText(Item1.Caption, Item2.Caption)
  else
    Result := CompareText(Item1.SubItems[ColumnIndex - 1],
      Item2.SubItems[ColumnIndex - 1])
end;
 
procedure TFrmSrvrMain.ListView1ColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  ListView1.CustomSort(@CustomSortProc, Column.Index);
end;
 
//===============================================================
 
//增加
i := ListView1.Items.Count;
with ListView1 do
begin
  ListItem := Items.Add;
  ListItem.Caption := IntToStr(i);
  ListItem.SubItems.Add('第 ' + IntToStr(i) + ' 行');
  ListItem.SubItems.Add(' 第三列內容');
end;
 
//按標題刪除
for i := ListView1.Items.Count - 1 downto 0 do
  if ListView1.Items[i].Caption = Edit1.Text then
  begin
    ListView1.Items.Item[i].Delete(); //刪除當前選中行
  end;
 
//選中一行
if ListView1.Selected <> nil then
  Edit1.Text := ListView1.Selected.Caption;
 
// listview1.Items[Listview1.Items.Count -1].Selected := True;
// listview1.Items[Listview1.Items.Count -1].MakeVisible(True);
 
procedure TForm1.Button2Click(Sender: TObject); // 選擇第一條
begin
  listview1.SetFocus;
  listview1.Items[0].Selected := True;
end;
 
procedure TForm1.Button1Click(Sender: TObject); // 選擇最後一條
begin
  listview1.SetFocus;
  listview1.Items[Listview1.Items.Count - 1].Selected := True;
end;
 
//這是個通用的過程
 
procedure ListViewItemMoveUpDown(lv: TListView; Item: TListItem; MoveUp,
  SetFocus: Boolean);
var
  DestItem: TListItem;
begin
  if (Item = nil) or
    ((Item.Index - 1 <> = lv.Items.Count) and (not MoveUp)) then
    Exit;
  lv.Items.BeginUpdate;
  try
    if MoveUp then
      DestItem := lv.Items.Insert(Item.Index - 1)
    else
      DestItem := lv.Items.Insert(Item.Index + 2);
    DestItem.Assign(Item);
    lv.Selected := DestItem;
    Item.Free;
  finally
    lv.Items.EndUpdate;
  end;
  if SetFocus then
    lv.SetFocus;
  DestItem.MakeVisible(False);
end;
 
//此為調用過程,可以任意指定要移動的Item,下面是當前(Selected)Item
ListViewItemMoveUpDown(ListView1, ListView1.Selected, True, True); //上移
ListViewItemMoveUpDown(ListView1, ListView1.Selected, False, True); //下移
 
//TListView組件使用方法
 
//引用CommCtrl單元
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  ListView_DeleteColumn(MyListView.Handle, i); //i是要刪除的列的序號,從0開始
 
end;
 
//用LISTVIEW顯示表中的信息:
 
procedure viewchange(listv: tlistview; table: tcustomadodataset; var i:
  integer);
begin
  tlistview(listv).Items.BeginUpdate; {listv:listview名}
  try
    tlistview(listv).Items.Clear;
    with table do {table or query名}
    begin
      active := true;
      first;
      while not eof do
      begin
        listitem := tlistview(listv).Items.add;
        listitem.Caption := trim(table.fields[i].asstring);
        // listitem.ImageIndex:=8;
        next;
      end;
    end;
  finally
    tlistview(listv).Items.EndUpdate;
  end;
end;
 
//ListView使用中的一些要點。以下以一個兩列的ListView為例。
//→ 增加一行:
with ListView1 do
begin
  ListItem := Items.Add;
  ListItem.Caption := ' 第一列內容';
  ListItem.SubItems.Add('第二列內容');
end;
//→清空ListView1:
ListView1.Items.Clear;
//→得到當前被選中行的行的行號以及刪除當前行:
for i := 0 to ListView1.Items.Count - 1 do
  if ListView1.Items[i].Selected then //i=ListView1.Selected.index
  begin
    ListView1.Items.Delete(i); //刪除當前選中行
  end;
//當然,ListView有 OnSelectItem事件,可以判斷選擇了哪行,用個全局變量把它賦值出來。
//→讀某行某列的操作:
Edit1.Text := listview1.Items[i].Caption; //讀第i行第1列
Edit2.Text := listview1.Items[i].SubItems.strings[0]; //讀第i行第2列
Edit3.Text := listview1.Items[i].SubItems.strings[1]; //讀第i行第3列
//以次類推,可以用循環讀出整列。
//→ 將焦點上移一行:
for i := 0 to ListView1.Items.Count - 1 do
  if (ListView1.Items[i].Selected) and (i > 0) then
  begin
    ListView1.SetFocus;
    ListView1.Items.Item[i - 1].Selected := True;
  end;
//不過在Delphi6 中,ListView多了一個ItemIndex屬性,所以只要
ListView1.SetFocus;
ListView1.ItemIndex := 3;
//就能設定焦點了。
 
//Delphi的listview能實現交替顏色麼?
 
procedure TForm1.ListView1CustomDrawItem(
  Sender: TCustomListView; Item: TListItem; State: TCustomDrawState;
  var DefaultDraw: Boolean);
var
  i: integer;
begin
  i := (Sender as TListView).Items.IndexOf(Item);
  if odd(i) then
    sender.Canvas.Brush.Color := $02E0F0D7
  else
    sender.Canvas.Brush.Color := $02F0EED7;
  Sender.Canvas.FillRect(Item.DisplayRect(drIcon));
end;

2010/03/13

讀書心得:蟹工船

「誒!下地獄囉!」

上面就是這本書的開場白。

前陣子這本書的內容被翻拍成電影,印象中說是「以詼諧的手法來描述資本主義社會的黑暗」。

覺得可能會很好看,於是去圖書館借。

在排隊排了很長的一段時間,總算是排到了,收到通知的當天就去拿回來看。

剛開始看的時候,就感覺內容好昏暗
看到中期,啊,怎麼這麼可憐
看到後期,機車!怎麼還不反抗!
熬到結局………自己去看吧!(眾人毆)

看完之後的感想是,真的,現在台灣的社會環境和80年前的日本,其實有越來越像的趨勢

資本家越來越來富有,而窮苦的人卻越來越窮苦…

在這本書上可以看到幾種人,以很鮮明的個性表達了現在的各種角色,比方說像是:
書裡的漁、雜工 = 現在的窮忙的現代人 = 羊、鹿…只要是草食動物就行
書裡的薦頭 = 現在的人力仲介 = 誘餌、獅子的僕人?還是動物管理員?
書裡的監督 = 現在的中階幹部 = 鬚狗
而boss呢?還是boss = 獅子

就這樣,一個活生生的食物鏈就在這本書上演了。

其中,有2/3的劇情真的讓人高興不起來,不過卻又讓你有不得不看下去的衝動。

如果,我們的生活結構不去改變,那麼,就真的如同這本書的開場白一樣:
「誒!下地獄囉!」

不過,這樣一來,可憐的就是那些中階幹部了吧!哈哈!

最近開發案子之後的心得

自上次看了Delphi Databases: Dynamic Datamodules at Runtime後,就一直在找機會將這個方式移植到自己的專案上面。

所幸最近BOSS開了新的案子,才有機會將這個方式導入到專案中。

期間遇到的事情也不少,簡單的記錄一下好了。

一、auto_ptr很好用,但出問題時會不知道為什麼發生。(這和自己的功力有關)
二、DataModule Class只做了一個,然後包裝到其它的class中,這個方式會造成很多很多重複new delete的程式碼,比較好的作法應該還是依靠IDE對datamodule的實作,這樣new/delete就會很少出現。
三、承第二點,若未來要改成n-tier的作法,datamodule會改很大,所以也不建議這樣改。
四、想到再繼續寫…

也有一些優點的啦!
一、資料存取時,DB壓力較小
二、因為採用了部分設計模式,大部分程式都靠自己的CLASS解決了,IDE FORM裡的程式碼可以很簡潔。
三、承第二點,IDE FORM專門處理自己的事件,同樣的,商業程式碼也都轉向自己的CLASS來處理,在程式修改上可以很方便的處理。
四、發現用DELPHI來寫時,速度會更快(汗)
五、更加了解DBX的運作過程
六、多玩了DATASNAP的架構
七、一樣,想到再繼續寫…(逃)

2010/03/02

TClientDataSet Run-time時期應注意的地方

主旨:為了避免已建立的VCL的屬性莫名指向NULL,建議由使用者自己使用new建立TClientDataSet。

說明:

如果不想在Design-time時期設定好TClientDataSet各項參數,可以採用Run-time設計模式。

但如果TClientDataSet是於Design-time時期就已建立,在Run-time時期,該TClientDataSet開啟後,TDataProvider會指向NULL,此時再對該TClientDataSet->Close();,就會發生ACCESS ERROR。

解決方式:
Design-time時期於form表上加入TClientDataSet時,IDE會在 __published 區自動增加TClientDataSet *Temp;
而在Rum-time時,TClientDataSet資料傳輸的主控權即交給IDE自行處理。

所以使用者要擁有較高的管理權,可採自行於程式碼中 new /delete TClientDataSet即可。

2010/3/4 更新
在Help裡查到:
TClientDataSet若是使用SetProvider指向DataSetProvider(Run-time),則TClientDataSet Close()再開前需要再使用SetProvider指向DataSetProvider





動態建立 ClientDataSet 的範例可參考:
ADO and ClientDataSet

2010/03/01

No BOOLEAN field in Firebird?

No BOOLEAN field in Firebird?


There is no built-in boolean field, but you have several options:

1. use char(1)
2. use smallint
3. use domains

Domains are probably the best solution. You can create domain like this:


CREATE DOMAIN BOOLEAN
AS SMALLINT
CHECK (value is null or value in (0, 1));


Later in table definition you can refer to it as a regular datatype.


CREATE TABLE t1
(
C1 VARCHAR(10),
B1 BOOLEAN,
B2 BOOLEAN NOT NULL,
...
);


If you use a connectivity library like OleDB under .Net, you can override the OleDB provider's GetSchema method, so the DataTables you get from queries have native .Net booleans.