SendMessage、SendMessageTimeout、PostMessage、PostThreadMessage

本文详细介绍了Windows编程中SendMessage和PostMessage函数的区别与用法。解释了如何使用这些函数向窗口发送消息,以及如何处理自定义消息。此外还讨论了PreTranslateMessage的作用及局限性,并对比了两种消息发送方式的特点。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

  SendMessage函数将指定的消息发送到一个或多个窗口,消息会直接发送到窗口过程而不经过消息队列,且直到消息处理完成后,SendMessage才返回。函数返回值指定消息处理的结果,依赖于所发送的消息。函数原型:

LRESULT WINAPI SendMessage(
        HWND hWnd,
        UINT Msg,
        WPARAM wParam,
        LPARAM lParam
); <span style="font-size:14px;"> 
</span>

  hWnd:接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。

  Msg:要发送的消息。

  wParam:消息的附加信息。

  IParam:消息的附加信息

有穿过消息队列的消息才能被虚函数PreTranslateMessage()截获,故采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息不会被PreTranslateMessage()截获。

  如果调用SendMessage向本窗口发送消息的话,会直接调用本窗口的窗口过程,执行完窗口过程SendMessage才返回 —— 相当于 “函数直接同步调用”,而非 “消息投递”。

 SendMessageTimeout是SendMessage的超时版,即可以设置等待的超时时间。其增加了三个参数,第一个参数设置行为,推荐的组合是SMTO_NORMAL|SMTO_ABORTIFHUNG,表示正常等待目标线程处理消息,如果检测到目标线程处于挂起状态的话取消等待函数直接返回,第二个参数指定等待时间,单位为毫秒,第三个参数是输出参数,表示目标线程处理消息的结果。

  PostMessage函数将一个消息放入(寄送)到与指定窗口创建的线程相关联的消息队列里,不等待线程处理消息就立刻返回。消息队列里的消息通过调用GetMessage或PeekMessage取得。函数执行成功返回非0,否则返回0。函数参数意义与SendMessage相同。

系统只对系统级的消息(0 ~ WM_USER-1)进行封送处理。发送自定义消息(>= WM_USER)到另一个进程,你需要自己对消息进行封送处理。
如果发送一个范围低于 WM_USER 的消息给异步消息函数(PostMessage、  SendNotifyMessageSendMessageCallback),它的消息参数不能包含指针。否则,操作将失败。函数将在接收线程处理消息之前返回,发送者将在内存被使用之前释放。
请不要使用 PostMessage 函数投递 WM_QUIT 消息;应该使用 PostQuitMessage 函数代替。

  对于接收消息的窗口,有两种处理消息的方法:

1、重写PreTranslateMessage来截获消息,只有穿过消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在。

2、添加以下代码来处理自定义消息:

①、自定义消息ID:
#define WM_MY_MESSAGE(WM_USER + 100)
②、添加消息处理函数:
protected:
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam)
    {
        ......
    }
③、映射消息ID和消息处理函数:
BEGIN_MESSAGE_MAP(CMFCApplication17Dlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDOK, &CMFCApplication17Dlg::OnBnClickedOk)
    ON_WM_TIMER()
    ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage) //映射消息ID和消息处理函数
END_MESSAGE_MAP()

BOOL WINAPI PostMessage(
            HWND hWnd,
            UINT Msg,
            WPARAM wParam,
            LPARAM lParam
);
  

  从以上两个函数的功能可以看出SendMessage与PostMessage最大的区别有三点:1、SendMessage发送的消息不会进队而PostMessage发送的消息会进队。2、SendMessage属于阻塞函数,PostMessage属于非阻塞函数,即使是向本窗口线程发送消息,PostMessage也是将消息添加到目标线程的消息队列后立即返回,不会像SendMessage那样同步执行窗口过程。3、一些特定的消息只能通过SendMessage来发送,如WM_COPYDATA。

  PostThreadMessage函数用来向线程而非窗口发送消息,对于线程消息,MSG结构体中hwnd成员应为NULL。收消息的线程需要自己实现消息的获取和处理(比如调用PeekMessage、GetMessage来获取消息)。类似PostMessage,PostThreadMessage也是异步发送消息。

Win32、MFC下投递异步消息(执行异步任务)

  1、win32下使用全局的PostMessage()方法,MFC下使用CWnd::PostMessage()方法,示例代码如下所示。如果是在非MFC窗口线程中向MFC窗口发送消息的话,使用win32的PostMessage()方法,MFC的CWnd::PostMessage()方法仅对MFC线程有效。调用win32的PostMessage()方法投递异步消息(不管是在窗口线程里调用还是在非窗口线程里调用)不用担心目标窗口被关闭释放,因为:①、消息投送到目标窗口的消息队列之前窗口已经释放的话,消息无法被投递到窗口的消息队列,不会触发任何窗口过程函数。可以在调用PostMessage之前先调用IsWindow(hwnd)来判断窗口是否有效以避免无效投递。②、消息投送到目标窗口的消息队列之后,还未来得及处理该消息窗口就被关闭的话,窗口关闭时,Windows 会自动清理该窗口对应的消息队列,所有未处理的消息会被直接丢弃,不会被DispatchMessage()分发到窗口过程函数去处理。③、消息成功投送到目标窗口的消息队列,且正在处理该消息的时候窗口被关闭的话,会先等待该消息处理完成后再关闭释放窗口。

/******对话框头文件******/

class CMyDialog : public CDialogEx {
    DECLARE_MESSAGE_MAP()
public:
    afx_msg LRESULT OnMyTask(WPARAM wParam, LPARAM lParam); 
    void test();
};

/******对话框cpp文件******/

BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
    ON_MESSAGE(WM_USER + 100, &CMyDialog::OnMyDelayTask) // 绑定自定义消息
END_MESSAGE_MAP()

//发送自定义消息
void CMyDialog::test()
{
    this->PostMessage(WM_USER + 100, (WPARAM)123, 0);
}

//自定义消息的处理
LRESULT CMyDialog::OnMyTask(WPARAM wParam, LPARAM lParam)
{
    int value = (int)wParam;

    return 0;
}

2、无窗口环境下的话,可以使用MFC的PostThreadMessage,该方法用来向指定的线程(通过线程ID)发送消息,接收消息的线程必须有消息循环(GetMessage())。如果是向窗口发送消息的话,优先使用PostMessage,因为如果使用PostThreadMessage向窗口线程发送消息的话,如果接收线程处于模态循环中(如MessageBox/DialogBox),消息会丢失。

3、MFC在窗口空闲的时候(MFC消息循环无任何待处理消息)会触发OnIdle()虚函数,所以可以直接重写该方法来执行一些非紧急、轻量的后台任务(如缓存清理、数据统计等):如果主线程一直有消息(如频繁重绘),OnIdle()可能迟迟不执行,所以不适合紧急任务。在OnIdle()中执行耗时操作的话,无法响应新的窗口消息,从而导致窗口卡顿。

JUCE中三种异步任务投递方式

1、callAsync(),通用异步任务开启,适合在非juce主线程中来通知juce主线程开启异步任务,任务开启后有可能宿主已经被析构,需要通过weak_ptr+shared_from_this来进行判断。

2、AsyncUpdater::triggerAsyncUpdate(),异步更新方法,适合juce主线程内非Component窗口下异步方法的执行,异步方法handleAsyncUpdate()执行的时候宿主有可能已被析构,所以需要在宿主类的析构方法中添加cancelPendingUpdate()方法来取消handleAsyncUpdate()的执行。也可以在非juce界面主线程中调用AsyncUpdater::triggerAsyncUpdate()来通知juce主线程进行handleAsyncUpdate()方法的执行,但是因为handleAsyncUpdate()无参数,所以如果想要附加数据进行通知的话,使用上面的callAsync(附加数据以捕获的形式传递给lambda/function)。

3、postCommandMessage(),适合juce主线程内Component窗口下异步消息通知方法,不用担心消息发送后窗口被析构(内置宿主感知,类似repaint()等方法)。        

跨平台(同时支持 Windows/Linux)异步消息投递

使用std::queue和std::mutex+std::condition_variable实现一个线程安全的队列等待消息,队列类型为std::function<void()>,向队列中投递任务即为向队列增加一个std::function,然后进行通知。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值