windows自定义消息

Introduction

There are two kinds of messages which you, as a programmer, can defined. The first kind is the compile-time-constant variety. I have found, after years of experience, These Are Not Your Friend. I'll nonetheless describe them as many people still prefer them, but as a matter of opinion I avoid them as much as I can. The second kind are messages which are guaranteed to be unique within a system. I much prefer these, and use them exclusively.

WM_USER messages (obsolete) WM_APP messages Registered Window Messages Guaranteeing uniqueness of registered messages GUIDGEN Defining messages Passing pointers in messages Sending messages across threads Sending messages across processes Sending Messages to Views (new 13-Oct-99) Hazards of Cross-Process Messages (new 13-Oct-99) WM_COPYDATA
介绍

作为应用程序员,我们可以自定义两种消息. 第一种方式在编译的时候就确定,无法改变. 通过本人多年的经验,我发现这种自定义消息方式并不可靠。许多人可能仍然喜欢它,当本人却尽量避免使用它。第二种方式可以确保注册消息在整个系统唯一, 我更倾向于这种方式,并大量使用它们.

WM_USER 消息 (已过时) WM_APP 消息 注册窗口消息 确保注册消息全局唯一 GUIDGEN工具 定义消息 在消息中传递指针 在线程间发送消息 在进程间发送消息 发送消息到视图 (new 13-Oct-99) 跨进程消息的危害 (new 13-Oct-99) WM_COPYDATA消息

WM_USER: obsolute

Older books on Windows programming tell about how to define user-defined messages using the symbol WM_USER. This technique is obsolete. There were too many problems with WM_USER-based symbols conflicting with messages that Microsoft was using. The new method is to use WM_APP as the base. If you have something that uses WM_USER, the usage is identical to the usage of messages based on WM_APP. Therefore, I will not reproduce the discussion here.

WM_USER: 已过时

关于windows编程的一些老书肯能会告诉你如何通过WM_USER自定义消息。这种技术已经过时了, 因为通过WM_USER自定义的消息会和微软保留的消息产生冲突,并带来很多问题。 新方法是将WM_APP作为用户自定义消息的起始值. 如果你了解如何使用WM_USER, 你会发现使用WM_APP作为消息起始值的用法和它完全一样, 这里就不再赘述.

WM_APP: constant messages
If you are comfortable with the idea of compile-time constant messages--and after you read the next section, you may not be--then you can use definitions based on the symbol WM_APP, which Microsoft now specifies as the desirable symbol to use. The correct form of the definition of such a symbol is
#define UWM_MYMESSAGE (WM_APP + n)

where n is some integer, typically a small integer like 1, 2, 3, etc. This defines a value which identifies the message. While strictly speaking the parentheses are not mandatory, good programming practice demands their presence.

I prefer a naming convention that does not conflict with the Microsoft naming convention. For one thing, it makes your code difficult for someone else to read and understand; for another, it makes it hard for you to read and understand. My preference is to use a UWM_ prefix (User Window Message); other people have used WMU_ (Window Message, User), and you can pick any convention you want, but do not use prefixes that conflict with those already in use by Microsoft.

Note that there is absolutely no requirement that every user-defined message be unique. Messages are always interpreted in the context of a particular window. Thus, you can have messages like

#define UWM_PAINT_VIEW_PURPLE (WM_APP + 7)
#define UWM_RESET_VIEW        (WM_APP + 7)

These are perfectly valid providing that the view that accepts the purple request is never sent a reset request, and vice-versa.

To create an entry in the table to dispatch on these messages, you make an entry

ON_MESSAGE(UWM_RESET_VIEW, OnReset)
This requires that you define the handler  OnReset. In the message handler part of your  .h file, you add the declaration
afx_msg LRESULT OnReset(WPARAM, LPARAM);
When your window class receives the UWM_RESET_VIEW message, it will call the OnReset handler.

WM_APP: 数值无法改变的消息

如果你满意编译时间常量消息--当你读完下一节内容,我想你就不会这么认为了--你可以基于WM_APP注册消息,这是微软官方推荐的做法. 正确定义消息的方式如下:

#define UWM_MYMESSAGE (WM_APP + n)

这里n是一些整数,一般可以取值为1,2,3等较小的整数. 定义的这个整数表示了一条消息. 严格来讲,语句中的括号不是必须的,但好的编程方式鼓励这么做.

对于消息的命名,我尽量避免和微软的消息命名冲突。其一,这可能导致阅读代码的其他人非常难以理解和困惑, 其二,对自己阅读代码和理解也会造成困难. 我的经验是在消息的前面加上UWM_前缀(User Window Message); 其他人也有使用WMU_(Window Message,User),你可以选择其中任一种,但是确保消息的命名不要和微软保留的消息冲突.

注意,没有要求说一定要保证每一条用户自定义消息数值唯一, 消息通常是是针对特定的窗口,不同的窗口环境,消息可能有不同的含义,因此你可以这样定义两条相同数值的消息.

 
 
#define UWM_PAINT_VIEW_PURPLE (WM_APP + 7)
#define UWM_RESET_VIEW        (WM_APP + 7)

接受purple消息请求的视图窗口可以从不发送reset消息,这是完全有可能的,反之亦然.

为了自定义消息的正确分发,你需要在MFC消息表格中增加一个条目,如下所示:

ON_MESSAGE(UWM_RESET_VIEW, OnReset)
以上要求你定义个消息处理函数OnReset. 你需要在头文件消息处理部分增加以下声明:

afx_msg LRESULT OnReset(WPARAM, LPARAM);
当你窗口接收到WWM_RESET_VIEW消息时,MFC框架将自动调用OnReset消息处理函数.


Registered Window Messages:non-constant messages

There are several problems with constant messages.

  • You can't send them between processes reliably. If you accidentally send a message to a process that has never heard of your message, it could crash. If you receive a message that you think you understand, you might crash.
  • You can't create a DLL that notifies its clients via messages. This is because you might choose (WM_APP+7) as your desired message, and some other DLL writer you never heard of might have also chosen (WM_APP+7) as his or her desired message. The poor programmer who is trying to use both DLLs is in deep trouble, because of the conflict.
  • You can't even think of sending one of these messages down through your window hierarchy by usingSendMessageToDescedants, because some window you never heard of may be using that message. Consider the example in the previous section where a message to paint the view purple and a message to reset the view were the same code. If you sent this message to all descendants of your main frame, some would reset and some would change to purple, which is not a particularly desired outcome.

注册窗口消息: 可以改变数值的消息

注册常量消息存在几个问题.

1. 你无法在进程间可靠地传递消息. 如果你不小心发送一个未知的消息到另外一个进程,这个进程将会崩溃. 即使你的进程接收了一个你已经注册过的常量消息,你的进程也有可能崩溃.

2. 你无法在DLL中,通过常量消息(constant messages)通知客户应用程序。因为当你选择(WM_APP+7)这个数值作为自定义消息时,其他你可能重来就没有听过的DLL程序也有可能注册了(WM_APP+7)这个消息。如果这个时候,客户应用程序同时使用了这两个DLL,将会导致消息冲突.

3. 你甚至都无法通过调用SendMessageToDescedants将自定义消息传递给你的子孙窗口,因为有可能其中的一些窗口可能无法处理这个消息. 从我们之前的例子中,我们注册两个两个相同的消息值,一个用来填充窗口为紫色,一个用来重置窗口为默认颜色。如果你发送这个消息给主窗口的所有子窗口,那么有些子窗口可能会重新刷新窗口为默认颜色,有些窗口可能就会填充为紫色了,这种结果可能不是我们有意为之。


The way this is solved is by using a Registered Window Message. This is a message which is guaranteed to be unique. Only those windows or processes or DLLs that create it, and those which specifically use it, will actually have the same message number.

通过Registered Window Message这个API函数我们可以解决以上问题。它保证注册的消息为全局唯一。为此只有那些注册过这个消息的窗口、进程或DLL,才有相同的消息号.


There is a range of messages, 0xC000 through 0xEFFF, which is reserved for use by registered window messages. When you call the API function ::RegisterWindowMessage you pass it a string. It looks up the string in an internal table. If it finds the string, it returns the integer which has been assigned.  If it does not find the string, it creates a new entry in the table, assigns it a new integer value from the range 0xC000 through 0xEFFF, and returns that integer value. The table in which these strings are kept is global to all processes on the machine, so if two completely different programs register the same string, they both get the same integer. They may now communicate with each other via these messages.

从0xC000到0xEFFF这个范围的消息为windows保留.当我们调用函数::RegisterWindowMessage,我们需要传递一个字符串. ::RegisterWindowMessage会查找在系统内部维护的一张表格. 如果它找到了这个字符串,它将返回与之对应的整数值.如果它没有找到,它将新增一个条目到表格,并重新赋一个范围从0xC000到0xEFF的全新整数,并返回给应用程序. 这张消息表格里面的字符串是整个电脑系统全局唯一的,所以如果两个完全不同的程序注册了相同的消息字符串,它们将取得同一个消息整数。这样它们就可以通过消息相互通信了.


So a simple form of the user-defined message would be to declare a variable, which I usually just make static in each module that uses it:

static const UINT UWM_RESET_VIEW = 
        ::RegisterWindowMessage(_T("UWM_RESET_VIEW"));
所以最简单的注册自定义消息的方式是声明一个变量,通常我在程序中使用static变量:

static const UINT UWM_RESET_VIEW = 
        ::RegisterWindowMessage(_T("UWM_RESET_VIEW"));


I'll tell you later why this still isn't quite adequate, but take it as a working example for the moment.

The way you handle a registered message is just like you handle a constant user-defined message. The macro is slightly different, but the rest of the handling is the same. Add the line to your MESSAGE_MAP:

ON_REGISTERED_MESSAGE(UWM_RESET_VIEW, OnReset)
As with the constant messages, you will need to define the OnReset handler by adding a declaration to the handler section of your class in the .h file:

afx_msg LRESULT OnReset(WPARAM, LPARAM);
The handlers for a registered window message and for a constant user-defined message are absolutely identical. In fact, in the handler, you can't really tell if the programmer has rewritten the code to use one or the other.


稍后你将知道这种注册自定义消息的方式仍然不是很合适,但现在你可以将它看做一个可以工作的例子. 你处理注册消息的方式和你处理通过宏定义的自定义消息是一样的。宏的写法上稍有不同,但消息处理函数是一样的。你需要添加下面这一行代码到你的MESSAGE_MAP中:

ON_REGISTERED_MESSAGE(UWM_RESET_VIEW, OnReset)
与通过宏定义来注册消息一样,你需要在你的类的头文件声明部分添加一个消息处理函数OnReset.  

afx_msg LRESULT OnReset(WPARAM, LPARAM);

通过 ::RegisterWindowMessage 注册的消息处理函数和通过#define语句注册的消息处理函数完全一样,因此,我们没有必要告诉程序员重新编写消息处理函数。



猜你喜欢

转载自blog.csdn.net/shb8845369/article/details/37649853
今日推荐