MFC添加自定义消息及重写消息过程

2023-05-16

由于MFC中无法通过类向导来自定义消息,所以需要手动添加,主要过程如下:

本文基于vs2008下通过线程实现数据实时更新的对话框运用程序

1. 定义消息(Resource.h文件中):

由于很多新控件也会用到WM_USER消息,所以定义WM_USER+100或更高,避免冲突


#define   WM_UPDATEDATA    (WM_USER + 100)  

 

2015-3-18 更新

1. 定义消息( stdafx.h文件中):

由于很多新控件也会用到WM_USER消息,所以定义WM_USER+100或更高,避免冲突,消息定义到stdafx.h文件中,Resource.h文件由系统维护,经常出现自定义的消息丢失。


#define   WM_UPDATEDATA    (WM_USER + 100)  

2. 声明消息响应函数:

放在消息映射部分,即DECLARE_MESSAGE_MAP()之前

复制代码


// 实现
protected:
    HICON m_hIcon;

    // 生成的消息映射函数
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    afx_msg LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);
    DECLARE_MESSAGE_MAP()  

复制代码

3. 实现消息响应函数:

由于在vs2008中未定义ON_MESSAGE_VOID,所以定义的消息响应函数必须有返回值,即LRESULT;而消息参数是WPARAM wParam和LPARAM lParam


LRESULT CEditTestDlg::OnUpdateData(WPARAM wParam, LPARAM lParam)
{
    UpdateData(wParam);
    return 0;
}  

4. 将消息映射到消息处理函数:

放在AFX_MSG_MAP之前,把WM_UPDATEDATA消息映射到消息响应函数OnUpdateData

复制代码


BEGIN_MESSAGE_MAP(CEditTestDlg, CDialog)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_MESSAGE(WM_UPDATEDATA, OnUpdateData)
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDC_BUTTON1, &CEditTestDlg::OnBnClickedButton1)
END_MESSAGE_MAP()  

复制代码

5. 发送自定义消息:

以下函数是在一个新的线程中调用的,PostMessage,SendMessage,SendMessageTimeout均可用,推荐使用SendMessageTimeout函数

复制代码


static UINT SendMsgThread(LPVOID lpParam)
{
    CEditTestDlg *dlg = (CEditTestDlg*) lpParam;
    int i = 0;
    while (i < 100)
    {
        Sleep(20);
        i += 1;
        dlg->m_value2.Format(_T("%d"), i);
        //PostMessage(dlg->m_hWnd,WM_UPDATEDATA,FALSE,NULL);
        //SendMessage(dlg->m_hWnd,WM_UPDATEDATA,FALSE,NULL);
        SendMessageTimeout(dlg->m_hWnd, WM_UPDATEDATA, FALSE,NULL, SMTO_BLOCK, 1000, NULL);
    }
    return 0;
}  

复制代码

 

 

如果用户需要一个定义整个系统唯一的消息,可以调用SDK函数RegisterWindowMessage定义消息:
在Resource.h中将代码


#define   WM_UPDATEDATA    (WM_USER + 100)  

修改为


static UINT WM_UPDATEDATA=RegisterWindowMessage(_T("User"));  

并使用ON_REGISTERED_MESSAGE宏指令取代ON_MESSAGE宏指令,其余步骤同上。
注:如果仍然使用ON_MESSAGE宏指令,compile可以通过,但是无法响应消息。

//*************重写消息

c++消息机制是遇到两个参数不是很明白 查了下资料 记录下(以下都是我个人理解,错误请帮忙指正):

 

LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam, LPARAM lParam);

这是一个向指定的窗口发送指定消息的函数

HWND hWnd 这个参数是窗口的句柄,相当于id的玩意 不难理解

UINT Msg 这个是要发送的消息 如:WM_CLOSE,WM_SETTEXT。。。。。。。好多

WPARAM wParam和LPARAM lParam这两个参数不是很陌生 下面也是我看其他的资料得到的理解

 

Windows的消息必须参考帮助文件才能知道具体的含义。如果是你定义的消息,愿意怎么使这两个参数都行。但是习惯上,我们愿意使用LPARAM传 递地址,而WPARAM传递其他参数。”

接下来谈谈什么是消息机制:系统将会维护一个或多个消息队列,所有产生的消息都回被放入或是插入队列中。系统会在队列中取出每一条消息,根据消息的接收句柄而将该消息发送给拥有该窗口的程序的消息循环。每一个运行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口过程。而在没有消息时消息循环就将控制权交给系统所以Windows可以同时进行多个任务。下面的伪代码演示了消息循环的用法:

 

 

例如:主程序MyDlg.cpp

1.自定义消息:#define WM_TRAY WM_USER 100

2.函数原形:afx_msg LRESULT OnTrayNotify(WPARAM wParam,LPARAM lParam);

3.消息映射:ON_MESSAGE(WM_TRAY,OnTrayNotify)

4.原函数:

LRESULT CMyDlg::OnTrayNotify(WPARAM wParam,LPARAM lParam)

{

return m_tray.OnTrayNotify(wParam,lParam);

}

 

上面程序呢 主要过程是这样 自定义了一个消息WM_TRAY 

再创建一个函数OnTrayNotify 

然后将消息和函数绑定在一起ON_MESSAGE(WM_TRAY,OnTrayNotify)

每当接受这个消息时就运行函数

而WPARAM wParam,LPARAM lParam这两个参数就是消息附带的参数跟着消息一起传递过来

 

在Win32 SDK中消息本身是作为一个结构体记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。这个记录类型叫做MSG,它在window中是这样声明的:
typedef struct tagMSG { // msg
HWND hwnd; //窗口句柄
UINT message; //消息常量标识符
WPARAM wParam; //32位消息的特定附加信息,具体表示什么处决于message
LPARAM lParam; //32位消息的特定附加信息,具体表示什么处决于message
DWORD time; //消息创建时的时间
POINT pt; //消息创建时的鼠标位置
} MSG;

hwnd 接收消息的32位窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。


message 用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。


wParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。 lParam 通常是一个指向内存中数据的指针。由于wParam,lParam和指针都是32位的,需要时可以强制类型转换。具体表示什么,与message相关,他们是事先定义好的。


如果自定义消息:#define WM_MYMESSAGE WM_USER+100,需确定wParam,lParam的意义 (假设wParam=0时发送数据,wParam=1时接收数据,lParam为CMyClass* 指针,指向一个CMyClass对象,准备要发送的数据或接收数据 发送WM_MYMESSAGE时 SendMessage(hwnd,WM_MYMESSAGE,0,pMyClassObject) 接收消息的窗口,接收WM_MYMESSAGE中(CMyClass*)lParam参数即pMyClassObject传过来的数据

 

//**********

1. 怎样使用MFC发送一个消息用MFC发送一个消息的方法是,

首先,应获取接收消息的CWnd类对象的指针;

然后,调用CWnd的成员函数SendMessage( )。

LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);

pWnd指针指向目标CWnd类对象。变量Msg是消息,wParam和lParam变量包含消息的参数,如鼠标单击哪里或选择了什么菜单项。目标窗口返回的消息结果放在变量Res中。

发送消息到一个没有CWnd类对象的窗口,可以用下列目标窗口的句柄直接调用Windows API:

LRESULT Res=::SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

这里的hWnd是目标窗口的句柄。

2. 怎样用MFC寄送一个消息

用MFC寄送一个消息与发送一个消息几乎相同,但寄送时用PostMessage( ) ,而不是用SendMessage( );返回值Res也不一样,Res不是一个由目标窗口返回的值,而是一个布尔值,用来表示消息是否成功地放到消息队列中。

3. 检索一个寄送消息

正常情况下,一旦消息被寄送后,应用程序在后台发送它。但是在特殊情况下,需要你自己去删除一个消息,例如想在应用程序接收到某种消息之前停止应用程序。有两种方法可以从应用程序消息队列中删除一个消息,但这两种方法都没有涉及MFC。

■ 第一种方法:在不干扰任何事情之下窥视消息队列,看看一个消息是否在那里。

BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ) ;

■ 第二种方法:实际上是等待,一直等到一个新的消息到达队列为止,然后删除并返回该消息。

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

在这两种方法中,变量hWnd指定要截获消息的窗口,如果该变量设为NULL,所有窗口消息将被截获。wMsgFilterMin和wMsgFilterMax变量与SendMessage( )中的变量Msg相对应,指定查看消息的范围。如果用"0,0",则所有的消息都将被截获。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,则所有键盘或鼠标的消息将被截获。wRemoveMsg变量指定PeekMessage( )是否应该真正地从队列中删除该消息。(GetMessage( )总是删除消息)。该变量可以取两个值:

■ PM_REMOVE,PeekMessage( )将删除消息。

■ PM_NOREMOVE,PeekMessage( )将把消息留在队列里,并返回它的一个拷贝。

当然,如果把消息留在消息队列中,然后再次调用PeekMessage( )查看相同类型的消息,则将返回完全相同的消息。

lpMsg变量是一个指向MSG结构的指针,MSG包含检索到的消息。

typedef struct tagMSG {

HWND hwnd; // window handle message is intended for

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time; // the time the message was put in the queue

POINT pt; // the location of the mouse cursor when the

// message was put in the queue

} MSG;

4. MFC怎样接收一个寄送的消息

MFC处理一个寄送和发送消息的唯一明显不同是寄送的消息要在应用程序的消息队列中花费一些时间。在消息泵(message pump)弹出它之前,它要一直在队列中。

消息泵

MFC应用程序中的消息泵在CWinApp的成员函数Run()中。应用程序开始运行时,Run()就被调用,Run()把时间分割成两部分。一部分用来执行后台处理,如取消临时CWnd对象;另一部分用来检查消息队列。当一个新的消息进来时,Run()抽取它—即用GetMessage( )从队列中取出该消息,运行两个消息翻译函数,然后用DispatchMessage( )函数调用该消息预期的目标窗口进程。

消息泵调用的两个翻译函数是PreTranslateMessage( )和::TranslateMessage( )。目标窗口的MFC类可调用reTranslateMessage在发送消息给它之前进行消息翻译,例如,CFrameWnd用PreTranslateMessage( )将加速键(如,Ctrl+S存储文件)转换为命令消息。翻译前的消息通常被处理掉,而翻译后的消息(如果有的话)将被重新寄送到队列里。::TranslateMessage是一个窗口函数,将原始键码转换为键字符。消息一旦被DispatchMessage()发送,MFC处理它就像处理SendMessage()发送的消息一样。

5. MFC怎样处理一个接收到的消息

处理接收到的消息的目的非常简单:将消息指向一个函数,该函数通过消息中的消息标识符处理它。非MFC窗口用简单的case语句来实现该目标,每个case语句执行一些函数,或调用其他一些函数。

MainWndProc(HWND hWnd, UINT message, W PARAM wParam,LPARAM lParam)

{

switch(message)

{

case WM_CREATE:

: : :

break;

case WM_PAINT:

: : :

break;

default:

return(DefWindowProc(hWnd,message,wParam,lParam));

}

return(NULL);

}

任何遗漏的消息将被传输到一个默认的消息处理函数,但是,case语句不能很好地适应C++和封装技术。在C++环境中,要求消息被一个专门处理该类型消息的类的成员函数处理。因此,MFC不采用case语句,而采用更加复杂和回旋的方法。但它允许用私有类处理消息,而只需做下面三件事情:

■ 从将要接收消息的CWnd类对象派生类(对于命令消息是CCmdTarget)。

■ 在派生类中写一个处理消息的成员函数。

■ 在类中定义一个查找表(叫做消息映像),该表具有成员函数的条目和它要处理的消息的标识符。

然后,MFC依次调用下面的函数,指引输入消息到处理函数。

1) AfxWndProc( )接收消息,寻找消息所属的CWnd对象,然后调用AfxCallWndProc( )。

2) AfxCallWndProc( )存储消息(消息标识符和参数)供未来参考,然后调用WindowProc( )。

3) WindowProc( ) 发送消息给OnWndMsg( ) ,然后,如果消息未被处理,则发送给DefWindowproc( )。

4) OnWndMsg( )要么为WM_COMMAND消息调用OnCommand( ),要么为WM_NOTIFY消息调用OnNotify( )。任何被遗漏的消息都将是一个窗口消息。OnWndMsg( )搜索类的消息映像,以找到一个能处理任何窗口消息的处理函数。如果OnWndMsg( )不能找到这样的处理函数,则把消息返回到WindowProc( ),由它将消息发送给DefWindowProc( )。

5) OnCommand()查看这是不是一个控件通知(lParam不是NULL);如果它是,OnCommand( )就试图将消息映射到制造通知的控件;如果它不是一个控件通知,或者控件拒绝映射的消息,OnCommand( )就调用OnCmdMsg( )。

6) OnNotify( )也试图将消息映射到制造通知的控件;如果映射不成功, OnNotify( )就调用相同的OnCmdMsg( )函数。

7) 根据接收消息的类,OnCmdMsg( )将在一个称为命令传递(Command Routing)的过程中潜在地传递命令消息和控件通知。例如,如果拥有该窗口的类是一个框架类,则命令和通知消息也被传递到视图和文档类,并为该类寻找一个消息处理函数。

为什么要消息映像?

这毕竟是C++语言;为什么OnWndMsg( )不为每个窗口消息调用一个预定义的虚拟函数?因为它太占CPU。若是那样,当扫描一个消息映像以加速该过程时,OnWndMsg( )可能会做出意想不到的事情,并陷入汇编器。注意通过重载WindowProc( )、OnWndMsg( )、OnCommand( )、OnNotify( ) 或OnCmdMsg( )可以修改这一过程。重载OnWndMsg( )可以在窗口消息被排序之前插入该过程。重载OnCommand( )或OnNotify( )可以在消息被反射之前插入该过程。

 

//***********

windows消息机制(MFC)

消息分类与消息队列

Windows中,消息使用统一的结构体(MSG)来存放信息,其中message表明消息的具体的类型,

而wParam,lParam是其最灵活的两个变量,为不同的消息类型时,存放数据的含义也不一样。

time表示产生消息的时间,pt表示产生消息时鼠标的位置。

按照类型,Windows将消息分为:

(0) 消息ID范围

系统定义消息ID范围:[0x0000, 0x03ff]
用户自定义的消息ID范围: 
WM_USER: 0x0400-0x7FFF (例:WM_USER+10) 
WM_APP(winver> 4.0):0x8000-0xBFFF (例:WM_APP+4) 
RegisterWindowMessage:0xC000-0xFFFF【用来和其他应用程序通信,为了ID的唯一性,使用::RegisterWindowMessage来得到该范围的消息ID 】

(1) 窗口消息:即与窗口的内部运作有关的消息,如创建窗口,绘制窗口,销毁窗口等。

     可以是一般的窗口,也可以是MainFrame,Dialog,控件等。 

     如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL等

(2) 当用户从菜单选中一个命令项目、按下一个快捷键或者点击工具栏上的一个按钮,都将发送WM_COMMAND命令消息。

     LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID;如果是控件, HIWORD(wParam)表示控件消息类型。

     #define LOWORD(l) ((WORD)(l))

     #define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))

(3) 随着控件的种类越来越多,越来越复杂(如列表控件、树控件等),仅仅将wParam,lParam将视为一个32位无符号整数,已经装不下太多信息了。

    为了给父窗口发送更多的信息,微软定义了一个新的WM_NOTIFY消息来扩展WM_COMMAND消息。

    WM_NOTIFY消息仍然使用MSG消息结构,只是此时wParam为控件ID,lParam为一个NMHDR指针,

    不同的控件可以按照规则对NMHDR进行扩充,因此WM_NOTIFY消息传送的信息量可以相当的大。

注:Window 9x 版及以后的新控件通告消息不再通过WM_COMMAND 传送,而是通过WM_NOTIFY 传送,
      但是老控件的通告消息, 比如CBN_SELCHANGE 还是通过WM_COMMAND 消息发送。

(4) windwos也允许程序员定义自己的消息,使用SendMessage或PostMessage来发送消息。

windows消息还可以分为:

(1) 队列消息(Queued Messages) 
消息会先保存在消息队列中,消息循环会从此队列中取出消息并分发到各窗口处理 
如:WM_PAINT,WM_TIMER,WM_CREATE,WM_QUIT,以及鼠标,键盘消息等。
其中,WM_PAINT,WM_TIMER只有在队列中没有其他消息的时候才会被处理,
WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。

(2) 非队列消息(NonQueued Messages) 
消息会绕过系统消息队列和线程消息队列,直接发送到窗口过程进行处理 
如:WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR,WM_WINDOWPOSCHANGED

Windows系统的整个消息系统分为3个层级:

    ① Windows内核的系统消息队列

    ② App的UI线程消息队列

    ③ 处理消息的窗体对象

Windows内核维护着一个全局的系统消息队列;按照线程的不同,系统消息队列中的消息会分发到应用程序的UI线程的消息队列中;

应用程序的每一个UI线程都有自己的消息循环,会不停地从自己的消息队列取出消息,并发送给Windows窗体对象;

每一个窗体对象都使用窗体过程函数(WindowProc)来处理接收到的各种消息。

复制代码

复制代码


 1 LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 2 {
 3     PAINTSTRUCT ps;
 4     HDC hdc;
 5 
 6     switch (message)
 7     {
 8     case WM_COMMAND:
 9         break;
10     case WM_PAINT:
11         hdc = BeginPaint(hWnd, &ps);
12         // TODO: 在此添加任意绘图代码...
13         EndPaint(hWnd, &ps);
14         break;
15     case WM_DESTROY:
16         PostQuitMessage(0);
17         break;
18     default:
19         return DefWindowProc(hWnd, message, wParam, lParam);
20     }
21     return 0;
22 }  

复制代码

复制代码

需要的话,在WindowProc中,可以用::GetMessageTime获取当前消息产生的时间,
用::GetMessagePos获取当前消息产生时鼠标光标所在的位置。

(1) 各个窗口消息由各个窗体(或控件)自身的WindowProc(虚函数)接收并处理。

(2) WM_COMMAND命令消息统一由当前活动主窗口的WindowProc接收,经过绕行后,可被其他的CCmdTarget对象处理。

(3) WM_COMMAND控件通知统一由子窗口(控件)的父窗口的WindowProc接收并处理,也可以进行绕行被其他的CCmdTarget对象处理。

     (例如:CFormView具备接受WM_COMMAND控件通知的条件,又具备把WM_COMMAND消息派发给关联文档对象处理的能力,

         所以给CFormView的WM_COMMAND控件通知是可以让文档对象处理的。)

     另外,WM_COMMAND控制通知会先调用ReflectLastMsg反射通知子窗口(控件),如果子窗口(控件)处理了该消息并返回TRUE,则消息会停止分发;

     否则,会继续调用OnCmdMsg进行命令发送(如同WM_COMMAND命令消息一样)。

注:WM_COMMAND命令消息与WM_COMMAND控件通知的相似之处:
WM_COMMAND命令消息和WM_COMMAND控制通知都是由WindowProc给OnCommand处理,
OnCommand通过wParam和lParam参数区分是命令消息或通知消息,然后送给OnCmdMsg处理。
事实上,BN_CLICKED控件通知消息的处理和WM_COMMAND命令消息的处理完全一样。
因为该消息的通知代码是0,ON_BN_CLICKED(id,memberfunction)和ON_COMMAND(id,memberfunction)是等同的。

(4)WM_NOTIFY消息只是对WM_COMMAND控件通知进行了扩展,与WM_COMMAND控件通知具有相同的特点。

SendMessage与PostMessage

PostMessage 把消息投递到消息队列后,立即返回; 
SendMessage把消息直接送到窗口过程处理,处理完才返回。

GetMessage与PeekMessage

GetMessage 有消息且该消息不为WM_QUIT,返回TRUE。
              有消息且该消息为WM_QUIT,返回FALSE。
                  没有消息时,挂起该UI线程,控制权交还给系统。
PeekMessage 有消息返回TRUE,如果没有消息返回FALSE;不会阻塞。
                   是否从消息队列中删除此消息(PM_REMOVE),由函数参数来指定。

要想在没有消息时做一些工作,就必须使用PeekMessage来抓取消息,以便在没有消息时,能在OnIdle中执行空闲操作(如下):

复制代码

复制代码


 1 while (TRUE) 
 2 {
 3     if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) 
 4     {
 5         if (msg.message == WM_QUIT)
 6             break;
 7         TranslateMessage(&msg);
 8         DispatchMessage(&msg);
 9     }
10     else 
11     {
12         OnIdle();
13     }
14 }  

复制代码

复制代码

例如:MFC使用OnIdle函数来清理一些临时对象及未使用的动态链接库。

只有在OnIdle返回之后程序才能继续处理用户的输入,因此不应在OnIdle进行较长的任务。

MFC消息处理

在CWnd中,MFC使用OnWndMsg来分别处理各类消息:

如果是WM_COMMAND消息,交给OnCommand处理;然后返回。

如果是WM_NOTIFY消息,交给OnNotify处理;然后返回。

如果是WM_ACTIVATE消息,先交给_AfxHandleActivate处理,再继续下面的处理。

如果是WM_SETCURSOR消息,先交给_AfxHandleSetCursor处理,然后返回。

如果是其他的窗口消息(包括WM_ACTIVATE消息),则

  首先在消息缓冲池(一个hash表,用于加快消息处理函数的查找)进行消息匹配,
    若匹配成功,则调用相应的消息处理函数;
    若不成功,则在消息目标的消息映射数组中进行查找匹配,看它是否能处理当前消息。
  如果消息目标处理了该消息,则会匹配到消息处理函数,调用它进行处理;

否则,该消息没有被应用程序处理,OnWndMsg返回FALSE。

MFC消息映射

消息映射实际是MFC内建的一个消息分派机制。

把MFC中的宏进行展开(如下),可以得到消息映射表整个全貌。

注:GetMessageMap为虚函数。
     {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0}:对象消息映射表的结束标识

窗口消息只能由CWnd对象来处理,采用向基类直线上朔的方式,来查找对应的消息响应函数进行处理。

一旦找到消息响应函数(若有返回值且为TRUE),就停止上朔。因此,我们经常会看到这样的代码:

增加一个消息处理函数来写我们的逻辑时,MFC ClassWizard会在该函数之前或之后显示调用其基类对应的函数,保证基类中逻辑被执行。

命令消息可由CCmdTarget对象接收并处理(OnCmdMsg为虚函数),除了向基类直线上朔方式外,还有命令绕行机制(要防止形成圈,死循环)。

在某种程度上,控制通知消息由窗口对象处理是一种习惯和约定。然而,控件通知消息也是可以有CCmdTarget对象接收并处理,并进行命令绕行的。

下图为MFC经典单文档视图框架的命令消息绕行路线:

函数调用过程如下(如果没有任何对象处理该条WM_COMMAND消息,最后会被::DefWindowProc处理)。

非模态对话框的消息处理


1 static CAboutDlg aboutDlg;
2 aboutDlg.Create(IDD_ABOUTBOX, this);
3 aboutDlg.ShowWindow(SW_SHOW);  

应用程序只有一个消息循环。

对于窗口消息,非模态对话框(及其子控件)与父窗口(及其子控件)都是用自身的WindowProc函数接收并处理,互不干扰。

对于命令消息,由当前活动主窗口的WindowProc接收(例如:当前活动主窗口为非模态对话框,则命令消息会被非模态对话框接收)。

可以在当前活动主窗口的OnCmdMsg中做命令绕行,使得其他的CCmdTarget对象也可以处理命令消息。

对于控件通知,由其父窗口的WindowProc接收并处理,一般不进行命令绕行被其他的CCmdTarget对象处理。

模态对话框的消息处理


1 CAboutDlg aboutDlg;
2 aboutDlg.DoModal();  

(1) 模态对话框弹出来后,首先会让父窗口失效,使其不能接受用户的输入(键盘鼠标消息)。


1 EnableWindow(hwndParent, FALSE) ;  

(2) 父窗口消息循环被阻塞(会卡在DoModal处,等待返回),由模态对话框的消息循环来接管(因此整个程序不会卡住)。

    接管后,模态对话框的消息循环仍然会将属于父窗口及其子控件的窗口消息(不包括键盘鼠标相关的窗口消息)发送给它们各自的WindowProc窗口函数,进行响应处理。

(3) 模态对话框销毁时(点击IDOK或IDCANCEL),父窗口消息循环重新激活,继续DoModal后的逻辑。

    激活后,父窗口有可以重新接受用户的输入(键盘鼠标消息)。


1 EnableWindow(hwndParent, TRUE) ;  

从上面的过程中,我们可以得到如下结论:

对于窗口消息,模态对话框主窗口(及其子控件)与父窗口(及其子控件)都是用自身的WindowProc函数接收并处理,互不干扰。

只是父窗口(及其子控件)无法接受到键盘鼠标消息相关的窗口消息。

对于命令消息,由模态对话框主窗口的WindowProc接收。可以在模态对话框主窗口的OnCmdMsg中做命令绕行,使得其他的CCmdTarget对象也可以处理命令消息。

对于控件通知,由其父窗口的WindowProc接收并处理,一般不进行命令绕行被其他的CCmdTarget对象处理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MFC添加自定义消息及重写消息过程 的相关文章

  • MFC winMain 如何最终进入可执行文件?

    In MFC wWinMain定义于appmodul cpp 据我所知 该文件内置于 mfc90ud dll 中 但是 当我运行我的应用程序时 调用堆栈显示MyApplication exe wWinMain 它是如何采取的wWinMain
  • MFC> 将对话框连接到对话框类

    我在现有的资源文件中定义了一个新对话框及其控件 我还创建了一个新文件 它将处理从此对话框生成的事件 但我不确定如何连接这两者 是声明enum IDD IDD NEW DIALOG 连接两者所需的一切 或者我们应该添加一些其他声明 在 MFC
  • Boost::序列化和 MFC Doc/View 架构

    我正在移植现有的 MFC C 应用程序以对 XML 文件使用 Boost Serialization 我的 CDocument 对象包含应用程序的所有数据 我已将序列化函数实现为 template
  • CloseWindow和WM_CLOSE有什么关系

    我现在有点困惑 是吗 WM CLOSE http msdn microsoft com en us library windows desktop ms632617 28v vs 85 29 aspx and CloseWindow htt
  • 使用 CSplitterWnd 在 CChildFrame 中创建多个视图

    我正在使用 MFC MDI 我需要创建如下视图 我的 ChildWnd 分为两部分 它们是LeftView CView 和RightView CScrollView LeftView 分为两部分 TreeView 和 FormView 我怎
  • 如何在 MFC 中创建 GUI

    我需要能够即时创建指南 MFC中有没有办法做到这一点 我了解了如何在 net 中做到这一点 但我们还没有做到这一点 如果没有 您是否有一些我可以使用的代码的指针 don t forget to add Rpcrt4 lib to your
  • 跨平台 Windows / OS X 开发,具有大量现有代码库

    对于使用 MFC 和 WinAPI 用 C 编写的复杂应用程序 有大量现有代码库 需要将其移植到 Mac OS X 理想的解决方案是在不同平台之间拥有尽可能多的通用代码 尤其是诸如业务之类的代码逻辑 GUI 可能会有所不同 具体取决于跨平台
  • MFC中如何将BYTE数组转换为CString?

    如何在 MFC 中将 BYTE 数组转换为 CString 试试这个 例如 如果 x 是你的字节数组 那么 BYTE x 5 x 0 A x 1 0 x 2 B x 3 C x 4 0 CString str LPCSTR x sizeof
  • 如何在现有 Windows 应用程序中获得 ATL 支持

    我正在 Visual Studio 2012 中使用 Qt 5 3 1 构建一个应用程序 我还想使用一个硬件库 这需要我向项目添加一个简单的 ATL 对象 这可以通过使用 Visual Studio 向导来完成 该向导抱怨我的项目既不是 M
  • 如何发光最小。最大和关闭按钮?

    我按照以下指南使用 DWM API 创建自定义 Aero 框架 My work void CMainFrame OnActivate UINT nState CWnd pWndOther BOOL bMinimized CFrameWnd
  • 如何找到激活时打开给定 HMENU 的菜单项(如果有)?

    我想用原型实现一个功能 Locates the menu item of the application which caused the given menu mnu to show up return true if the given
  • 如何将基数词转换为序数词

    有没有一种简单的方法可以将数字 1 2 3 转换为 1st 2nd 3rd 并且以这种方式我可以为该函数提供一种语言并具有它会返回我目标语言的正确形式吗 标准 C stl 或 boost 都可以 MFC 或 ATL win32 api 或我
  • 您可以将 CMFCVisualManager 与基于对话框的应用程序一起使用吗?

    您可以将 CMFCVisualManager 与基于对话框的应用程序一起使用来更改应用程序的外观吗 如果是这样 它是如何完成的 这个想法是使用随 MSVC 2008 发布的 MFC 功能包来更改控件 例如按钮 的形状 颜色等 不 不能这样做
  • 获取正在运行的程序的属性

    我想开发一个程序 其 ID 是一张牌 因为它在另一个正在运行的程序 例如扑克或红心游戏或其他程序 中播放 我首先尝试获取有关已运行的游戏程序的所需信息 但我从一开始就遇到了问题 我正在运行 MSVC 2013 并开发 MFC 应用程序 现在
  • 屏幕截图忽略了一些窗口

    我正在 MFC 中工作 我正在尝试捕获桌面的 bmp 我正在使用 GetDC NULL 来执行此操作 但它似乎忽略了特殊的皮肤窗口 它似乎忽略了用 UpdateLayeredWindow 绘制的窗口 此行为似乎仅发生在 Vista x64
  • 用于基于 MFC 的 GUI 应用程序的免费自动化测试工具

    我是 Windows 开发和测试平台的新手 我正在开发一个基于 MFC 的 GUI 应用程序 并正在寻找有助于 UI 自动化测试的工具 我搜索时出现了一大堆工具 但大多数似乎都不支持MFC 我考虑的选项是 A 编码 UI UI 自动化 看起
  • Windows API 中逻辑坐标和设备坐标之间的混淆

    我一直在研究一个使用两个函数的 Visual Studio C Windows 应用程序项目SetWindowExt and SetViewportExt 我对这两个函数的作用以及为什么它们是必要的感到困惑 搜索这些函数 我得出了逻辑坐标和
  • 如何中止使用 wininet 发送的请求?

    我有一个 MFC 应用程序 用于向相应的服务器发送 post 请求 这是一个http请求 用于上传文件 但有要求中止发送的请求 就像用户正在发送一个大文件一样 用户应该能够在请求完成之前取消请求 我正在使用 wininet api Http
  • Visual C++ MFC 中窗口启动时的事件顺序

    假设我有一个从 CWnd 派生的类 它具有事件处理函数OnPaint OnCreate and OnSize 如您所知 所有这些都是在窗口启动时发生的 但我想看看它们之间的顺序是什么 例如 当我在其中一个中设置断点时 在结束函数后 控制不会
  • 支持 ARM 上的 Windows 10 桌面应用程序 - MFC 和 COM 以及 OPOS 可以工作吗?

    我试图了解将在 x86 Windows 10 上运行的 C MFC 应用程序移植到具有 Qualcomm Snapdragon 处理器的 ARM Windows 10 设备的障碍 32位应用程序具有以下特点 MFC 与 C 用于用户界面 C

随机推荐

  • CAS SSO 单点登录框架 学习

    1 了解单点登录 SSO 主要特点是 SSO 应用之间使用 Web 协议 如 HTTPS xff0c 并且只有一个登录入口 SSO 的体系中有下面三种角色 1 User 多个 2 Web 应用 多个 3 SSO 认证中心 一个 2 SSO
  • C# override详解

    重载 重写 覆写 xff0c 分别指的是overload override new 一 override重写 xff0c 是在子类中重写父类中的方法 xff0c 两个函数的函数特征 xff08 函数名 参数类型与个数 xff09 相同 用于
  • C# -WinForm 中英文实现, 国际化实现的简单方法

    来源 xff1a http www jb51 net article 45675 htm xff0c 今天看到了借鉴过了 xff0c 保存一下 xff0c 下次开发直接用嘻嘻 软件行业发展到今天 xff0c 国际化问题一直都占据非常重要的位
  • C#的Winform多语言实现(resx文件)

    使用resx文件实现Winform多语言切换 xff0c 本博文提供的代码可以实现简体中文 繁体中文 英文的切换 如果需要增加其它语言的切换 xff0c 只需要编写相应的语言的resx文件即可 并且 xff0c 当修改了语言之后 xff0c
  • c#中中读取嵌入和使用资源文件的方法

    Dotnet中嵌入资源 xff08 位图 图标或光标等 xff09 有两种方式 xff0c 一是直接把资源文件加入到项目 xff0c 作为嵌入资源 xff0c 在代码中通过Assembly的GetManifestResourceStream
  • c#中英文切换实例

    1 创建两个资源文件 Resource en US resx Resource zh CN resx 注意中间部分每种语言文件名固定 xff0c 后缀名是 resx 首部分是自定义的名字 2 创建读写工具类 上面两个资源文件都是在Utili
  • TCP/IP协议讲的明明白白

    从字面意义上讲 xff0c 有人可能会认为 TCP IP 是指 TCP 和 IP 两种协议 实际生活当中有时也确实就是指这两种协议 然而在很多情况下 xff0c 它只是利用 IP 进行通信时所必须用到的协议群的统称 具体来说 xff0c I
  • TCP/IP协议与Http协议的区别详解

    TPC IP协议是传输层协议 xff0c 主要解决数据如何在网络中传输 xff0c 而HTTP是应用层协议 xff0c 主要解决如何包装数据 关于TCP IP和HTTP协议的关系 xff0c 网络有一段比较容易理解的介绍 xff1a 我们在
  • C#中使用MD5对用户密码加密与解密

    C 中常涉及到对用户密码的加密于解密的算法 xff0c 其中使用MD5加密是最常见的的实现方式 本文总结了通用的算法并结合了自己的一点小经验 xff0c 分享给大家 一 使用16位 32位 64位MD5方法对用户名加密 1 xff09 16
  • C#中修改文件或文件夹的权限,为指定用户、用户组添加完全控制权限

    写在前面 在windows系统中 xff0c c盘中的目录权限比较高 xff0c 有时制作安装包的时候 xff0c 默认的安装路径就是在c盘 xff0c 但对运行可执行文件 xff0c 有时候需要为其添加完全控制权限 xff0c 或者读写权
  • C#使用SafeNet的开发狗和超级狗加密使用方法

    1 在安装光盘中选择Windows文件夹 xff0c 单击Setup exe执行安装程序 xff0c 安装过程默认选择 xff0c 单击下一步即可 2 安装完毕后插入超级狗 xff0c 在菜单栏中打开 超级狗开发商向导 xff0c 选择下载
  • Keil5 新建工程编译后Download按钮变灰

    Keil5 新建工程编译后Download按钮变灰 百度看到很多文章解决变灰方法为 这个主要目的是生成 HEX文件 xff0c 但有时候当你在线下载时点了Creat HEX File 并进行编译后 Download按钮依然呈现灰色 此时问题
  • C# SQLite 数据库操作语句与文件视图管理器

    sqlite数据库视图管理器 SQLiteSpy是一款专业的SQLite数据库管理 xff0c 主要用于读取sqlite数据库 xff0c 建立新的SQL查询 xff0c 视图 xff0c 复制和删除表 编辑单元格 xff0c 以及查看索引
  • C#JSON与XML相互转换

    using System using System Collections Generic using System Linq using System Text using System Xml using Newtonsoft Json
  • 让使用SQLite的.NET应用自适应32位/64位系统

    Windows 7 64 bit 刚扩到4G内存 果断换64位的 那速度杠杠的 Microsoft Visual Studio 2010 Ultimate 32bit 在64位系统里运行正常 sqlite netFx40 setup bun
  • WinForm 布局控件“WeifenLuo.WinFormsUI.Docking“的使用

    本篇介绍Winform程序开发中的布局界面的设计 xff0c 介绍如何在我的共享软件中使用布局控件 34 WeifenLuo WinFormsUI Docking 34 布局控件 34 WeifenLuo WinFormsUI Dockin
  • C#中JSON和对象之间互相转换功能示例

    本文实例讲述了C 实现JSON和对象之间互相转换功能 分享给大家供大家参考 xff0c 具体如下 xff1a 1 首先是声明用户信息对象 xff0c DataContract修饰类 xff0c 表示可以被解析成JSON xff0c Data
  • C#使用DataContractJsonSerializer来进行JSON解析

    在使用DataContractJsonSerializer进行解析之前 xff0c 我们首先需要将两个包引入进来 xff1a using System Runtime Serialization using System Runtime S
  • 步进电机驱动器脉冲频率确定 步进电机驱动器

    1 步进电机为什么要配步进电机驱动器才能工作 xff1f 步进电机作为一种控制精密位移及大范围调速专用的电机 它的旋转是以自身固有的步距角角 xff08 转子与定子的机械结构所决定 xff09 一步一步运行的 其特点是每旋转一步 xff0c
  • MFC添加自定义消息及重写消息过程

    由于MFC中无法通过类向导来自定义消息 xff0c 所以需要手动添加 xff0c 主要过程如下 xff1a 本文基于vs2008下通过线程实现数据实时更新的对话框运用程序 1 定义消息 xff08 Resource h文件中 xff09 x