进入MFC讲坛的前言(一)

在这里,我想谈谈自己学习MFC的一些体会。我是从1997年才开始在Window下编写程序的。在这之前,我编写过一些DOS程序,包括一个简单的全屏幕编辑器和一个带函数的表达式解释器,都是一些小的程序。Window 3.1流行后,我开始在它下面编写程序。

   从编写DOS程序到编写Window程序,需要从编程思想上作一个比较大的调整。在DOS下编写程序,程序的总体流程完全由应用程序自己控制;但在 Window下,程序的总体流程是由操作系统控制的,这一点对在DOS下“胡作非为”的DOS程序员而然,特别不习惯,思想上一时很难转过弯来,总觉得操 作系统所控制的应用程序流程能够满足我们所提出的任意要求吗?万一某个应用程序所需要的流程同它相抵触,那该怎么样?

  但后来随着学习的深入,我觉得这种担心是完全多余的,就我个人而然在还没有碰到上面的问题。

   另外一个转变就是,在Window下,程序是由事件(或消息)驱动的,程序员在程序中主要是提供事件处理程序的代码,然后由操作系统来调用这些代码,从 程序员的角度看,就是操作系统在“回调”他或她所写的代码。这一点也很不习惯,因为在DOS下,都是应用程序调用操作系统的代码(API),现在一下反过 来了,角色变化了,受不了!不过,随作编程量的增加,这一点也慢慢淡化了。

  刚开始,我是用SDK编程的,使用了半年后,我受不了了, 太麻烦了,编写一个简单的显示”hello, world!”的程序就得上百行代码,再加上讨厌的make文件和.def文件(那时我使用的是Borland C++ 3.1,而且也不知道有OWL这个东西)。后来听人说,现在在Window下编写C或C++程序用的都是MFC,MFC的功能很强大!于是,我到图书馆去 借了两本讲VC的书,照着书上的内容,折腾了一个礼拜。

  说实在话,那一个礼拜是把我搞得最迷糊的一个礼拜,MFC把我给吓坏了。是 的,用MFC编写一个“hello, world!”程序只需自己编写一行代码,但我不知道我所编写的那一行代码是什么时候执行的,我不知道MFC在背后干了什么。这些倒不是最主要的,更让我 难以接受的是,我觉的我所有的编程行动都在MFC的控制之下,而且控制得更“死”了,我的思想钻进上面所提到的“死胡同”中去了。后来我想,如果那时候我 看了一些有关构件(Framework)的文章或书,我想,这个“死胡同”对我而然,应该是不存在的。

  其实,所有这些都是由于对 MFC不熟悉所造成的,MFC是一个框架(Framework)式类库,框架式类库同一般的类库的不同之处在于,库中的各个类之间是有联系的,它们是按照 框架所定义的模式去协作完成任务的。所以,要学习MFC,首先就要了解各个类之间是如何协作的以及它们的接口。

  另外,我觉得,如果熟悉SDK的话,对理解MFC和使用MFC编写程序是有很大帮助的,因此在后面的讲解中,我会根据需要穿插一些SDK方面的知识,以助理解。

  最后,必须具有一定的C++知识,完全不知道C++为何物而去使用MFC,我实在难以想象其最后的结果,最好掌握C++的基本知识。
MFC应用程序的控制流程

  一般的Window应用程序基本流程

  WinMain()函数

   任何一个应用程序都有一个入口函数,在Window下,程序的入口函数根据应用程序的类型,有两种选择:控制台程序的入口函数是main(),一般的 Window界面程序的入口函数是WinMain()。这里只探讨同我们下面的讨论有关的WinMain()函数。下面是该函数的原型:(Visuall C++中)

  int APIENTRY WinMain(

    HINSTANCE hInstance,

    HINSTANCE hPrevInstance,

    LPSTR lpCmdLine,

    int nCmdShow)

  其中:

  hInstance是标识当前进程的实例,它实际上是进程所占据的地址空间的首地址,在很多Window API中,都要将它作为一个参数传进去,所以,应用程序一般都会将它保存在一个全局量中。

  hPreInstance是应用程序前一个实例的实例句柄。这是16位Window的残留物,在Win32应用程序中,这个参数始终为NULL。所以,某些从16为移植到32位的应用程序,如果使用了hPreInstance,就应该对代码作相应的修改。

  lpCmdLine是命令行参数,这同main()中的argv[]类似。

  nCmdShow用来指明应用程序的主窗口的显示方式(最大化显示,最小化显示,一般化显示)。

   一个实例

  下面是一个显示”Hello, world”的程序的代码,它体现了一般的Window应用程序的基本流程。

  int APIENTRY WinMain(HINSTANCE hInstance,

      HINSTANCE hPrevInstance,

      LPSTR lpCmdLine,

      int nCmdShow)

  {

   MSG msg;

   file://注册窗口类

   WNDCLASSEX wcex;

   wcex.cbSize = sizeof(WNDCLASSEX);

   wcex.style = CS_HREDRAW | CS_VREDRAW;

   wcex.lpfnWndProc = (WNDPROC)WndProc;

   wcex.cbClsExtra = 0;

   wcex.cbWndExtra = 0;

   wcex.hInstance = hInstance;

   wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_HELLOWORLD);

   wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

   wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

   wcex.lpszMenuName = (LPCSTR)IDC_HELLOWORLD;

   wcex.lpszClassName = szWindowClass;

   wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

   RegisterClassEx(&wcex);

   file://创建一个该类型的窗口

   HWND hWnd;

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd) return FALSE;

    file://一nCmdShow所指定的方式显示窗口

    ShowWindow(hWnd, nCmdShow);

    UpdateWindow(hWnd);

    file://启动消息循环,将消息发送给相应的窗口函数

    while (GetMessage(&msg, NULL, 0, 0))

     {

      TranslateMessage(&msg);

      DispatchMessage(&msg);

      }

    return msg.wParam;

   }

   file://窗口函数

  LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

  {

   PAINTSTRUCT ps;

   HDC hdc;

   char* szHello = “Hello, world!”;

    switch (message)

    {

     case WM_PAINT:

      hdc = BeginPaint(hWnd, &ps);

      RECT rt;

      GetClientRect(hWnd, &rt);

      DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);

      EndPaint(hWnd, &ps);

      break;

     case WM_DESTROY:

      PostQuitMessage(0);

      break;

     default:

      return DefWindowProc(hWnd, message, wParam, lParam);

     }

    return 0;

    }

  上面程序的执行过程如下:

  1、注册一个窗口类

  这是为后面的创建窗口作准备,在使用CreateWindwo()和CreateWindowEx()创建窗口时,都必须提供一个标识窗口类的字符串。创建窗口类的主要意图是向操作系统提供窗口处理函数。

  2、创建窗口

     启动消息循环,分发并处理消息。

     其中的关键部分是消息循环:

     while (GetMessage(&msg, NULL, 0, 0))

     {

      TranslateMessage(&msg);

      DispatchMessage(&msg);

     }

调用GetMessage()从线程的消息队列中取出一条消息,将消息翻译后,再调用

DispatchMessage()将该消息分发至相应的窗口过程。(实际上DispatchMessage()是将该消息作为参数调用对应的窗口的窗口函数,这就是分发的实质),在后面我们会详细讨论MFC的消息环同上面的消息环的区别。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值