60個BCB(C++Build)初學者 應用例項
1.怎樣在C++Builder中建立使用DLL
2.用C++Bulider在WIN.INI中儲存資訊
3.如何在C++Builder中檢測硬體
4.C++Builder如何響應訊息及自定義訊息
5.利用C++ Builder開發動畫DLL
6.用C++ Builder 3製作螢幕保護程式
7.TCP/IP頭格式
8.UDP
9.判斷windows的Desktop及其它目錄
10用C++Builder建立數字簽名
11用Enter 鍵控制焦點切換的方法
12.攔 截 Windows 消 息
13.使用CommaText
14.程式開始時先顯示資訊框
15.怎樣獲取程式的命令列引數?
16.如何監視剪貼簿
17.如何使用OnIdle事件
18.用C++Builder編寫序列非同步通訊程式
19.C++BUILDER非可視元件的訊息處理技巧
20.用C++Builder 建立資料庫VCL使用經驗
21.用C++ Builder建立基於Internet的點對點Chat
22.用C++Builder獲取應用程式圖示
23.BIG5到GB的轉換技術
24.C++BUILDER讓你的工作列圖示動起來
25.TFORM
26.用BCB在windows桌面建立快捷方式
27.讀磁片磁區
28.I/O 埠讀寫的實現
29.檢測滑鼠位置
30.令Win32 應用程式跳入系統零層
31.如何取得Memo的行和列
32.使用Sockets
33.Windows95/98下怎樣隱藏應用程式不讓它出現在CTRL-ALT-DEL對話方塊中?
34.怎樣隱藏應用程式的任務條圖示
35.編寫自己的Ping.exe程式
36.用C++Builder在WINNT下編制一個Service
37.如何在C++ BUILDER中自動關閉WINDOWS屏保
38.顯示/隱藏工作列圖示
39.信箱監視程式
40.C++Building製作鬧鐘
41.撥號上網IP地址的檢知
42.用C++ Builder編寫Tray程式
43.怎樣用程式碼來最小化或恢復程式
44.製作主視窗顯示前的版權視窗
45.判斷是否已經聯到 internet
46.獲取登陸使用者名稱
47.隱藏桌面圖示
48.程式啟動時執行
49.控制面板的呼叫
50.模擬鍵盤按鍵
51.讓標題欄閃爍
52.啟動螢幕保護
53.年月日星期的取法
54.鍵盤事件
55.隱藏工作列
56.禁止關機
57.怎樣以最小化方式啟動程式
58.在Memo中增加一行後,如何使最後一行能顯示
59.設定桌布方法
怎樣在C++Builder中建立使用DLL
自從C++Builder從去年浪漫情人節上市以來,吸引了大量的Delphi、VC、Vb的程式設計師到它的懷抱,大量的C、C++程式設計師感嘆道:總算有了C的視覺化開發工具,對我也是一樣,從BC、Delphi到C++Builder。
動態連結庫(DLL)是Windows程式設計常遇到的程式設計方法,下面我就介紹一下在BCB (C++Builder下簡稱BCB) 中如何建立使用DLL和一些技巧。
一、建立:
使用BCB File|NEW建立一個新的DLL工程,並儲存好檔案BCB,生成一個DLL的程式框架。
1.DllEntryPoint函式為一個入口方法,如果使用者在DLL被系統初始化或者登出時被呼叫,用來寫入對DLL的初始化程式和解除安裝程式;引數:hinst用來指示DLL的基地址;reason用來指示DLL的呼叫方式,用於區別多執行緒單執行緒對DLL的呼叫、建立、解除安裝DLL;
2.在程式中加入自己所要建立的DLL過程、函式;
3.用dllimport描述出口;
例程式如下:
#include
#pragma hdrstop
extern 揅?__declspec(dllexport) int test();
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}
int test()
{
return 3;
}
注意:動態連結庫中呼叫過程、函式時有不同的CALL方式 __cdecl、 __pascal, __fastcall、__stdcall,BCB中預設的方式為__cdecl(可不寫),如果考慮相容性可用時__stdcall宣告方法為:
extern 揅?__declspec(dllexport) int __stdcall test();
對於其中過程、函式也改為:
int __stdcall test()
整的不容易啊支援下吧
二、使用DLL
在BCB中使用DLL有兩種方法:
1.用靜態呼叫法
首先需要在BCB的專案中加入輸入介面庫(import library),開啟工程專案,使用BCB View|Project Manager開啟專案列表,向專案中加入介面庫(*.lib)。
其次在標頭檔案中加入介面宣告。
例程式如下:
//define in include file
extern 揅?__declspec(dllimport) int __cdecl test();
//use function in main program
int I;
I=test();
注意:
(1)動態連結庫呼叫過程、函式時CALL方式 與建立時方式一樣不寫為__cdecl,其它需要宣告。
(2)BCB建立的DLL有對應的輸入介面庫(import library),如只有DLL而無庫時,可用BCB的implib工具產生:implib xxx.lib xxx.dll;另外可用:tlib xxx.lib,xxx.lst 產生DLL的內部函式列表,許多Windows的未公開技術就是用這種方法發現的。
2.動態呼叫法
動態呼叫法要用Windows API 中的LoadLibrary()和GetProcAddress()來調入DLL庫,指出庫中函式位置,這種方法較常見。
例程式如下:
HINSTANCE dd;
int _stdcall (*ddd)(void);
dd=LoadLibrary(搙xx.dll?;
ddd=GetProcAddress(dd,搕est?;
Caption=IntToStr(ddd());
FreeLibrary(dd);
三、注意:
建立DLL時編譯連結時注意設定Project Options。
Packages標籤:去除Builder with runtime packages檢查框。
Linker標籤:去除Use dynamic RTL檢查框。
否則建立的DLL需要Runtime packages or Runtime library。
用C++Bulider在WIN.INI中儲存資訊
現在許多軟體把程式中需要的資料儲存在登錄檔中,這樣當用戶裝的軟體越來越多時,致使登錄檔越來越龐大,容易使系統出錯。當然,微軟也建議在登錄檔中儲存資料,但當我們需要儲存的資料不多時完全可以把資料儲存在WIN.INI中,這樣可以很方便地維護,實現方法相對來說比較簡單。下面我以Borland C++ Builder為例來說說如何實現。
原理其實很簡單,只需呼叫API的 WriteProfileString和GetProfileInt函式就可以了。這兩個函式的原型是:BOOL WriteProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTR lpString );
UINT GetProfileInt(LPCTSTR lpAppName,LPCTSTR lpKeyName,INT nDefault);
其中lpAppName指在WIN.INI中段的名字,即用[]括起來的字串,lpKeyName指在這個段中每一個專案的名字,lpString指這個專案的值,即“=”後的數, nDefault為當GetProfileInt沒有找到lpAppName和lpKeyName時返回的值,即預設值,前者返回為布林值(true 或 false),後者返回為無符號整形值。當在WriteProfileString函式中 lpKeyName 為空(NULL)時,則清除這個段的全部內容,lpString 為空時,則清除這一專案的內容,即這一行將清除掉。
下面舉一例子來說明這兩個函式的用法。新建一個應用程式,在Form1上放兩個Edit和三個Button,其中Edit的Text為空,三個Button的Caption分別為“新增”、“檢視”、“清除”。雙擊“新增”按鈕加入下面程式碼:
WriteProfileString(“例子程式”,“專案”,Edit1→Text.c_str());
雙擊“檢視”按鈕加入如下程式碼:
unsigned int Temp;
Temp=GetProfileInt(“例子程式”,“專案”,100);
Edit2→Text=IntToStr(Temp);
雙擊“清除”按鈕加入如下程式碼:
WriteProfileString(“例子程式”,NULL,NULL);
然後按F9鍵執行程式。
下來可以檢驗一下程式的正確性。在Edit1中輸入數字,如“3265”,按“新增”按鈕,這時執行“sysedit”來檢視“WIN.INI”檔案的最後面,可以看到加入瞭如下內容:
[例子程式]
專案=3265
其中“[]”和“=”是函式自動加上的。按下“檢視”按鈕,在Edit2中出現“3265”,當按下“清除”按鈕可清除新增的部分。經過檢視可知程式已達到預期的目的。
喜愛程式設計的朋友可以把上述方法應用到自己的程式中去,來達到儲存資料資訊的作用。當確實要把資訊儲存到登錄檔中,可以在C++ Builder中定義一個TRegistry類的物件來進行相關的操作,或者直接呼叫Windows的API函式,具體如何程式設計大家可以參閱相關資料或者同我聯絡。
如何在C++Builder中檢測硬體
在我們編寫的程式中常常要和硬體打交道,那麼如何在程式中確定系統中是否有該裝置,它的執行狀態又是怎樣的呢?對於初學者來說,這個問題常常不好解決,其實只需簡單地利用幾個API函式,硬體的問題並不神祕。下面就讓我們一起看看在C++ Builder中是如何檢測硬體的。
1. 檢測CPU的型號
先讓我們從最簡單的做起,看一看自己的CPU型號。首先,在C++ Builder中畫出圖1所示的窗體,在下面的幾個例子中我們將一直使用這個窗體作示範,它包括一個用來啟用測試的Button和一個用來顯示結果的Memo。我們可以用GetSystemInfo這個API獲得CPU的型號。將下列程式碼新增到Button的Click事件裡就可以了:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
//獲得CPU型號
SYSTEM_INFO systeminfo;
GetSystemInfo (&systeminfo);
Memo1→Lines→Add(撃腃PU型別是:敓玈tring( systeminfo.dwProcessorType ));
}
執行它,點選Test試試,CPU型號出來了吧!
2.檢測記憶體狀態
獲得記憶體狀態的方法和CPU型號差不多,只是他用到的是另外一個API:GlobalMemoryStatus。
其中,成員dwTotalPhys用來獲得實體記憶體總量,而dwAvailPhys顧名思義是有效實體記憶體的意思。我們只要把下面幾行程式碼加到上面程式的後面就可以了(不用重做,下同):
//獲得記憶體狀態
MEMORYSTATUS memory;
memory.dwLength =sizeof(memory); //初始化
GlobalMemoryStatus(&memory);
Memo1→Lines→Add(撃奈錮砟詿媸?Mb):敓玈tring(int(memory.dwTotalPhys /1024/1024)));
Memo1→Lines→Add(撈渲鋅捎媚詿媸?Kb):敓玈tring(int( memory. /1024)));
怎麼樣,看出點門道了麼?兩段程式的格式幾乎一模一樣,其實,GetSystemInfoGlobalMemoryStatus還可以獲得許多其他有關CPU和記憶體的資訊,就按照上面的格式去套就行了,更詳細的資料可以去看C++ Builder4的Help。
3. 檢測可用硬碟空間
好了,經過前面兩個簡單問題的熱身,我們來處理一個稍微複雜的問題:我們知道安裝程式大都有一個檢測硬碟空間的過程,那麼這是怎麼實現的呢?他用到的是API函式GetDiskFreeSpace,這個函式輸入一個引數:目標盤的路徑;返回四個引數,依次是每簇的扇區數、每扇區的位元組數、空閒的簇數、總簇數。假如我們需要檢測C盤的總容量和可用容量,那麼可以把以下程式碼加到上面的程式中:
//獲得C盤可用空間
DWORD sector,byte,cluster,free;
long int freespace,totalspace;
GetDiskFreeSpace(揅:?&sector,&byte,&free,&cluster); //獲得返回引數
totalspace=int(cluster)*int(byte)*int(sector)/1024/1024; //計算總容量
freespace=int(free)*int(byte)*int(sector)/1024/1024; //計算可用空間
Memo1→Lines→Add(揅盤總空間(Mb):敓玈tring(totalspace));
Memo1→Lines→Add(揅盤可用空間(Mb):敓玈tring(freespace));
怎麼樣?現在可以自己做安裝程式了吧!
C++Builder如何響應訊息及自定義訊息
Inprise(Borland) C++Builder中,可以象在Delphi中一樣響應訊息,只是看起來要稍複雜一點。
對於系統已定義的訊息,可以直接響應:
#define WM_MY_OPEN_CMDLINE_FILE (WM_USER+1) //程序間通訊的自定義訊息
#define WM_MY_SEARCH_NODE (WM_USER+2) //查詢命令的自定義訊息
class TSomeForm : public TForm
{
//...類中的其它程式碼
protected:
//訊息的響應過程
void __fastcall OpenCmdLineFile(TMessage Message);
void __fastcall SearchDocumentNode(TMessage Message);
void __fastcall GetWindowMinMaxInfo(TWMGetMinMaxInfo Message);
//以下通過巨集定義實現訊息的正確響應
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_MY_OPEN_CMDLINE_FILE, TMessage, OpenCmdLineFile)
MESSAGE_HANDLER(WM_MY_SEARCH_NODE, TMessage, SearchDocumentNode)
MESSAGE_HANDLER(WM_GETMINMAXINFO , TWMGetMinMaxInfo, GetWindowMinMaxIn fo)
END_MESSAGE_MAP(TForm)
};//end class
//以下為實現程式碼
void __fastcall TSomeForm::OpenCmdLineFile(TMessage Message)
{//直接通過訊息結構傳遞引數
LPSTR lpCmdLine=(LPSTR)Message.LParam;//從Message中取得引數
this->HandleCmdLineFile(lpCmdLine);//處理命令列的引數
return;
}
void __fastcall TSomeForm::SearchDocumentNode(TMessage Message)
{//響應查詢訊息
//Message中的引數在此處不需要。
this->SearchNode();
return;
}
void __fastcall TSomeForm::GetWindowMinMaxInfo(TWMGetMinMaxInfo Messag
e)
{//設定主視窗的最小尺寸
MINMAXINFO *MinMaxInfo=Message.MinMaxInfo;
MinMaxInfo->ptMinTrackSize.x=400;
MinMaxInfo->ptMinTrackSize.y=300;
return;
}
其中:TMessage和TWMGetMinMaxInfo型別的定義可參見:
C:/Program Files/Borland/CBuilder/inlucde/vcl/Messages.hpp;其它的訊息
響應方法與此相同。
另外,可以為自定義的訊息也定義一個對應的訊息結構(如:TSearchNode_Mes
sage),至於如何定義訊息結構, 可以參考:
C:/Program Files/Borland/CBuilder/inlucde/vcl/Messages.hpp
利用C++ Builder開發動畫DLL
我們在Windows98環境下執行拷貝檔案、查詢檔案或計算機等耗時比較長的操作時,Windows會顯示一個小小的動畫,指示正在進行的操作,與死板的靜止影象相比增色不少。那麼我們自己開發軟體時,能否也顯示一個這樣的動畫提示呢?我在開發一個外貿應用軟體系統時,遇到的資料量很大,當通過複合條件查詢時,因為不是資料庫表的每個專案都有索引,所以很費時,系統也會表現出長時間停頓,使用者感覺極為不爽。我經過一段時間的探索,開發了一個能夠在採用的開發環境PowerBuilder下呼叫的動畫DLL,由於採用多執行緒程式設計,PB呼叫的DLL函式能夠及時將控制權交還為PB,不影響應用系統的運轉。使用者能夠看到一個東西在動,也就不會想到系統是不是停止響應了,感覺時間也似乎沒那麼久了。
程式碼與編譯選項
(1) 在C++Builder的File選單下選擇New,在New Item對話方塊的New屬性中選擇DLL,C++Builder就會建立一個空白的DLL專案。
(2) 在File選單下選擇New Form,C++Builder建立一個空白的Form,修改它的屬性為
BorderStyle=bsDialog
BorderIcons的子屬性均為False
FormStyle=fsStayOnTop
Position= poScreenCenter
Name=StatusForm
(3) 在Form上新增一個Win32下的Animate控制元件Animate1,修改它的屬性為
Align=alTop
(4) 在Form上新增一個Standard下的Button控制元件Button_Cancel,再新增System下的Timer控制元件Timer1,設定定時Interval時間位250,以較快的響應使用者的取消請求。
因為PB應用系統與動畫窗體程式碼分別屬於兩個執行緒,不能採用PB執行緒直接關閉動畫窗體執行緒的視窗,否則會引起系統執行不正常,因此採用PB執行緒設定關閉標誌,而動畫執行緒採用Timer控制元件定時檢查標誌,一旦檢測到關閉標誌,就關閉視窗,清除執行緒標誌,結束動畫執行緒。
下面給出編碼及編碼原理:
1.DLL DLL主體程式碼:
TCommonAVI g_CommonAVI[]={
aviNone, aviFindFolder,
aviFindFile, aviFindComputer,
aviCopyFiles, aviCopyFile,
aviRecycleFile, aviEmptyRecycle,
aviDeleteFile
};
int gi_Canceled=0,gi_AVIType=0;
int gi_RequestClose=0,gi_WindowActive=0;
char lpsWinTitle[256];
HWND hWndParent=NULL;
extern "C" __declspec(dllexport)
int pascal DllEntryPoint(HINSTANCE hinst,
unsigned long reason, void*);
extern "C" __declspec(dllexport) int
pascal ShowStatusWindow(int AVIType,
LPSTR WinTitle,long hWnd);
extern "C" __declspec(dllexport) int
pascal GetStatus(int ai_CloseWin);
extern "C" __declspec(dllexport) int
pascal CloseStatusWindow();
class TFormThread : public TThread{
public:// User declarations
__fastcall TFormThread(bool CreateSuspended);
void __fastcall Execute(void);
};
__fastcall TFormThread::
TFormThread(bool CreateSuspended):
TThread(CreateSuspended){
}
void __fastcall TFormThread::Execute(void){
gi_WindowActive=1;
StatusForm=new TStatusForm(NULL);
StatusForm- >Caption=lpsWinTitle;
StatusForm- >ShowModal();
gi_WindowActive=0;
delete StatusForm;
gi_RequestClose=0;
}
TFormThread *FormThread;
__declspec(dllexport) int WINAPI DllEntryPoint
(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}
__declspec(dllexport) int pascal
ShowStatusWindow(int AVIType,LPSTR
WinTitle,long hWnd){
hWndParent=(HWND)hWnd;
memset(lpsWinTitle,0,sizeof(lpsWinTitle));
strncpy(lpsWinTitle,WinTitle,sizeof(lpsWinTitle)-1);
if (AVIType >0 && AVIType< =8)
gi_AVIType=AVIType;
FormThread=new TFormThread(true);
FormThread- >Priority = tpNormal;
FormThread- >Resume();
}
__declspec(dllexport) int pascal
GetStatus(int ai_CloseWin){
if (gi_Canceled)
if (gi_WindowActive){
gi_RequestClose=1;
while(gi_RequestClose);
}
return gi_Canceled;
}
__declspec(dllexport) int pascal
CloseStatusWindow(){
if (gi_WindowActive){
gi_RequestClose=1;
while(gi_RequestClose);
}
return gi_Canceled;
}
2.窗體StatusForm的程式碼:
TStatusForm *StatusForm;
//-----------------------------------
extern int gi_Canceled;
extern int gi_AVIType;
extern TCommonAVI g_CommonAVI[];
__fastcall TStatusForm::
TStatusForm(HWND ParentWindow)
: TForm(ParentWindow)
{
gi_Canceled=0;
}
//-----------------------------------
//取消按鈕並不直接關閉窗體,
而指示設定取消標誌,供呼叫者檢視
void __fastcall TStatusForm::
Button_CancelClick(TObject *Sender)
{
gi_Canceled=1;
// ModalResult=mrCancel;
}
//-----------------------------------
// 啟用動畫,在FORMCREATE事件中
void __fastcall TStatusForm::
FormCreate(TObject *Sender)
{
Animate1- >CommonAVI=g_CommonAVI[gi_AVIType];
Animate1- >Active = true;
}
//-----------------------------------
extern int gi_RequestClose;
// 定時器事件檢測到結束標誌關閉窗體
void __fastcall TStatusForm::
Timer1Timer(TObject *Sender)
{
if (gi_RequestClose){
ModalResult=mrOk;
}
}
//-----------------------------------
(5) 設定編譯選項:Project->Options開啟Project Options對話方塊,清除Linker屬性頁中的Use Dynamic RTL標誌,清除Packages屬性頁中的Build with runtime packages。這樣只要單個DLL就可以運行了,而不必安裝一些動態連線執行時間庫。使用動畫DLL
上面編譯出DLL可以由其它任何開發語言呼叫,下面給出在PB中的使用方法。
(1) 定義:
//Declare - > Global External Functions
FUNCTION Long ShowStatusWindow(Long
AVIType,String WinTitle,long hWnd) &
LIBRARY "STATWIN.DLL" ALIAS FOR "ShowStatusWindow"
FUNCTION Long GetCancelStatus(Long CloseWindow) &
LIBRARY "STATWIN.DLL" ALIAS FOR "GetStatus"
FUNCTION Long CloseStatusWindow() &
LIBRARY "STATWIN.DLL" ALIAS FOR "CloseStatusWindow"
(2) 呼叫:
long ll_EndTime
//顯示查詢資料夾動畫
ShowStatusWindow(2)
setpointer(HourGlass!)
ll_EndTime = Cpu() + 10 * 1000
DO
if GetCancelStatus(0)=1 then
exit
end if
// 做想做的事情
LOOP UNTIL cpu() > ll_EndTime
CloseStatusWindow()
用C++ Builder 3製作螢幕保護程式
螢幕保護程式是以scr為副檔名的標準Windows可執行程式,在啟用控制面板的顯示器屬性的"螢幕保護程式"頁時,該模組會自動在Windows啟動目錄(Windows目錄和系統目錄)下查詢副檔名是scr的基於Windows的可執行檔案。使用螢幕保護程式,不僅可以延長顯示器的使用壽命,還可以保護私人資訊。
編制螢幕保護程式不僅要涉及訊息的處理,還要涉及命令列引數的處理。在WIN32SDK文件中描述了編制基於WIN32的標準的螢幕保護程式所必須遵守的嚴格標準。按照這些標準,螢幕保護程式必須要輸出兩個函式:ScreenSaverProc和ScreenSaverConfigureDialog,但是,在Windows系統中的很多螢幕保護程式並沒有遵循這些標準(使用impdef或者tdump實用工具檢視即可)。並且使用該文件中介紹的方法編寫螢幕保護程式,不僅要使用資源編輯器,並且在連結時還要利用Scrsaver.lib檔案(在C++Builder3環境下,不能成功連線)。不僅要涉及訊息的處理,還要涉及命令列引數的處理。
C++Builder3是一種快速的應用程式開發工具,提供了許多型別的應用程式開發模板,但沒有提供開發螢幕保護程式的模板,並且在其線上幫助中也沒有提及如何開發這類應用程式。經過本人的研究,找到了用C++Builder3編制螢幕保護程式的方法。
在控制面板的"顯示器屬性"項的"螢幕保護程式"頁中進行設定時,要遇到三種類型的命令列引數,並且,各種情況下的螢幕保護程式的顯示結果也各不相同,一般來講,就需要三種類型的窗體(或兩種,在隨後的內容中討論)。下面將分四步來具體地說明如何編制螢幕保護程式。
一、螢幕保護程式的選擇
如果在標題為"螢幕保護程式"的下拉列表框中選中了某個保護程式時,系統會自動啟動該程式,這個程式的顯示範圍是在這個頁面上的顯示器圖形的螢幕範圍,同時,會將兩個命令列引數:一個是"/p";另一個是顯示視窗的控制代碼,傳遞給這個被選中的程式。因此,這類程式首先應該能夠處理命令列引數。在C++Builder3中,與命令列引數處理有關的函式是:ParamCount()和ParamStr(),具體的申明方式如下:
1.externPACKAGEint__fastcallParamCount(void);
該函式返回命令列引數的個數,但不包含應用程式本身。
2.externPACKAGEAnsiString__fastcallParamStr(intIndex);
該函式返回指定索引值的命令列引數。ParamStr(0)返回的是應用程式本身。
所以,在這以步驟中的引數判斷的語句如下:
if(UpperCase(ParamStr(1))==
"-p"||UpperCase(ParamStr(i))=="/p")
{
//addthecodeinhere
}
在完成了引數判斷後,就應該對顯示視窗的處理,為能夠使程式在顯示器圖形的螢幕區域內顯示,就要重新設定程式的父視窗和顯示區域。這要涉及到父視窗控制代碼的獲得及父視窗的設定,以及API函式的呼叫。這種環境下的父視窗控制代碼就是傳遞過來的第二個命令列引數;要設定父視窗,只需設定窗體的ParentWindow屬性即可。這段程式如下:
RECTrc;//Line1
HWNDhWnd=(HWND)
(atol(ParamStr(2).c_str()));//Line2
::GetClientRect(hWnd,&rc);//Line3
ParentWindow=hWnd;//Line4
Left=rc.left;//Line5
Top=rc.top;//Line6
Width=rc.right-rc.left;//Line7
Height=rc.bottom-rc.top;//Line8
在上面的程式片段中,第2行語句是將傳遞過來的第2個引數轉換成視窗控制代碼;然後,第3行語句利用這個視窗控制代碼,呼叫API函式以獲得該視窗的客戶區域;第4行語句將選中的螢幕保護程式的父視窗設定為指定的視窗;餘下的語句是將該程式的視窗大小設定成副視窗的客戶區大小。這一程式片段的位置應該是在窗體的OnCreate事件處理中。
需要說明的是,這種型別(包括第三步介紹的窗體)的窗體樣式應是:
FormStyle=fsStayOnTop;
窗體邊界的樣式應為:
BorderStyle=bsNone;
當然,這時也不需要滑鼠圖形,因此,可以將滑鼠的形狀設為crNone:
Cursor=crNone;
二、初始化引數的設定
單擊"顯示器屬性"模組的"螢幕保護程式"頁面中的"設定"按鈕時,系統會啟動指定的保護程式的初始值設定對話方塊,這時傳遞過來的命令列引數是:"/c"或"-c"(引數的處理與前面介紹的相同)。通過該對話方塊,可以設定保護程式的一些初始引數,比如圖形的變化快慢等。在這段程式中,還要涉及到初始化檔案或登錄檔的讀寫,用以記錄初始化引數,便於保護程式啟動時使用。
三、預覽及執行
預覽的效果就是螢幕保護程式被啟用後的顯示。單擊單擊"顯示器屬性"模組的"螢幕保護程式"頁面中的"預覽"按鈕,就可以觀察保護程式執行的實際效果。這時,系統啟動該程式時傳遞過來的命令列引數是:"/s"或"-s"。對於命令列引數的處理與前面的步驟相同,但在這一步中,還要對幾個訊息進行處理,這些訊息是:WM_MOUSEMOVE,WM_LBUTTONDOWN,WM_MBUTTONDOWN,WM_RBUTTONDOWN,WM_KEYDOWN,WM_ACTIVATE。對WM_MOUSEMOVE和WM_ACTIVATE訊息的處理形式如下:
void__fastcallHandleSomeMessage(TMessage&Msg)
{
switch(Msg.Msg)
{//......
caseWM_ACTIVATE:if(Msg.WParamLo==WA_INACTIVE)
Close();
break;
caseWM_MOUSEMOVE:if(OldMouseX==-1&&OldMouseY==-1)
//Intheconstructor,OldMouseXand
OldMouseYmustbeinitializedby-1.
{OldMouseX=Msg.LParamLo;
OldMouseY=Msg.LParamHi;
}
elseif(OldMouseX!=Msg.LParamLo
||OldMouse!=Msg.LParamHi)
Close();
break;
......
}
}
對於其他的訊息僅僅是呼叫Close()函式來關閉應用程式即可。應用這種訊息處理方式時,必須要類定義時進行訊息對映,不然的話,就要在相應的訊息響應中進行處理(使用一定的布林變數,就可以與第一步合用一個窗體)。
與第一步類似,在該步驟中,也不需要具體的滑鼠指標的形狀,因此,將滑鼠指標設為crNone:
Cursor=crNone;
四、修改專案原始檔
在C++Builder3中,一個窗體也就是一個類,換句話說,具有某些特性的類也就是一個窗體,因此,編制螢幕保護程式時,也不需要什麼主窗體,同時,也不用自動建立某些窗體了,這時就要修改專案原始檔,下面所列出的程式就是筆者在編制某螢幕保護程式時使用的專案原始檔,供讀者參考。
WINAPIWinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
CreateMutex(NULL,true,"ScreenSaver");
if(GetLastError()!=ERROR_ALREADY_EXISTS)
{
try
{
Application->Initialize();
Application->Title="螢幕保護程式測試";
if(UpperCase(ParamStr(1))==
"/C"||UpperCase(ParamStr(1))=="-C"
||ParamCount()==0)
{TScrSaverConfiguerF*ScrCfg=
newTScrSaverConfiguerF(NULL);
ScrCfg->ShowModal();
deleteScrCfg;
return0;
}//單擊"設定"按鈕
elseif(UpperCase(ParamStr(1))==
"/P"||UpperCase(ParamStr(1))=="-P")
{TScrForP*ScrFP=newTScrForP(NULL);
ScrFP->ShowModal();
deleteScrFP;
return0;
}//在"螢幕保護程式"下拉列表框中選擇一個程式
elseif(UpperCase(ParamStr(1))==
"/S"||UpperCase(ParamStr(1))=="-S")
{TScreenSaveF*ScreenSave=newTScreenSaveF(NULL);
ScreenSave->ShowModal();
deleteScreenSave;
return0;
}//單擊"預覽"按鈕,及執行螢幕保護程式
else
return1;
}
catch(Exception&exception)
{
Application->ShowException(&exception);
}
}
return0;
}//theWinMainFunctionend
前面介紹了在C++Builder3下編制螢幕保護程式的方法.對於C++Builder3這種RAD工具來講,開發這類程式也是相當方便的,按照前述的方法,可以在極短的時間開發出螢幕保護程式。對於螢幕保護程式,在本文中沒有說明的就是如何設定口令的問題,這部分就由讀者自己摸索吧。
TCP/IP頭格式
一、先是常用的IP頭格式。
IP頭格式:
版本號 (4位)
IP頭長度 (4位)
服務型別 (8位)
資料包長度 (16位)
標識段 (16位)
標誌段 (16位)
生存時間 (8位)
傳輸協議 (8位)
頭校驗和 (16位)
傳送地址 (16位)
目標地址 (16位)
選項
填充
簡單說明
============
1. IP頭長度計算所用單位為32位字, 常用來計算資料開始偏移量
2. 資料包長度用位元組表示, 包括頭的長度, 因此最大長度為65535位元組
3. 生存時間表示資料被丟失前儲存在網路上的時間, 以秒計.
4. 頭校驗和的演算法為取所有16位字的16位和的補碼.
5. 選項長度是可變的, 填充區域隨選項長度變化, 用於確保長度為整位元組的倍數.
描述
============
struct iphdr {
BYTE versionihl;
BYTE tos;
WORD tot_len;
WORD id;
WORD frag_off;
BYTE ttl;
BYTE protocol;
WORD check;
DWORD saddr;
DWORD daddr;
};
二、TCP頭格式
TCP頭格式:
源埠 (16位)
目的埠 (16位)
序號 (32位)
確認號 (32位)
資料偏移 (4位)
保留 (6位)
標誌 (6位)
視窗 (16位)
校驗和 (16位)
緊急指標 (16位)
選項
填充
簡單說明
============
1. 資料偏移用於標識資料段的開始
2. 保留段6位必須為0
3. 標誌包括緊急標誌、確認標誌、入棧標誌、重置標誌、同步標誌等。
4. 校驗和計算方式為將頭與16位二進位制反碼和中的16位二進位制反碼加在一起。
5. 選項長度是可變的, 填充區域隨選項長度變化, 用於確保長度為整位元組的倍數.
6. 更詳細的說明請參閱有關資料。
描述
============
struct tcphdr {
WORD SourPort;
WORD DestPort;
DWORD SeqNo;
DWORD AckNo;
BYTE HLen;
BYTE Flag;
WORD Window;
WORD ChkSum;
WORD UrgPtr;
};
UDP
一、說明
使用UDP時,直接使用API代替控制元件。
第一個程式(ReadBufferUdp)使用來接收到快取中。
"Destino"變數非常重要,如果你從其他地方接收資料到Buffer,你必須設定Destino = 0 並且在以後執行的時候賦值你將要傳送的包的地址給它(after the execution it will have the address which send you the packet.)。
如果你只想從一個指定的地址接收資料,你必須設定變數Destino =
.
"gvEncerrar" 用來中止處理過程。(gvEncerrar被設定為全域性變數。)
超時時間設定。"Inicio + 12" = 12 sec of timeout.
第三個程式是用來準備WinSock程式。
二、程式碼
int ReadBufferUdp(unsigned long *Destino,void *T,int Size)
{
char Buffer[128];
SOCKADDR_IN SockAddr;
int LenSockAddr=sizeof(SOCKADDR_IN);
fd_set FdRead;
struct timeval t_val;
int Ret;
time_t Inicio = time(NULL);
Application->ProcessMessages();
if(gvEncerrar)
return false;
FD_ZERO(&FdRead);
FD_SET(gvSocket,&FdRead);
t_val.tv_sec=0;
t_val.tv_usec=0;
while((Ret=select(0,&FdRead,NULL,NULL,&t_val))!=1 && (Inicio + 12) >
time(NULL) && !gvEncerrar)
{
FD_ZERO(&FdRead);
FD_SET(gvSocket,&FdRead);
t_val.tv_sec=0;
t_val.tv_usec=0;
Application->ProcessMessages();
}
if(Ret != 1)
return false;
if(recvfrom(gvSocket,Buffer,Size,0,(LPSOCKADDR)&SockAddr,&LenSockAddr)!=Size)
return false;
if(*Destino == 0)
{
*Destino = SockAddr.sin_addr.s_addr;
}
else
if(*Destino != SockAddr.sin_addr.s_addr)
return false;
memcpy(T,Buffer,Size);
return true;
}
int WriteBufferUdp(unsigned long Destino,void *T,int Size)
{
SOCKADDR_IN SockAddr;
int Sent;
Application->ProcessMessages();
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = gvPortUdp;
SockAddr.sin_addr.s_addr = Destino;
Sent = sendto(gvSocket,(char
*)T,Size,0,(LPSOCKADDR)&SockAddr,sizeof(SockAddr));
if(Sent != Size)
return false;
else
return true;
}
void InicializaTCPIP()
{
WORD wVersionRequested;
WSADATA wsaData;
IN_ADDR In;
PSERVENT PServent;
SOCKADDR_IN SockAddrIn;
wVersionRequested = MAKEWORD( 1, 1 );
if(WSAStartup( wVersionRequested, &wsaData ))
{
ShowMessage("Erro na inicializao do TCP/IP");
Application->Terminate();
return;
}
// Get the port on service file
if((PServent=getservbyname("your_service_name","udp"))==NULL)
{
ShowMessage("Erro obtendo port do servi transurb/udp");
Application->Terminate();
return;
}
gvPortUdp = PServent->s_port;
sprintf(StrAux,"Servi transurb/udp port:%d",ntohs(gvPortUdp));
Log(StrAux);
// Open de Socket
if((gvSocket = socket(AF_INET,SOCK_DGRAM,0))==INVALID_SOCKET)
{
ShowMessage("Erro na criao do socket");
Application->Terminate();
return;
}
Log("Socket criado com sucesso");
// Do the bind
SockAddrIn.sin_family = AF_INET;
SockAddrIn.sin_port = gvPortUdp;
SockAddrIn.sin_addr.s_addr = NULL;
if(bind(gvSocket,(LPSOCKADDR)&SockAddrIn,sizeof(SockAddrIn))==SOCKET_ERROR)
{
ShowMessage("Erro no bind do socket");
Application->Terminate();
return;
}
Log("Bind do socket com sucesso");
}
判斷windows的Desktop及其它目錄
使用API函式SHGetSpecialFolder。shlobj.h裡有SHGetSpecialFolder的原型宣告。這個函式可以幫我們找到windows的Desktop目錄、啟動目錄、我的文件目錄等。
SHGetSpecialFolder需要三個引數。第一個引數是HWND,它指定了"所有者視窗":在呼叫這個函式時可能出現的對話方塊或訊息框。第二個引數是一個整數id,決定哪個目錄是待查詢目錄,它的取值可能是:
CSIDL_BITBUCKET 回收站
CSIDL_CONTROLS 控制面板
CSIDL_DESKTOP Windows 桌面desktop
CSIDL_DESKTOPDIRECTORY desktop的目錄
CSIDL_DRIVES 我的電腦
CSIDL_FONTS 字型目錄
CSIDL_NETHOOD 網路上的芳鄰
CSIDL_NETWORK 網路上的芳鄰virtual folder
CSIDL_PERSONAL 我的文件
CSIDL_PRINTERS 印表機
CSIDL_PROGRAMS 程式組
CSIDL_RECENT 大多數最近開啟的文件列一
CSIDL_SENDTO “傳送到”選單項
CSIDL_STARTMENU 任務條啟動選單項
CSIDL_STARTUP 啟動目錄
CSIDL_TEMPLATES 臨時文件
最後一個引數是pidl地址。SHGetSpecialFolderLocation把地址寫到pidl。
下面的程式碼演示了怎樣使用SHGetSpecialFolderLocation:
//----------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
LPITEMIDLIST pidl;
LPMALLOC pShellMalloc;
char szDir[MAX_PATH];
if(SUCCEEDED(SHGetMalloc(&pShellMalloc)))
{
if(SUCCEEDED(SHGetSpecialFolderLocation(NULL,
CSIDL_DESKTOPDIRECTORY,
&pidl)))
{
// 如果成功返回true
if(SHGetPathFromIDList(pidl, szDir))
{
Label1->Caption = szDir;
}
pShellMalloc->Free(pidl);
}
pShellMalloc->Release();
}
}
//----------------------------------------------------------------------
注意: 有些目錄是空的。有些特定的目錄在這個檔案系統上並沒有一個相應的目錄。
取得本地internet機器的名字及IP地址
一、下面的例子使用 Winsock API 取得本地主機的名字及地址
void __fastcall TForm1::Button1Click(TObject *Sender)
{
hostent *p;
char s[128];
char *p2;
//Get the computer name
gethostname(s, 128);
p = gethostbyname(s);
Memo1->Lines->Add(p->h_name);
//Get the IpAddress
p2 = inet_ntoa(*((in_addr *)p->h_addr));
Memo1->Lines->Add(p2);
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
WORD wVersionRequested;
WSADATA wsaData;
//Start up WinSock
wVersionRequested = MAKEWORD(1, 1);
WSAStartup(wVersionRequested, &wsaData);
}
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
WSACleanup();
}
用C++Builder建立數字簽名
如果你在網路上傳遞一份資料,但卻存在著種種不安全的因素,使你對資料能否原封不動地到達目的地而心存疑惑,這時,你就可以給資料加上數字簽名,從而使對方可以通過驗證簽名來檢查你所傳過去的資料是否已被他人修改。
一、程式原理
數字簽名的工作原理還是比較簡單的,它是根據你所提供的原始資料,經過複雜的演算法,產生特定的資料簽名,對方通過同樣的過程也產生簽名,如果資料已被修改,那麼就不可能得到兩份一模一樣的簽名,從而就可判斷資料已被他人修改。程式設計人員利用Windows的CAPI介面,就可以實現資料的加密、解密和數字簽名。
二、程式清單
下面用C++ Builder的語句來看一下它的具體實現過程。
先來建立數字簽名,假定其資料來自於一個檔案。
//變數宣告:
HCRYPTPROV hProv;
// CSP的控制代碼
HCRYPTHASH hHash;
// 雜湊的控制代碼
const int BUFFER=4096;
// 緩衝區大小常數
BYTE pBuffer[BUFFER];
// 存放讀檔案內容的緩衝區
BYTE pSignature[256];
// 存放簽名的緩衝區
DWORD dSignatureLen=256;
// 簽名的長度
TFileStream *sourceFile;
// 一個檔案流
if(!CryptAcquireContext(&hProv,NULL,NULL,PROV-RSA-FULL,0))
// 連線預設的CSP,接受它的控制代碼放入hProv
{
// 錯誤處理
}
if(!CryptCreateHash(hProv,CALG-MD5,0,0,&hHash))
// 建立一個雜湊物件,得到它的控制代碼放入hHash
{
// 錯誤處理
}
do
{
dReadLen=sourceFile->Read(pBuffer,BUFFER);
if(!CryptHashData(hHash,pBuffer,dReadLen,0))
// 根據檔案的內容計算雜湊值
{
// 錯誤處理
}
}while(!(dReadLen
if(!CryptSignHash(hHash,AT-SIGNATURE,NULL,0,pSignature,&dSignatureLen))
//使用私人金鑰對雜湊值進行數字簽名
//簽名資料放入pSignature,長度放入dSignatureLen
// 錯誤處理
}
對基於檔案的資料簽名進行檢驗。
//變數宣告:
HCRYPTPROV hProv;
// CSP的控制代碼
HCRYPTHASH hHash;
// 雜湊的控制代碼
HCRYPTKEY hPublicKey;
// 公共金鑰的控制代碼
const int BUFFER=4096;
// 緩衝區大小常數
BYTE pBuffer[BUFFER];
// 存放讀檔案內容的緩衝區
TFileStream *sourceFile; // 一個檔案流
BYTE pSignature[256];
// 上一段得到的簽名的緩衝區
DWORD dSignatureLen;
// 上一段得到的簽名的長度
if(!CryptAcquireContext(&hProv,NULL,NULL,PROV-RSA-FULL,0))
// 連線預設的CSP,接受它的控制代碼放入hProv
{
// 錯誤處理
}
if(!CryptGetUserKey(hProv,AT_SIGNATURE,&hPublicKey); // 得到公共金鑰的控制代碼
{
// 錯誤處理
}
if(!CryptCreateHash(hProv,CALG-MD5,0,0,&hHash)) // 建立一個雜湊物件,得到它的控制代碼放入hHash
{
// 錯誤處理
}
do
{
dReadLen=sourceFile->Read(pBuffer,BUFFER);
if(!CryptHashData(hHash,pBuffer,dReadLen,0))
// 根據檔案的內容計算雜湊值
{
// 錯誤處理
}
}while(!(dReadLen
if(!CryptVerifySignature(hHash,pSignature,dSignatureLen,hPublicKey,NULL,0))
{
if(GetLastError()==NTE-BAD-SIGNATURE) ShowMessage(″檔案已被修改″);
}
else
{
ShowMessage(″檔案沒被修改″);
}
以上是一個數字簽名的簡單實現,得到的簽名資料可以單獨儲存,也可以分開儲存。
用Enter 鍵 控 制 焦 點 切 換 的 方 法
在Windows 環 境 下, 要 使 一 個 控 件 取 得 焦 點, 可 在 該 控 件 上 用 鼠 標 單 擊 一 下,或 按Tab 鍵 將 焦 點 移 至 該 控 件 上。 這 種 控 制 焦 點 切 換 的 方 法 有 時 不 符 合 用 戶 的 習慣。 就 圖 一 而 言, 用 戶 就 希 望 用Enter 鍵, 控 制 焦 點 由Edit1 切 換 到 Edit2。 要 實 現這 樣 的 功 能 需 借 助WinAPI 函 數SendMessage 來 完 成。 方 法 是: 先 設Form1 的KeyPreview 屬 性 為true, 然 後 在Form1 的OnKeyPress 事 件 中 加 入 如 下 的 代 碼。這 樣, 用 戶 就 可 以 通 過 按Enter, 鍵 控 制 焦 點 按 定 義 好 的Taborder 順 序 來 移 動 了!
void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
if(Key==VK_RETURN)
{
SendMessage(this- >Handle,WM_NEXTDLGCTL,0,0);
Key=0;
}
}
攔 截 Windows 消 息
- --Borland C++ Builder的API後門
---- 引子
---- C++ Builder不愧為Borland公司的優秀產品,用它來開發Windows程式非常快捷高效,但在程式設計過程中你也會發現它的一些限制性,讓你無法實現自己的想法。比如你無法在修改表單的系統選單;比如使用跟蹤欄時,你找不到StartTrack和EndTrack事件,而偏偏你的程式需要這兩個事件。Windows API程式設計中,你就不會有這些麻煩,只需處理一下WM_SYSCOMMAND和WM_HSCROLL(或WM_VSCROLL)訊息,就能實現上述功能。Windows API的缺點是程式設計十分麻煩,太多的時間要耗在細節上面,但它的功能卻是最強大的。C++ Builder的VCL在功能上只是它的一個子集,因為VCL是在API的基礎上封裝的,封裝時捨棄了一些不常用到的功能。但是程式設計師的想象力沒有被封裝,他們總懷著更大的熱情去實現別出心裁的想法,修改系統選單和給跟蹤欄增加StartTrack和ndTrack事件只是其中的小把戲而已。可是VCL並沒有這些功能,怎麼辦?
---- 幸好,Borland公司沒有把路堵死,而是留了個後門--允許程式設計師自己攔截並處理Windows訊息,就象API程式設計一樣。於是,辦法有了...
---- 方法
---- 攔截Windows訊息需要以下幾步:
---- 在表單標頭檔案內(如Unit1.h)
---- 1. 在類宣告中建立訊息對映表,把某條訊息的處理權交給自定義的訊息處理函式。
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(Windows訊息名,TMessage,訊息處理函式名)
MESSAGE_HANDLER(...)
END_MESSAGE_MAP(TForm)
---- 2. 在類宣告的private區內宣告訊息處理函式。
private: // User declarations
void __fastcall 訊息處理函式名(TMessage &Message);
在表單檔案內(如Unit1.cpp)
---- 3. 寫出訊息處理函式,在這裡實現你需要的功能。比如
void __fastcall MainForm::OnWMHScroll (TMessage &Message)
{
... // 在此加入你自己的程式碼
TForm::Dispatch(&Message);
}
---- 解釋
---- 1. 關於TMessage
---- TMessage是VCL預定義的結構,定義如下:
struct TMessage
{
unsigned int Msg; //訊息
int WParam; //字引數
int LParam; //長字引數
int Result; //訊息結果
};
---- 2. 關於TForm::Dispatch(&Message)
---- 自定義的訊息處理函式末尾最好加一句TForm::Dispatch(&Message),這一句的作用是讓訊息繼續傳遞下去。如果沒有這一句,訊息將被完全攔截,VCL類可能由於得不到訊息而無法實現正常功能。
---- 例項一:修改系統選單
---- 有一些程式,主視窗很小,選單也沒有,如果想加入關於或設定對話方塊,最好的辦法是拿系統選單開刀。Windows API程式設計中,修改系統選單與實現其他功能一樣,不太容易,也不會太難。但在C++ Builder中,表單類(TForm)沒有提供有關係統選單的任何屬性與方法,實現其他功能易如反掌,而修改系統選單似乎難於上青天。
---- 還好,Borland公司允許程式設計師自已處理Window訊息,於是機會來了!
一、用Window API函式修改系統選單
假定表單名為MainForm,設定MainForm::OnCreate()函式:
1. 用GetSystemMenu(MainForm->Handle,false)取得系統選單控制代碼;
2. 用AppendMenu,DeleteMenu,ModifyMenu函式修改系統選單,把新的ID號賦於自定義的選單項。
這時執行程式,可以看到系統選單也被修改,但自定義的選單項卻不能被響應。
二、攔截WM_SYSCOMMAND訊息以響應自定義的選單項
在表單標頭檔案內(如Unit1.h)
1. 在表單類定義末尾加入訊息響應表,取得WM_SYSCOMMAND訊息的處理權
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_SYSCOMMAND,TMessage,OnWMSysCommand)
END_MESSAGE_MAP(TForm)
2. 在表單類定義的private區內加入訊息處理函式宣告
private: // User declarations
void __fastcall OnWMSysCommand(TMessage& Message);
在表單檔案內(如Unit1.h)
3. 寫出訊息響應函式
void __fastcall TForm1::OnWMSysCommand(TMessage& Message)
{
if(Message.WParam==ID_SysMenu_MyItem)
{
// Your Code Here, Do Something
}
TForm::Dispatch(&Message);
}
三、完整程式示例
例項二:給跟蹤欄增加OnStartTrack和OnEndTrack事件
當跟蹤欄用於進度控制時,OnStartTrack和OnEndTrack很可能是你需要的事件。比如在控制多媒體播放進度的場合,當用戶移動滑塊時,你需要OnStartTrack事件讓播放停止,需要OnEndTrack事件定位新的播放位置。但Borland公司沒有提供這兩個事件,我等程式設計愛好者只好自力更生,打攔截Windows訊息的主意了。
一、攔截WM_HSCROLL訊息,給跟蹤欄增加OnStartTrack和OnEndTrack事件
在表單標頭檔案內(如Unit.h)
1. 在表單類定義末尾加入訊息響應表,把WM_HSCROLL訊息處理權交給OnWMHScroll函式。
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_HSCROLL,TMessage,OnWMHScroll)
END_MESSAGE_MAP(TForm)
2. 在表單類定義的private區內加入OnWMHScroll函式宣告。
private: // User declarations
void __fastcall OnWMHScroll(TMessage &Message);
3. 在表單類定義的private區內加入StartTrack和EndTrack函式宣告。
private: // User declarations
void __fastcall TrackBar1StartTrack(TObject *Sender);
void __fastcall TrackBar1EndTrack(TObject *Sender);
在表單檔案內(如Unit.cpp)
4. 寫出OnWMHScroll函式,使它能根據訊息引數呼叫StartTrack和EndTrack函式,在實際意義上產生OnStartTrack和OnEndTrack事件。
5. 寫出StartTrack和EndTrack函式。
如果是垂直跟蹤欄,把上面的WM_HSCROLL改為WM_VSCROLL即可。
二、完整程式示例
尾聲
Borland C++ Builder程式設計中,攔截Windows訊息是一項高階程式設計技術,能讓你儘量挖掘Windows的潛力,尤其讓曾用API程式設計的程式設計師感到心慰。攔截Windows訊息是API盡情發揮的舞臺,當VCL不能為你做什麼時,請想起底層的API。
使用CommaText
有時需要一個方便的方法存放一個StringList,它只有簡單的一行。例如,當你想使用一個INI檔案,如何向一個INI檔案中寫入一行呢,使用CommaText 就能完成這個工作。
這裡有個例子,功能是建立一個blah.ini檔案,並寫入一個如下形式的值:
[My Section]
Memo1=(你在Memo1中輸入的文字)
1.在Form1上有兩個按鈕btnLoad and btnSave和一個Memo1
2.還要加入:
#include
3.定義變數:
const String iniFile="blah.ini",iniSection="My Section",iniValue="Memo1";
4.儲存按鈕程式碼:
void __fastcall TForm1::btnSaveClick(TObject *Sender)
{
TIniFile *ini=new IniFile(ExtractFilePath(Application->ExeName)+iniFile);
ini->WriteString(iniSection,iniValue,Memo1->Lines->CommaText);
delete ini;
}
5.裝載按鈕程式碼:
void __fastcall TForm1::btnLoadClick(TObject *Sender)
{
TIniFile *ini=new TIniFile(ExtractFilePath(Application->ExeName)+iniFile);
Memo1->Lines->CommaText=ini->ReadString(iniSection,iniValue,"");
delete ini;
}
6.以下程式碼支援載入後對內容進行排序,到實際儲存不變:
void __fastcall TForm1::btnSortLoadClick(TObject *Sender)
{
TStringList *sl=new TStringList;
TIniFile *ini=new TIniFile(ExtractFilePath(Application->ExeName)+iniFile);
sl->CommaText=ini->ReadString(iniSection,iniValue,"");
sl->Sort();
Memo1->Lines=sl;
delete ini;
delete sl;
}
程式開始時先顯示資訊框
一、軟體進入主視窗前,先顯示一個資訊框,告訴使用者一些有關該軟體的資訊,比如軟體名稱,版本號等。該資訊框在顯示1~2秒後自動消失。
1.建立New Application,這時系統自動生成一個Form1,這作為主Form.
2.File->New Form 建立一個新Form為Form2,這個作為資訊框。
3.在Form2上新增元件TTimer(System控制元件條上),用於設定資訊框的顯示時間。
4.TTimer的事件OnTimer中加入:Form2->Close();
5.在WinMain()函式中加入:
Application->CreateForm(__classid(TForm2), &Form2);
Form2->ShowModal( ); //這句要自己加入
Application->Run();
並且要把Form2的標頭檔案Unit2.h包括到WinMain()所在的Project1.cpp中。
6.執行程式,將先顯示Form2,顯示時間由TTimer的Interval屬性決定,1000是一秒。
二、軟 件 封 面 的 實 現
現 代 軟 件 設 計 的 流 行 做 法 是, 在 程 序 運 行 完 成 初 始 化 之 前, 先 調 用 一 幅 畫 面 做 為封 面, 通 常 是1/4 屏 幕 大 小, 顯 示 一 下 軟 件 的 名 稱、 作 者、 版 本 等 信 息。 要 用C++ Builder 實 現 這 樣 的 功 能, 方 法 很 簡 單:
① 自 定 義 一 窗 體 類 TSplashForm, 將 其 設 置 成" 透 明 窗 口", 即 BorderIcons 下 的 所 有 選 項 均 置 成false, BorderStyle=bsNone,FormStyle=fsStayOnTop, Position=poScreenCenter;
② 在TSplashForm 窗 體 上 放 置 一TPanel( 相 當 於 圖 形 的 鏡 框);
③ 在TPanel 上 放 置 一TImage 控 件, 調 入 所 需 要 的 圖 形;
④ 對WinMain 函 數 稍 加 修 改, 加 入 如 下 所 示 代 碼 即 可。 需 要 指 出 的 是, 這 段 程式碼 通 過 函 數 FindWindow, 搜 索 內 存 中 是 否 有 窗 口 標 題 為 "Demo" 應 用 程 序 存 在,若 存 在, 則 退 出 程 序 的 運 行。 該 功 能 可 防 止 程 序 的 再 次 運 行。 在 某 些 場 合 這 樣 設計 是 必 須 的。
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
if(FindWindow(NULL,"Demo")!=0)
{
Application- >MessageBox (" 程 序 已 經 運 行!"," 警
告",MB_ICONSTOP);
return 0;
}
TSplashForm *splash=new TSplashForm(Application);
splash- >Show();
splash- >Update();
Application- >Initialize();
Application- >CreateForm(__classid(TForm1), &Form1);
splash- >Close();
delete splash;
Application- >Run();
}
catch (Exception &exception)
{
Application- >ShowException(&exception);
}
return 0;
}
怎樣獲取程式的命令列引數?
你可以用下面的兩種不同的技巧來解決這個問題。
技巧1:首先,也許是最簡單的方法是呼叫VCL ParaStr()函式。你可使用ParamCount()函式來確定到底有多少個命令列引數傳遞給了應用程式。
ParamStr需要一個整數引數並且返回一個AnsiString物件。若引數為0,ParamStr 將返回可執行檔案的全稱路徑。若引數為1,將返回程式名及第一個命令列引數。若引數為2,將返回第二個引數,等等。
作為一個實踐,開啟一個新的專案,在主視窗上放置5個Label,將下面的程式碼新增到視窗的建構函式中:
Label1->Caption = ParamStr(0);
Label2->Caption = ParamStr(1);
Label3->Caption = ParamStr(2);
Label4->Caption = ParamStr(3);
Label5->Caption = ParamStr(4);
再執行程式。一般應能看到類似字串:
E:/CBUILDER/PROJECTS/PROJECT1.EXE
如果沒傳遞引數到程式,那麼Label2到Label5是空字串。關閉程式,從C++Builder選單中選擇 Run | Parameters。輸入幾個引數(-debug -testing -param)再次執行程式。你將看到:
E:/CBUILDER/PROJECTS/PROJECT1.EXE
-debug
-testing
-param
提示: ParamStr 對目錄中的空格能智慧判斷。為證實這點,把生成的EXE檔案拷貝到Program Files目錄下再執行它,你將會看到ParamStr(0)返回全路徑,幷包含空格。
技巧2:第二個方法就是呼叫GetCommandLine API函式。GetCommandLine不需要引數,並且返回一個C風格的char *,包含全部的命令列引數。你將不得不分解字串以取得相關引數。
Label5->Caption = AnsiString(GetCommandLine());
執行後,Label5將為:
"E:/CBuilder/Projects/Project1.exe" -debug -testing -param
如何監視剪貼簿
在Form1的.h的private加上:
void __fastcall ClipboardChanged(TMessage& Msg);
在Form1的.h的public加上:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_DRAWCLIPBOARD,TMessage,ClipboardChanged)
END_MESSAGE_MAP(TForm)
在Form1的.cpp內加上:
void __fastcall TForm1::ClipboardChanged(TMessage& Msg)
{
POINT MousePos;
GetCursorPos(&MousePos);
PopupMenu4->PopupComponent=Form1;
PopupMenu4->Popup(MousePos.x,MousePos.y); //一有變化,就彈出一個選單,複製,剪下或清除都能引發此函式
}
在Form1的.cpp內有一個ToolButton
void __fastcall TForm1::ToolButton9Click(TObject *Sender)
{
static HWND LastHandle;
static bool clip=false;
if(clip==true)
{
ToolButton9->Down=false;
ChangeClipboardChain(Form1->Handle,LastHandle); //結束監視
}
else
{
ToolButton9->Down=true;
Clipboard()->Clear();
Application->Minimize();
LastHandle=SetClipboardViewer(Form1->Handle); //啟動監視
}
clip=!clip;
}
如何使用OnIdle事件
使用OnIdle事件隨時監視剪貼簿內容以改變彈出選單的可執行項。
在Form1的.h的private加上:
void __fastcall OnIdle(TObject* Sender,bool& Done);
在Form1的.cpp內加上:
void __fastcall TForm1::OnIdle(TObject* Sender,bool& Done)
{
bool TextSelected=DBRichEdit1->SelLength>0;
N17->Enabled=TextSelected;//剪下,複製,清除
N18->Enabled=TextSelected;
N20->Enabled=TextSelected;
bool CBHasText=Clipboard()->HasFormat(CF_TEXT);// 需加入#include
N19->Enabled=CBHasText;//貼上
bool HasText=RichEdit1->Lines->Count>0;
N21->Enabled=HasText;//全選
bool HasChanged=RichEdit1->Modified;
ToolButton2->Enabled=HasChanged;
ToolButton4->Enabled=HasChanged;
}
在Form1的OnCreate內加上:
Application->OnIdle=OnIdle;
用C++Builder4.0編寫Win 95下的序列非同步通訊程式
·串列埠操縱的基本方法·
在Win32下,對串列埠的操作就如同對檔案一樣開啟或關閉,對序列資料的讀寫可在使用者定義的讀寫緩衝區中進行。具體使用的函式為:
首先用CreateFile( )開啟通訊串列埠,其中引數lpFileName指向串列埠邏輯名,如“COM1”或“COM2”等,引數dwDesiredAccess定義檔案的讀寫許可權,一般設為GENERIC-READ|GENERIC-WRITE;引數dwShareMode定義資源共享方式,此處必須設為0,為獨佔方式;lpSecurityAttributes定義安全屬性,Win 95下為NULL;dwCreationDistribution定義檔案建立方式;dwFlagsAndAttributes定義檔案屬性和標記,應設為FILE-FLAG-OVERLAPPED,表示非同步通訊方式;hTemplateFile 指向一個模板檔案的控制代碼,在 Windows 95下為NULL。
然後用BuildCommDCB( )和SetCommState( )函式通過通訊裝置控制塊DCB(Device Control Block)設定串列埠通訊引數(如波特率、停止位、資料位、校驗位等),其中BuildCommDCB( )中的字串引數lpDef 定義同DOS命令中MODE的引數格式,關於DCB更具體的設定需要根據使用者對資料流定義、握手訊號及通訊控制要求具體定義,參見有關Windows技術資料。用GetCommState()可以得到當前的DCB引數值。如果需要還可通過SetCommTimeouts()和GetCommTomeouts()重新設定讀寫的超時引數;讀寫緩衝區的設定使用SetupComm(),引數dwInQueue和 dwOutQueue分別定義為輸入和輸出緩衝區的大小。
在串列埠初始化完畢後,還要建立與通訊有關的事件物件。一般使用CreateEvent()函式,它返回一事件控制代碼,其中引數lpEventAttributes指向安全屬性結構地址,在Win 95(無安全屬性)中為NULL;布林引數bManualReset 定義事件重置方式,true 表示手工重置,false表示自動重置(相關函式為SetEvent()和ResetEvent());引數bInitialState定義事件初始狀態,true表示發訊號,否則為不發訊號;lpName是為多程序設定的事件名,對於單程序定義為NULL。然後用SetCommMask()定義使用者程式可監視的通訊事件類別。
以上設定完成後,使用者程式就可以等待通訊事件的產生,一般呼叫函式WaitCommEvent()監視通訊事件,其中引數lpEvtMask指向產生事件的掩碼地址,用於判斷事件產生的性質,lpOverlapped指向重疊結構地址,可簡單定義為NULL。對於串列埠事件的響應一般有四種方式:查詢、同步I/O、非同步I/O和事件驅動I/O,需要根據使用者不同控制要求而定。查詢方式佔用較長的計算機時間,同步I/O方式直到讀取完指定的位元組數或超時時才返回,容易造成執行緒阻塞,非同步I/O用於後臺處理,事件驅動是由系統通知使用者程式發生的事件並進行串列埠操作。比較而言事件驅動I/O方式較靈活。
當有通訊事件產生時,就可用函式ReadFile()和WriteFile()直接對串列埠緩衝區進行讀寫操作了。其中lpBuffer 指向讀寫緩衝區,nNumberOfBytes為要讀寫的位元組數,lpNumberOfBytes為實際讀寫的位元組數,lpOverlapped指定同步或非同步操作。通訊結束後,呼叫函式CloseHandle()將串列埠關閉。
·應用例項說明·
使用以上的API函式,筆者給出了簡化後的串列埠初始化的例項。圖1為使用C++ Builder 元件生成的串列埠通訊基本引數設定的介面例項。
HANDLE hcom; //定義控制代碼
DCB dcb;
OVERLAPPED e; //定義重疊結構
void -fastcall TForm1::OkBtnClick(TObjectSender)
{ hcom=CreateFile("COM2",GENERIC-READ|GENERIC-WRITE,0,NULL,OPEN-EXISTING,
FILE-ATTRIBUTE-NORMAL|FILE-FLAG-OVERLAPPED,NULL); //開啟通訊口
BuildCommDCB("9600,O,8,1",&dcb);
//第一個字串引數實際使用時由圖1選擇後組合,這裡僅簡單說明其格式
SetCommState(hcom,&dcb);
SetupComm(hcom,512,512);//設定讀寫緩衝區
e.hEvent=CreateEvent(NULL,false,false,NULL); //設定事件
SetCommMask(hcom,EV-RXCHAR| EV-TXEMPTY); //設定事件掩碼
OkBtn-〉Enabled=false;}
C++BUILDER非可視元件的訊息處理技巧
一個非可視的元件必須對Windows作業系統或使用者定義的訊息作出響應。然而,由於一個非可視元件沒有視窗,因此它也沒有視窗控制代碼,自然它也不能接收到訊息,為了解決這一問題,我們的思路是建立一個隱藏的視窗,使非可視元件能夠接收到訊息。
為了給你的非可視元件建立一個隱藏的視窗,需要有以下:
1.一個私有變數型(Private Variable)的HWnd來取得視窗控制代碼。
2.一個用來捕捉視窗傳送給元件的函式(a WndProc)。
3.對AllcolateHwnd的呼叫使之建立視窗控制代碼並設定WndProc。
為了清楚的解釋上述思路和展示建立過程,下面我們將以一個具體的例項來說明。
首先我們先建立一個新的元件,在C++Builder中,選擇FILE|NEW...雙擊元件圖示顯示一個新的元件對話方塊改變Ancestor Type為Tcomponent和Class name為TTest並設定完畢。
然後,切換到新元件的標頭檔案,在類的私有部分(private section)加入以下宣告:
HWnd FHandle;
void-fastcall WndProc
(TMessage& Msg);
第一行聲明瞭一個呼叫Fhandle的HWnd變數,這個變數將用於視窗建立後捕獲視窗控制代碼。第二行聲明瞭一個用於接收訊息的WndProc函式。這個函式的宣告必須加以標識,以便限定它是一個WndProc,然後在類宣告Public(公有)部分構造以下宣告:
Viod DoIt( );
這個公有函式將被我們用來測試元件,類宣告應如下:
class PACKAGE TTest : public
TComponent
{
private:
HWnd FHandle;
void-fastcall WndProc
(TMessage& Msg);
protected: