WTL 窗口创建消息队列

2024-02-11 12:48
文章标签 创建 队列 窗口 消息 wtl

本文主要是介绍WTL 窗口创建消息队列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ATLAPP.H包含了消息循环类、接口类、和产生应用程序所必需的一些基础类定义。

       类定义如下:

              CmessageFilter类---用于消息过滤的

        CidleHandler 类---用于空闲消息处理的

        CmessageLoop类---用于消息循环的

              CappModule 类---应用程序基础类

              CserverAppModule类---用于Com服务构架的应用程序类

       另外还有3个全局函数:

              AtlGetDefaultGuiFont()获得默认的显示字体

              AtlCreateBoldFont()   产生一个粗体字体

              AtlInitCommonControls()初始化一些控件所需共同的DLL

      WTL程序的结构

       一个窗口程序的创建到销毁过程主要经过如下几个阶段

1. 注册窗口类

2. 创建窗口

3. 进入消息循环

如果用C写过Win32窗口程序的人一定会记得如下的结构:

//窗口过程处理函数

LRESULT CALLBACK WndProc(HWND hwnd,UINT Message,WPARAM wParam,LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow)

{

          HWND hwnd = NULL;

          MSG msg;

          

          WNDCLASS wndclass;

          wndclass.style       = CS_HREDRAW | CS_VREDRAW;

          wndclass.lpfnWndProc = WndProc;

     …

     //注册窗口

     if(!RegisterClass(&wndclass))

     {

          MessageBox(NULL,TEXT("Porgram requires Windows NT!"),szAppName,MB_ICONERROR);

          return 0;

          }

     //创建窗口

     hwnd = CreateWindow(szAppName,TEXT("My Application"),

     WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,

     CW_USEDEFAULT,CW_USEDEFAULT,

     CW_USEDEFAULT,CW_USEDEFAULT,

     NULL,NULL,hInstance,NULL);

 

          ShowWindow(hwnd,iCmdShow);

          UpdateWindow(hwnd);

        

         //进入消息循环

          while(GetMessage(&msg,NULL,0,0))

          {

               TranslateMessage(&msg);

               DispatchMessage(&msg);

     }

 

     return msg.wParam;

}

那么你可能会问WTL的WinMain函数再哪里?如果你通过WTL/ATL导向生成一个应用程序,那么你会在跟工程名字同名的.cpp文件中发现如下的代码:

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)

{

     HRESULT hRes = ::CoInitialize(NULL);

// If you are running on NT 4.0 or higher you can use the following call instead to

// make the EXE free threaded. This means that calls come in on a random RPC thread.

//     HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);

          ATLASSERT(SUCCEEDED(hRes));

 

// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used

          ::DefWindowProc(NULL, 0, 0, 0L);

 

        AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES); // add flags to support other controls

 

     hRes = _Module.Init(NULL, hInstance); //等下分析它的实现      ATLASSERT(SUCCEEDED(hRes));

 

     int nRet = Run(lpstrCmdLine, nCmdShow);//程序的关键分

 

     _Module.Term();

     ::CoUninitialize();

 

     return nRet;

}   

从这个_tWinMain函数的定义,你可以发现程序的关键部分是我紫色标记出来的Run()函数。这个函数是一个自定义的函数,不过如果通过ATL/WTL导向程序,那么会自动生成这样一个Run()函数的,下面我们先分析一下这个自动生成的Run函数。

int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)

{

     CMessageLoop theLoop;                  //定义消息循环

     _Module.AddMessageLoop(&theLoop);   //将消息添加到消息循环

 

     CMainFrame wndMain;                //应用程序框架类

 

     //生成框架

     if(wndMain.CreateEx() == NULL)

     {

          ATLTRACE(_T("Main window creation failed!\n"));

         return 0;

     }

 

     //显示框架

     wndMain.ShowWindow(nCmdShow);

 

     //运行消息循环

     int nRet = theLoop.Run();

 

     //清除消息

     _Module.RemoveMessageLoop();

     return nRet;

}

通过这个Run函数我们可以看到在函数中完成了如下几个过程:

1. 生成一个消息循环对象(theLoop)

2. 在全局的_Module中加入这个消息循环

3. 生成一个应用程序框架对象

4. 显示应用程序框架

5. 开始消息循环

6. 结束消息循环

7. 返回WinMain函数,结束程序

实现分析

在这篇文章我不想过多的分析应用程序框架和窗口的细节,这些内容将放在以后的几篇文章中详细分析,本文主要对ATLAPP.H头文件中实现的一些过程进行详细分析。

首先从全局变量_Module开始。

_Module维持着生成应用程序的主线程,控制着程序的消息循环队列,是一个CAppModule的对象。该CAppModule从ATL::CcomModule继承。

在WTL::CappModule中定义了8个公有成员函数,分别为:

AddMessageLoop()添加一个消息循环,进入消息循环队列里。

RemoveMessageLoop()移除消息循环队列。

GetMessageLoop()获得消息循环。

InitSettingChangeNotify()初始化环境

AddSettingChangeNotify()添加一个窗口句柄。

RemoveSettingChangeNotify()清理环境

除了8个公有成员函数外,该类还定义了3个公有成员变量

m_dwMainThreadID负责保存该应用程序的主线程ID

m_pMsgLoopMap负责存储消息循环

m_pSettingChangeNotify负责存放窗口句柄

下面分别来分析几个主要成员函数的实现:

BOOL AddMessageLoop(CMessageLoop* pMsgLoop)

{

     CStaticDataInitCriticalSectionLock lock;

     //锁住关键片断,由于进程同步的关系!!!

     if(FAILED(lock.Lock()))

     {

          ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddMessageLoop.\n"));

          ATLASSERT(FALSE);

     return FALSE;

     }

        

     ATLASSERT(pMsgLoop != NULL);

     ATLASSERT(m_pMsgLoopMap->Lookup(::GetCurrentThreadId()) == NULL);   // not in map yet

 

     BOOL bRet = m_pMsgLoopMap->Add(::GetCurrentThreadId(), pMsgLoop);

 

     lock.Unlock();

 

     return bRet;

}

     关键部分我用红色的字体标记出来了,意思是什么?通过当前线程的Id来标示一个消息循环,存储在m_pMsgLoopMap中。

 

BOOL RemoveMessageLoop()

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveMessageLoop.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

         BOOL bRet = m_pMsgLoopMap->Remove(::GetCurrentThreadId());

 

          lock.Unlock();

 

         return bRet;

     }

       关键部分同样通过红色字体标记出来,嗯,没错正如AddMessageLoop函数一样,该函数也是通过线程Id来寻找消息循环移除对象的。

 

CMessageLoop* GetMessageLoop(DWORD dwThreadID = ::GetCurrentThreadId()) const

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::GetMessageLoop.\n"));

              ATLASSERT(FALSE);

              return NULL;

         }

 

          CMessageLoop* pLoop = m_pMsgLoopMap->Lookup(dwThreadID);

 

          lock.Unlock();

 

         return pLoop;

     }

该函数通过线程Id在m_pMsgLoopMap消息队列中寻找对应的消息循环,找到后返回。

 

     BOOL InitSettingChangeNotify(DLGPROC pfnDlgProc = _SettingChangeDlgProc)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::InitSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

          if(m_pSettingChangeNotify == NULL)

         {

              typedef ATL::CSimpleArray<HWND>   _notifyClass;

              ATLTRY(m_pSettingChangeNotify = new _notifyClass);

              ATLASSERT(m_pSettingChangeNotify != NULL);

         }

 

         BOOL bRet = (m_pSettingChangeNotify != NULL);

          if(bRet && m_pSettingChangeNotify->GetSize() == 0)

         {

              // init everything

              _ATL_EMPTY_DLGTEMPLATE templ;

              //增加一个无模式对话框

              HWND hNtfWnd = ::CreateDialogIndirect(GetModuleInstance(), &templ, NULL, pfnDlgProc);

              ATLASSERT(::IsWindow(hNtfWnd));

              if(::IsWindow(hNtfWnd))

              {

// need conditional code because types don't match in winuser.h

#ifdef _WIN64

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, (LONG_PTR)this);

#else

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, PtrToLong(this));

#endif

                   //加入该窗口句柄

                   bRet = m_pSettingChangeNotify->Add(hNtfWnd);

              }

              else

              {

                   bRet = FALSE;

              }

         }

 

          lock.Unlock();

 

         return bRet;

     }

该函数用来初始化一个存放窗口句柄的对象

 

     BOOL InitSettingChangeNotify(DLGPROC pfnDlgProc = _SettingChangeDlgProc)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::InitSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

          if(m_pSettingChangeNotify == NULL)

         {

              typedef ATL::CSimpleArray<HWND>   _notifyClass;

              ATLTRY(m_pSettingChangeNotify = new _notifyClass);

              ATLASSERT(m_pSettingChangeNotify != NULL);

         }

 

         BOOL bRet = (m_pSettingChangeNotify != NULL);

          if(bRet && m_pSettingChangeNotify->GetSize() == 0)

         {

              // init everything

              //??空的ATL Dialog Template吗?

              _ATL_EMPTY_DLGTEMPLATE templ;

              //增加一个无模式对话框

              HWND hNtfWnd = ::CreateDialogIndirect(GetModuleInstance(), &templ, NULL, pfnDlgProc);

              ATLASSERT(::IsWindow(hNtfWnd));

              if(::IsWindow(hNtfWnd))

              {

// need conditional code because types don't match in winuser.h

#ifdef _WIN64

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, (LONG_PTR)this);

#else

                   ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, PtrToLong(this));

#endif

                   bRet = m_pSettingChangeNotify->Add(hNtfWnd);

              }

              else

              {

                   bRet = FALSE;

              }

         }

 

          lock.Unlock();

 

         return bRet;

     }

 

     //清理消息

     void TermSettingChangeNotify()

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::TermSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return;

         }

 

          if(m_pSettingChangeNotify != NULL && m_pSettingChangeNotify->GetSize() > 0)

              //销毁窗口

              ::DestroyWindow((*m_pSettingChangeNotify)[0]);

         delete m_pSettingChangeNotify;

          m_pSettingChangeNotify = NULL;

 

          lock.Unlock();

     }

 

BOOL AddSettingChangeNotify(HWND hWnd)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

          ATLASSERT(::IsWindow(hWnd));

         BOOL bRet = FALSE;

          if(InitSettingChangeNotify() != FALSE)

              bRet = m_pSettingChangeNotify->Add(hWnd);

 

          lock.Unlock();

 

         return bRet;

     }

 

BOOL RemoveSettingChangeNotify(HWND hWnd)

     {

          CStaticDataInitCriticalSectionLock lock;

          if(FAILED(lock.Lock()))

         {

              ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveSettingChangeNotify.\n"));

              ATLASSERT(FALSE);

              return FALSE;

         }

 

         BOOL bRet = FALSE;

          if(m_pSettingChangeNotify != NULL)

              bRet = m_pSettingChangeNotify->Remove(hWnd);

 

          lock.Unlock();

 

         return bRet;

     }

 

     现 在回到刚才提到的Run()函数,里面最开始就定义了一个CmessageLoop循环对象,然后通过_Module对象的AddMessageLoop 成员函数加入到循环队列里面,直到_Module调用了RemoveMessageLoop移除循环队列,程序才结束循环,返回到WinMain函数。

     在这里还有一个比较重要的类,那就是CMessageLoop,是他维持了系统的消息,维持了程序的生命周期。那么下面我们来看看这个类的定义和具体的实现方法。

CmessageLoop包含了如下一些成员函数和成员变量

成员变量

//处理消息

     ATL::CSimpleArray<CMessageFilter*> m_aMsgFilter;

     //处理空闲句柄

     ATL::CSimpleArray<CIdleHandler*> m_aIdleHandler;

     //Win32API消息结构

     MSG m_msg;

 

     成员函数(用红色标记的函数是虚函数)

AddMessageFilter         加入一条消息过滤

RemoveMessageFilter      移除一条消息过滤

AddIdleHandler       加入一个空闲句柄

RemoveIdleHandler         移出一个空闲句柄

AddUpdateUI              为了兼容老的ATL而设计的

RemoveUpdateUI           为了兼容老的ATL而设计的

IsIdleMessage            过滤一些比如WM_MOUSEMOVE之类的消息

Run                      消息循环。关键部分!!!

PreTranslateMessage      消息过滤

OnIdle                   空闲处理

 

再这里我不准备对每个函数都进行详细的分析,主要分析核心的函数Run,CmessageLoop由它来维持着系统的消息循环。

函数如下:

int Run()

     {

         //空闲?

         BOOL bDoIdle = TRUE;

         //空闲计数器

         int nIdleCount = 0;

         //返回标志

         BOOL bRet;

 

         //开始消息循环了哦!!!

          for(;;)

         {

 

              //当bDoIdle为TRUE,并且不能从消息队列里面取出消息了,那么开始空闲操作了!

              //PM_NOREMOVE:再PeekMessage函数处理后不将消息从队列里移除

              while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))

              {

                   if(!OnIdle(nIdleCount++))

                        bDoIdle = FALSE;

              }

             

              //从当前线程获取一个消息

              //返回-1表示出现一个错误

              //返回 0表示提交了一个WM_QUIT,程序将要退出

              //成功获得一个消息,返回不等于0的值

              bRet = ::GetMessage(&m_msg, NULL, 0, 0);

 

              if(bRet == -1)

              {

                   ATLTRACE2(atlTraceUI, 0, _T("::GetMessage returned -1 (error)\n"));

                   continue;   // error, don't process

              }

              else if(!bRet)

              {

                   ATLTRACE2(atlTraceUI, 0, _T("CMessageLoop::Run - exiting\n"));

                   break;   // WM_QUIT, exit message loop

              }

 

              //如果熟悉使用c语言来写Win32的程序员会发现,原来WinMain中的哪个处理消息循环的语句放到这里来了!!!

              if(!PreTranslateMessage(&m_msg))

              {

                   //translates virtual-key messages into character messages.

                   ::TranslateMessage(&m_msg);

                   //dispatches a message to a window procedure

                   ::DispatchMessage(&m_msg);

              }

             

              //判断是否为空闲消息?

              //排除WM_MOUSEMOVE WM_NCMOUSEMOVE WM_SYSTIMER消息

              if(IsIdleMessage(&m_msg))

              {

                   bDoIdle = TRUE;

                   nIdleCount = 0;

              }

         }

 

         return (int)m_msg.wParam;

     }

以上就是对ATLAPP.H中的几个比较重要的类的分析,还有其他几个类的分析我将放在以后的文章中

(待续。。。)

这篇关于WTL 窗口创建消息队列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/699821

相关文章

hdu1180(广搜+优先队列)

此题要求最少到达目标点T的最短时间,所以我选择了广度优先搜索,并且要用到优先队列。 另外此题注意点较多,比如说可以在某个点停留,我wa了好多两次,就是因为忽略了这一点,然后参考了大神的思想,然后经过反复修改才AC的 这是我的代码 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

poj 3190 优先队列+贪心

题意: 有n头牛,分别给他们挤奶的时间。 然后每头牛挤奶的时候都要在一个stall里面,并且每个stall每次只能占用一头牛。 问最少需要多少个stall,并输出每头牛所在的stall。 e.g 样例: INPUT: 51 102 43 65 84 7 OUTPUT: 412324 HINT: Explanation of the s

poj 2431 poj 3253 优先队列的运用

poj 2431: 题意: 一条路起点为0, 终点为l。 卡车初始时在0点,并且有p升油,假设油箱无限大。 给n个加油站,每个加油站距离终点 l 距离为 x[i],可以加的油量为fuel[i]。 问最少加几次油可以到达终点,若不能到达,输出-1。 解析: 《挑战程序设计竞赛》: “在卡车开往终点的途中,只有在加油站才可以加油。但是,如果认为“在到达加油站i时,就获得了一

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

poj3750约瑟夫环,循环队列

Description 有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。 Input 第一行输入小孩的人数N(N<=64) 接下来每行输入一个小孩的名字(人名不超过15个字符) 最后一行输入W,S (W < N),用

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

POJ2010 贪心优先队列

c头牛,需要选n头(奇数);学校总共有f的资金, 每头牛分数score和学费cost,问合法招生方案中,中间分数(即排名第(n+1)/2)最高的是多少。 n头牛按照先score后cost从小到大排序; 枚举中间score的牛,  预处理左边与右边的最小花费和。 预处理直接优先队列贪心 public class Main {public static voi