Windows编程之SDK窗口程序浅析

系统 1863 0

Windows编程之SDK窗口程序浅析

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

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

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

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

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

有以上分析可分模块完成注册窗口类和创建窗口工作,即分别设计功能函数 InitWindowsClass InitWindows ,改造后的完整程序如下,从以下程序可以更清楚的认识 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 指向的窗口类模板。窗口类是一个内核对象,不同的窗口类以名称( WNDCLASS lpszClassName 字段)区分。

例如后面 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 ;

然后给定义窗口风格以及用于该窗口类的消息处理函数。然后 CreateWindow lpszProviderClass ,……)将使用名称为 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 ,则只获取指定窗口的消息。

wMsgFilterMin wMsgFilterMax 指定获取消息码的范围。一般将两参数都置零,表示无消息 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 );

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

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

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

<3> GetMessage = WaitMessage + PeekMessage ( PM_REMOVE )

3 )翻译消息

如果输入列表中有一个属于你的应用程序的消息,并且是按键消息时, TranslateMessage 将虚拟键消息转换为字符消息( WM_KEYDOWN + WM_KEYUP = WM_CHAR WM_SYSKEYDOWN + WM_SYSKEYUP = WM_SYSCHAR ),再将字符消息( WM_CHAR WM_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 函数 将取得的消息发送到窗口消息处理函数 WndProc WndProc 采用 switch-case 分支结构对不同的消息不同的响应处理。

WndProc WNDPROC 函数指针,其类型如下:

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

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

LRESULT DispatchMessage ( CONST MSG * lpMsg )

{

// 根据 lpMsg->hwnd 查找用于创建该窗口的 WNDCLASS CreateWindow lpClassName 指定)的 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 应用程序执行流程图:

Windows编程之SDK窗口程序浅析


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论