• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

Windows编程之SDK窗口程序浅析

VC/MFC/Windows 弦苦 2094次浏览 0个评论

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的函数来承担的。

#include <windows.h> BOOL InitWindowsClass(HINSTANCE hInstance); // 注册窗口类 BOOL InitWindows(HINSTANCE hInstance, int nCmdShow); // 创建窗口 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // 窗口函数声明 HWND hMainWnd; // 全局实例窗口句柄 LPCTSTR lpszProviderClass = __TEXT(“MyWndClass”); // 应用程序入口函数(此程序第一个被执行) int WINAPI WinMain( HINSTANCE hInstance, // 程序实例句柄 HINSTANCE hPrevInstance, // 为保持与Win16兼容的句柄 LPSTR lpCmdLine, // 命令行参数 int nCmdShow // 初始化窗口显示方式 ) { MSG Msg; // 注册窗口类 if(!InitWindowsClass(hInstance)) // 注意这里的实例句柄hInstance { return FALSE; } if(!InitWindows(hInstance,nCmdShow)) // 注意这里的实例句柄hInstance { return FALSE; } // 消息循环 while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); // 翻译消息 DispatchMessage(&Msg); // 将消息传递给处理函数 } return Msg.wParam; // 返回消息的附加参数 } // 消息处理函数,Windows系统规定每个消息处理函数的定义形式都相同 LRESULT CALLBACK WndProc( HWND hWnd, // 窗口句柄 UINT nMessage, // 接受到的待处理的消息标识 WPARAM wParam, // 消息附参数 LPARAM lParam // 消息附参数 ) { switch(nMessage) { // 响应WM_CREATE消息 case WM_CREATE: MessageBox(hWnd, __TEXT(“收到WM_CREATE消息!”), __TEXT(“通知”), MB_OK); break; //响应WM_LBUTTONDOWN消息 case WM_LBUTTONDOWN: MessageBox(hWnd, __TEXT(“收到WM_LBUTTONDOWN消息!”), __TEXT(“通知”), MB_OK); break; // 响应WM_CHAR消息 case WM_CHAR: TCHAR buffer[30]; wsprintf(buffer, __TEXT(“你刚才按下了%c键!”), wParam); MessageBox(hWnd, buffer, __TEXT(“通知”), MB_OK); break; // 响应WM_DESTROY消息 case WM_DESTROY: PostQuitMessage(0); break; // 必须调用函数DefWindowProc(),这是Windows系统多规定的 default: return DefWindowProc(hWnd,nMessage,wParam,lParam); } return FALSE; } // 注册窗口类 BOOL InitWindowsClass(HINSTANCE hInstance) { WNDCLASS WndClass; // 窗口类结构体 // 窗口类的定义 WndClass.style = CS_HREDRAW|CS_VREDRAW; // 窗口类型 WndClass.lpfnWndProc = (WNDPROC)WndProc; // 窗口处理函数为WndProc() WndClass.cbClsExtra = NULL; // 窗口类无扩展 WndClass.cbWndExtra = NULL; // 窗口实例无扩展 WndClass.hInstance = hInstance; // 当前实例句柄 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION); // 窗口最小化图标,为默认图标 WndClass.hCursor = LoadCursor(NULL,IDC_ARROW); // 用箭头作为鼠标图标 WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 以白色作为窗口颜色 WndClass.lpszMenuName = NULL; // 窗口无菜单 WndClass.lpszClassName = lpszProviderClass; // 窗口所属类名 return RegisterClass(&WndClass); } // 创建窗口 BOOL InitWindows(HINSTANCE hInstance, int nCmdShow) { // 创建窗口 HWND hWnd; hWnd = CreateWindow( lpszProviderClass, // 注册的窗口类名 __TEXT(“Hello Windows!”), // 窗口标题名 WS_OVERLAPPEDWINDOW, // 窗口的风格 CW_USEDEFAULT, // 显示窗口的左上角的X坐标,取默认值 CW_USEDEFAULT, // 显示窗口的左上角的Y坐标,取默认值 CW_USEDEFAULT, // 显示窗口的右下角的X坐标,取默认值 CW_USEDEFAULT, // 显示窗口的右下角的Y坐标,取默认值 NULL, // 此窗口无父窗口 NULL, // 菜单句柄(此处设为没有菜单句柄) hInstance, // 程序实力句柄 NULL // 指向一个传递给窗口的指针型参数,此处设置为空 ); if(!hWnd) // 创建窗口失败,返回FALSE { return FALSE; } hMainWnd = hWnd; // 将创建的窗口句柄赋值给全局窗口句柄 ShowWindow(hWnd, nCmdShow); // 显示窗口 UpdateWindow(hWnd); // 刷新窗口 return TRUE; }

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应用程序执行流程图:

Windows编程之SDK窗口程序浅析


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明Windows编程之SDK窗口程序浅析
喜欢 (1)

您必须 登录 才能发表评论!

加载中……