PostMessage,SendMessage,GetMessage,PeekMessage,TranslateMessage,DispatchMessage的用法集合

本文深入探讨了Windows消息处理机制中的SendMessage、PostMessage与PostThreadMessage函数的功能、区别及常见问题,包括剪切、复制、粘贴操作的实现、线程间通讯、消息队列管理、消息类型处理与过滤等核心概念。
摘要由CSDN通过智能技术生成

SendMessage函数功能描述:将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口过程,直到窗口过程处理完消息后才返回。

Postmessage函数则是将消息放入消息队列里,并立即返回。

PostThreadMessage函数: 

BOOL PostThreadMessage( DWORD idThread,
                        UINT Msg,
                        WPARAM wParam,
                        LPARAM lParam
                      );


     它可以用于线程之间的异步通讯,因为它不用等待调用者返回,

.常见问题
1)使用SendMessage来实现剪切、复制和粘贴

SendMessage(hwnd, WM_COPY, 0, 0);
SendMessage(hwnd, WM_CUT, 0, 0);
SendMessage(hwnd, WM_PASTE, 0, 0);

2)SendMessage与PostMessage的区别
PostMessage将消息放入消息队列后马上返回,而SendMessage直到窗口过程处理完消息后才返回,PostMessage 是异步的,SendMessage 是同步的。PostMessage 只把消息放入队列,不管消息是否被处理就返回,消息可能不被处理;而 SendMessage 等待消息被处理完了之后才返回,如果消息不被处理,发送消息的线程将一直被阻塞。

3、如果在同一个线程内,SendMessage 发送消息时,由 USER32.DLL 模块调用目标窗口消息处理程序,并将结果返回。SendMessage 在同一线程中发送消息并不入线程消息队列。PostMessage 发送消息时,消息要先放入线程的消息队列,然后通过消息循环分派到目标窗口(DispatchMessage)。

  如果在不同线程内,SendMessage 发送消息到目标窗口所属线程的消息队列,然后发送消息的线程在 USER32.DLL 模块内监视和等待消息处理,直到目标窗口处理完返回。SendMessage 在返回前还做了很多工作,比如,响应别的线程向它 SendMessage。Post 到别的线程时,最好用 PostThreadMessage 代替 PostMessage,PostMessage 的 hWnd 参数可以是 NULL,等效于 PostThreadMessage GetCurrentThreadId。Post WM_QUIT 时,应使用 PostQuitMessage 代替。


4)SendMessage发送WM_COPYDATA消息在进程间传送数据
WM_COPYDATA消息主要目的是允许在进程间传递少量只读数据。SDK文档推荐用户使用SendMessage()函数,接收方在数据复制完成前不返回,这样发送方就不可能删除和修改数据。
例如:

std:string strData = "VC COM";
COPYDATASTRUCT cds;
cds.dwData = 0;
cds.cbData = strData.Length();
cds.lpData = strData.c_str();
::SendMessage(hwnd, WM_COPYDATA, NULL, (LPARAM)&cds);

3、PostThreadMessage有时会失败,报1444错误(Invalid thread identifier. ),其实这不一定是线程不存在的原因,也有可能是线程不存在消息队列(message queue)造成的。事实上,并不是每个thread都有message queue,那如何让thread具有呢? 答案是,至少调用message相关的function一次,比如GetMessage,PeekMessage
4、如果是postthreadmessage动态分配的memory给另外一个thread,要注意内存的正确释放(注意,此处不释放可能导致内存泄露)。并且PostThreadMessage不能够post WM_COPYDATE之类的同步消息,否则会报错!


GetMessage、PeekMessage、WaitMessage ,消息的接收主要有3个函数,该函数用于查看应用程序的 消息队列
消息的接收主要有3个函数: GetMessage、PeekMessage、WaitMessage
GetMessage 原型如下:
BOOL GetMessage(
        LPMSG lpMsg,
        HWND hWnd,
        UINT wMsgFilterMin,
        UINT wMsgFilterMax
        );
PeekMessage 原型如下:
BOOL PeekMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
该函数用于查看应用程序的消息队列,如果其中有消息就将其放入lpMsg所指的结构中,不过,与GetMessage不同的是, PeekMessage 函数 不会等到有消息放入队列时才返回 。同样,如果hWnd为NULL,则PeekMessage 获取属于调用该函数应用程序的任一窗口的消息 ,如果hWnd=-1,那么函数 只返回把hWnd参数为NULL的PostAppMessage函数送去的消息 。如果 wMsgFilterMin和wMsgFilterMax都是0,则PeekMessage就 返回所有可得到的消息 最后一个参数可以是PM_NOREMOVE 或者 PM_REMOVE,表示消息信息是否应该从 消息队列 中删除。 函数获取之后将删除 消息队列 中的除  WM_PAINT 消息之外的其他消息,至于 WM_PAINT 则只有在其处理之后才被删除((((   从队列中删除WM_PAINT消息的情况是当显示区域重新变得有效的时候, 用ValidateRect和ValidateRgn或者BeginPaint和EndPaint来完成   ))))。
WaitMessage 原型如下:
BOOL WaitMessage();
当一个应用程序无事可做时,该函数就将 控制权交给另外的应用程序 ,同时 将该应用程序挂起 ,直到一个新的消息被放入应用程序的队列之中才返回。
消息的处理
接下来我们谈一下消息的处理,首先我们来看一下VC中的消息泵:
while(GetMessage(&msg, NULL, 0, 0))
   {
       if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))
       {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
       }
   }
首先,GetMessage从进程的主线程消息队列中获取一个消息并将它复制到MSG结构,如果队列中没有消息,则 GetMessage 函数将等待一个消息的到来以后才返回。如果你将一个窗口句柄作为第二个参数传入GetMessage,那么 只有指定窗口的的消息 可以从 队列 中获得。GetMessage也可以从 消息队列 中过滤消息 只接受消息队列中落在范围内的消息 。这时候就要利用 GetMessage/PeekMessage指定一个消息过滤器 。这个过滤器是一个 消息标识符的范围 或者是 一个窗体句柄 ,或者两者同时指定。当应用程序要查找一个后入 消息队列 的消息是很有用。 WM_KEYFIRST  和  WM_KEYLAST  常量用于接受所有的键盘消息。  WM_MOUSEFIRST  和  WM_MOUSELAST  常量用于接受所有的鼠标消息。
然后TranslateAccelerator判断该消息 是不是一个按键消息并且是一个加速键消息 ,如果是,则该函数将把几个按键消息转换成一个 加速键消息 传递给窗口的 回调函数 。处理了加速键之后,函数TranslateMessage将把两个按键消息 WM_KEYDOWN WM_KEYUP  转换成一个 WM_CHAR (该消息的wParam附加参数包含了字符的ASCII码) ,不过需要注意的是,消息 WM_KEYDOWN,WM_KEYUP 仍然将传递给窗口的 回调函数 注意,TranslateMessage函数并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。
处理完之后,DispatchMessage函数将把此消息发送给 该消息指定的窗口中已设定的回调函数 DispatchMessage实际上是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理(响应)。 如果消息是WM_QUIT,则  GetMessage返回0 ,从而 退出循环体 。应用程序可以使用 PostQuitMessage 来结束自己的 消息循环 。通常在主窗口的 WM_DESTROY消息中调用。
下面我们举一个常见的小例子来说明这个消息泵的运用:
if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST,WM_KEYLAST, PM_REMOVE))
   {
      if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)...
   }
这里我们接受所有的键盘消息,所以就用WM_KEYFIRST 和 WM_KEYLAST作为参数。
所以这段小代码就是判断是否按下了Esc键,如果是就进行处理。

 


 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值