Windows法式外部運轉機制實例詳解。本站提示廣大學習愛好者:(Windows法式外部運轉機制實例詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是Windows法式外部運轉機制實例詳解正文
本文以孫鑫先生VC++教程中的法式為基本,具體講授了Windows法式外部運轉機制,信任可以贊助年夜家更好的懂得Windows法式運轉道理及響應的VC++法式設計。詳細內容以下:
創立一個Win32運用法式步調:
1、編寫WinMain函數;
2、創立窗口(步調以下):
a、設計(一個)窗口類(WNDCLASS)
b、注冊(該)窗口類。
c、創立窗口。
d、顯示並更新窗口。
3、編寫新聞輪回。
4、編寫窗口進程函數。
//WinMain.cpp #include <windows.h> #include <stdio.h> LRESULT CALLBACK WinAzeProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { //設計一個窗口類 WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wndcls.hCursor = LoadCursor(NULL, IDC_CROSS); wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); wndcls.hInstance = hInstance; //運用法式實例句柄由WinMain函數傳出去 wndcls.lpfnWndProc = WinAzeProc; wndcls.lpszClassName = "aze_003"; wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&wndcls); //注冊窗口類 //創立窗口,界說一個變量用來保留勝利創立後前往的句柄 HWND hwnd; hwnd = CreateWindow("aze_003", "first Application", WS_OVERLAPPEDWINDOW, 0, 0, 600, 500, NULL, NULL,hInstance, NULL); ShowWindow(hwnd, SW_SHOWNORMAL); //顯示窗口 UpdateWindow(hwnd); //刷新窗口 //界說新聞構造體,開端新聞輪回 MSG msg; while( GetMessage(&msg, NULL, 0, 0) ) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //編寫窗口進程函數 LRESULT CALLBACK WinAzeProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch(uMsg) { case WM_CHAR: char szChar[20]; sprintf(szChar, "char code is %d", wParam); MessageBox(hwnd, szChar, "char", 0); break; case WM_LBUTTONDOWN: MessageBox(hwnd, "mouse clicked", "message", 0); HDC hdc; hdc = GetDC(hwnd); //不克不及在呼應WM_PAINT新聞時挪用 TextOut( hdc, 0, 50, "法式員之家!",strlen("法式員之家!") ); ReleaseDC(hwnd, hdc); break; case WM_PAINT: HDC hDC; PAINTSTRUCT ps; hDC = BeginPaint(hwnd, &ps); //BeginPaint只能在呼應WM_PAINT新聞是挪用 TextOut(hDC, 0, 0, "http://www.sunxin.org", strlen("http://www.sunxin.org")); EndPaint(hwnd, &ps); break; case WM_CLOSE: if( IDYES == MessageBox(hwnd, "能否真的加入?", "message", MB_YESNO) ) { DestroyWindow(hwnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; }
法式運轉後顯示界面以下:
窗口分為客戶區(是窗口的一部門)與非客戶區。
題目欄、菜單欄、體系菜單、最小(年夜)化框、可調邊框統稱為窗口的非客戶區,由Windows體系治理;運用法式重要治理客戶區的外不雅及操作(顯示文字、繪制圖形)。
對話框、新聞框也是一種窗口;對話框上還包含很多子窗口:按鈕、單選按鈕、復選框、組狂、文本編纂框等。
2、窗口與句柄:
在Windows運用法式中,窗口是經由過程窗口句柄(HWND)來標識的;要對某個窗口停止操作,就必需要獲得這個窗口的句柄。
句柄是Windows法式中一個主要的概念(圖標句柄(HICON)、光標句柄(HCURSOR)、畫刷句柄(HBRUSH))。
3、新聞與新聞隊列:
Windows法式設計形式是一種事宜驅動方法的法式設計形式,重要是基於新聞的。(當體系感知到一事宜時(如點擊鼠標),體系會將這個事宜包裝成一個新聞,送達到運用法式的新聞隊列中,然後運用法式從新聞隊列中掏出新聞並停止呼應。在這個處置進程中,操作體系也會給運用法式“發送新聞”。“發送新聞”:現實指:操作體系挪用法式中一個擔任處置新聞的窗口進程函數)
(1)新聞:Windows中,新聞由MSG構造體表現,以下:
//The MSG structure contains message information from a thread's message queue. typedef struct tagMSG { HWND hwnd; //新聞所屬的窗口,新聞都是與窗口相干聯的 UINT message; //the message identifier WPARAM wParam; //指定新聞的附加新聞 LPARAM lParam; //指定新聞的附加新聞 DWORD time; //新聞送達到隊列中的時光 POINT pt; //鼠標確當前地位 } MSG, *PMSG;
Windows中,新聞是由一個個數值表現的;Windows將新聞對應的數值界說為WM_XXX宏(WM:Window Message)的情勢,XXX對應某種新聞的英文拼寫的年夜寫情勢。如:WM_LBUTTONDOWN:鼠標左鍵按下新聞、WM_KEYDOWN:鍵盤按下新聞、WM_CHAR:字符新聞···
(2)新聞隊列:每個Windows運用法式開端履行後,體系都邑為改法式創立一個新聞隊列,這個新聞隊列用來寄存改法式創立的窗口的新聞。
(3)進隊新聞 與 不進隊新聞:
進隊的新聞將由體系放入到運用法式的新聞隊列中,然後由運用法式掏出並發送;
不進隊新聞在體系挪用窗口進程時,直接發送給窗口;
二者終究都是有體系挪用窗口進程函數抵消息停止處置。
4、WinMain函數
(一)MSDN上的WinMain函數界說以下(備有詳實的正文):
//The WinMain function is called by the system as the initial entry point for a Windows-based application. int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance以後窗口句柄 HINSTANCE hPrevInstance, // handle to previous instance前一個翻開的窗口句柄 LPSTR lpCmdLine, // command line 指定傳遞給運用法式的*敕令行*參數 int nCmdShow // show state 指定窗口應當若何顯示,如:最年夜(小)化、隱蔽等 );
(二)窗口類的構造體的界說:
(1)本文法式中對應代碼以下:
typedef struct _WNDCLASS { UINT style; //指定*這一類型*窗口的款式,如:CS_HREDRAW、CS_VREDRAW、CS_NOCLOSE、CS_DBLCLKS WNDPROC lpfnWndProc; //函數指針,指向窗口進程函數(窗口進程函數是一回調函數) int cbClsExtra; //普通為0 int cbWndExtra; //同上 HINSTANCE hInstance; //指定包括窗口進程的法式的實例句柄 HICON hIcon; //指定窗口類的圖標句柄 HCURSOR hCursor; //指定窗口類的光標句柄 HBRUSH hbrBackground; //指定窗口類的配景畫刷句柄;當窗口產生重繪值,體系應用這裡指定的畫刷來查處窗口的配景 LPCTSTR lpszMenuName; //指定菜單資本的名字 **菜單其實不是一個窗口** LPCTSTR lpszClassName; //指定窗口類的名字 } WNDCLASS, *PWNDCLASS;
回調函數不是由該函數的完成方直接挪用,而是在特定的事宜或前提產生時有另外一方挪用的,用於該事宜或前提停止呼應。
回調函數的完成機制是:
①界說一個回調函數。
②供給函數完成的一方在初始化的時刻,將回調函數的函數指針注冊給挪用者。
③當特定的事宜或前提產生的時刻,挪用者應用函數指針挪用回調函數對事宜停止處置。
針對Windows的新聞處置機制,窗口進程函數被挪用的進程以下:
①在設計窗口類的時刻,將窗口進程函數的地址賦值給lpfnWndProc成員變量;
②挪用RegisterClass(&wndclass)注冊窗口類,那末體系就有了我們所編寫的窗口進程函數的地址。
③當運用法式吸收到某一窗口的新聞時,挪用DispatchMessage(&msg)將抵消息回傳給體系。體系則應用先前注冊窗口類時獲得的函數指針,挪用窗口進程函數抵消息停止處置。
提醒:一個Windows法式可以包括多個窗口進程函數,一個窗口進程老是與某一個特定的窗口類相干聯(經由過程WNDCLASS構造體中的lpfnWndProc成員變量指定),基於該窗口類創立的窗口應用統一個窗口進程。
lpfnWndProc成員變量的類型是WNDPROC,界說以下:
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM); //LRESULT=long, CALLBACK=_stdcall WNDPROC是函數指針類型。
留意:WNDPROC被界說為指向窗口進程函數的指針類型,窗口進程函數的格局必需與WNDPROC雷同。
在VC++中,資本是經由過程標識符(ID)來標識的,統一個ID可以標識多個分歧的資本(資本的ID實質上是一個整數)。如:菜單資本:IDM_XXX(M表現Menu)、圖標資本:IDI_XXX(I表現圖標)、按鈕資本:IDB_XXX(B表現Button)
可以挪用GetStockObject(int fnObject) 來獲得體系的尺度畫刷。聲明以下:
//The GetStockObject function retrieves a handle to one of the stock pens, brushes, fonts, or palettes. HGDIOBJ GetStockObject( int fnObject // stock object type );
GetStockObject函數:前往多種資本對象的句柄,如:畫刷、畫筆、字體、調色板等;
函數前往時,需停止類型轉換。如:
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
(2)注冊窗口類:設計窗口類(WNDCLASS)後,須要挪用RegisterClass函數對其停止注冊,注冊勝利後,才可以創立該類型的窗口。聲明以下:
ATOM RegisterClass( CONST WNDCLASS *lpWndClass // class data, 窗口類對象的指針 // Pointer to a WNDCLASS structure. You must fill the structure with the appropriate class attributes before passing it to the function. );
(3)創立窗口:CreateWindow函數聲明以下:
HWND CreateWindow( LPCTSTR lpClassName, // registered class name 即:窗口類WNDCLASS的lpszClassName成員指定的稱號(必需先注冊) LPCTSTR lpWindowName, // window name 指定窗口的名字 DWORD dwStyle, // window style 指定創立窗口的款式 如:WS_OVERLAPPEDWINDOW int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width int nHeight, // window height HWND hWndParent, // handle to parent or owner window 指定被創立窗口的父窗口句柄 HMENU hMenu, // menu handle or child identifier HINSTANCE hInstance, // handle to application instance LPVOID lpParam // window-creation data 作為WM_CREATE新聞的附加參數lParam傳入的數據指針(普通為:NULL) );
假如窗口創立勝利,CreateWindow函數將前往體系為該窗口分派的句柄;不然,前往NULL。
·留意:在創立窗口之前應先界說一個窗口句柄變量來吸收創立窗口以後的句柄值。
顯示及更新窗口:
(4)顯示窗口:ShowWindow聲明以下:
BOOL ShowWindow( HWND hWnd, // handle to window 該參數為勝利創立窗口後前往的誰人窗口句柄 int nCmdShow // show state 如:SW_HIDE、SW_SHOW、SW_SHOWNORMAL、SW_SHOWMINIMIZED、SW_SHOWMAXIMIZED·· );
(5)更新(刷新)窗口:UpdateWindow函數聲明原型以下:
BOOL UpdateWindow( HWND hWnd // handle to window 創立勝利後的窗口句柄 );
UpdateWindow函數經由過程發送一個WM_PAINT新聞來刷新窗口,UpdateWindow將WM_PAINT新聞直接發送給了窗口進程函數停止處置,而沒有放到新聞隊列外面。
(三)、新聞輪回
窗口 創立、顯示、更新後;須要編寫一個新聞輪回,赓續的從新聞隊列中掏出新聞,並停止呼應。
GetMessage()函數:從新聞隊列中掏出新聞
BOOL GetMessage( LPMSG lpMsg, // message information 指向一個新聞(MSG)構造體,GetMessage從線程的新聞隊列中掏出的新聞信息將保留在該構造體對象中 HWND hWnd, // handle to window 指定吸收屬於哪個窗口的新聞;NULL:用於吸收屬於挪用線程的一切窗口的窗口新聞 UINT wMsgFilterMin, // first message 指定獲得打的新聞的最小值 UINT wMsgFilterMax // last message 假如wMsgFilterMin=0和wMsgFilterMax=0,則吸收一切新聞 );
GetMessage函數吸收到除WM_QUIT外的新聞均前往非零值。
//新聞輪回代碼,普通情勢 MSG msg; while( GetMessage(&msg, NULL, 0, 0) ) { TranslateMessage(&msg); //TranslateMessage函數將虛擬鍵新聞*轉換*為字符新聞,被送達到挪用線程的新聞隊列中,當下一次挪用GetMessage函數時被掏出 DispatchMessage(&msg); //DispatchMessage函數分配一個新聞到窗口進程,有窗口進程函數抵消息停止處置 //DispatchMessage現實上是將新聞會傳給操作體系,有操作體系挪用窗口進程函數抵消息停止處置(呼應) }
Windows運用法式的新聞處置機制以下圖所示:
Windows運用法式的新聞處置進程:
(1)操作體系就收到運用法式的窗口新聞,將新聞送達到該運用法式的新聞隊列中。
(2)運用法式在新聞輪回匯總挪用GetMessage函數從新聞隊列中掏出一條一條的新聞。掏出新聞後,運用法式可以抵消息停止一些預處置,如:廢棄對某些新聞的呼應,或許挪用TranslateMessage發生新的新聞。
(3)運用法式挪用DisPatchMessage,將新聞回傳給操作體系。新聞是由MSG構造體對象來表現的,個中就包括了吸收新聞的窗口的句柄。故:DisPatchMessage函數總能停止准確的傳遞。
(4)操作應用WNDCLASS構造體的lpfnWndProc成員保留的窗口進程函數的指針挪用窗口進程,抵消息停止處置(即“體系給運用法式發送了新聞”)。
彌補:
(1)從新聞隊列中獲得新聞還可以挪用PeekMessage函數,函數原型以下:
BOOL PeekMessage( LPMSG lpMsg, // message information HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax, // last message UINT wRemoveMsg // removal options );
前四個參數與GetMessage函數的參數感化雷同;
最初一個參數指定新聞獲得的方法;假如設為PM_NOREMOVE, 那末新聞將不會從新聞隊列中被移除;假如設為PM_REMOVE, 那末新聞將從新聞隊列中被移除(與GetMessage函數的行動分歧)
(2)發送新聞可使用SendMessage和PostMessage函數。
SendMessage將新聞直接發送給窗口,並挪用該窗口的窗口進程停止處置;在窗口進程抵消息處置終了後,該函數才前往(SendMessage發送的新聞為不進隊新聞)。
PostMessage函數將新聞放入與創立窗口的線程相干聯的新聞隊列後立刻前往。
PostThreadMessage函數,用於向線程發送新聞。
關於線程新聞,MSG構造體中的hwnd成員為NULL。
(四)、編寫窗口進程函數:用於處置發送給窗口的新聞
LRESULT CALLBACK WindowProc( //窗口進程函數的名字可以隨意取,如:WinAzeProc,但函數聲明與界說要分歧; HWND hwnd, // handle to window UINT uMsg, // message identifier 新聞代碼 WPARAM wParam, // first message parameter 新聞代碼的兩個附加值 LPARAM lParam // second message parameter );
提醒:體系經由過程窗口進程函數的地址(指針)來挪用窗口進程函數,而不是名字。
//編寫窗口進程函數 LRESULT CALLBACK WinAzeProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch(uMsg) { case WM_CHAR: //經由過程挪用TranslateMessage函數轉換獲得 char szChar[20]; sprintf(szChar, "char code is %d", wParam); MessageBox(hwnd, szChar, "char", 0); break; case WM_LBUTTONDOWN: MessageBox(hwnd, "mouse clicked!", "message", 0); HDC hdc; hdc = GetDC(hwnd); //用hdc保留GetDC函數前往的與特定窗口相干聯的DC的句柄。 //GetDC()不克不及在呼應WM_PAINT新聞時挪用 TextOut( hdc, 0, 50, "法式員之家!",strlen("法式員之家!") ); //TextOut應用獲得的DC句柄在指定的地位(0,50)出輸入一行文字 ReleaseDC(hwnd, hdc); //釋放hdc break; case WM_PAINT: //當窗口客服區的一部門或許全體變成“有效”時,體系會發送WM_PAINT新聞,告訴運用法式從新繪制窗口 //窗口剛創立時,客戶區是有效狀況,當挪用UpdateWindow函數時,會發送WM_PAINT新聞給窗口進程,對窗口停止刷新 //當窗口從無到有、轉變尺寸、最小化在恢復、被其他窗口隱瞞後在顯示時,窗口的客戶區都將變成有效,此時體系會給運用法式發送WM_PAINT新聞,告訴運用法式從新繪制 //提醒:窗口年夜小產生變更時,能否產生重繪,取決於WNDCLASS構造體中style成員能否設置了CS_HREDRAW和CS_VREDRAW標記 HDC hDC; PAINTSTRUCT ps; //ps用於吸收繪制的信息 hDC = BeginPaint(hwnd, &ps); //BeginPaint只能在呼應WM_PAINT新聞是挪用 TextOut(hDC, 0, 0, "http://www.sunxin.org", strlen("http://www.sunxin.org")); EndPaint(hwnd, &ps); break; case WM_CLOSE: if( IDYES == MessageBox(hwnd, "能否真的加入?", "message", MB_YESNO) ) { DestroyWindow(hwnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); //DefWindowProc挪用默許的窗口進程,對運用法式沒有處置的其他新聞供給默許處置。 //關於年夜多半的新聞,運用法式可以直接挪用DefWindowProc函數停止處置。 //在編寫窗口進程時,應將DefWindowProc函數的挪用放到default語句中,並將該函數的前往值作為窗口進程函數的前往值。 } return 0; }
提醒:要在窗口中輸入文字或許顯示圖形,須要用到裝備描寫表(Device ConText)。
裝備描寫表(簡稱DC):
DC是一個包括裝備(物理輸入裝備,如顯示器、裝備驅動器)信息的構造體,在Windows平台下,一切的圖形操作都是應用DC來完成的。
第30、31行代碼:在挪用BeginPaint時,假如客戶區的配景還沒有被擦除,那末BeginPaint會發送WM_ERASEBKGND新聞給窗口,體系就會應用WNDCLASS構造體的hbrBackGround成員指定的畫刷來擦除配景。假如我們想要讓某個圖形時鐘在窗口中顯示,就應當將圖形的繪制操作放到呼應WM_PAINT新聞的代碼中,如TextOut()的地位。
第34-48行代碼:DestroyWindow函數在燒毀窗口後會向窗口進程發送WM_DESTROY新聞。留意:此時窗口固然燒毀了,但運用法式並沒有加入。故:假如本身要掌握法式能否加入,應當在WM_CLOSE新聞的呼應代碼中完成。
對WM_CLOSE新聞的呼應其實不是必需的,假如運用法式沒有對該新聞停止呼應,體系將把這條新聞傳給DefWindowProc函數,而DefWindowProc函數則條用DestroyWindow函數來呼應 這條WM_CLOSE新聞。
第40-42行代碼:DestroyWindow函數在燒毀窗口後,會給窗口進程發送WM_DESTROY新聞, 然後在該新聞的呼應代碼中挪用PostQuitMessage函數。PostQuitMessage函數項運用法式的新聞隊列中送達一條WM_QUIT新聞並前往。GetMessage函數只要在收到WM_QUIT新聞時才前往0,此時新聞輪回才停止,法式退成。
想讓法式正常加入,我們必需呼應WM_DESTROY新聞,並在新聞呼應代碼中挪用PostQuitMessage,向運用法式的新聞隊列中送達WM_QUIT新聞。傳遞給PostQuitMessage函數的參數值將作為WM_QUIT新聞的wParam參數,這個值平日用做WinMain函數的前往值。