`
sabolasi
  • 浏览: 909866 次
文章分类
社区版块
存档分类
最新评论

Windows编程之SDK窗口程序浅析

 
阅读更多

SDK编写Windows程序需完成3个框架模块:

<1>填空赋值WNDCLASS定义窗口类并注册;

<2>创建窗口,显示、刷新窗口;

<3>消息循环,编写消息处理函数。

熟悉框架后,我们的重点工作放在感兴趣的消息处理上。

有以上分析可分模块完成注册窗口类和创建窗口工作,即分别设计功能函数InitWindowsClassInitWindows,改造后的完整程序如下,从以下程序可以更清楚的认识windows程序框架。

Windows窗口应用程序与DOS控制台应用程序是不同的,编写一个基于Windows API的典型的应用程序需要编写处理以下四个任务:

l 初始化

l 实例化

l 启动消息循环

l 响应消息

前面三个任务总是发生在WinMain函数中,WinMain是每一个Windows程序的入口点,相当C/C++中的main。第四个任务是传统上称为WndProc的函数来承担的。

1.初始化(注册窗口类,创建窗口)

要定义一个窗口类struct WNDCLASS WndClass;该数据结构实际存储的是一个窗口的属性集。调用ATOM RegisterClass(CONST WNDCLASSW *lpWndClass);函数注册lpWndClass指向的窗口类模板。窗口类是一个内核对象,不同的窗口类以名称(WNDCLASSlpszClassName字段)区分。

例如后面MFC中的AfxRegisterClass函数中,注册窗口类前先调用GetClassInfo函数检测预注册的窗口类是否已注册。GetClassInfo函数用来获取指定名称的窗口类信息,其原型如下:

BOOL GetClassInfo(HINSTANCE hInstance, LPCTSTR lpClassName, LPWNDCLASSW lpWndClass);

以下为AfxRegisterClass函数代码片段:

// WINCORE.CPP

BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)

{

WNDCLASS wndcls;

if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName, &wndcls))

{

// class already registered

return TRUE;

}

if (!::RegisterClass(lpWndClass))

{

TRACE1("Can't register window class named %s/n",

lpWndClass->lpszClassName);

return FALSE;

}

//……

}

对于一个已注册的窗口类,可调用BOOL UnregisterClass(LPCTSTR lpClassName, HINSTANCE hInstance);函数进行注销。

以下为Win32SDK示例程序中的注册窗口类、创建窗口的代码片段:

lpszProviderClass = __TEXT("MyWndClass");

WndClass.lpszClassName = lpszProviderClass;

然后给定义窗口风格以及用于该窗口类的消息处理函数。然后CreateWindowlpszProviderClass,……)将使用名称为lpszProviderClass的窗口类WndClass作为模版创建类的实例。

在调用CreateWindow(Ex)时,第一个参数也可以直接使用系统预定义(Predefined)的子窗口控件类,例如"BUTTON""EDIT""COMBOBOX"等这些是系统内部已注册的WNDCLASS,免去调用RegisterClass

创建窗口后,调用GetClassName函数获取指定窗口hWnd所使用的窗口类名称。其函数原型如下:

int GetClassName(HWND hWnd, LPTSTR lpClassName, int nMaxCount);

2.实例化(显示窗口,刷新窗口)

一旦窗口被创建,还需要完成若干步才能运行你的程序。首先,当一个窗口被创建时,它通常是不可见的,一般要调用BOOL ShowWindow(HWND hWnd, int nCmdShow);函数来显示窗口,调用BOOL UpdateWindow(HWND hWnd); 函数来刷新窗口。

函数UpdateWindow向窗口hWnd发送第一个WM_PAINT消息以更新它的客户区。当ShowWindow使窗口显示在屏幕上时,窗口的客户区会被WndClass.hbrBackground擦除,调用UpdateWindow函数将促使客户区重绘,以显示其内容。

3.消息循环

1)消息的产生

无论用户移动鼠标或按下某个键,Windows系统将创建一个消息(对应数据结构为MSG)并将之投放到对应窗口(MSG.hwnd指标)所在线程的消息队列中。

2)获取消息

应用程序调用GetMessage函数从调用线程消息队列中取出(pop)消息。

GetMessage函数原型如下:

BOOL GetMessage(LPMSG lpMsg, HWND hWnd , UINT wMsgFilterMin, UINT wMsgFilterMax);

取出的消息存放至参数一lpMsg所指内存(本地声明的MSG结构体变量)。

参数二hWnd标识要检查消息的窗口,如果为NULL,则获取属于调用线程消息队列中任一(窗口)消息;如果不为NULL,则只获取指定窗口的消息。

wMsgFilterMinwMsgFilterMax指定获取消息码的范围。一般将两参数都置零,表示无消息ID范围限制。

注意该函数是阻塞的,直到有一个(hWnd的)消息到来,它才返回。如果消息为WM_QUIT,则该函数返回FALSE,结束消息循环;否则返回TRUE,继续下一轮消息循环。

<1>等待消息

函数BOOL WaitMessage(VOID);使调用线程挂起,直到一个新的消息放到调用线程消息队列中才返回。

<2>取出消息

函数PeekMessage查看调用线程消息队列中(指定窗口hWnd)是否有消息。如果有消息则取出放入lpMsg所指MSG结构体中,返回TRUE;如果无(指定窗口hWnd的)消息则立即退出(即非阻塞),返回FALSE。其函数原型如下:

BOOL PeekMessage(LPMSG lpMsg, HWND hWnd , UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg);

前四个参数同GetMessagewRemoveMsg参数指定当有消息读取消息后,是否从消息队列中移除该消息。其为下列值之一:

PM_NOREMOVE: 读取后消息依然留在队列中;

PM_REMOVE: 读取后消息从队列中移除;

<3>GetMessage = WaitMessage + PeekMessage(PM_REMOVE)

3)翻译消息

如果输入列表中有一个属于你的应用程序的消息,并且是按键消息时,TranslateMessage将虚拟键消息转换为字符消息(WM_KEYDOWN + WM_KEYUP = WM_CHARWM_SYSKEYDOWN + WM_SYSKEYUP = WM_SYSCHAR),再将字符消息(WM_CHARWM_SYSCHAR)投递到调用线程的消息队列中。

TranslateMessage函数内部机制大致如下:

BOOL TranslateMessage(CONST MSG *lpMsg)

{

if (lpMsg->message == WM_KEYDOWN || lpMsg->message == WM_SYSKEYDOWN)

{

UINT message;

if (lpMsg->message == WM_KEYDOWN)

{

message = WM_CHAR;

}

else if (lpMsg->message == WM_SYSKEYDOWN)

{

message = WM_SYSCHAR;

}

PostMessage(lpMsg->hwnd, message, lpMsg->wParam, lpMsg->lParam);

return TRUE; // 消息被转换

}

return FALSE;

}

4)路由消息

取出消息后,需要进行处理。应用程序调用DispatchMessage函数将取得的消息发送到窗口消息处理函数WndProcWndProc采用switch-case分支结构对不同的消息不同的响应处理。

WndProcWNDPROC函数指针,其类型如下:

typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

DispatchMessage函数内部机制大致如下:

LRESULT DispatchMessage(CONST MSG *lpMsg)

{

// 根据lpMsg->hwnd查找用于创建该窗口的WNDCLASSCreateWindowlpClassName指定)的lpfnWndProc;

CallWindowProc(lpfnWndProc, lpMsg->hwnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam);

}

4.消息处理

Windows编程中常见的消息有:窗口创建消息WM_CREATE,窗口绘制消息WM_PAINT,按键消息WM_KEYDOWN, WM_CHAR, 窗口关闭消息WM_CLOSE,窗口销毁消息WM_DESTROY,退出应用程序消息WM_QUIT等。

默认情况下,关闭窗口的处理流程如下:

点击关闭按钮à系统向指定窗口发送WM_CLOSE消息àWM_CLOSE消息响应中调用DestroyWindow向窗口发送WM_DESTROY消息àWM_DESTROY消息响应中调用PostQuitMessage向窗口发送WM_QUIT消息结束窗口所属线程的消息循环(GetMessage函数返回),终止窗口所属线程。

实际应用软件在响应关闭消息时通常将窗口最小化(到托盘),并不真正关闭。应用程序将在没有窗口的条件下继续运行。

参考窗口破过程与Windows消息循环WM_CLOSE、WM_DESTROY和WM_QUIT三者的区别

每个Windows程序(进程)都至少包含一个窗口和一个应用程序的实例,在程序中表现为窗口句柄hWnd和实例句柄hInstance. 我们可以透过以下几条程序语句一窥Windows程序运行机制:

WndClass.hInstance=hInstance;//这一赋值表明要注册的窗口类属于当前实例

ShowWindow(hWnd,nCmdShow); UpdateWindow(hWnd); //显示刷新该程序窗口

GetMessage //提取消息队列中的消息

TranslateMessage(&Msg); //翻译消息,该函数负责将消息的虚拟键转换成字符消息

DispatchMessage(&Msg); //将参数lpMSG标识的消息发送给窗口函数WndProc

switch(nMessage) case: //按接受到的消息值nMessage判断是哪一种消息并处理

  以下为Windows应用程序执行流程图:

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics