Windows開發的常識1)視窗Windows中最基本的概念也許就是視窗了,每一個前景程式都至少有一個視窗,一個視窗也是你可以看到的部分,比如,QQ有如下的登入視窗基本上你在Windows中可見的都是一個視窗,視窗也是Windows中用於使用者直接互動的基本元素(GUI程式)。2)控制代碼視窗、檔案、socket、訊號量、管道、郵槽(mailslot)……都是Windows平台中的基本對象,為了操作這些對
象,我們需要一個能夠引用這些對象的東西,這個引用這些對象的東西就是控制代碼(Handle)。控制代碼對於資源就像遙控器對於電視機,用遙控器能更好地操作電視機而不用關心內部實現的細節,控制代碼也是這樣的,用控制代碼你能更好地操作Windows對象,而不需要關係其內部的實現細節。事實上,你想操作Windows對象也只能通過控制代碼操作。比如,你想操作一個線程,你看看SuspendThread的原型如下所示:(第一個參數就是一個線程控制代碼)
SyntaxDWORD WINAPI SuspendThread( __in HANDLE hThread);
又比如,你想讀一個檔案,其函數ReadFile的原型如下:(第一個參數就是一個檔案的控制代碼。)
BOOL WINAPI ReadFile( __in HANDLE hFile, __out LPVOID lpBuffer, __in DWORD nNumberOfBytesToRead, __out_opt LPDWORD lpNumberOfBytesRead, __inout_opt LPOVERLAPPED lpOverlapped);
…………類似的例子還有很多,基本上想操作Windows對象都需要指向它們的控制代碼,控制代碼在語義上與C中的指標一樣,但是,文法上是完全不一樣的。
3)進程一些作業系統教材上對進程的定義為:進程是一個程式的一次運行過程。這個定義其實是對的,但是,也是錯的,為什麼呢?在Windows之前還有許多其它的作業系統,比如OS 360、UNIX……進程的概念提出來的時候Windows 還根本沒出生呢。不過,在Windows中,進程的概念完全不是這樣的,在Windows裡面,進程是程式隔離的基本單元,每一個32位應用程式在自己的進程空間裡面運行,進程只為這個程式提供4G的虛擬位址空間,並且,不同的進程之間互相不干擾(當然,後面會講到,進程之間會通訊)。4)線程在Windows裡面,真正運行程式的其實是線程,線程在進程提供的4G虛擬位址空間裡面運行,它執行PE檔案的.text段。也就是說,線程才是真正的執行體。基本的正常的Windows程式碼建立一個工程,還是按照上次的方法,在上一節裡面有,然後輸入以下代碼:
#include <windows.h>LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ static TCHAR szAppName[] = TEXT ("BossJue"); HWND hwnd; MSG msg; WNDCLASSEX wndclassex = {0}; wndclassex.cbSize = sizeof(WNDCLASSEX); wndclassex.style = CS_HREDRAW | CS_VREDRAW; wndclassex.lpfnWndProc = WndProc; wndclassex.cbClsExtra = 0; wndclassex.cbWndExtra = 0; wndclassex.hInstance = hInstance; wndclassex.hIcon = LoadIcon (NULL, IDI_APPLICATION); wndclassex.hCursor = LoadCursor (NULL, IDC_ARROW); wndclassex.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); wndclassex.lpszMenuName = NULL; wndclassex.lpszClassName = szAppName; wndclassex.hIconSm = wndclassex.hIcon; if (!RegisterClassEx (&wndclassex)) { MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW, szAppName, TEXT ("WindowTitle"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow (hwnd, iCmdShow); UpdateWindow (hwnd); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } return msg.wParam;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static const LPTSTR text = TEXT("Hello,World"); HDC hdc; PAINTSTRUCT ps; switch (message) { case WM_CREATE: return (0); case WM_PAINT: hdc = BeginPaint (hwnd, &ps); TextOut (hdc, 0, 0, text, lstrlen(text)); EndPaint (hwnd, &ps); return (0); case WM_DESTROY: PostQuitMessage (0); return (0); } return DefWindowProc (hwnd, message, wParam, lParam);}
運行
按下Ctrl + F5,你可能會碰到如下的連結錯誤:
不要急,這個錯誤的設定是因為Windows把這個程式當作console程式,而console程式預設入口函數是main,而我們在這裡面並沒有定義main,而是定義了一個叫WinMain的入口函數,所以連結器不認,而我們希望的卻是編譯器認為我們這個程式是一個GUI程式,GUI程式的入口函數是WinMain(如果你用#pragma
comment(linker…………)修改的話當然也是可以的,這種方法我在第一章裡面就寫到了。當然,我們還可以直接在Visual Studio IDE裡面直接設定,方法是開啟項目屬性,然後如下設定:這樣設定之後,再按Ctrl + F5,有沒有看到如下的一個視窗呢?對,就是這樣,你看到的就是一個正常的Windows程式的編寫。代碼解釋編寫Windows程式的過程編寫一個Windows程式,你需要做的是三步,第一步是註冊一個視窗類別,第二步是建立視窗,第三步是編寫訊息響應函數。其中第一步和第二步是基本固定的,除了個別的參數需要自己調整之外,最重要的是第三步,一個Windows程式中,代碼量最大的基本都在第三步。在Windows應用程式裡面,如果我們想要接收使用者的輸入、在視窗上面顯示一些資訊……我們都需要處理相關的訊息,Windows把與這個程式相關的事件都以訊息告訴程式,至於怎麼處理這些訊息,則是我們自己的事情。上面的RegisterClassEx
(&wndclassex)其實就是註冊視窗類別CreateWindowEx (WS_EX_OVERLAPPEDWINDOW,
szAppName,
TEXT ("WindowTitle"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL); 其實就是建立視窗,LRESULT CALLBACK WndProc (HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam)其實就是那個訊息處理函數,只不過,這個訊息處理函數不需要我們自己調用,當有訊息的時候,Windows會自己調用這個函數來處理,我們只需要編寫處理代碼,但是,編寫的代碼不需要我們自己手動調用(這也許就是Callback函數的原因)。剩下的剩下的如果還有不懂的自己可以讀msdn,比如,對CreateWindow不熟悉,可以直接在MSDN裡面搜尋CreateWindow,MSDN不僅會告訴你這個函數怎麼用,還會告訴你訊息迴圈是什麼之類的,在此我就不仔細展開說明了。