全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:98186
推到 Plurk!
推到 Facebook!
[<<] [1] [2] [>>]

心得分享系列(BCB)

 
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2002-06-13 09:44:45 IP:210.208.xxx.xxx 未訂閱
心得分享 : 如果您不想寫一個資料庫程式還要手動去設 BDE 別名時 .... void __fastcall TForm1::FormCreate(TObject *Sender) { shortString MyAlias="MyAlias"; if (!Session->IsAlias(MyAlias)) { Session->AddStandardAlias(MyAlias, ExtractFilePath(Application->ExeName) "DB\\LocalDB", "PARADOX"); //Session->SaveConfigFile(); //不存也可,但程式結束,此臨時別名也會消失 ShowMessage("自動加入資料庫別名:" MyAlias); }
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#2 引用回覆 回覆 發表時間:2002-06-13 09:48:03 IP:210.208.xxx.xxx 未訂閱
心得分享 : Paradox 資料庫的極限 在 BDE 之 Configuration 中設定 Native 驅動器 Paradox 之 BLOCK SIZE 可決定 paradox 資料表的容量 內定值是 2048 也就是 128MB 根據敝人親自測試 (不是道聽塗說) 在一百萬筆商品主檔中 (約佔 350MB 磁碟空間 , 故 BLOCK SIZE 要設為 16384) , 搜尋一筆資料約只費 0.1 秒 ... 故適用在收銀機系統或圖書資料查詢等情況 在不需做複雜關聯的情況下 用其他大型資料庫根本是殺機用牛刀 ... BlockSize 對 Max FileSize 參考 : Paradox Max FileSize 64M with BlockSize 1024 Paradox Max FileSize 128M with BlockSize 2048 (default) Paradox Max FileSize 256M with BlockSize 4096 Paradox Max FileSize 1G with BlockSize 16384 Paradox Max FileSize 4G with BlockSize 32768
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#3 引用回覆 回覆 發表時間:2002-06-13 09:50:32 IP:210.208.xxx.xxx 未訂閱
心得分享 : 資料庫的搜尋方法 常用的搜尋方法有兩種,一種是 FindKey方法,另一種是 Locate方法;FindKey 的方法在早期就已使用,現在只是為了相容性而存在,而且要使用FindKey方法來搜尋某鍵值,必須存在該鍵值的索引;而 Locate方法不一定需要索引(有則會自動引用),建議讀者盡量使用Locate方法,兩者方法都會在搜尋到鍵值時會將資料庫記錄指標停在找到的記錄上,並傳回一個 true 值 ■ FindKey 使用 FindKey只搜尋"一個"鍵位值時 (要先設好 Table1 之索引鍵) Table1->FindKey(new TVarRec(搜尋值), 0); 使用 FindKey搜尋"複合鍵位"(兩個以上)值時 (要先設好 Table1 之索引鍵) Table1->FindKey(OPENARRAY(TvarRec, (搜尋值1, 搜尋值2, 搜尋值3))) ■ Locate 使用 Locate 只搜尋"一個鍵位"值時 (Table1 不需有索引) Table1->Locate(“欲搜尋之欄位名稱”, 搜尋值, TLocateOptions() << loPartialKey); 使用 Locate 搜尋"複合鍵位"(兩個以上)值時 (Table1 不需有索引) Variant locvalues[]={搜尋值1, 搜尋值2, 搜尋值3}; Table1->Locate("欄位名稱1; 欄位名稱2; 欄位名稱3", VarArrayOf(locvalues,(sizeof(locvalues)/16-1)),TLocateOptions() << loPartialKey); 註 : 有些市售騙錢 BCB 書籍講了一堆...卻只單純介紹"單鍵"搜尋 ....
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#4 引用回覆 回覆 發表時間:2002-06-13 09:51:15 IP:210.208.xxx.xxx 未訂閱
心得分享 : 防止程式重複執行 要防止自己的程式重複執行,使用的方法有好幾種,有些方法甚至要去修改*.bpr檔(C Builder 專案檔),但筆者不建議讀者任意修改 C Builder 自動維護的專案檔;使用本節介紹的方法只要將下列程式碼原封不動的抄到您的 FormCreate() 事件中即可,不但不會讓相同的程式同時存在第二個實體,而且在執行時發現第一個實體已存在而且是最小化時,還會將其彈出展開於桌面上.... //------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { Application->Title=Form1->Caption; //TrayIcon1->Hint=Form1->Caption; HANDLE PrevInstHandle; HANDLE Mutex = OpenMutex(SYNCHRONIZE,false,Application->Title.c_str()); if (Mutex != null) { String AppTitle=Application->Title; SetWindowText(Application->Handle,null); PrevInstHandle = FindWindow("TApplication", AppTitle.c_str()); if (PrevInstHandle != 0) { if (IsIconic(PrevInstHandle)) ShowWindow(PrevInstHandle, SW_RESTORE); else BringWindowToTop(PrevInstHandle); SetForegroundWindow(PrevInstHandle); } Application->ShowMainForm = false; Application->Terminate(); } else CreateMutex(null, false, Application->Title.c_str()); // 接下來可以插入您其它要寫在 FormCreate 中的程式 }
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#5 引用回覆 回覆 發表時間:2002-06-13 09:54:35 IP:210.208.xxx.xxx 未訂閱
心得分享 : 如何讓程式執行時,馬上隱藏於桌面右下角 1.在設計程式時,加入一個 TrayIcon 元件(在 C Builder 之 Sample 元件頁中第二個)於主 Form 中,並將該元件 Visible 屬性設為 true 2.在主 Form 的 OnActive 事件中寫入兩行程式碼: Application->Minimize(); ShowWindow(Application->Handle, SW_HIDE); 3.以後這個程式於執行時,會馬上隱藏於桌面右下角成為 Tray Icon 重點在第二點 .... << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#6 引用回覆 回覆 發表時間:2002-06-13 09:56:24 IP:210.208.xxx.xxx 未訂閱
心得分享 : 以程式控制 Windows 2000 關機 以程式控制 Windows 2000 關機 //------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { HANDLE hToken; TOKEN_PRIVILEGES tkp; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken); LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, false, &tkp, 0, (PTOKEN_PRIVILEGES)null, 0); // 下面八個動作中任選一種執行 .... // 一般關閉,關閉前會詢問其它執行中的程式是否存檔 //ExitWindowsEx(EWX_LOGOFF,0); // 登出, 重新登入使用者 //ExitWindowsEx(EWX_REBOOT,0); // 重新開機 //ExitWindowsEx(EWX_SHUTDOWN,0); // 結束作業系統,出現"您可以放心關機畫面" //ExitWindowsEx(EWX_POWEROFF,0); // 結束作業系統,並關閉電源(僅支援ATX 規格) // 強制關閉,其它執行中的應用程式資料並不會被儲存 //ExitWindowsEx(EWX_LOGOFF | EWX_FORCE,0); // 登出, 重新登入使用者 //ExitWindowsEx(EWX_REBOOT | EWX_FORCE,0); // 重新開機 //ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE,0); // 結束作業系統 //ExitWindowsEx(EWX_POWEROFF | EWX_FORCE,0); // 結束作業系統,並關閉電源 (僅支援ATX 規格) } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#7 引用回覆 回覆 發表時間:2002-06-13 09:58:24 IP:210.208.xxx.xxx 未訂閱
心得分享 : 結束或最小化指定的程式 ■ 結束指定的程式 要結束指定的程式,得先要知道要結束的對象程式標題名稱是什麼,然後利用FindWindow() 來找出其視窗 HANDLE,再呼叫 PostMessage() 送出 WM_CLOSE 訊息以結束該視窗,PostMessage()的用法與 SendMessage()完全相同,所不同的是 SendMessage() 會等待接收訊息的視窗處理完訊息後才返回,而PostMessage()則是送出訊息命令後馬上返回 //------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { HANDLE DestHandle; DestHandle = FindWindow(null,"test.txt - 記事本"); if (DestHandle != 0) PostMessage(DestHandle, WM_CLOSE, 0, 0); } //------------------------------------------------------------------- ■ 最小化指定的程式 指定某一程式最小化有三種做法:(DestHandle 承上節求出) 1. CloseWindow(DestHandle); (勿以為這是「結束」指定程式的用法) 2. ShowWindow(DestHandle, SW_MINIMIZE); 3. SendMessage(DestHandle, WM_SIZE, SIZE_MINIMIZED, 0); ■ FindWindow() 的問題 以上,當我們在尋找目標視窗時,FindWindow()內需一字不漏的填入完整的視窗標題名稱,否則會找不到目標視窗;所以我們進一步利用 GetWindow()函式,搜尋全部可見視窗,再逐一比對其視窗標題名稱,這樣就可做到所謂「概略搜尋」,以下的程式範例會將桌面上所有視窗標題名稱含有”記事本”字樣的視窗通通關閉 //------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { HANDLE hCurrentWindow = GetWindow(Handle,GW_HWNDFIRST); char buff[255]; String Text; while (hCurrentWindow!=0) { if ( ( GetWindowText(hCurrentWindow,buff,255)>0) && IsWindowVisible(hCurrentWindow) ) { //ShowMessage(buff); Text=buff; if (Text.Pos("記事本")!=0) { PostMessage(hCurrentWindow, WM_CLOSE, 0, 0); } } hCurrentWindow = GetWindow(hCurrentWindow,GW_HWNDNEXT); } } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#8 引用回覆 回覆 發表時間:2002-06-13 10:00:05 IP:210.208.xxx.xxx 未訂閱
心得分享 : 執行特定程式或文件 ShellExecute 使用 ShellExecute 一般我們看到市售程式中,按個按鈕可連到開發公司的首頁,或按個按鈕可自動開啟 OutLook Express 準備寄信給作者,這些功能都可用 ShellExecute 實做之 ■ 語法 ShellExecute( HWND hwnd, // 父視窗 Handle LPCTSTR lpOperation, // 開啟或操作方式 LPCTSTR lpFile, // 檔案名稱 LPCTSTR lpParameters, // 參數內容 LPCTSTR lpDirectory, // 命令所在目錄 int nShowCmd // 執行時視窗型態 ); 不重要的參數可用 null 取代,其它詳細內容請參考線上說明 ■ 應用 用「系統預設」的瀏覽器開啟某一個網址: ShellExecute(null,null,"http://www.hinet.net",null,null,SW_SHOW); 用「系統預設」郵件軟體開啟一個新郵件: ShellExecute(null,null,"mailto:test@ms5.hinet.net",null,null,SW_SHOW); 或 ShellExecute(null,null,"mailto:test@ms5.hinet.net?Subject=哈囉&body=您好",null,null,SW_SHOW); 用「系統預設」的程式開啟某個檔案 (不用管該檔案是 Word 檔還是 Excel 檔,系統會先自動呼叫開啟該檔案型態的主程式): ShellExecute(null,null,"c:\\test.doc",null,null,SW_SHOW); 或 ShellExecute(null,null,"c:\\test.xls",null,null,SW_SHOW); 指定用某程式開啟或列印某個檔案: ShellExecute(null,"open","notepad.exe","c:\\test.txt", null,SW_SHOW); 或 ShellExecute(null,"print","notepad.exe","c:\\test.txt", null,SW_SHOW); 啟動某個應用程式: ShellExecute(null,null,"c:\\PosAgent.exe",null,null,SW_SHOW); 或啟動某個內部命令 ShellExecute(null,null,"cmd.exe","/k dir c:\\",null, SW_SHOW); << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#9 引用回覆 回覆 發表時間:2002-06-13 10:01:34 IP:210.208.xxx.xxx 未訂閱
心得分享 : 如何模擬 FileListBox 功能 如果程式需要抓取一堆文字檔來處理,可使用現成的 FileListBox 元件並將元件之Directory屬性指向目標目錄位址,我們要找的文字檔就會出現在 FileListBox 中了,利用修改其 ItemIndex 屬性值,就可逐筆取出 FileListBox1->FileName 傳回之檔案名稱;FileListBox 元件還有個好處,那就是它還可利用 Mask 屬性過濾副檔名,我們可以要求只列出某一目錄中副檔名為 *.TXT (可同時選擇多種不同副檔名)的檔案;但如果程式是一個無 Form 的不可視程式,或程式的 Form 中沒有地方放這個 FileListBox 元件, 而我們卻需要其「列出檔案清單」的功能,在背景做一些檔案處理的動作(如一直偵測某一目錄發現收到文字檔後 , 做轉資料庫的動作) 但畫面上卻不需要上插入 FileListBox 這個元件,那該如何做呢? 1.以程式碼建立一個 TstringList 物件(非可視物件) 2.利用 FindFirst() 函式收集檔案名稱,放入這個 TstringList 物件中 3.TstringList 物件的使用方法類似 ListBox 或 FileListBox 元件 4.用完要自行釋放這個 TstringList 物件 void __fastcall TForm1::Button1Click(TObject *Sender) { String Dir; String FileName; TSearchRec SearchRec; int iAttributes = 0; iAttributes |= faAnyFile * true; //偵測 c:\ 底下有無 *.tot 這個類型的檔案 //有的話則收集到 TempList 這個區域物件中 , 用完即釋放掉 Dir=("C:\\"); TStringList *TempList = new TStringList; // declare the list try { //use the string list TempList->Clear(); //搜集 *.tot if (FindFirst(Dir "*.tot", iAttributes, SearchRec) == 0) { do { if ((SearchRec.Attr & iAttributes) == SearchRec.Attr) { TempList->Add(SearchRec.Name); } } while (FindNext(SearchRec) == 0); FindClose(SearchRec); } for ( int i = 0; i <= (TempList->Count-1); i ) { FileName=(Dir TempList->Strings[i]); //本迴圈可抓出所有符合條件之檔名至 FileName 變數中 //.......................... //可將後續運用程式碼寫在這裡 //.......................... } } __finally { delete TempList; // destroy the list object ProgressBar2->Position=100; Label2->Visible=true; } } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#10 引用回覆 回覆 發表時間:2002-06-13 10:02:33 IP:210.208.xxx.xxx 未訂閱
心得分享 : 利用迴圈變更50個TLabel property for (int i = 1; i <= 50; i ) { ((TLabel *)FindComponent("Label" IntToStr(i)))->Caption=""; } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#11 引用回覆 回覆 發表時間:2002-06-13 10:03:37 IP:210.208.xxx.xxx 未訂閱
心得分享 : PACK 一個 Paradox Table //--------------------------------------------------------------------------- // <<使用方法>> if (ParadoxPack("AbcPSV","TOT")) ShowMessage("資料庫 TOT ...PACK 完成"); // <<注意事項>> // 1. 在呼叫 ParadoxPack 前 , 連到 TOT 的 TTable 元件 Active 必須是 false // 2. 第一個傳入參數一定要用資料庫別名 , 不能用路徑 // (如 ParadoxPack("C:\\AbcPSV\\DB","TOT")) 是錯誤的 ) //--------------------------------------------------------------------------- bool _ParadoxPack(char *Alias, char *TblName) { hDBIDb hDb ; DBIResult dbResult ; CRTblDesc TblDesc ; bool r=false; //Initialize the BDE DbiInit(null); //Open a Database DbiOpenDatabase(Alias,null,dbiREADONLY,dbiOPENSHARED,null,0,null,null,hDb); memset((void *) &TblDesc, 0, sizeof(CRTblDesc)); lstrcpy(TblDesc.szTblName, TblName); lstrcpy(TblDesc.szTblType, szPARADOX); TblDesc.bPack = true; dbResult = DbiDoRestructure(hDb, 1, &TblDesc, null, null,null, false); if(dbResult == DBIERR_NONE) r=true; //Close Database DbiCloseDatabase(hDb); return r; } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#12 引用回覆 回覆 發表時間:2002-06-13 10:04:23 IP:210.208.xxx.xxx 未訂閱
心得分享 : 以程式碼建立 Paradox Table 下面的程式碼會在您事先設好的 "My_Alias" BDE 別名路徑下 自動建立一個名叫 Plu.db 的資料表實體 (若名稱中加入日期流水號 , 每執行一次就會產生一個 Table) 欄位有字串及整數型態 , 並以 PLU_NO 欄位當 Primery Key void __fastcall TForm1::Button1Click(TObject *Sender) { TTable *NewTable = new TTable(Form1); NewTable->Active = false; NewTable->TableType = ttParadox; //自行更改 NewTable->DatabaseName = "My_Alias"; //自行更改 NewTable->TableName = "Plu.db"; //自行更改 NewTable->FieldDefs->Clear(); NewTable->FieldDefs->Add("PLU_NO",ftString,16,true); NewTable->FieldDefs->Add("TEN_CODE",ftString,4,false); NewTable->FieldDefs->Add("DEPT_NO",ftString,8,true); NewTable->FieldDefs->Add("PACK_AMT",ftInteger,0,true); NewTable->FieldDefs->Add("PACK_QTY",ftInteger,0,true); NewTable->FieldDefs->Add("UNIT_PRICE",ftInteger,0,true); NewTable->FieldDefs->Add("SALE1",ftInteger,0,true); NewTable->FieldDefs->Add("SALE2",ftInteger,0,true); NewTable->FieldDefs->Add("SALE3",ftInteger,0,true); //設定以 PLU_NO 欄位當 Primery Key NewTable->IndexDefs->Clear(); NewTable->IndexDefs->Add("","PLU_NO", TIndexOptions() << ixPrimary << ixUnique); NewTable->CreateTable(); NewTable->Free(); } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#13 引用回覆 回覆 發表時間:2002-06-13 10:05:16 IP:210.208.xxx.xxx 未訂閱
心得分享 : 列出目前桌面上所有視窗代碼 主 Form 上放一個 Timer1 , ListBox1 , CheckBox1(其 Caption 設為"只列出可視視窗") , 然後在 OnTimer 中加入下列程式碼 , 就可在 ListBox1 列出目前桌面上所有視窗的 Caption , 類別名稱 , Hanlde 代碼 除錯監控時很好用 void __fastcall TForm1::Timer1Timer(TObject *Sender) { int olditemindex=ListBox1->ItemIndex; ListBox1->Items->Clear(); HANDLE hCurrentWindow = GetWindow(Handle, GW_HWNDFIRST); char szText[254]; char className[254]; //GetClassName(hWnd,className,95); //GetWindowText(hWnd,title,110); String t; String c; while (hCurrentWindow != 0) { if ((GetWindowText(hCurrentWindow, szText, 255) > 0) && (IsWindowVisible(hCurrentWindow) || !CheckBox1->Checked)) { GetClassName(hCurrentWindow,className,255); t=szText; c=className; ListBox1->Items->Add(t " --> " c " ; Handle = " IntToStr((int)hCurrentWindow)); } hCurrentWindow = GetWindow(hCurrentWindow, GW_HWNDNEXT); } if (olditemindex<=(ListBox1->Items->Count)) ListBox1->ItemIndex=olditemindex; } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#14 引用回覆 回覆 發表時間:2002-06-13 10:07:33 IP:210.208.xxx.xxx 未訂閱
心得分享 : 如何使用TChart? 首先先在 Form 中放一個 Tchar 物件 什麼屬性都不要設 再放一個 button , 然後在 Button 的 OnClick 事件中加入下段程式碼 你就知道大概如何手動控制 Tchar 了 , 其他請舉一反三 void __fastcall TForm1::Button1Click(TObject *Sender) { Chart1->RemoveAllSeries(); // 清除Chart1上所有舊 Series Chart1->View3D=false; // 不要 3D 立體 Chart1->Legend->Visible=false; // 不秀圖例說明 // 設定此 char Title 名稱 //(是 StringList 不是 String 所以不能用 Chart1->Title->Text="xxx" ...) Chart1->Title->Text->Clear(); Chart1->Title->Text->Add("test"); // 動態宣告一個 THorizBarSeries 型態的 Series // 其它的 Series 型態有哪些 請看 TChartSeries 之 Help Series1=new THorizBarSeries(Chart1) ; Series1->ParentChart=Chart1; Series1->Marks->Visible=true; // 設定要提示說明 Series1->Marks->Style=smsValue; // 提示說明內容為 Label //(註) Series1->Marks->Style 內容請參考 TSeriesMarksStyle 之 Help Series1->SeriesColor=clBlue; // 設線條1為藍色,不設則自動給色 // 輸入假資料 int V; // Value String L; //Label for (int i = 1; i <= 8; i ) { V=i; // Series 值 L="V" IntToStr(i); // 軸名稱 Series1->Add( V , L , clTeeColor ); } } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#15 引用回覆 回覆 發表時間:2002-06-17 16:18:24 IP:210.208.xxx.xxx 未訂閱
心得分享 : 拆解字串段的自寫函式 _StringSegment()    String A="ABCD,EFG,H,IJK,LM"; String B=_StringSegment(A , "," , 3); // 以逗號來做分隔 , 求第 3 段字串    所以 B 就等於 "H" 也可不用逗號做分隔 , 用您指定的其他符號做分隔    String __fastcall TForm1::_StringSegment(AnsiString Str , AnsiString Comma , int Seg) { if ((Str=="") || (Seg<1)) return ""; String C=Comma; if (C=="") C=","; String s=Str; String sTmp; String r; int iPosComma; TStringList *TempList = new TStringList; // declare the list TempList->Clear(); while (s.Pos(C)>0) { iPosComma = s.Pos(C); // locate commas sTmp = s.SubString(1,iPosComma - 1); // copy item to tmp string TempList->Add(sTmp); // add to list s = s.SubString(iPosComma 1,s.Length()); // delete item from string } // trap for trailing filename if (s.Length()!=0) TempList->Add(s); if (Seg > TempList->Count) r=""; else r= TempList->Strings[Seg-1]; delete TempList; // destroy the list object return r; } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#16 引用回覆 回覆 發表時間:2002-06-17 17:17:44 IP:210.208.xxx.xxx 未訂閱
心得分享 : 序列埠讀寫 在 Windows 作業系統下(尤其是 NT & Win 2000),作業系統已禁止我們直接對硬體I/O Port做讀寫的動作,所以要控制 RS-232 Port,我們可利用檔案裝置的觀念來看待 RS-232 Port;這觀念源自 Unix 作業系統,所有的 I/O 週邊都可把它看成是一個檔案代碼,如 Com1 的檔案代號就是 “Com1”, Com2 的檔案代號就是 “Com2”,所以利用處理文字檔的觀念,我們就可以讀寫 RS-232 Port 序列埠的資料寫入 以程式碼的撰寫容易程度來看,RS-232 Port的「寫入」比「讀出」簡單,基本上有下列三種寫入方式,讀者可以看得出來,只要會處理文字檔的讀寫就看得懂本程式在做什麼,程式範例是假設有要秀出一字串到一週邊(如收銀機的「客戶抬頭顯示器」),除送出欲秀出的字串內容資料外,前導命令碼要加在資料之前,以便通知該週邊秀出方式的命令,本範例之週邊(假設接在 Com2)前導控制碼及資料格式假設為「Esc ’q’ ’A’」 「欲秀出的字串」 「Return Code」,那我們來看看如何送出資料: ■ 使用傳統 C 的 文字檔處理語法 //------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { String m="fprintf 測試"; //欲秀出的字串 FILE *outfile; if ((outfile=fopen("COM2","w"))== NULL) return; fprintf(outfile,"\x1bqA%s\r",m); fclose(outfile); } //------------------------------------------------------- ■ 使用標準 C 的 文字檔串流(stream)處理語法 //------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { String m="ofstream 測試"; //欲秀出的字串 String s="\x1bqA" m "\r"; char *s1=s.c_str(); ofstream outfile("COM2"); if (!outfile) return ; outfile << s1; outfile.close(); } //------------------------------------------------------- ■ 使用 Win32 API 之 CreateFile 函式(較複雜,但可控制BaudRate、ByteSize、Parity、StopBits等參數)很多市售的 RS-232 元件內部也都是用這種方式處理 RS-232資料輸出 //------------------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { String m="CreateFile 測試"; //欲秀出的字串 HANDLE hCom = CreateFile("COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if(hCom) { DCB dcb; ZeroMemory(&dcb, sizeof(dcb)); dcb.DCBlength = sizeof(dcb); dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; // NOPARITY and friends are dcb.StopBits = ONESTOPBIT; // #defined in windows.h // Set the port properties and write the string out the port. if(SetCommState(hCom,&dcb)) { DWORD ByteCount; String s="\x1bqA" m "\r"; char *msg=s.c_str(); WriteFile(hCom,msg, strlen(msg), &ByteCount,NULL); } CloseHandle(hCom); } } //------------------------------------------------------- 註 : 以上只是本人研究心得 , 若要深入研究 BCB 現也已有 RS-232 控制之參考書籍可參考之 << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#17 引用回覆 回覆 發表時間:2002-06-17 17:18:41 IP:210.208.xxx.xxx 未訂閱
心得分享 : 序列埠讀寫 基本資料讀出方式 //--------------------------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma link "ComPort" #pragma resource "*.dfm" TForm1 *Form1; HANDLE ComPortHandle[10]; bool ComPortActive[10]; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::FormActivate(TObject *Sender) { _ComPortOpen(1); //開啟 com port } //--------------------------------------------------------------------------- void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { _ComPortClose(1); //關閉 com port } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { ShowMessage(_ComPortRead(1)); //測試讀出值 (如刷條碼機之讀出值) } //--------------------------------------------------------------------------- // 以下為副程式區 //--------------------------------------------------------------------------- bool __fastcall TForm1::_ComPortOpen(int ComPortID) { bool r=false; if (ComPortActive[ComPortID]) return r; String ComPortName="COM" IntToStr(ComPortID); ComPortHandle[ComPortID] = CreateFile( ComPortName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if(ComPortHandle[ComPortID]) { DCB dcb; ZeroMemory(&dcb, sizeof(dcb)); dcb.DCBlength = sizeof(dcb); dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; // NOPARITY and friends are dcb.StopBits = ONESTOPBIT; // #defined in windows.h // Set the port properties and write the string out the port. if(SetCommState(ComPortHandle[ComPortID],&dcb)) { r=true; ComPortActive[ComPortID]=true; } } return r; } //--------------------------------------------------------------------------- bool __fastcall TForm1::_ComPortClose(int ComPortID) { bool r=false; if (CloseHandle(ComPortHandle[ComPortID])) { r=true; ComPortActive[ComPortID]=false; } return r; } //--------------------------------------------------------------------------- String __fastcall TForm1::_ComPortRead(int ComPortID) { Char Buff[128]; int DataSize; DWORD ByteCount; //底下這一段只是為了找出 DataSize 值 DWORD ErrorFlags; _COMSTAT ComPortStat; ClearCommError(ComPortHandle[ComPortID],&ErrorFlags,&ComPortStat); /* // lngSize 是有傳入要求指定的指定長度時 If ComPortStat.cbInQue > DataSize Then DataSize = ComPortStat.cbInQue Else DataSize = lngSize End If */ DataSize=ComPortStat.cbInQue; if (DataSize<=0) DataSize=0; //ShowMessage(IntToStr(DataSize)); ReadFile(ComPortHandle[ComPortID],Buff, DataSize,&ByteCount,NULL); return String(Buff); } //--------------------------------------------------------------------------- void __fastcall TForm1::_ComPortClear(int ComPortID) { PurgeComm(ComPortHandle[ComPortID],PURGE_RXCLEAR); } //--------------------------------------------------------------------------- 註 : 以上只是本人研究心得 , 若要深入研究 BCB 現也已有 RS-232 控制之參考書籍可參考之 << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#18 引用回覆 回覆 發表時間:2002-06-17 17:19:45 IP:210.208.xxx.xxx 未訂閱
心得分享 : 鍵盤掃描碼處理 在鍵盤按鍵被押下時,第一個會產生的是所謂的掃描碼(Scan Code),也就是硬體直接傳回的按鍵代碼,然後作業系統會把它轉換為虛擬碼(Virtual Key ),也就是用一些有意義的符號或代碼來代替之,以便應用程式能夠處理;例如一般鍵盤上都有兩個「Enter」鍵(第二個在鍵盤最右下角),它們在硬體上的掃描碼(Scan Code)一定不同,但虛擬碼(Virtual Key)都是 VK_RETURN (CharCode=13),一般在高階的程式應用中,大部分都只會針對虛擬碼(Virtual Key)做處理,這樣做的好處是可以將硬體隔離於應用程式之外。 在一個 Windows 程式裡,若有某鍵被按下,會有兩個訊息送到程式本身,一個稱之為 WM_KEYDOWN,幾乎鍵盤任何按鍵都會送出;而另一個訊息稱之為 WM_CHAR,只有在數值或字母按鍵被按下時才會送出;換句話說,若是按下了 「A」鍵,會同時得到 WM_KEYDOWN 及 WM_CHAR 兩個訊息,按下了 「F1」卻只會得到 WM_KEYDOWN 訊息。 OnKeyDown事件處理程序對應於 WM_KeyDown 訊息, OnKeyPress事件處理程序則對應於 WM_CHAR訊息 (OnKeyPress事件乾脆改名叫 OnChar 事件比較容易讓人理解),所以 OnKeyDown事件處理程序傳入的 key 變數是 WORD 型態,而 OnKeyPress 事件處理程序傳入的 key 變數是 char 型態。 由上面的介紹可以知道,要偵測所有的按鍵(包含文數字及所有功能鍵)要用OnKeyDown事件處理程序來做,偵測範圍會比較廣,但有個鍵例外,那就是「Tab」鍵;按下 Tab 鍵會轉移元件焦點(Focus),且不會觸發 OnKeyDown及OnKeyPress 事件;另外,OnKeyDown事件也只能偵測到鍵盤虛擬碼(Virtual Key)層級,它雖幾乎可偵測所有的按鍵,但它無法分辨按下的是左邊的 Shift或是右邊的Shift?或按下的是兩個 Enter 鍵中的哪一個?所以,要能分辨所有按鍵個體,必須能夠偵測到鍵盤的硬體掃描碼(Scan Code)層級。 ■ 偵測鍵盤上所有按鍵個體 在 Form 中有個 OnShortCut 事件處理程序,抓取其傳入的 Msg 變數就可以完全偵測所有按鍵個體,更令人興奮的是它連 Tab 鍵都能抓得到,原因是從 Msg 變數中可以直接抓出鍵盤的硬體掃描碼(Scan Code);如欲抓取掃描碼可讀出 Msg.KeyData值,抓取虛擬碼(Virtual Key) 則可讀出 Msg.CharCode 值。以下整理出跟按鍵偵測有關的事件處理程序及其偵測範圍: OnKeyPress 只能抓到數值或字母按鍵及 Esc鍵、空白鍵,但不含功能鍵(F1-F12) OnKeyDown 能抓到所有的鍵(除 Tab 鍵)但不能分辨「對稱鍵」的不同 OnShortCut 能抓到所有的鍵(含 Tab 鍵)且能分辨「對稱鍵」的不同 註:「對稱鍵」指的是左右兩邊皆有的鍵,如「Enter」、「Shift」、「Ctrl」、「Alt」等等 << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#19 引用回覆 回覆 發表時間:2002-06-17 17:23:45 IP:210.208.xxx.xxx 未訂閱
心得分享 : 如何將 Ping 功能寫成一個函式 使用方法 : bool Ping(String ip_address); 用法就這麼簡單 , 相關的函式用法 翻 MSDN 要傳入一大堆參數指標 , 頭都昏了 , 因為我又不是要用來寫專業 的 Ping 程式 , 我大部份的用途都是在 Ping 區網內另一台主機在不在 我只要知 回傳值是 True 或 False 即可 , 根本不須知 ping 之 reply time 是多少 .... 不過這個函式有個缺點 , 若該 ip 不能反查出主機名稱 (就是網路芳鄰看到的名稱啦) 則就算 ip 存在 , 傳回值仍是 False 不過針對大部份的 Windows 系統電腦應都沒問題 不能反查出主機名稱的 ip 有 1. 區網內的路由器 - Router (ip 大部份為 x.x.x.254) 2. Linux 主機 ############################################################################### # 原始參考資料 http://rad.bytesandmore.de/index.htm?http://rad.bytesandmore.de/cpp/snipp/sc08012.php # bruce0211@yahoo.com.tw 改寫 (2002/06/05) # # bool Ping(AnsiString ip_address) 函式 # # 使用方法 : # # if (Ping("tfm002")) # ShowMessage("true"); # else # ShowMessage("false"); # ############################################################################### ############################################################################### # ping.cpp 由此開始 ############################################################################### #include #pragma hdrstop #include "Ping.h" //--------------------------------------------------------------------------- // ip_address 可填入 ip 位置或主機名稱 // 但本函式有個 bug , 若該 ip 不能解析出主機名稱者 // 如 Router 或某些 Linux 主機 , 則回傳值仍是失敗 >_< //--------------------------------------------------------------------------- bool Ping(AnsiString ip_address) { // ICMP.DLL laden: HANDLE hIcmp = LoadLibrary("ICMP.DLL"); if(hIcmp == NULL) { return false; } // Zeiger auf die Funktionen besorgen: PF_CMPCREATEFILE pfIcmpCreateFile = (PF_CMPCREATEFILE) GetProcAddress(hIcmp, "IcmpCreateFile"); PF_ICMPCLOSEHANDLE pfIcmpCloseHandle = (PF_ICMPCLOSEHANDLE) GetProcAddress(hIcmp,"IcmpCloseHandle"); PF_ICMPSENDECHO pfIcmpSendEcho = (PF_ICMPSENDECHO) GetProcAddress(hIcmp,"IcmpSendEcho"); // Funktionszeiger prufen: if (pfIcmpCreateFile == NULL || pfIcmpCloseHandle == NULL || pfIcmpSendEcho == NULL) { FreeLibrary(hIcmp); return false; } // WinSock initialisieren WSADATA wsaData; int ilRetVal = WSAStartup(0x0101, &wsaData ); if(ilRetVal) { WSACleanup(); FreeLibrary(hIcmp); return false; } // Check WinSock version if(0x0101 != wsaData.wVersion) { WSACleanup(); FreeLibrary(hIcmp); return false; } // Prufen, ob es sich bei der Zieladresse um IP-Adresse handelt und // ggf. den die Adresse zum Namen ermitteln: struct in_addr iaDest; // Struktur fur die Internet-Adresse iaDest.s_addr = inet_addr(ip_address.c_str()); LPHOSTENT pHost; // Zeiger auf die Host Entry Struktur if (iaDest.s_addr == INADDR_NONE) pHost = gethostbyname(ip_address.c_str()); else pHost = gethostbyaddr((BYTE *)&iaDest, sizeof(struct in_addr), AF_INET); // 若該 ip 不能解析出主機名稱者 // 如 Router 或某些 Linux 主機 , 則回傳值仍是失敗 , 問題應出在這裡 // 但又不能拿掉 .... if(pHost == NULL) { WSACleanup(); FreeLibrary(hIcmp); return false; } // IP-Adresse kopieren DWORD* pAddress = (DWORD*)(*pHost->h_addr_list); // ICMP Echo Request Handle besorgen: HANDLE hIcmpFile = pfIcmpCreateFile(); ICMPECHO icmpEcho; // ICMP-Echo Antwortbuffer IPINFO ipInfo; // IP-Optionenstruktur int ilTimeSum = 0; // Summe der Round Trip Time-Daten int ilCount = 0; // Anzahl der Round Trip Time-Daten for (int ilPingNo = 0; ilPingNo < 3; ilPingNo ) { // Default-Werte festlegen: ::ZeroMemory(&ipInfo, sizeof(ipInfo)); ipInfo.bTimeToLive = 255; // ICMP Echo anfordern: pfIcmpSendEcho(hIcmpFile, // Handle von IcmpCreateFile() *pAddress, // Ziel-IP Addresse NULL, // Zeiger auf den Buffer mit den // zu sendenden Daten 0, // Buffergrosse in Bytes &ipInfo, // Request-Optionen &icmpEcho, // Antwort-Buffer sizeof(struct tagICMPECHO), // Buffergrosse 5000); // Max. Wartezeit in Millisekunden // Ergebnisse anzeigen: iaDest.s_addr = icmpEcho.dwSource; // falls Fehler aufgetreten: if(icmpEcho.dwStatus) { break; } ilTimeSum = icmpEcho.dwRTTime; ilCount ; if(ilPingNo < 2) Sleep(200); } // Echo-Request File Handle schliessen: pfIcmpCloseHandle(hIcmpFile); // ICMP.DLL freigeben: FreeLibrary(hIcmp); // Winsock schliessen: WSACleanup(); // Den Mittelwert aller Round Trip Times zuruckgeben: //return ilRetVal = ilCount ? ilTimeSum/ilCount : -1; if (ilRetVal = ilCount) return true; else return false; } ############################################################################### # ping.h 由此開始 ############################################################################### //---------------------------------------------------------------------------- #ifndef PingHPP #define PingHPP //--------------------------------------------------------------------------- #include // Definition der IP-Optionenstruktur typedef struct tagIPINFO { BYTE bTimeToLive; // Time To Live BYTE bTypeOfService; // Type Of Service BYTE bIpFlags; // IP-Flags BYTE OptSize; // Grosse der Options Data Buffers BYTE FAR *Options; // Zeiger auf Options Data Buffer } IPINFO, *PIPINFO; // Definition der ICMP-Echo Antwortstruktur typedef struct tagICMPECHO { DWORD dwSource; // Zieladresse DWORD dwStatus; // IP-Status DWORD dwRTTime; // Round Trip Time in Millisekunden WORD wDataSize; // Grosse des Antwort-Buffers WORD wReserved; void FAR *pData; // Zeiger auf die Antwort-Daten IPINFO ipInfo; // Antwort-Optionen } ICMPECHO, *PICMPECHO; // Zeiger auf die Funktionen aus der ICMP.DLL deklarieren: typedef HANDLE (WINAPI *PF_CMPCREATEFILE)(VOID); typedef BOOL (WINAPI *PF_ICMPCLOSEHANDLE)(HANDLE); typedef DWORD (WINAPI *PF_ICMPSENDECHO)(HANDLE,DWORD,LPVOID,WORD, PIPINFO,LPVOID,DWORD,DWORD); //--------------------------------------------------------------------------- bool Ping(AnsiString ip_address); //--------------------------------------------------------------------------- #endif 另外 , 標準 WinInet 32 API 也有個 InternetCheckConnection() 可查某一主機是否連線 , 測試碼如下 但我試過 , 目標 url 之主機必須啟動 http 或 ftp service 才會傳回 true 所以功能用途比前面找到的方法更狹隘 .... char url[]="http://172.30.96.200"; bool status = InternetCheckConnection(url,FLAG_ICC_FORCE_CONNECTION, 0 ); if (status) ShowMessage("true"); else ShowMessage("false"); << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#20 引用回覆 回覆 發表時間:2002-06-18 10:38:24 IP:210.208.xxx.xxx 未訂閱
心得分享 : Set (集合型態) 的使用方法 Set (集合型態) 是 Object Pascal 特有型態 , 在 BCB 中是用一個名為 Set 的類別來模擬 Set 型態 , 由於在 VCL 元件中有許多屬性都會利用到 Set 型態 , 所以在此介紹其使用方法 ■ 加入集合元素 語法 : 集合型態原型 << 元素一 [<< 元素二] [<< 元素二] [...] 舉例 : 1. 在 MessageDlg 談出示對話盒中 , 若需要有兩個按鈕 "YES" & "NO" 則使用 TMsgDlgButtons() << mbYes << mbNo 完整範例如 if (MessageDlg("Delete Record?", mtConfirmation, TMsgDlgButtons() << mbYes << mbNo, 0) == mrYes) DataModule3->Table1->Delete(); 2. 我們要將某一元件之字型設為粗體 , 使用物件檢視器(Object Inspector)來設定很簡單 , 只要在 Font 屬性中展開 Style 次屬性 , 再將 fsBold 設為 true 即可 但在程式碼中如何做到以上動作呢 ? (以設定 Edit1 字型為粗體為例) Edit1->Font->Style = TFontStyles() << fsBold; 若要同時設定其字型為粗體加底線 : Edit1->Font->Style = TFontStyles() << fsBold << fsUnderline; ■ 判斷是否含有集合元素 □ 判斷 Edit1 目前是否為粗體字型 if (Edit1->Font->Style.Contains(fsBold)) ShowMessage("true"); else ShowMessage("false"); □ 判斷 KeyDown 事件中是否帶有 Shift void __fastcall TFm_POS1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if (Shift.Contains(ssShift)) { ...... ...... } } □ 在做 StringGrid 變色特效時 , 判斷當在固定列或選擇列(藍色光棒列)則不于變色 void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { // 如果是固定列或選擇列(藍色光棒列)則不于變色 if (State.Contains(gdFixed) || State.Contains(gdSelected)) return; if (ARow==2) StringGrid1->Canvas->Font->Color=clRed; //字型變色 if (ACol==3) StringGrid1->Canvas->Brush->Color=clLime ; // 垂直 cell 背景變色 if (ARow==3) StringGrid1->Canvas->Brush->Color=clYellow; // 水平 cell 背景變色 // output the text StringGrid1->Canvas->TextRect (Rect, Rect.Left, Rect.Top,StringGrid1->Cells[ACol][ARow]); } □ 註 : 根據測試 Contains() 中好像無法同時塞入兩個集合元素值 ... ■ 移除集合元素 同 "加入集合元素" 語法 , 但只要將 "<<" 改為 ">>" 即可 if (Edit1->Font->Style.Contains(fsBold)) Edit1->Font->Style = TFontStyles() >> fsBold; << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#21 引用回覆 回覆 發表時間:2002-06-19 00:21:57 IP:163.32.xxx.xxx 未訂閱
心得分享 : 事件共用 以設計一個小算盤為例 首先插入一個 Panel1 將其 Caption 清空 再插入十個 Button , 其 Caption 分別為 "0" - "9" (當作計算機按鍵用) 然後在 Button1 之 OnClick 事件中寫入下列程式碼 void __fastcall TForm1::Button1Click(TObject *Sender) { Panel1->Caption=Panel1->Caption ((TButton *)Sender)->Caption; } 其他九個按鍵之 OnClick 都指到 Button1 的 OnClick (共用) 如此十個按鍵在執行時都可以按出數字來 不用分別為十個 Button 寫十個 OnClick 事件程式碼 ...... << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#22 引用回覆 回覆 發表時間:2002-06-20 13:07:17 IP:210.208.xxx.xxx 未訂閱
心得分享 : 取得 IP 位址之函式 , String 型態 String __stdcall GetHostIpAddress(void) { struct hostent *thisHost; struct in_addr in; char MyName[80]; char *ptr,*hostname; WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested=MAKEWORD(2,0); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0)return ""; if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=0) { WSACleanup(); return ""; } if (gethostname(MyName,80)==SOCKET_ERROR) return ""; if (!(thisHost=gethostbyname(MyName))) return ""; hostname=thisHost->h_name; memset((void*)&in,sizeof(in),0); in.s_addr=*((unsigned long*)thisHost->h_addr_list[0]); if (!(ptr=inet_ntoa(in))) return ""; WSACleanup(); return (String)ptr; } << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >> 發表人 - bruce0211 於 2002/07/05 13:41:34
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#23 引用回覆 回覆 發表時間:2002-06-25 18:00:43 IP:210.208.xxx.xxx 未訂閱
心得分享 : 動態連結檔簡易設計說明    ■ 特別說明    1. 動態載入部分 , DLL函式引用在參考資料中都只提到局部引用( 即在一個 BUTTON CLICK 事件中介紹如何載入及引用 DLL函式然後當場釋放 DLL)我光把他改成全域引用(在 FORM CREATE 載入 , Form Close 才釋放 , 這才符合真實應用環境 ) 就測了好久 , 在 MSDN 中還用到 TYPEDEF 來定義空函式指標 , 我測的結果根本不需要 , 就已可做到全域引用 2. 靜態載入部份 , 光參考 DLL 標頭檔 (*.H) 的巨集修改 (為了同時能適用 IMPORT 跟 EXPORT) , 不知是我資質愚昧還是參考資料表達的不詳細, 後來才搞懂其意義 , 但我也發現 , 不使用 DLL 之 *.H 標頭檔 , 不但沒關係 , 程式反而更簡潔有力 , 所以我的測試範例中根本不使用 DLL 的 *.H 標頭檔  3. 也許有些只是我的個人偏見 , 但在遇到瓶頸前我會再繼續研究修改     ■ 使用樣板工具建立 DLL 雛形    □ 在 C++Builder 主選單中選擇「File」->「New…」出現下列樣板工具盒 □ 選擇 「DLL Wizard」出現對話選擇 □ 因為我們開發的 DLL 是要支援 C++Builder 本身程式用的,所以直接按「OK」即可,注意!有支援 VCL 的 DLL 只能給 C++Builder & Delphi 使用,若您的DLL不需用到 VCL 元件,而只有一些函數處理程序的話可將「Use VCL」勾勾拿掉,下一步其將自動產生DLL雛形程式碼;在這個空的DLL雛形程式碼內,筆者將自行加入三個自訂函式,以便測試這個 DLL 的功能,第一個函式傳入一個字元陣列然後用ShowMessage秀出這個字元陣列(函式本身沒有回傳值);第二個函式傳入兩個字元陣列,將這兩個字元陣列合併後回傳,回傳型態也是字元陣列;第三個函式傳入兩個整數,也是將這兩個整數相加後回傳,回傳型態是整數值;直接參考程式碼最明瞭,讀者應有能力看出哪些是系統產生的雛形樣板,哪些是作者加入的部分,這個 DLL完整程式碼如下:(專案名稱叫 MyDLL.bpr,程式碼名稱沿用系統內定之名稱 Unit1.pas) □ 本 DLL 提供三個自寫副程式 Showit() 秀一段訊息 , CharAdd() 做字串相加 , IntAdd() 做整數相加    MyDLL 專案之完整 Unit1.pas //-------------------------------------------------------    #include  #include #include #pragma hdrstop extern "C" __declspec(dllexport) void __stdcall Showit(char *message); extern "C" __declspec(dllexport) void __stdcall CharAdd(char *inchr1,char *inchr2 ,char *r); extern "C" __declspec(dllexport) int __stdcall IntAdd(int a,int b); #pragma argsused int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; } //------------------------------------------------------- void __stdcall Showit(char *message) { ShowMessage(message); } //------------------------------------------------------- void __stdcall CharAdd(char *inchr1,char *inchr2 ,char *r) { sprintf(r,"%s%s%c",inchr1,inchr2,'\0'); } //------------------------------------------------------- int __stdcall IntAdd(int a,int b) { return(a b); } //------------------------------------------------------- □ 程式說明 1.本 DLL原始碼 Unit.pas 不需有 Unit.h 標頭檔 2.本 DLL 編譯好後可同時適用於動態載入及靜態載入兩種呼叫方式 (下節介紹) 3.為了考慮共用性,函式命名方式加上 extern “C” 這個修飾詞來達到使用標準C的命名法,而不使用 C 的命名法,因為使用 C 的命名法會在編譯時在函式名稱後加上參數型態等註記,造成其他程式如 VC 、VB 無法使用的困擾 4.函式彙出或彙入之宣告,以32 位元系統之識別字 __declspec(dllexport) 及 __declspec(dllimport) 來取代舊 16 位元系統的識別字 _export 及 _import,因為舊 16 位元系統識別字在某些 C 編譯器下無法通過編譯 5.參數之指標堆疊以 __stdcall 型態為共通及最常用方式 6.在 DLL 中使用「String」參數型態非常麻煩跟無效率,所以建議都用字元陣列來處理字串問題 (如本例中第二個函式傳入兩個字元陣列,將這兩個字元陣列合併後回傳,其實就是用來代替字串相加的處理程序 ) 7.到C Builder之「Project」主選單將整個專案「Builde All Projets」,系統即自動產生 MyDLL.dll 及 MyDLL.lib ( 註:DLL 專案不能直接用來「RUN」) ■ 使用 DLL 檔 – 動態載入方式 □ 動態載入DLL之特點 1.DLL 內容若有修改或擴充,只要主程式所叫用到的函式本身之傳入傳出參數結構不變,重新編譯DLL即可,主執行檔 (EXE 檔)不用重新編譯 2.呼叫這個 DLL 的主執行檔之呼叫程序撰寫較麻煩,需由程式設計者自行引用或釋放 DLL 所佔用之記憶體 3.應用架構如下 (1) 先自行宣告會用到的空函式指標,以便載入DLL時套用DLL內原本之函式 (2) LoadLibrary(“MyDLL.dll”); //載入 DLL (3) GetProcAddress(DLL之Handle,”函式名稱”); //找出 DLL 內函式之位址指標 (4) FreeLibrary(DLL之Handle) ; // 釋放 DLL 4.若該 DLL 不是自己寫的,可用 C Builder 內附之 tdump.exe 工具查看該 DLL 內有哪些函式可被開放叫用 5.開始撰寫測試程式,畫面如下,目的為測試之前設計的三個函式功能 測試程式 PJ_3_2_2.exe 之 Unit 完整程式碼: //------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" //------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" //先自行宣告會用到的空函式指標,以便載入DLL時套用DLL內原本之函式 void(__stdcall *Showit)(char *message); void (__stdcall *CharAdd)(char *inchr1,char *inchr2 ,char *buff); int(__stdcall *IntAdd)(int a,int b); HINSTANCE DLLInst; TForm1 *Form1; //------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { //於 Form 建立時順便載入 DLL (動態載入) DLLInst=LoadLibrary("MYDLL.DLL"); (FARPROC&)Showit=GetProcAddress(DLLInst,"Showit"); (FARPROC&)CharAdd=GetProcAddress(DLLInst,"CharAdd"); (FARPROC&)IntAdd=GetProcAddress(DLLInst,"IntAdd"); // 如此,主程式內就有 Shoeit()、CharAdd()、IntAdd()三個 DLL函式可用了 } //------------------------------------------------------- void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { //於 Form 關閉時時釋放 DLL FreeLibrary(DLLInst); } //------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { //測試傳入字串陣列然後秀出 Showit("test"); } //------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { //測試傳入兩字串陣列然後回傳合併結果 char buff[256]; CharAdd("ab","cd",buff); ShowMessage(buff); } //------------------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { //測試傳入兩整數然後回傳相加結果 ShowMessage(IntAdd(1,2)); } //------------------------------------------------------- ■ 使用 DLL 檔 – 靜態載入方式 □ 靜態載入DLL之特點 1.DLL 內容在修改或擴充時,就算主程式所叫用到的DLL函式本身之傳入傳出參數結構不變,但DLL內函式位置順序有變,不只是DLL檔,連整個主執行檔 (EXE 檔)都需重新編譯 2.主執行檔 (EXE 檔)編譯時需在專案檔(.bpr檔)中加入LIB 檔 (MyDLL.lib,當在製作MyDLL.dll 時,C Builder 已同時產生),不像動態載入之主執行檔在編譯時根本不需DLL相關檔案存在 (只要執行時DLL檔存在就好了) 3.呼叫這個 DLL 的主執行檔之呼叫程序撰寫較簡單,系統自動管理或釋放 DLL 所佔用之記憶體 4.若主執行(EXE)檔編譯時要引用 MyDLL.h 標頭檔,此標頭檔還要大費周章經過一番修改(可參考其他 C Builder 相關書籍),本書不使用引用 MyDLL.h 標頭檔的方法來編譯主程式,所以不用 MyDLL.h 標頭檔,減低應用複雜性 5.開始撰寫測試程式,同前例但使用靜態載入語法,目的同樣為測試之前設計的三個函式功能,您可發覺程式碼較動態載入方式簡單 測試程式 PJ_3_2_3.exe 之 Unit 完整程式碼: //------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" //------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //這個地方宣告方式跟動態載入不同 extern "C" __declspec(dllimport) void __stdcall Showit(char *message); extern "C" __declspec(dllimport) void __stdcall CharAdd(char *inchr1,char *inchr2 ,char *r); extern "C" __declspec(dllimport) int __stdcall IntAdd(int a,int b); //以上三行也可拿掉並放在獨立之標頭檔內如 test.h 然後在本 //程式碼開頭 include "test.h" //------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { //測試傳入字串陣列然後秀出 Showit("test"); } //------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { //測試傳入兩字串陣列然後回傳合併結果 char buff[256]; CharAdd("ab","cd",buff); ShowMessage(buff); } //------------------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { //測試傳入兩整數然後回傳相加結果 ShowMessage(IntAdd(1,2)); } //------------------------------------------------------- 注意使用靜態載入方式,主執行檔 (EXE 檔)編譯時尚需在專案檔(.bpr檔)中加入LIB 檔 (MyDLL.lib) << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#24 引用回覆 回覆 發表時間:2002-06-25 18:36:27 IP:210.208.xxx.xxx 未訂閱
心得分享 : 多執行緒測試    一般書上或找到的範例 , 皆將 Thread 寫在另一個 Unit (如 Unit2)中 本範例將整個程式寫在一起 , 不知是否正確 , 但 Run 起來沒問題    ■ 測試目的     利用一個無窮迴圈來顯示目前時間 , 但卻不會影響 User 在 Memo 中編輯資料 ; 若此無窮迴圈不是放在 Thread 中 , 則整個 cpu 時間會被其佔用 ,  程式 Hold 住根本無法在 Memo 中編輯資料 ....    1.建一個主 Form 2.放一個 Button1 ,其 Caption 設為 "開始 Show Time Thread" 3.放一個 Button2 ,其 Caption 設為 "結束 Show Time Thread" 4.再放一個 Edit1 & 一個 Memo1    完整程式碼如下 :    //------------------------------------------------------- #ifndef Unit1H #define Unit1H //------------------------------------------------------- #include  #include #include #include <Forms.hpp> //------------------------------------------------------- class TMyThread : public TThread { public: __fastcall TMyThread(void); private: void __fastcall Execute(void); void __fastcall ShowTime(void); }; //------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TButton *Button1; TButton *Button2; TEdit *Edit1; TMemo *Memo1; void __fastcall FormCreate(TObject *Sender); void __fastcall FormClose(TObject *Sender, TCloseAction &Action); void __fastcall Button1Click(TObject *Sender); void __fastcall Button2Click(TObject *Sender); private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //------------------------------------------------------- extern PACKAGE TForm1 *Form1; //------------------------------------------------------- #endif //------------------------------------------------------- #include #pragma hdrstop #include "Unit1.h" //------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; TMyThread *MyThread; //------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { // } //------------------------------------------------------- void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { if (MyThread) MyThread->Terminate(); } //======================================================= // TMyThread 程序實作區段開始 //======================================================= __fastcall TMyThread::TMyThread(void):TThread(true) { Resume(); } //------------------------------------------------------- void __fastcall TMyThread::Execute(void) { FreeOnTerminate=true; while (!Terminated) { // Methods and properties of objects in VCL can only be // used in a method called using Synchronize() Synchronize(ShowTime); // 要改變 VCL 物件內容必須透過 Synchronize 呼叫 // 不能把 Form1->Edit1->Text=TimeToStr(Now()) 寫在這裡 Sleep(1000); } } //------------------------------------------------------- void __fastcall TMyThread::ShowTime(void) { //本執行緒真正的動作寫在此 Form1->Edit1->Text=TimeToStr(Now()); } //======================================================= // TMyThread 程序實作區段結束 //======================================================= void __fastcall TForm1::Button1Click(TObject *Sender) { MyThread = new TMyThread; } //------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { MyThread->Terminate(); } //------------------------------------------------------- << BCB 源碼任務工作室 http://home.kimo.com.tw/bruce0211/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#25 引用回覆 回覆 發表時間:2002-07-20 14:45:55 IP:210.208.xxx.xxx 未訂閱
心得分享 : 公司統一編號檢查函式 bool __fastcall TForm1::Chk_unify(String d) { bool r=false; String temp; int x,y; if (d.Length()!=8) return r; String A[10]; String A7=""; String Key="12121241"; //統編檢查對應表 int sum1=0; int sum2=0; for (int i=1;i<=8;i ) { x=StrToInt(d.SubString(i,1)); y=StrToInt(Key.SubString(i,1)); temp=FormatFloat("00",x*y); A[i]=IntToStr(StrToInt(temp.SubString(1,1)) StrToInt(temp.SubString(2,1))); if (i==7 && A[i].Length()==2) { A7=A[i].SubString(2,1); A[i]=A[i].SubString(1,1); sum2=sum2 StrToInt(A7); } else { sum2=sum2 StrToInt(A[i]); } sum1=sum1 StrToInt(A[i]); } if ( ((sum1)==0) || ((sum2)==0) ) r=true; return r; } << BCB 源碼任務 http://home.kimo.com.tw/bruce0829/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#26 引用回覆 回覆 發表時間:2002-07-20 17:08:21 IP:210.208.xxx.xxx 未訂閱
心得分享 : 刪除整個目錄的函式 void __fastcall TForm1::_DelTree(String dir) { String d=dir; String FileName; TSearchRec SearchRec; if (!DirectoryExists(dir)) return; // 要 #include "FileCtrl.hpp" 才有這個函式 if (d.SubString(d.Length(),1)!="\\") d=d "\\"; if (FindFirst(d "*.*", faAnyFile, SearchRec) == 0) { do { if (SearchRec.Attr == faDirectory) { if ((SearchRec.Name!=".") && (SearchRec.Name!="..")) { _DelTree(d SearchRec.Name); //遞迴呼叫 RemoveDir(d SearchRec.Name); } } else { FileSetAttr(d SearchRec.Name,faArchive); DeleteFile(d SearchRec.Name); } } while (FindNext(SearchRec) == 0); FindClose(SearchRec); } RemoveDir(d); } << BCB 源碼任務 http://home.kimo.com.tw/bruce0829/ >>
天外來客
初階會員


發表:22
回覆:199
積分:44
註冊:2001-11-27

發送簡訊給我
#27 引用回覆 回覆 發表時間:2002-07-25 11:20:49 IP:210.208.xxx.xxx 未訂閱
心得分享 : 參數檔 (INI 檔) 處理    INI 檔應用的歷史從 Windows 3.1就已存在,雖然它也是文字檔的一種,但它利用「節區(Section)」及「識別字(KeyWord)」觀念,類似資料庫的索引,所以用來當作少量變數資料的儲存器非常適合,除變數管理容易外尚可做程式流程控制,市售套裝軟體有用 ini 檔來控制其軟體版本例子,其主執行檔可能只有一個,但依 INI 檔設定可開放其全功能版(豪華版)與非全功能版;INI檔案 Size 雖然有 64k的限制,但已足夠存放上百個變數資料;在 32 位元系統上,正統取代 INI 機能的方法為使用 Windows Regist,但為了儲存Windows 作業系統本身的資訊,Regist已夠龐大,如果我們每個應用程式又去 Regist內挖一塊空間當作自己儲藏變數或參數的地方,會讓 Regist變得更龐大更複雜,所以筆者還是建議每個程式都使用自己的INI檔來儲存自己的變數,況且 INI檔不會被淘汰,連 Linux Kylix 下都看得到其蹤影    ■ 應用實例    假設程式中需要紀錄目前發票號碼(invo_no)、交易序號(tran_no),而每次交易完都要將這兩個變數值加1,若程式從頭到尾都不關機,那沒有問題,程式會記錄目前的發票號碼及交易序號已排到幾號;但若程式中途有關機後,下次開機,程式怎知發票號碼及交易序號已排到幾號?所以除了將變數資料存於資料庫中外,更簡單的方法就是使用INI檔來記錄變數值,在程式重新啟動時再將變數值取回,所以該INI檔內容可能長得如下:    [Varible] invo_no=00000005 tran_no=3    其中,用中括號刮起來的Variable就是「節區(Section)」名稱(可自訂),而其內的 INVO_NO、TRAN_NO則稱為「識別字(KeyWord)」(可自訂);不可有相同名稱的兩個節區(Section),但不同的節區中可有相同名稱的識別字(KeyWord),取用時按節區(Section)索引分開取用,如下圖:    [Varible] invo_no=00000005 tran_no=3    [Varible_2] invo_no=00000007 tran_no=5    ■ 程式實例    接下來我們介紹使用 INI 檔來儲存或取回變數的方法,在程式中要使用 INI 檔有幾個步驟:    1.程式開頭必須 #include "inifiles.hpp" 2.程式中首先需建立一個 INI 物件以對應實際的 INI 檔,不用時再釋放這個 INI 物件,通常 INI 檔實 體與主執行檔存在同一路徑,甚至檔名也和主執行檔相同,但副檔名則為 *.ini 3.使用 INI 物件的某些方法(Method)來存取變數,取用的方法依變數型態有下列幾種方法(假設這個 INI 物件名為 MyIni) MyIni->ReadInteger(節區名,識別字,整數Default值); MyIni->ReadString(節區名,識別字,字串Default值); MyIni->ReadBool(節區名,識別字,布林Default值) 4.寫入的方法依變數型態也有下列幾種方法 MyIni->WriteInteger(節區名,識別字,欲寫入之整數變數值); MyIni->WriteString(節區名,識別字,欲寫入之字串變數值); MyIni->WriteBool(節區名,識別字,欲寫入之布林變數值)    以下為一完整使用INI程式碼的範例 //------------------------------------------------------- #include  #pragma hdrstop #include "Unit1.h" #include "inifiles.hpp" //要 include 這個東西才有 TIniFile 類別 //------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; TIniFile *MyIni; //------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { //建立 ini 物件及實體存放路徑 MyIni = new TIniFile(ChangeFileExt( Application->ExeName, ".ini" ) ); //讀取變數(若變數值不存在則用自動使用 Default 值) Left=MyIni->ReadInteger( "Form","Left",100); Caption=MyIni->ReadString( "Form","Caption","Default Caption"); } //------------------------------------------------------- void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { //將變數寫入 ini 物件 MyIni->WriteInteger("Form","Left",Left); MyIni->WriteString("Form","Caption",Caption); // 釋放 ini 物件 delete MyIni; } //------------------------------------------------------- ■ INI 物件的生命週期 在上面的程式碼中,可以看見我們把 INI 物件的建立寫在 FormCreate 事件中,而釋放 INI 物件則寫在 FormClose 事件中;事實上,變數真正寫到實體檔案中,是在INI 物件釋放的時候(也就是類似關閉檔案動作);平常則只是暫存於記憶體中,如此可減少磁碟 I/O 動作,但若程式不正常關閉,則INI 物件來不及將暫存於記憶體中的變數存檔,會導致欲存檔的變數遺失;所以為保險起見,我們可在準備寫入變數的時候才去建立 INI 物件,存入後馬上釋放 INI 物件以確保寫入實體檔案中,但這樣卻會增加磁碟I/O讀寫頻繁,其中的斟酌端看應用方向的特性。 //------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { TIniFile *MyIni; MyIni = new TIniFile(ChangeFileExt( Application->ExeName, ".ini" ) ); MyIni->WriteInteger("Form","Left",Left); MyIni->WriteString("Form","Caption",Caption); delete MyIni; } //------------------------------------------------------- << BCB 源碼任務 http://home.kimo.com.tw/bruce0829/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#28 引用回覆 回覆 發表時間:2002-08-12 11:37:43 IP:210.208.xxx.xxx 未訂閱
心得分享 : 本文檔 ( TEXT檔) 處理    本文檔 (TEXT 檔,或稱文字檔)的應用非常廣泛,最常見的應用就是不同平台或系統間的資料交換動作,以本文檔作為溝通橋樑;處理本文檔的讀寫有使用傳統 C 的語法及標準 C++ 的文字檔串流(stream)處理語法,雖然在 32位元平台下建議使用標準 C++ 的文字檔串流處理語法,但使用傳統 C 的語法有個好處,那就是 C 語法中 fprintf 及 fscanf 可以「格式化」輸出或輸入的字串的內容。     ■ 本文檔的輸出    □使用傳統 C 的處理語法    1.要先於程式開頭 #include "stdio.h" 2.append (附增)模式之宣告 outfile=fopen("C:\\TEST.TXT","a+"); 3.overwrite(覆寫)模式之宣告 outfile=fopen("C:\\TEST.TXT","w");    範例: //------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) {   String m="fprintf 測試"; //欲輸出的字串行   FILE *outfile;   if ((outfile=fopen("C:\\TEST.TXT","w"))== NULL) return;      fprintf(outfile,"%s\n",m);      fclose(outfile); } //-------------------------------------------------------    □使用標準 C++ 的文字檔串流(stream)處理語法    1.要先於程式開頭 #include "fstream.h"    2.使用 ofstream 物件作為檔案串流輸出物件    3.append (附增)模式之宣告 ofstream outfile("c:\\test.txt" , ios::app);    4.overwrite(覆寫)模式之宣告 (Default) ofstream outfile("c:\\test.txt" , ios::trunc); 或 ofstream outfile("c:\\test.txt")    範例:
//-------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  String m="ofstream 測試"; //欲輸出的字串行
  ofstream outfile("C:\\TEST.TXT");
  if (!outfile) return ;
  
  outfile << m.c_str();      outfile.close();
}
//-------------------------------------------------------
■ 本文檔的讀入 □ 使用傳統 C 的處理語法 由於 fscanf() 很難用 , 而且遇到空白就當作結束 , 所以拿來讀文字檔會很吃力 這裡省略不介紹 □ 使用標準 C 的文字檔串流(stream)處理語法 1.要先於程式開頭 #include "fstream.h" 2.使用 ifstream 物件作為檔案串流讀入物件 3.事先宣告一足夠容納讀入之每行長度的 buffer 當讀入緩衝區,若讀入之行長度超出緩衝區大小,有可能導致程式死當;故雖然在大部分的情況下,我們都可預測每行的讀入長度,但預防有例外狀況讀入某行長度特長,故筆者習慣將緩衝區大小設為 65535 4.使用 while (!infile.eof()) 迴圈來作逐行讀入 5.若於最後一行總是會讀入空白行時,可改用 while (!infile.getline(buff,sizeof(buff)).eof())迴圈來作逐行讀入 範例:
 
//-------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ 
char buff[65535];
  String temp;
ifstream infile;
  infile.open("c:\\test.txt");
  if (!infile) return ;      while (!infile.eof())
        {
           infile.getline(buff,sizeof(buff));
           temp = buff;
           if (Trim(temp)=="") continue;
                  
           // 讀入之 buff 變數的應用例
           Memo1->Lines->Add(temp);            }      infile.close();
}
//-------------------------------------------------------
<< BCB 源碼任務 http://home.kimo.com.tw/bruce0829/ >>
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#29 引用回覆 回覆 發表時間:2002-08-22 23:31:54 IP:163.32.xxx.xxx 未訂閱
心得分享 : Tag & Hint 屬性的妙用    ■ 每個 VCL 物件幾乎都有 Tag 屬性 , Tag 可用來放一個 整數值 , 由於此屬性值不會影響物件外觀,或其他物件行為 , 所以通常都用來當作"藏值"的地方 ; 利用事件共用 , 可將一堆相同的物件集合分別藏不同的值 , 然後共用同一事件後再例用分離法 , 將此值取出 ; 如之前介紹的計算機按鈕 , 設計計算機程式時有十個相同的按鈕 0 - 9, 但不用為十個按鈕寫十個 OnClick , 共用同一事件但要能分辨彼此按鍵代表不同的值 ; 但今天若應用在別的地方 , 在 POS 系統觸控螢幕操作畫面 , 按下不同按鈕得出不同的條碼值 , 然後再去資料庫中搜尋商品 , 由於條碼值太長 (13 位數) , 最好是當字串處理 , 那該將每個按鈕所代表不同的條碼值藏在哪呢 ? 筆者第一個想到的就是 Hint 屬性 , 由於面板商品不需 Hint , Hint 設定後只要 ShowHint 不要設為 true , Hint 就不會跑出來 , Hint 就可拿來當作物件本身偷藏"字串"值的地方 , 以下是範例介紹     ■ 動態產生"面板商品"按鈕的範例    □ 須使用按鈕 或是"看起來"像按鈕,且能按下去的元件 □ 要求條件 : Caption 要有兩行 , 按鈕可變顏色 , 要有立體感 , 但不能聚焦(Focused)  □ 測試 使用 Panel , 可變色 , 可設立體感 , 但 Caption 不能折行 使用 TSpeedButton 可設立體感 ,  Caption 可折行 , 但不能變色 使用 Label , 可變色 , 可折行 , 但不能設立體感 最後方案 使用 Label + Bevel(模擬按鈕立體感,及被按凹下去的感覺) ....當然也可使用 Label + Panel    □ 開始實做 1.首先開一個約 800 x 600 的 Form 2.再放一個 800 x 400 左右的 Panel (名叫 Panel1) 在 Form 中間偏下的部位  3.在中間偏上的部分放兩個 button , 一個用來產生動態物件 , 另一個用來釋放動態物件  4.按下"產生動態物件"按鈕後會在 Panel1 上產生一個 8 x 6 的按鈕陣列 , 重點是每個按鈕按下去所得的值都不相同 , 可用來藏"條碼值" , 然後做後續應用 .... 5.完整程式碼如下    
//---------------------------------------------------------------------------    #include 
#pragma hdrstop    #include "Unit1.h"
#include "fstream.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TLabel *lb[49]; // 宣告動態 Label 物件陣列 , Global 變數
TBevel *bv[49]; // 宣告動態 Bevel 物件陣列 , Global 變數
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
// 產生動態物件的按鈕事件
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  //須使用不能聚焦元件....
  //使用 Panel , 可變色 , 可設立體感 , 但 Caption 不能折行
  //使用 TSpeedButton 可設立體感 ,  Caption 可折行 , 但不能變色
  //使用 Label , 可變色 , 可折行 , 但不能設立體感
  //最後方案 使用 Label   Bevel(模擬立體感)          int x=0; int y=1;
  for (int i=1;i<=48;i  )
      {
        x  ;
        if (x>8)
           {
              x=1;
              y  ;
           }            bv[i]= new TBevel(this);
        bv[i]->Parent=Panel1;
        bv[i]->Width=98;
        bv[i]->Height=55;
        bv[i]->Style=bsRaised;
        bv[i]->Left=(x-1)*(bv[i]->Width 1)-0;
        bv[i]->Top=(y-1)*(bv[i]->Height 1)-0;            lb[i]= new TLabel(this);
        lb[i]->Parent=Panel1;
        lb[i]->&FF_Plu_MouseDown; //指派動態物件所需的事件
        lb[i]->&FF_Plu_MouseUp;     //指派動態物件所需的事件
        lb[i]->AutoSize=false;
        lb[i]->Alignment=taCenter;
        lb[i]->Width=96;
        lb[i]->Height=53;
        lb[i]->Left=bv[i]->Left 1;
        lb[i]->Top=bv[i]->Top 1;            lb[i]->Tag=i;
        lb[i]->Hint="條碼值 : 47100000000" FormatFloat("00",i);            lb[i]->Caption="\n" IntToStr(i) "\n" "test";
        switch (y)
               {
                  case 0 : lb[i]->Color=clBlack;    break; //黑
                  case 1 : lb[i]->Color=0x000080FF; break; //棕
                  case 2 : lb[i]->Color=0x00FF80FF; break; //紅
                  case 3 : lb[i]->Color=0x004080FF; break; //橙
                  case 4 : lb[i]->Color=0x0080FFFF; break; //黃
                  case 5 : lb[i]->Color=0x0080FF80; break; //綠
                  case 6 : lb[i]->Color=0x00FF8000; break; //藍
                  case 7 : lb[i]->Color=0x00FF0080; break; //紫
                  case 8 : lb[i]->Color=clBtnFace;  break; //灰
                  case 9 : lb[i]->Color=clWhite;    break; //白
               }          }    }
//---------------------------------------------------------------------------
// 自行寫給動態物件用的 OnMousDown() 事件
//---------------------------------------------------------------------------
void __fastcall TForm1::FF_Plu_MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
   //按下 Label 時將 Label 中的 Tag 值取出 ,
   //再呼叫對應 index 之 Bevel 物件做出外框 "按下"的感覺
   bv[((TLabel *)Sender)->Tag]->Style=bsLowered;       //按下 Label 時改變 Label 位置以便有"按下"的感覺"
   ((TLabel *)Sender)->Left  ;
   ((TLabel *)Sender)->Top  ;
   ((TLabel *)Sender)->Width--;
   ((TLabel *)Sender)->Height--;        }
//---------------------------------------------------------------------------
//自行寫給動態物件用的 OnMousUp() 事件
//---------------------------------------------------------------------------
void __fastcall TForm1::FF_Plu_MouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
   //放開 Label 時將 Label 中的 Tag 值取出 ,
   //再呼叫對應 index 之 Bevel 物件做出外框 "浮起"的感覺
   bv[((TLabel *)Sender)->Tag]->Style=bsRaised;       //放開 Label 時改變 Label Caption 位置以便有"浮起"的感覺"
   ((TLabel *)Sender)->Left--;
   ((TLabel *)Sender)->Top--;
   ((TLabel *)Sender)->Width  ;
   ((TLabel *)Sender)->Height  ;       ShowMessage(((TLabel *)Sender)->Hint); // 將被按下的 Label 偷藏的字串變數帶出以便應用    }    //---------------------------------------------------------------------------
//刪除動態物件的按鈕事件
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  for (int i=1;i<=48;i  )
      {
        delete lb[i];
        delete bv[i];
      }
}
<< BCB 源碼任務 http://home.kimo.com.tw/bruce0829/ >> 發表人 - bruce0211 於 2002/08/23 00:25:40
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#30 引用回覆 回覆 發表時間:2002-08-31 13:55:58 IP:210.208.xxx.xxx 未訂閱
心得分享 : 固定 TMediaPlayer 元件撥放影片的 Size     ■前言 我們通常拿 TMediaPlayer 來撥放媒體影片 , 而 TMediaPlayer 的 Display 屬性可設定影片要播放在哪個地方 , 如可以指定播放在某一 Panel 上 (哪些元件可供做播放平台可自行測試), 或是都不設 , 讓程式執行時系統自動另開一 Form 以供播放之用 , 但影片本身播放時的高度跟寬度有大有小 , 如何固定 Size 呢 ? TImage 有一屬性叫做 Stretch 可將影像調整跟 TImage 本身大小一樣 ; 但出乎意料之外的 , TMediaPlayer 的 Display 屬性居然不能設定播放平台為 TImage 元件 , 由於客戶的需求是不管影片本身解析度如何 , 播放出來都要為 800 x 600 Size , 我研究了好久才試出來 , 只要在播放之前指定播放 Size 為所要播放的元件平台 Size 即可    ■實作 : 如要將影像播放在 Panel1 上 1.將 Panel1 Size 拉大為 800 x 600 2.在 Play() 之前設定程式碼 : 
  TRect r;
  r.Left=0;
  r.Top=0;
  r.Right=Panel1->Width ;
  r.Bottom=Panel1->Height;
  MediaPlayer1->DisplayRect=r;      MediaPlayer1->Play();
則以後不管任何影片播放時 , 長寬度大小一定為 800 x 600 ■補充 : 將影片連續播放 1.將 TMediaPlayer 之 AutoRewind 屬性設為 true 2.在 TMediaPlayer 之 OnNotify 事件中加入程式碼如下 :
void __fastcall TForm1::MediaPlayer1Notify(TObject *Sender)
{
  if (MediaPlayer1->NotifyValue == nvSuccessful)
      MediaPlayer1->Play();    }
<< BCB 源碼任務 http://home.kimo.com.tw/bruce0829/ >> 發表人 - bruce0211 於 2002/08/31 13:57:46
bruce0211
版主


發表:157
回覆:668
積分:279
註冊:2002-06-13

發送簡訊給我
#31 引用回覆 回覆 發表時間:2002-11-27 08:47:53 IP:210.208.xxx.xxx 未訂閱
心得分享 : 如何判斷關閉程式的動作    ■前言 有些常駐程式須判斷被關閉的時機, 讓 user 不可以任意關掉程式 但卻允許 user 關機 所以要判斷使用者是按了程式右上角 [X] 按鈕 或是 使用者按了螢幕左下角 [關機] 指令 下面是簡單判斷的程式碼...    
//---------------------------------------------------------------------------    #include 
#pragma hdrstop    #include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------    void __fastcall TForm1::FormCreate(TObject *Sender)
{
  WindowProc=MyWndProc; //攔截訊息
}
//---------------------------------------------------------------------------
void __fastcall TForm1::MyWndProc(TMessage &Message)
{      if (Message.Msg == WM_SYSCOMMAND)
     {
       if (Message.WParam== SC_CLOSE)  //若Message為 Close Window
          {
            ShowMessage("使用者按了程式右上角 [X] 按鈕");
          }
     }      if (Message.Msg == WM_ENDSESSION)
      ShowMessage("使用者按了螢幕左下角 [關機] 指令");      //將訊息還給 Form 原來處理程序,否則只是收到關閉指令,但不會真正執行關閉動作   
  WndProc(Message);    }
■補充 通常我們就在以上兩個時機點帶入一個旗標, 到FormCloseQuery()事件中再決定關機與否 , 但根據我的測試,WM_ENDSESSION 觸發的時機會比 FormCloseQuery() 事件晚到 (WM_SYSCOMMAND 則不會) , 也就是說錯過了關機判斷時機後,才會有 WM_ENDSESSION 訊息被收到 , 這時可改判斷是否收到 WM_QUERYENDSESSION 來代替判斷 WM_ENDSESSION , 程式流程就會正確判斷無誤 << BCB 源碼任務 http://home.kimo.com.tw/bruce0829/ >> 發表人 - bruce0211 於 2002/11/28 06:57:30
[<<] [1] [2] [>>]
系統時間:2024-03-28 19:55:02
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!