天天看點

Windows SDK程式設計(Delphi版) 之 Windows程式設計概述與架構

   Windows的目的是使那些熟悉系統基本知識的人能夠坐下來,不必進行任何預訓練,就能實際運作任何應用程式。為實作此目的,Windows向使用者提供了一些始終不變的接口。理論上來說,如果使用者能運作起Windows,那麼也就能運作那些基于那種接口的所有程式。而我們作為程式員的職責就是用Windows提供的這些接口來開發基于Windows的應用程式。Windows提供的這些接口,我們通俗的稱為Win32 SDK API。

   為許多作業系統編寫程式時,是你的程式使其與作業系統發生作用。比如在DoS程式中,是程式在要求做諸如是輸入和輸出這樣的工作。不同的是,用傳統的方法編寫的程式調用作業系統,作業系統卻不調用你的程式。不過在大多數情況下,windows以相反的方式進行工作,既Windows會來調用你的程式。其過程為:Windows一直處于等待狀态,直到由Windows發送一條消息,該消息通過由Windows調用的特殊函數傳送到使用者的程式中。隻要程式接收到一條消息,它就會産生相應的動作,這便是Windows應用程式的的驅動方式,我稱它為消息驅動模型。

   Windows程式設計基礎

      入口函數WinMain:

      以前學過C的人都曉得,C中的入口就是main()函數,任何程式開始,都由main進入開始執行,在Windows的程式設計中,也一樣有一個入口函數,本函數與C的入口函數差不多,不過是WinMain,所有的Windows程式都由Winmain入口開始往下執行。另外,WinMain有一個調用約定,必須制定為WINAPI約定。和C的main傳回一樣,傳回整形。Windows的标準語言是C,是以這個說明是針對于C版本的,那麼我們Delphi程式員呢?如果有人看過Delphi的工程檔案dpr裡面的内容的話,就應該曉得,Delphi的入口是由dpr檔案的begin end之間入口開始往下執行。這個我們可以看着為Delphi的編譯器的一個魔法,它将WinMain函數在Delphi的内部給我們已經制定好了,編譯的時候自動進入Winmain,然後再Winmain中進入begin end之間開始我們的視窗過程,是以我們可以将begin end這個看做為WinMain的一個子部分,那麼就可以簡單的了解為這個就相當于是一個WinMain了,同時也不用我們自己去聲明一個WinMain這樣的入口函數,而隻需在工程檔案的begin .. end之間寫我們的代碼就可。

     視窗過程

      首先 視窗過程是由Windows調用的,而不是由我們程式自己調用的函數。  所有的Windows程式必須包括一些特殊的函數,他們不由我們程式自己調用,而是由Windows作業系統來調用。這個函數通常被稱為視窗過程或者視窗函數。當Windows需要向程式中傳遞一條消息時,Windows将調用視窗函數來處理這個消息。視窗函數在它的參數内接受消息。所有的視窗函數的傳回類型為LRESULT,調用約定為CALLBACK。LRESULT實際上就是一個整形,在Delphi中可以用LongInt來表示。調用約定CallBack表示此過程屬于系統回調函數,在Delphi中用Stdcall約定就行。實際上凡是關乎作業系統的相關函數都指定為Stdcall調用約定。一個視窗函數過程内部,我們通常能夠看到一個很大的Case end這樣的結構。用來标記對不同的消息做不同的處理。Windows的消息有很多很多個,很多時候,我們不必為我們關心的消息提供消息處理過程,此時我們就可以使用Windows的預設處理過程為DefWindowProc函數,用這個函數就可以按照系統的預設方式進行處理。

     視窗類 

       這裡說的視窗類,是指視窗的類型和樣式,而不是我們面向對象中所說的那個類了,這個需要分解清楚。Windows的應用程式都是由一個個的視窗組成的。而要生成這些視窗,我們必須先注冊一個視窗類給系統,這樣以後建立視窗的時候,就會根據你所給定的樣式等資訊進行建立了。

     消息循環

       在前面說了Windows是消息驅動程式運作的,在每個應用程式中,都有一個自己的消息隊列,從應用程式的消息隊列中不斷的取回消息建構了消息循環運轉。是以所有的應用程式都必須在内部建立一個消息循環,此循環從應用程式的消息隊列中讀取任何未處理的消息,然後将它送還給Windows,這樣以該消息作為參數的就會被對應的視窗過程調用以對該消息進行處理。

    通過前面幾點的了解,應該大緻的曉得了一個Windows應用程式所具備的幾個要素和步驟了:

1、聲明入口函數(這個在Delphi中沒有)

2、定義一個視窗類

3、注冊視窗類

4、建立視窗

5、顯示視窗

6、開始消息循環。

通過這6個步驟,就基本上能夠實作一個Windows的視窗應用程式了。下面我來給一個樣本程式

代碼

本程式,就是一個最基本的Windows程式,本程式不包含任何功能,僅僅是顯示一個視窗。現在來分解一下這個程式。由于Delphi中不必用WinMain,是以我自己在内部建構了一個WinMain函數,傳遞一個參數HthisInstance,本參數表示目前應用程式的執行個體,實際上Delphi運作啟動程式的時候,這個執行個體已經通過Delphi内部包裝的WinMain函數給回報回來了,這個執行個體句柄,我們也可以通過GetModuleHandle(0)來獲得。那麼這個參數的目的有什麼作用呢?還是需要将WinMain的原型拿過來分析說明一下!WinMain的原型為:

  int WINAPI WinMain(Hinstance hThisInst,Hinstance hPrevInst,LPSTR lpszArgs,int nWinMode);

hThisInst和hPrevInst都是句柄類型,hThisInst指程式的目前執行個體,因為Windows是個多任務作業系統,一次可以同時運作相同程式的多個執行個體,是以用這個執行個體句柄來标記到底屬于哪個。hPrevInst這個我們可以不同管他,在我們的系統中,他始終為nil,他存在的唯一理由就是與Win3.1時代的程式相容,意思是指前一個程式執行個體。lpszArgs指定為指令行參數,就想ping 127.0.0.1這個裡面的127.0.0.1這樣的就是參數,對應着Delphi的paramstr(1)等。nWinMode參數儲存的值決定如何顯示視窗。

    定義視窗類。我這裡用的是TWndClassEx,在Windows中聲明如下

  tagWNDCLASSEXA = packed record

    cbSize: UINT;  //指定本類結構體大小

    style: UINT;  //指定為視窗樣式

    lpfnWndProc: TFNWndProc;  //指定視窗過程

    cbClsExtra: Integer;  //附加的資訊

    cbWndExtra: Integer;

    hInstance: HINST;//所屬的應用程式執行個體

    hIcon: HICON; //圖示32*32,大圖示

    hCursor: HCURSOR; //光标

    hbrBackground: HBRUSH; //背景畫布對象句柄

    lpszMenuName: PAnsiChar;//菜單資源名

    lpszClassName: PAnsiChar;//類名

    hIconSm: HICON;//小圖示16*16

  end;

  每個Windows應用程式都有兩個與其相關的圖示,一個是标準尺寸(32*32),另一個是小圖示,當應用程式被最小化時,使用小圖示。當應用程式快捷方式,以及在硬碟中顯示時,顯示标準圖示。這裡我通過了

LoadIcon這個函數來加載一個圖示資源。原型為

HICON LoadIcon(Hinsance hinst,LPCSTR IconResName);

本函數将傳回一個圖示句柄。hInst指定包含圖示的執行個體子產品,我這裡指定的是0,0表示調用系統的,如果指定為我們本應用程式的執行個體的話,這個傳回是會失敗的哦,因為我們應用程式内部并不包含對應的圖示資源。第二個參數指定為圖示資源名。系統預設的有

IDI_APPLICATION   預設圖示

IDI_ERROR  錯誤符号

IDI_INFORMATION  資訊

IDI_QUESTION  問号

IDI_WARNING  感歎号

IDI_WINLOGO  視窗标志

讀取滑鼠光标,用LoadCursor,用法與LoadIcon差不多,一些系統的預設光标樣式

IDC_ARROW   預設箭頭指針

IDC_CROSS    十字線

IDC_IBEAM     垂直工字型

IDC_WAIT      沙漏

視窗的背景,通過使用API函數來獲得背景畫刷的句柄。我這裡用的是CreateSolidBrush,目的是建立一個畫刷的GDI對象,CreateSolidBrush中的參數指定為畫刷的顔色。另外,還有一種方式就是通過GetStockObject函數來獲得一個系統内部的畫刷對象。GetStockObject函數用于獲得一些标準顯示對象的句柄,包括畫刷,畫筆和字元字型等。原型為

HGDIOBJ GetStockObject(int Object);

汗水傳回object所指定的對象的句柄(HGDIOBJ表示一個GDI句柄)

一些系統的内置畫刷:

BLACK_BRUSH  黑色

DKGRAY_BRUSH  黑灰

HOLLOW_BRUSH 

LIGRAY_BRUSH  淺灰

WHITE_BRUSH  白色

比如,可以嘗試,将我上面的CreateSolidBrush換成GetStockObject(WHITE_BRUSH)這樣的看一下效果。

一旦,當視窗類型,指定好了之後,我們就可以通過使用RegisterClassEx來注冊一個視窗類了。

ATOM RegisterClassEx(wndclassex: TWNDCLASSEX)

ATOM表示一個原子類型,表示全系統唯一。

建立視窗

  通過上面的步驟,注冊成功了之後,就可以依據注冊的視窗類來建立一個視窗了,建立視窗我用了CreateWindowEX,

function CreateWindowEx(dwExStyle: DWORD; lpClassName: PChar;

  lpWindowName: PChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;

  hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND;

參數一指定了視窗的擴充樣式,我這裡啥擴充都沒,是以指定為0,參數二指定為類名,這個名字必須要和我們注冊的視窗類的名字一樣,是以直接指定為WndClass.lpszClassName,參數三指定為視窗标題,參數4指定為視窗的樣式,我這裡指定的是層疊樣式視窗,其常見類型為:

WS_ORERLAPPED   邊框重疊視窗

WS_MAXIMIZEBOX  最大化

WS_MINIMIZEBOX  最小化

WS_SYSMENU  系統菜單

WS_HSCROLL  水準滾動

WS_VSCROLL  垂直滾動

爾後的參數就是指定了視窗的位置,寬度和高度,我這裡都是指定了CW_USEDEFAULT采用了系統預設的。如果視窗建立成功,将會傳回建立的視窗句柄,否則傳回0。

  建立了視窗之後,窗體并不會被顯示,要顯示視窗,我們需要嗲用ShowWindow函數,本函數參數一指定要顯示的視窗句柄,參數2指定為顯示方式,顯示方式有下面幾種形式:

SW_HIDE     隐藏

SW_MINIMIZE  最小化

SW_MAXIMIZE  最大化

SW_RESTORE  恢複為正常大小

ShowWindows函數,傳回視窗的前一個顯示狀态,如果顯示了視窗則傳回非0值,如果沒顯示,則傳回0

最後,調用了UpdateWindow函數,本函數的目的是告訴Windows向使用者的應用程式發送一條消息,該消息是需要更新一下主視窗。

   最後便是消息循環了。消息循環是所有Windows應用程式的組成部分,它的作用就是接受并處理Windows發送的消息。運作程式時,它不斷的被系統發送消息。直到所有這些消息都被讀取以及處理。否則就一直儲存在應用程式的消息隊列裡。每次應用程式準備好去讀取另一個消息時。就必須調用GetMessage。原型為

BOOL GETMessage(msg: tagMsg;hwnd: THandle;min,max: UINT);

msg指定消息結構體,這個在windows單元中有定義,在本結構體中有包含hwnd視窗句柄,用來标石采用哪個視窗過程處理。也包含有消息id,同時表示采用視窗過程的那個消息處理方式處理。其中的WPARAM和lparam是消息的附加資訊。time指定了消息的發送時間,以毫秒為機關指定,pt包含滑鼠的坐标。在應用程的消息隊列中如果沒有一條消息,則GetMessage調用會向Windows回傳一個控制指令(這個以後再說)。

   GetMessage的hwnd參數将指定所獲得的消息傳給哪個視窗。一個應用程式可能有很多個視窗,而使用者也許隻想接收某個具體視窗的消息,如果想接收指向所有應用程式的所有消息,則指定為0。其他兩個參數指定了要接收的消息的範圍,通常希望接收所有的消息,是以一般都指定為0.當使用者程式結束時,getmssage傳回0,此時消息循環接收。否則傳回非0,如果發生錯誤,則傳回-1(至于錯誤如何處理,請大家思考一下)。然後消息循環段内,有兩個函數

TranslateMessage和DispatchMessage。這兩個函數,第一個函數是将系統産生的虛拟鍵代碼轉成字元消息。可能并不是所有程式都需要調用它,但是打部分程式還是需要處理按鍵資訊的。一旦讀取并轉換了消息,通過DispatchMessage就将消息再派遣回Windows,于是Windows儲存該消息,直到能将其傳遞給程式的視窗uhanshu為止。一旦消息循環結束,WinMain也就結束,程式也随之結束。

   最後就是視窗函數,我在上面定義的視窗函數為WndProc,可見我在這個函數中僅僅處理了一個唯一的消息就是視窗釋放的時候,發送了一個PostQuitMessage的處理。該函數将發送一個WM_QUIT的消息給應用程式,然後GetMessage獲得WM_QUIT的時候,就會為False,進而退出循環結束消息。其他的消息處理,我全部都是調用的預設處理過程DefWindowProc來對消息進行處理。

本文轉自 不得閑 部落格園部落格,原文連結:http://www.cnblogs.com/DxSoft/archive/2010/06/09/1754677.html   ,如需轉載請自行聯系原作者