今天的演示内容,在网上随便搜索就能搜索到一箩框,更有许多讲解非常精辟的文章。 我再三思考还是有写的必要, 原因有三:
1. 自己对Win32程序结构再也梳理学习一下;
2. 它是Windows 开发基础和前提;
3. 做为自己后期程序开发模板;
那我们开始吧!
众所周知, Windows 是消息驱动系统, 用户的所有行为和事件都被Windows 转成消息。而软件开发人员则通过消息来实现与Windows交互从而实现用户业务。那Windows 内部又是怎么处理的呢? 对消息的处理是我们Win32程序的根本, 因而我们就从消息的生命周期来着手讲解, 消息生命周期可以简单的概括成几个过程:
a. 消息产生: Windows 监控所有的具有输入事件硬件设备,当系统收到设备输入事件时, Windows 将对应的事件转成消息, 从而消息就产生了;
b. 消息投递: 每个消息都有一个目标窗体接收,而目标窗体应用程序都会有消息队列,当Windows 产生消息后就直接投递到其消息队队中,从而实现了Windows 消息到应用程序的传递;
c. 消息处理: 应用程序自身会不断的从自己的消息队列中获取消息并进行消息转换以及消息分发给Windows, 由Windows 回调应用程序的消息处理函数, 每一个消息必须被处理,
若用户发现自己不关心的消息时并系统去处理, 从而实现了用户的行为得到了交互.
如图:
(图1)
讲解了消息生命周期后,我们进入主题,再来看看Win32的程序结构:
- BOOL WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPTSTR szCmdLine, int nShowCmd)
- {
- const TCHAR* szClsName = _T("Win32_APP");
- const TCHAR* szCaption = _T("Win32 APP");
- if (RegisterWndClass(hInstance, szClsName) )
- {
- HWND hWnd = CreateWnd(hInstance, szClsName, szCaption);
- if (NULL != hWnd && IsWindow(hWnd))
- {
- ShowWnd(hWnd);
- RunMessageLoop();
- }
- }
- return TRUE;
- }
这里我做了封装,封装的也是从结构的角度进行的,所以能基本体现Win32程序结构, 如上述代码可以看出, Win32程序包括这几个步骤:
Step1. RegisterWndClass - 这步主要用于注册一个窗体类, Windows要求所有窗体实例化之前都必须注册类名,注册最主要的目的是设置窗体过程函数,让系统知道消息用谁来处理;
这里我也同时展示下窗体过程函数定义(在过程函数中为了解决过程序case太多, 我再次做的封装处理)
- bool RegisterWndClass(HINSTANCE hInstance, const TCHAR* szClsName)
- {
- ATOM lRes = 0;
- if (NULL != szClsName && NULL != *szClsName)
- {
- WNDCLASS wc = {0};
- wc.hInstance = hInstance;
- wc.lpszClassName = szClsName;
- wc.lpfnWndProc = _WndProc;
- wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
- wc.style = CS_HREDRAW|CS_VREDRAW;
- wc.lpszMenuName = NULL;
- wc.hIcon = (HICON)LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
- wc.hCursor = (HCURSOR)LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
- lRes = ::RegisterClass(&wc);
- assert(0 != lRes);
- }
- return (0 != lRes);
- }
- LRESULT CALLBACK _WndProc(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam)
- {
- for (int ii = 0; ii < sizeof(MsgMapTable) / sizeof(*MsgMapTable); ii++)
- {
- if (nMessage == MsgMapTable[ii].nMessage)
- {
- MsgMapTable[ii].MsgProc(hWnd, wParam, lParam);
- break;
- }
- }
- return DefWindowProc(hWnd, nMessage, wParam, lParam);
- }
Step2. CreateWnd – 这步根据Step1注册的类创建窗体。
- HWND CreateWnd(HINSTANCE hInstance, const TCHAR* szClsName, const TCHAR* szCaption,
- DWORD dwStyle, int nX, int nY, int nWidth, int nHight)
- {
- assert(NULL != szClsName && NULL != szCaption);
- HWND hWnd = NULL;
- if (NULL != szClsName)
- {
- hWnd = CreateWindow(szClsName, szCaption, dwStyle, nX, nY, nWidth, nHight, NULL, NULL, hInstance, NULL);
- assert(NULL != hWnd && IsWindow(hWnd));
- }
- return hWnd;
- }
Step3. ShowWnd - 显示Step2创建的窗体
- void ShowWnd(HWND hWnd)
- {
- ShowWindow(hWnd, SW_SHOW);
- UpdateWindow(hWnd);
- return;
- }
Step4. RunMessageLoop - 消息循环,这个就是我们开篇讲的消息生命周期中监控、获取、转换、分发消息的部分, 它的使命是安排应用程序的生命.
- void RunMessageLoop()
- {
- MSG Msg;
- while (GetMessage(&Msg, NULL, 0, 0))
- {
- TranslateMessage(&Msg);
- DispatchMessage(&Msg);
- }
- return;
- }
这样梳理之后, 我们对Win32程序结构应该有了一定的理解了; 我们再先看看消息映射(其实MFC也与其类同)
struct TMsgMap
{
typedef LRESULT (*MSGPROC)(HWND hWnd, WPARAM wParam, LPARAM lParam);
UINT nMessage;
MSGPROC MsgProc;
};
const TMsgMap MsgMapTable[] = {
WM_CREATE, OnCreate,
WM_DESTROY, OnDestroy,
WM_PAINT, OnPaint,
WM_TIMER, OnTimer
};
在消息过程函数中我们用一个for循环处理消息列表,什么样的消息用什么样的消息处理函数. 这样封装后, 我们是不是就只要在这里实现自己感兴趣的消息处理函数并进行消息映射就可以呢? 不用怕巨大的switch了~ 你是否认为感觉清爽了许多? 我们以后就可以用这个做为win32程序结构模板了呢? 其实它还有许多要改进的地方, 你认为最需求修改是什么呢?
、