windowsx.h標頭檔:(全部都是網上查的)
http://www.codeproject.com/win32/msgcrackwizard.asp
第一部分:
介紹:
WINDOWSX.H 標頭檔為W32SDK的程式員提供方便(工具?)
很多初中級程式員用C/C++編寫Windwos API的程式時,經常面對麵條式的switch...case語句塊
當你在Window過程(回呼函數、下稱過程)中加入大量諸如WM_COMMAND or WM_CHAR的訊息捕獲時。真是一場噩夢。
關於上千行代碼的Window過程的問題,隨著 C/C++ 7.0 編譯器和Windows SDK for Windows 3.1發行時帶的一個標頭檔而被解決。
這個標頭檔是<windowsx.h> 以及所包含的大量的有用的宏。按照微軟的說法:這些標頭檔所帶來的便利可重複用於下面這些地方(Groups)
:
.在C程式中使用STRICT宏進行嚴格的類型檢查。
.在windows程式中用宏簡化公用性操作。
.使用控制項宏同windows控制項進行通訊。
.windows環境下的訊息解析器(message crackers)(是一個方便的、可移植的、型別安全的處理訊息的方法)以及和他相關的參數和傳回值。
因為訊息解析器嚮導是用於訊息解析器的,其他由這標頭檔帶來的一些有用的宏,我就跳過不講了,如果你想看看關於
WINDOWSX.H的簡要介紹,可以看 MS Knowledge Base Article #83456.
(http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q83/4/56.asp)
好,讓我們說下訊息解析器的優點,當然也包括為什麼這裡提供的這個工具是如此有用。
當你使用W32 SDK編程,用windows過程(通常叫做WndProc)處理視窗和對話方塊訊息時,使用swich-case來捕獲你需要處理的訊息是
非常普遍的做法。假設你想處理WM_COMMAND, WM_KEYUP, WM_CLOSE and WM_DESTROY訊息,你是這樣作的:
LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
// ...
break;
case WM_KEYUP:
// ...
break;
case WM_CLOSE:
// ...
break;
case WM_DESTROY:
//...
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
這是自從Windows1.0誕生以來處理訊息最常見的風格了。而且很肯定,它工作得很好。
但問題是,當你加入一個或多個複雜的特色到你的程式中時,如MDI,OLE 公用控制項等等,
結果形成了一個上千行的Window過程,你開始用PageDn和PageUp來尋找你想要修改的訊息處理代碼了。
訊息解析器的第一個好處就是:他把麵條式的case標籤轉換成類似MFC中易於維護和處理的函數。
第二個好處是:處理函數中合適的參數。你可以簡單使用switch(id)代替原先的switch(LOWORD(wparam)),
因為訊息解析器傳遞給你的是"已解析"的參數,他等價於LOWORD(wparam)。
HANDLE_MSG 這個訊息處理宏在windowx.h中的定義如下:
#define HANDLE_MSG(hwnd, message, fn) /
case (message) : return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
你想要把你的代碼做成"訊息解析"版的,你需要提供一個解析宏HANDLE_MSG及其函數來處理你的訊息.
在window過程裡HANDLE_MSG宏需要三個參數:視窗控制代碼(hwnd), 訊息(WM_XXXX), 處理你訊息的函數(function)。
為更好地解釋這個:看下面,我們把上面那段代碼轉換成了下面的代碼:
LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
HANDLE_MSG (hwnd, WM_COMMAND, OnCommand);
HANDLE_MSG (hwnd, WM_KEYUP, OnKeyup);
HANDLE_MSG (hwnd, WM_CLOSE, OnClose);
HANDLE_MSG (hwnd, WM_DESTROY, OnDestroy);
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
哇,太好了,緊湊且易於管理的window過程。現在你可以去定義你的訊息處理函數了(OnKeyUp, OnClose, and OnDestroy)
還有一個真正的好處,你可以在visual studio IDE 環境中直接跳轉到訊息處理函數。
圖片:...(看原文)
有個問題是:當你每加入一個訊息處理,你必須在windowx.h中尋找相關參數的定義。
因為訊息處理參數的格式是明確的而不能由你隨心所欲。但在標頭檔中進行重複搜尋是乏味且易出錯的。
訊息解析嚮導工具用於解決這個:他允許你粘貼你需要的函數參數,
而你如果只是打草稿,他也會在在你訊息處理中寫上一個模板化的window或對話方塊過程 (??這句有疑問)
訊息前驅宏(message fowarding): WINDOWSX.H 的另一個特色 (訊息前向?)
WINDOWSX.H另一個特色是訊息前驅的可能性,
它是用於"解壓"訊息處理參數到其他函數調用(如PostMessage, SendMessage, CallWindowProc等)所需要的合適的WPARAM和LPARAM值。
假設我們想用SendMessage發送一個WM_COMMAND訊息到父視窗,"類比"一個在名為IDC_USERCTL控制項上的雙擊(通過發送BN_DBLCLK的通知碼)
我們通常這麼做的:
SendMessage (hwndParent, WM_COMMAND,
MAKEWPARAM(IDC_USERCTL, BN_DBLCLK), // WPARAM 的低16位是控制項ID,高16位是通知碼
(LPARAM)GetDlgItem(hwnd, ID_USERCTL)); //LPARAM 為控制項控制代碼
這是相當複雜的文法。SendMessage希望WPARAM參數的低字是控制項ID而高字是通知碼,LPARAM參數是控制項控制代碼,控制代碼我們通過GetDlgItem這
個API函數得到。
上述代碼可以被轉換為WINDOWSX.H的訊息前驅宏,FORWARD_WM_xxxxx。
對於每個訊息,訊息前向宏用同樣的方式"打包"訊息解析嚮導建立的函數參數,
並且傳遞給你的處理函數"已解壓"的參數(LPARAM/WPARAMs)。
例如:對於一個myWnd視窗的WM_COMMAND訊息,訊息解析器嚮導將產生如下的函數原形:
void myWnd_OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
那麼,這些解析的參數也同樣用於訊息前驅宏,這樣上面那使人混亂的SendMessage調用可以簡化為:
FORWARD_WM_COMMAND (hwndParent, IDC_USERCTL,
GetDlgItem(hwnd, ID_USERCTL), BN_DBLCLK, SendMessage);
使用所有這些訊息解析器支援的訊息簡單可行。(第一部分完)
在使用訊息分流器來處理一個訊息之前,應該開啟Wi n d o w s X . h檔案並搜尋要處理的訊息。例如,如果搜尋W M _ C O M M A N D,將會找到檔案中包含下面程式碼的部分:
/* void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) */
#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) /
((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)
#define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) /
(void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
第一行是注釋行,展示要編寫的函數原型。下一行是H A N D L E _ W M _ *宏,我們已經討論過。最後一行是訊息轉寄站( f o r w a r d e r)。假定在你處理W M _ C O M M A N D訊息時,你想調用預設的視窗過程,並讓它為你做事。這個函數應該是這個樣子:
void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) {
//Do some normal processing.
//Do default processing.
FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, DefWindowProc);
}
F O RWA R D _ W M _ *宏將分流開的訊息參數重新構造成等價的w P a r a m和l P a r a m。然後這個宏再調用你提供的函數。在上面的例子中,宏調用D e f Wi n d o w P r o c函數,但你可以簡單地使用S e n d M e s s a g e或P o s t M e s s a g e。實際上,如果你想發送(或登記)一個訊息到系統中的任何視窗,可以使用一個F O RWA R D _ W M _ *宏來協助合并各個參數。
應注意Windows.h和Windowsx的不同之處.
2.tchar.h 標頭檔
http://msdn2.microsoft.com/zh-cn/library/c426s321(VS.80).aspx
3.CmnHdr.h 標頭檔
它包含宏及連結程式指令.
讀者要想建立本書的樣本程式,必須要對編譯器和連結程式的開關選項進行設定,筆者已經將設定方面的細節
放在了CmmHdr.h標頭檔中了
因為無法將所有的設定都放在這個標頭檔裡,我們對每個樣本程式的項目設定做了一些改變。對每個項目,我們顯示Project Settings對話方塊,然後做下面所說的改變。
" 在G e n e r a l欄,設定Output Files目錄,這樣所有最終的. e x e和. d l l檔案都在一個目錄之下。
" 在C / C + +欄,選擇Code Generation 條目,並對Use Run-Time Library 欄位選擇Multithreaded DLL。
注意要對每個項目的D e b u g建立和R e l e a s e建立都做上述兩個改變。
所有的樣本程式都要包含C m m H d r. h標頭檔,並且要在其他標頭檔之前包含.這是因為CmnHdr.h標頭檔中有一些連結程式指令,比如#define _WIN32_WINNT 0x0500 (它是Windows版本建立選項)的定義必須放在Windows.h之前,才能調用了Microsoft Windows
2000中提供的新函數.否則編譯器將產生錯誤。微軟用_ W I N 3 2 _ W I N N T符號來保護這些函數,以使程式員開發的應用程式能夠運行在Windows 98及Windows NT的多個版本上。
Unicode建立選項.
筆者編寫的所有這些樣本程式既可按A N S I來編譯,也可按U n i c o d e來編譯。當針對x 8 6 C P U體繫結構來編譯這些程式時, A N S I為預設選擇,這樣程式可以在Windows 98上執行。但對其他C P U體繫結構建立程式就要用U n i c o d e,這樣程式可以佔用較少的記憶體,並且執行得更快。
為了對x 8 6體繫結構建立U n i c o d e版本,只需將定義U N I C O D E的那一行代碼的注釋符去掉,並重建程式。通過在CmnHd r. h定義U N I C O D E宏,可以很容易地控制如何建立樣本程式。關於U n i c o d e的詳細內容,可參見第2章。
視窗定義和第四級警告
本節我確保警告級設定為3,而且C m nH d r. h包含標準的Wi n d o w s . h標頭檔。當包含了Wi n d o w s . h時,在我編譯其餘代碼時就設定第4級警告。在第4級警告上,編譯器對那些我不認為有問題的內容發出“警告”,這樣我通過使用#pragma warning指令顯式地告訴編譯器忽略某些良性的警告錯。
Pragma訊息協助宏
使用chMsg宏,例如#pragma chMSG(Fix this later),這個宏讓編譯器輸出原始碼檔案的名字,以及p r a g m a出現的行號。使用Microsoft Visual Developer Studio,在輸出視窗上雙擊這一行,將會自動定位到相應檔案的確切位置上。還有一個方便之處, c h M S G宏不要求對文本串使用引號。.
chINRANGE和chDIMOF宏
chINRANGE 宏用來查看一個數值是否在另外兩個數值之間.
chDIMOF只是返回一個數組中元素的數目,這個宏是用s i z e o f操作符先計算整個數組的位元組數,然後再用這個數除以數組中一個資料項目所佔的位元組數,從而得出結果。
chBEGINTHREADEX宏
多線程樣本程式都使用了微軟的C/C + +運行時函數庫中的_ b e g i n t h r e a d e x函數,而不是作業系統的C r e a t e T h r e a d函數。我使用這個函數是因為_ b e g i n t h r e a d e x函數為新線程做好了準備,使新線程能夠使用C / C + +運行時函數庫中的函數,而且還因為它保證線上程返回時清除每個線程的C / C + +執行階段程式庫資訊.
儘管_ b e g i n t h r e a d e x函數用的參數值同C r e a t e T h r e a d函數用的參數值是一樣的,但二者的參數的資料類型都不相匹配。
為了避免編譯器警告,我在C m n H d r. h中定義了一個c h B E G I N T H RE A D E X宏,替我執行所有這些轉換:
chMB宏
c h M B宏只是顯示一個訊息框。訊息框的標題是調用進程可執行代碼的全路徑名
chASSERT和chVERIFY宏
在我開發這些樣本程式時,為了尋找潛在的問題,我在整個代碼中多處使用c h A S S E RT宏。這個宏測試由x所標識的運算式是否為T R U E,如果不是,則顯示一個訊息框指出失敗的檔案、行和運算式。在程式的發行建立中,這個宏什麼也不做。c h V E R I F Y宏與c h A S S E RT宏差不多,區別在於不論是調試建立(debug build)還是發行建立(release
build),c h V E R I F Y都要對錶達式進行測試。
chHANDLE_DLGMSG宏
當你通過對話方塊使用訊息分流器時,不應該使用微軟的Wi n d o w s X . h 標頭檔中的H A N D L E _ M S G宏,因為這個宏並不能返回T R U E或FA L S E來指出訊息是否由對話方塊的過程來處理。我定義的c h H A N D L E _ D L G M S G宏會通知視窗訊息的傳回值,適當地處理傳回值,以便在一個對話方塊過程中使用。
chSETDLGICONS宏
由於多數樣本程式使用一個對話方塊作為主視窗,你必須手工改變對話方塊表徵圖,以便讓它正確地顯示在Ta s k b a r(任務條)、任務切換視窗和程式本身的標題上。當對話方塊接收到一個W M _ I N I T D I A L O G訊息時,總要調用c h S E T D L G I C O N S宏,以正確設定表徵圖。
OS版本檢查內嵌函式
本書的大多數樣本程式可運行在所有平台上,但也有一些程式要求一些Windows 95和Windows 98所不支援的特性,有些程式要求一些只在Windows 2000中提供的特性。每個程式在初始化時要檢查宿主系統的版本,如果要求更適用的作業系統時,就顯示一個通知。
對那些不能在Windows 95和Windows 98上啟動並執行程式,你會看到,在程式的_ t Wi n M a i n函數中有一個對Wi n d o w s 9 x N o t A l l o w e d函數的調用。對於要求Windows 2000的樣本程式,你會看到在程式的_ t Wi n M a i n函中有一個對c
h Wi n d o w s 2 0 0 0 R e q u i r e d函數的調用。
確認宿主系統是否支援Unicode
有一種辦法能夠知道我的程式是對U n i c o d e建立的,但可能在Windows 98系統上運行。所以我建立了一個CUnicodeSupported C++類。這個類的建構函式只是檢查宿主系統是不是對U n i c o d e有良好的支援,如果不是,就顯示一個訊息框,並且進程結束。
讀者會看到在C m n H d r. h中,我建立了這個類的一個全域的靜態執行個體。當我的程式啟動時,C / C + +執行階段程式庫啟動代碼調用這個對象的建構函式。如果這個建構函式檢測到作業系統完全支援U n i c o d e,建構函式返回而程式繼續執行。通過建立這個類的全域執行個體,我不需要在每個樣本程式的原始碼模組中再增加特殊的代碼。對於非U n i c o d e的程式建立,不需要聲明或執行個體化上述的C
+ +類。讓程式只管運行就是。
強制連結程式尋找(w)WinMain進入點函數
我在C m n H d r. h中加入了一個p r a g m a,強制連結程式去尋找( w ) Wi n M a i n進入點函數,即使是用Visual C++建立了一個Win32 ConsoleA p p l i c a t i o n項目.