Win32與MFC,精華貼!!!值得擁有

Win32應用程序基礎知識  

  一個Windows程序至少包含兩個函數:WinMain和wndProc。每個程序都需要WinMain函數,因爲它是程序的入口。另外一個函數是一段處理窗口消息的窗口程序(儘管窗口程序這個名字並不明朗,實際上它能處理任何與此程序相關的消息)。

本文由http://blog.tianya.cn/blogger/post_read.asp?BlogID=89859&PostID=2626799#翻譯,由小楠瓜餅http://blog.csdn.net/Ibznphone轉發。

  好,讓我們開始探討一下WinMain究竟是什麼並談談它的功用,接下來我們將依此分析一下wndProc。
  一個WinMain函數分爲三部分:過程說明(函數聲明)、程序(函數)初始化和一個消息循環。
  當一個程序開始運行的時候,如果需要的話,Windows會傳給它某些信息,這些信息包括應用程序的當前及先前實例句柄,但並不侷限於此。讓我們看看由MSVC++編譯器產生的一個Win32 API函數:
  int APIENTRY WinMain(HINSTANCE hInstance,
   HINSTANCE hPrevInstance,
   LPSTR lpCmdLine,
   int nCmdShow)
  第一個參數,hInstance,定義爲int型,爲此應用程序當前運行實例句柄。
  第二個參數,hPrevInstance,爲同一應用程序的前一運行實例句柄。但是,在Windows NT 32位API中這個參數通常置NULL,即不考慮在同一應用程序下運行多個實例。原因就在這個事實後頭:安全考慮計,在32位Windows API中,每個程序的每一個副本都獨立運行於自己的地址空間內而與當前運行的其他實例沒有任何共享。
  第三個參數,lpCmdLine,包含有針對我們的程序的命令行語句(?)。
  最後一個參數,nCmdShow,在程序初次運行時用來聲明主窗體的類型。它告訴Windows顯示主窗體的方式,如最大化,最小化等等。
  函數返回值原型爲:APIENTRY,定義如下:
  #define APIENTRY WINAPI
  接下來,WINAPI聲明如下:
  #define WINAPI __stdcall
  那麼__stdcall究竟是什麼呢?很不幸的是答案已經超出了本文討論的範圍,因此我們必須跳過它,把它留給讀者思考。
  順便說一下,以上就是過程聲明(procedure declaration)的全部內容。現在進行第二個階段:程序初始化(program initialization)。
  初始化需要調用三個Windows API例程:
  RegisterClass (或它的擴展版本: RegisterClass Ex),
  CreateWindow (或它的擴展副本: CreateWindowEx),
  ShowWindow (注意,此API函數沒有擴展)。
  爲了創建一個窗口,我們必須把WNDCLASS EX結構成員填滿,並把此結構的一個實例傳給RegisterClass Ex API函數。下面的代碼就是實現方法:
  WNDCLASS EX wcl;
  wcl.cbSize = sizeof(WNDCLASS EX);
  wcl.hInstance = hInstance;
  wcl.lpfnWndProc = (WNDPROC)wndProc;
  wcl.style = CS_HREDRAW | CS_VREDRAW;
  wcl.hIcon = LoadIcon(hInstance, IDC_ARROW);
  wcl.hIconSm = NULL;
  wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
  wcl.lpszMenuName = NULL;
  wcl.cbCls Extra = 0;
  wcl.cbWndExtra = 0;
  wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  wcl.lpszClassName = "myClass";
  
  if(!RegisterClass Ex(&wcl))
   return 0;
  當我們這樣做的時候,Windows將把此結構的元素複製到叫作類庫(Class Database)的地方。當程序想創建一個窗口時,它就去“拜訪”(references)類庫的入口,之後,Windows就用(根據)此信息去創建窗口。哼,簡單吧!(Neat, huh?)(很簡單,是吧?)
  現在該看看此結構的成員(member)了。但它擁有許多成員,我們只是簡單的羅列給讀者去探討每個成員的功能(functionality)。然而我們對這些成員中的一員卻格外感興趣,因爲它給我們指引了窗口程序的方向;以及我們想在程序中實現的第二個功能。(?)沒錯,它就是lpfnWndProc成員。但是,請等等!我們必須首先結束WinMain函數的初始化部分。在此類相關數據“登記”(registered)完成後,我們調用API函數CreateWindowEx來創建實窗口。 
  HWND hWnd = CreateWindow("myClass",
   "WindowTitle",
   WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT,
   CW_USEDEFAULT,
   CW_USEDEFAULT,
   CW_USEDEFAULT,
   NULL,
   NULL,
   hInstance,
   NULL);
  接着我們用API函數ShowWindow彈出窗口:
  ShowWindow(hWnd, nCmdShow);
  這就是程序初始化的全部內容了。現在讓我們回到WNDCLASS EX結構的成員(函數):lpfnWndProc中去。就像從這個成員的匈牙利命名上你可能已經注意到的那樣,此成員是一個指向被稱爲窗口程序的函數的長指針。我們已經把此成員指配給了wndProc函數,因此,我們將在程序中聲明一個指定好了的函數:wndProc,它的工作就是處理窗口消息。
  現在是從程序初始化轉到消息循環的時候了,這是WinMain函數的第三部分也是最後一部分。
  每個Windows程序都有一個消息循環來使其不斷的獲取消息。照此思路,每個Windows程序都要與提供給它的、和一個應用程序能正確運轉密切相關不可或缺的消息保持聯繫的狀態。下面是典型的消息循環:
  MSG msg;
  while(GetMessage(&msg, NULL, 0, 0))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }
  這個循環將持續不斷地運行下去直至接收到WM_QUIT消息。程序一旦接收到這個消息,立即中斷消息循環,終止運行。可以肯定的說,通過上面提到的循環的每一次反覆都意味着一條消息的接收,不管是來自硬件事件隊列還是來自應用信息隊列(硬件觸發或軟件觸發的中斷)。
  正如你看到的,上面的消息循環主要由三個API函數構成:
  GetMessage:獲得消息(把消息拖拉進我們的程序)。
  TranslateMessage:把每一次按鍵產生的消息轉換成恰當的特徵值並把它放進應用程序的私有消息隊列裏作爲WM_CHAR類消息。
  DispatchMessage:最後的一個API函數。把重新獲取的消息(msg)交給窗口程序去處理。
  根據掌握的信息,我們現在已經作好了充分的準備來獲得窗口函數的泛函性(to discover the functionality of a window procedure)。回想一下我們已經把WNDCLASS EX結構中的lpfnWndProc成員指配給了wndProc,現在讓我們看看wndProc什麼模樣:
  LRESULT CALLBACK wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
   switch(message)
   {
   case WM_DESTROY:
   PostQuitMessage(0);
   break;
  
   default:
   return DefWindowProc(hWnd, message, wParam, lParam);
   }
  
   return 0;
  }
  這裏,LRESULT數據類型定義爲long。CALLBACK指定__stdcall爲慣用語。(?)
  無論何時產生了一條消息,我們的窗口程序便會被調用。如果這條消息與我們的程序配套,我們就可以操縱它,或者我們跳過消息並把它傳遞給API函數DefWindowProc處理。DefWindowProc會作出任何必需的動作使得我們的窗口盒框能運行。微軟在Windows軟件工具開發包(SDK)中爲我們提供了此程序的源代碼。再順便提一下,(如果)收到WM_DESTROY消息,我們會調用API函數PostQuitMessage處理應用程序的終止。
  MFC應用程序基礎知識
   一個MFC應用程序囊括了大多數的API函數,在某種意義上它簡化了每位程序員的編程生活。我們當然讀過許多關於MFC程序入口的書和文章,它們差不多都認爲一個MFC應用程序的入口就是該程序的InitInstance函數。
   很明顯,這樣就會帶來一個新問題:如果InitInstance函數是程序的入口的話,那麼WinMain函數處在一個什麼樣的位置呢??
   爲了闡明現象後面的本質,我想讓你新建一個例程,通過對MFC程序員隱藏的一些細節的探究與操作來闡述。要新建一個例程,首先打開MS VC 6.0,從文件(File)菜單欄選擇新建(New)命令,在(彈出的對話框)中選擇工程(Projects),選中其中的MFC App Wizard (.exe)並在工程名稱編輯框中鍵入"sdisample",然後按確定(OK)。(在彈出的對話框中)選擇單文檔(Single document),按結束(Finish)。再按OK讓嚮導爲你創建一個應用程序框架。
   首先,你會發現應用程序包含了以下的幾個類:
  · CAboutDlg 
  · CMainFrame 
  · CSdiSampleApp 
  · CSdiSampleDoc 
  · CSdiSampleView 
  其中類CsdiSampleApp來自(繼承)於CwinApp類:
   本文由http://blog.tianya.cn/blogger/post_read.asp?BlogID=89859&PostID=2626799#翻譯,由小楠瓜餅http://blog.csdn.net/Ibznphone轉發。
  上面提到的類包含一個叫作InitInstance的成員函數,它被聲明爲我們程序的入口(the entry point)。問題是爲什麼這個函數被稱爲是MFC程序的入口呢??問題的答案就在MFC結構體系背後。哈,吃驚吧?(Dumb, huh?)
   我們說過每個Windows程序包括兩個函數:WinMain 和wndProc。現在我們要說MFC應用程序同樣如此,它們也有WinMain 和wndProc。
   按F10運行你的程序,很快你就會發現運行的程序停在了下面的函數上面:
  extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
   HINSTANCE hPrevInstance,
   LPTSTR lpCmdLine,
   int nCmdShow)
  { // call shared/exported WinMain
   return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);}
   仔細看看!這個函數有幾個眼熟的參數,和我們的WinMain函數中的參數一樣!但是在WINAPI之前聲明的那些討厭的東東是什麼呢?extern "C"指示編譯器如何編譯此函數,WINAPI定義爲:
  #define WINAPI __stdcall
  那_tWinMain呢?下面是它的定義:
  #define _tWinMain WinMain
  哇噢……(Wow...)我們又回到了同樣的位
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章