深入浅出MFC学习笔记(一)

来源:互联网 发布:淘宝充值平台退款 编辑:程序博客网 时间:2024/05/08 14:11
 Windows程序的运行本质
Message Based, Event Driven! (以消息为基础, 以事件驱动之)
用户输入由系统通过USER模块捕捉后,以消息的形式(一种数据结构)进入程序之中,放在消息队列里,由程序决定如何响应之.

每一个Windows程序都有一个如下循环
MSG msg;while( GetMessage( &msg, NULL, NULL, NULL) ){    TranslateMessage(&msg);    DispatchMessage(&msg);}


用来获取,翻译并将消息分发的消息的处理函数.

消息操作系统内设的一种数据结构,就Windows程序而言,即MSG结构,

typedef struct tagMSG{   HWND      hwnd;   //Identifies the window whose window procedure receives the message.    UINT      message;   //Specifies the message number.    WPARAM    wParam;   //Specifies additional information about the message. The exact   //meaning depends on the value of the message member.    LPARAM    lParam;   //Specifies additional information about the message. The exact    //meaning depends on the value of the message member.    DWORD     time;   //Specifies the time at which the message was posted.    POINT     pt;   //Specifies the cursor position, in screen coordinates, when the    //message was posted. }MSG;


窗口来接受并处理消息,每一个窗口都有一个函数负责处理消息,程序员负责来设计这个"窗口函数"(Window procedure), 来判断消息的类别并决定处理的方式.

至于窗口的产生与显示则有专门的API来处理.

程序的进入点 WinMain
WinMain是整个Windows程序的进入点,
首先Loader加载程序,然后调用C startup code, 后者再调用WinMain开始执行程序, WinMain的四个参数由操作系统传入.

INT WINAPI WinMain(    HINSTANCE hInstance,     HINSTANCE hPrevInstance,     LPSTR lpszCmdLine,     int CmdShow);


进入程序后,Windows程序必须做些初始化工作,目的即产生应用程序的工作区--窗口,在产生窗口之前,必须先为应用程序设定属性,这就是窗口类的注册过程,这需要一个大的数据结构WNDCLASS作为参数,并且调用RegisterClass函数实现之.

WNDCLASS的结构如下

typedef struct {    UINT style;    WNDPROC lpfnWndProc;    int cbClsExtra;    int cbWndExtra;    HINSTANCE hInstance;    HICON hIcon;    HCURSOR hCursor;    HBRUSH hbrBackground;    LPCTSTR lpszMenuName;    LPCTSTR lpszClassName;} WNDCLASS, *PWNDCLASS;


cbClsExtra
Specifies the number of extra bytes to allocate following the window-class structure. The system initializes the bytes to zero.

cbWndExtra
Specifies the number of extra bytes to allocate following the windowinstance. The system initializes the bytes to zero. If an applicationuses WNDCLASS to register a dialog box created by using the CLASS directive in the resource file, it must set this member to DLGWINDOWEXTRA.

在填充WNDCLASS的时候,必须保证每个字段都有合适的值,否则将会导致RegisterClass的调用失败.

在窗口类注册成功后,就可以建立窗口了,建立的方法很简单, 只需要简单的调用 API 函数 CreateWindow 即可, 真个 CreateWindow 函数含有11个参数,可以涵盖了一个窗口的绝大部分属性,当然如果你需要更为详尽的设定整个装口的属性,你可以选择扩展类和扩展函数,调用方法不同的地方是 WNDCLASS改为WNDCLASSEX, 注册窗口类使用RegisterClassEx, 创建窗口使用CreateWindowEx 详见MSDN
HWND CreateWindow(          LPCTSTR lpClassName,    LPCTSTR lpWindowName,    DWORD dwStyle,    int x,    int y,    int nWidth,    int nHeight,    HWND hWndParent,    HMENU hMenu,    HINSTANCE hInstance,    LPVOID lpParam);


在接下来我们的程序设计中我们将把两个函数分别封装在两个自定义函数里面,其中窗口类的注册放在InitApplication函数里面, 而窗口的创建则安置在 InitInstance函数里面,

之所以这么做一方面是考虑到后面研究的MFC中把这两个函数封装成CWinApp的两个虚函数, 另一方面是考虑到这是以前Win 3.x的设计习惯,由于 Win3.x所有进程共享一个WNDCLASS结构,因才只有第一个运行的实例才需要注册窗口类也即 InitApplication,后来的窗口只需要依样画葫芦就可以了,这样也可以照顾到以前的老程序,尽管这个理由在今天听起来已经比较可笑了.

窗口的关键函数 窗口过程(window procedure)
前面消息循环的DispatchMessage函数通过USER模块的协调将翻译好的信号分发到相应窗口的窗口过程里面了,窗口程序一般通过一个大的还很有可能是潜逃的switch/case语句来分别匹配处理不同的消息.消息函数被设计为CALLBACK类型,不要把它理解的很复杂,其实这个属性就是说明该函数是由windows调用的而不是你自己,

窗口函数的形式比较统一

LRESULT CALLBACK WndProc(     HWND      hWnd,    UINT      Message,    WPARAM    wParam,    LONGPARAM lParam    );


不论什么消息都必须被Windows所处理,因此 switch/case指令中的 default:处必须调用DefWindowProc,来处理你不去处理的消息,

消息映射的雏形(Message Map)
为了把窗口模型设计的更加模块化一些,我们采用一种"消息映射表格的形式", 这还是仿照MFC,

首先定义一个MSGMAP_ENTRY 和一个 dim 宏

//这个是将消息跟函数处理对应联系起来struct MSGMAP_ENTRY{    UINT nMessage;    LONG (*pfn)(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);};//其实就是求取结构体里面的元素个数#define dim(X) (sizeof(x)/sizeof(x[0]))


然后就是定义真正的对应结构表了

struct MSGMAP_ENTRY _messageEntries[] ={    //为了简化,这里只定义了几个消息    WM_COMMAND,OnCommand,    WM_PAINT,OnPaint,    WM_DESTROY, OnDestroy,};//然后我们究竟可以修改我们的程序了,最终程序如下#include "windows.h"#include "tchar.h"HINSTANCE g_hInst= NULL;HWND g_hWnd= NULL;TCHAR *lpszClassName = _T("TestAPP");HDC hdc;PAINTSTRUCT ps;RECT rect;BOOL OnCommand(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);BOOL OnPaint(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);BOOL OnDestroy(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);typedef struct MessageMap {UINT Message;BOOL (*lpfn)(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);}MSGMAP;MSGMAP _messageEntries[] = {WM_COMMAND,OnCommand,WM_PAINT,OnPaint,WM_DESTROY, OnDestroy,};LRESULT CALLBACK WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam){for (int i=0; i<3; i++){if (Message == _messageEntries[i].Message){return ((*_messageEntries[i].lpfn)(hWnd, Message, wParam, lParam));}}return (DefWindowProc(hWnd, Message, wParam, lParam));}BOOL InitApplication(HINSTANCE hInstance){WNDCLASS wc;wc.hInstance= hInstance;wc.lpszClassName= lpszClassName;wc.lpszMenuName= NULL;wc.style= CS_VREDRAW|CS_HREDRAW;wc.lpfnWndProc= (WNDPROC)WndProc;wc.hbrBackground= (HBRUSH)::GetStockObject( WHITE_BRUSH);wc.hCursor= LoadCursor(NULL, IDC_ARROW);wc.hIcon= LoadIcon(NULL, IDI_APPLICATION);wc.cbClsExtra= 0;wc.cbWndExtra= 0;return RegisterClass(&wc);}BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){g_hInst = hInstance;g_hWnd= CreateWindow( lpszClassName, _T("测试Win32窗体"), WS_OVERLAPPEDWINDOW, 0, 0, 800, 600, NULL, NULL, hInstance, NULL);if (!g_hWnd)return FALSE;ShowWindow( g_hWnd, nCmdShow);UpdateWindow( g_hWnd);return TRUE;}INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){MSG msg;if (!hPrevInstance)if (!InitApplication(hInstance))return -2;if (!InitInstance(hInstance, nCmdShow))return -1;while(GetMessage(&msg,NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;}BOOL OnCommand(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam){return (DefWindowProc(hWnd, Message, wParam, lParam));}BOOL OnPaint(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam){hdc = BeginPaint(hWnd, &ps);GetClientRect(hWnd, &rect);DrawText( hdc, _T("测试Win32程序"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);EndPaint(hWnd, &ps);return TRUE;}BOOL OnDestroy(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam){PostQuitMessage(0);return TRUE;}


总结一下:
这次真的发现了有些东西真的是用出来的, 同样的一个线性表, 让我自己怎么都不会想到这么巧妙的使用方法, 学然后知不足啊.
还要继续...

原创粉丝点击