《Windows編程零基礎》__2 一個完整的程序

Windows開發的常識

1)窗口

Windows中最基本的概念也許就是窗口了,每一個前臺程序都至少有一個窗口,一個窗口也是你可以看到的部分,比如,QQ有如下的登錄窗口
基本上你在Windows中可見的都是一個窗口,窗口也是Windows中用於用戶直接交互的基本元素(GUI程序)。

2)句柄

窗口、文件、socket、信號量、管道、郵槽(mailslot)……都是Windows平臺中的基本對象,爲了操作這些對 象,我們需要一個能夠引用這些對象的東西,這個引用這些對象的東西就是句柄(Handle)。句柄對於資源就像遙控器對於電視機,用遙控器能更好地操作電視機而不用關心內部實現的細節,句柄也是這樣的,用句柄你能更好地操作Windows對象,而不需要關係其內部的實現細節。事實上,你想操作Windows對象也只能通過句柄操作。比如,你想操作一個線程,你看看SuspendThread的原型如下所示:(第一個參數就是一個線程句柄)
Syntax
DWORD 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不僅會告訴你這個函數怎麼用,還會告訴你消息循環是什麼之類的,在此我就不仔細展開說明了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章