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...)我们又回到了同样的位
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章