如何查找已注册消息的名称?

TL;DR

Windows 中,通过 RegisterWindowMessage() 注册的消息,其消息 ID0xC000 ~ 0xFFFF 之间。可以使用 GetClipboardFormatName() 根据消息 ID 反向查找已注册消息的名称。

缘起

前一阵子,我在学习 c# 中的 async/await 工作机制时遇到了一个有趣的现象 —— 在下列代码片段中,await 后面的代码是在 UI 线程执行的,不论执行多少次,都是在 UI 线程中执行的。

1
2
3
4
5
6
private async void Button2_Click(object sender, EventArgs e)
{
Debug.WriteLine(string.Format("Btn Click Begin(), in thread {0}", Thread.CurrentThread.ManagedThreadId));
await Task.Delay(10000);
Debug.WriteLine(string.Format("Btn Click End(), in thread {0}", Thread.CurrentThread.ManagedThreadId));
}

如果把 Button2_Click 中的三行代码放到控制台程序中,await 后面的代码会在哪个线程执行,是不确定的,很有可能与 await 前面的那行代码不在同一个线程中。

await 后面的代码会在哪个线程中执行,情况非常复杂,不在本文讨论范围。

那么问题来了,为什么 await 后面的代码可以在 UI 线程执行呢?或者说是怎么做到的呢?

查看调用栈

通过下图中的调用栈可以猜测,await 后面的代码能在 UI 线程执行是通过消息机制实现的。

async-await-msg

而且根据上图中调用栈信息可知,这是一个已注册的消息,因为消息 ID0xc396,位于0xC000 ~ 0xFFFF 的范围内。根据 MSDN 文档 可知,这个消息是通过 RegisterWindowMessage() 注册的。

1
The system returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the RegisterWindowMessage function to register a message. The message identifier returned by this function is guaranteed to be unique throughout the system. Use of this function prevents conflicts that can arise if other applications use the same message identifier for different purposes.

那么本文的重点来了,这个注册的消息对应的名字是什么?因为数字对我们来说是冰冷的,无意义的,名字才是有意义的。

注册消息的名字

google 中输入 get registered message name,第一条记录是 stackoverflow 上的一篇文章,里面介绍了可以通过 GetClipboardFormatName() 来根据消息 ID 获取对应的消息名字。这是因为操作系统内部将已注册的消息视为剪贴板格式,并且 GetClipboardFormatName() 函数可以根据特定格式获取名称。

测试代码

以下是使用 C# 编写的获取注册消息名称的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[DllImport("user32.dll")]
static extern int GetClipboardFormatName(int format, StringBuilder lpszFormatName, int cchMaxCount);

public static string GetRegisteredMessageName(int msg)
{
var sb = new StringBuilder(256);
GetClipboardFormatName(msg, sb, sb.Capacity);
return sb.ToString();
}

static void Main(string[] args)
{
try
{
int msgId = Convert.ToInt32(args[0], 16);
Console.WriteLine(string.Format("MsgName: {0}", GetRegisteredMessageName(msgId)));
}
catch (Exception)
{
Console.WriteLine("Usage: GetRegisteredMessageName 0xc001");
}
}

希望本篇文章可以帮助您了解如何查找已注册消息的名称。

总结

await 后面的代码执行在哪个线程中是不确定的,写代码的时候要注意线程相关问题。

可以使用 GetClipboardFormatName() 获取注册的消息名称。

参考资料

https://stackoverflow.com/questions/40417023/get-name-of-message-registered-by-registerwindowmessage

https://linux.m2osw.com/recover-name-message-registered-registerwindowmessage

BianChengNan wechat
扫描左侧二维码关注公众号,扫描右侧二维码加我个人微信:)
0%