Friday, May 14, 2004

 

The various ways of sending a message

1.
By Raymond Chen
From http://weblogs.asp.net/oldnewthing/archive/2004/11/19/266664.aspx

There are several variations on the SendMessage function, but some are special cases of others.

The simplest version is SendMessage itself, which sends a message and waits indefinitely for the response.

The next level up is SendMessageTimeout which sends a message and waits for the response or until a certain amount of time has elapsed. SendMessage is just SendMessageTimeout with an INFINITE timeout.

Another version of SendMessage is SendNotifyMessage, which is like SendMessage except that it doesn't wait for the response. It returns immediately and ignores the result produced by the receiving window.

The last SendMessage-style functions is SendMessageCallback. This sends a message and then returns immediately. When the recipient finally returns a response, the callback is called.

SendNotifyMessage is SendMessageCallback with a callback that does nothing.

That's how the four message-sending functions fit together.

Bonus remark: If you use any of the above send-type functions to send a message to a window that belongs to the sending thread, the call is made synchronously.

http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/messagesandmessagequeues/
messagesandmessagequeuesreference/messagesandmessagequeuesfunctions/sendmessage.asp

http://msdn.microsoft.com/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/MessagesandMessageQueues/Messagesand
MessageQueuesReference/MessagesandMessageQueuesFunctions/SendMessageTimeout.asp

http://msdn.microsoft.com/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/MessagesandMessageQueues/Messagesand
MessageQueuesReference/MessagesandMessageQueuesFunctions/SendNotifyMessage.asp

http://msdn.microsoft.com/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/MessagesandMessageQueues/Messagesand
MessageQueuesReference/MessagesandMessageQueuesFunctions/SendMessageCallback.asp

2.
(按:这也是很老的两篇文档)

作者:郑力群
来源:http://www.vcroad.net/

前言

  近来,有几个网友发妹儿问了我一些关于WM_NOTIFY通知消息的使用问题,为此,我写了这篇文章,将我对WM_NOTIFY消息的理解阐述如下,希望能对各位有所帮助。

  引入WM_NOTIFY消息的原因

  1、问题的提出:

  在Windows3.x中,不存在WM_NOTIFY消息,控制子窗口的通知消息同菜单的命令消息及加速键消息一样,均使用WM_COMMAND来发送,此时,根据WM_COMMAND消息传递的做法,可知道含有如下的传递结构:

wParam high-order : Notify Code :(如TTN_NEEDTEXT)
wParam low-order : ControlID
lParam : ControlHandle

  使用上述结构进行消息的传递时,如果一个通知消息有一些附加的消息需要发送时(如传递LVN_COLUMNCLICK时可能需要附加传送如点击的是那一列等信息),因为WM_COMMAND仅能传递如上的参数,故无法做到。

  2、解决的方案

  --Windows 3.x中的解决方案,为这些有附加消息传送要求的通知消息,为它们各自定义了许多特殊的消息。

  以WM_DRAWITEM为例:

  lParam参数成为指向DRAWITEMSTRUCT结构的指针,DRAWITEMSTRUCT结构如下:

typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
ULONG_PTR itemData;
} DRAWITEMSTRUCT;

  这样,就实现了附加参数的传递,类似的做法在Windows3.x中随处可见。

3.
前言

  我曾写过一篇文章对通知消息WM_NOTIFY进行分析,消息反射是MFC中对通知消息的处理方式,两者之间关系十分紧密,因此,我写了这篇文章,希望能够描绘出通知消息的完整印象。

消息反射的基础知识

1、消息反射解释:
  父窗口将控制子窗口发给它的通知消息,首先反射回子窗口进行处理(即给控制子窗口一个机会,让控制子窗口处理此消息),这样通知消息就有机会能被子窗口自身进行处理。

2、MFC中引入消息反射的原因:
  在Windows的消息处理中,控制子窗口的发给其父窗口的通知消息只能由其父窗口进行处理,这使得控制子窗口的自身能动性大大降低(你想,它连改变自己的背景色,处理一个自身滚动问题都要其父窗口来完成),为了解决这个问题,在MFC中引入了反射消息“Reflect Message”的概念,进行消息反射,可以使得控制子窗口能够自行处理与自身相关的一些消息,增强了封装性,从而提高了控制子窗口的可重用性。

消息反射的处理流程(不考虑OLE控制)

一、消息反射处理流程图:
  1、父窗口收到控制子窗口发来的通知消息后,调用它的虚函数CWnd::OnNotify.
CWnd::OnNotify()主体部分:
{
if (ReflectLastMsg(hWndCtrl, pResult)) //此时,hWndCtrl,为发送窗口,即子窗口的窗口句柄
return TRUE; // 子窗口已处理了此消息
AFX_NOTIFY notify;
notify.pResult = pResult;
notify.pNMHDR = pNMHDR;
return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);
}

  分析:首先,调用ReflectLastMsg(hCtrlChildWnd,...)给子窗口一个自身处理的机会,将消息反射给子窗口处理,函数返回TRUE,表明子窗口处理了此消息。反之,表示子窗口未处理此消息,此时,调用OnCmdMsg(...)由父窗口进行通常的处理。

  2、ReflectLastMsg中:
  主要是调用发送窗口的SendChildNotifyLastMsg(...)。

  3、SendChildNotifyLastMsg 中:
  调用发送窗口的虚函数OnChildNotify函数,进行处理。 如果没有处理,则调用ReflectChildNotify(...)函数进行标准的反射消息的消息映射处理。


二、消息处理

方式1:
  由上述处理流程可以看出来,子窗口要想自身处理此消息,重载子控件窗口的OnChildNotify虚拟函数应该是很容易想到的方式。

  注意:MFC中对各个子控件窗口一般都已经重载了OnChildNotify函数,它对应调用类的虚函数进行处理,所以,你重载对应的虚函数即可,如下例:
BOOL CStatusBarCtrl::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam,LRESULT* pResult)
{
if (message != WM_DRAWITEM) //对应不同的控制,会有不同的有特殊处理要求的消息。
return CWnd::OnChildNotify(message, wParam, lParam, pResult);
...
...
DrawItem((LPDRAWITEMSTRUCT)lParam);
return TRUE;
}
virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
void CStatusBarCtrl::DrawItem(LPDRAWITEMSTRUCT)
{
ASSERT(FALSE); // must override for self draw status bars
}

  你重载CSTatusBarCtrl类的DrawItem虚拟函数,即可实现对反射消息WM_DRAWITEM的处理。

方式2:
  从方式1可以看出,如果你不在被重载的OnChildNotify中对消息进行处理,函数会调用CWnd::OnChildNotify,它调用ReflectChildNotify函数进行标准的处理。
1、增加反射消息的映射入口。
2、增加对应的消息处理函数。
注意:可以使用MFC的ClassWizard作上述动作,在ClassWizard中,可处理的反射消息以一个"="号以示区别。返回值为TRUE,表示控件窗口已处理此反射消息,为FALSE,表示控件子窗口未处理此反射消息。

结语:

  消息反射不是很难的概念。它仅出现在MFC中;它的用意是方便控制子窗口的重用;对某些通知消息你可以重载对应的虚函数(WM_DRAWITEM...)进行处理;对其它你可以使用标准的消息反射映射进行处理。限于篇幅,一些细节问题,请阅读MFC中对应的源代码。

2.
原作:111222

前几天,我尝试在CListCtrl中映射HDN_BEGINTRACK这个WM_NOTIFY消息。

轻松的用ClassWizard建立了消息映射,几秒中完成了所有工作之后发生的事情却让我很长时间轻松不起来了。

消息根本映射不上!!

BEGIN_MESSAGE_MAP(CPrevList, CListCtrl)
//{{AFX_MSG_MAP(CPrevList)
ON_NOTIFY_REFLECT(HDN_BEGINTRACK, OnBegintrack)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CPrevList::OnBegintrack(NMHDR* pNMHDR, LRESULT* pResult)
{
HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;
// TODO: Add your control notification handler code here
AfxMessageBox("总算映射上了!");//程序始终执行不到这个位置
*pResult = 0;
}

此后我在这个问题上苦恼了很久,最后发现了两种解决办法

1,对消息映射两次,分别是XXXXW和XXXXA

BEGIN_MESSAGE_MAP(CPrevList, CListCtrl)
//{{AFX_MSG_MAP(CAdvHeaderCtrl)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(HDN_BEGINTRACKW, OnBeginTrack)
ON_NOTIFY_REFLECT(HDN_BEGINTRACKA, OnBeginTrack)
END_MESSAGE_MAP()

2,重载CWnd::OnNotify虚函数在其中对XXXXW和XXXXA分别处理

BOOL CPrevList::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
switch (((NMHDR*)lParam)->code)
{
case HDN_BEGINTRACKW:
case HDN_BEGINTRACKA:

AfxMessageBox("终于映射上了");

}

return CListCtrl::OnNotify(wParam, lParam, pResult);
}


反观问题的产生完全是操作系统惹的祸,我的操作系统是WIN2K(VC6无SP)

WIN2K使用Wide Char,就是常说的UNICODE,在WIN2K下消息对应着WM_XXXXW

而早期WIN9X使用ASCII,消息为WM_XXXXA。

要想使用某些控件的WM_NOTIFY消息就必须自己做类似上面的处理……

(按:我从未在Win9x下演练过,所以不太确定是否会出现111222所说的这种情况。姑且存目)



<< Home

This page is powered by Blogger. Isn't yours?