参考: wine 8.14
在发送消息到目的地时,有一种方式叫做广播。当用户需要发送广播消息时,会设置窗口handle 为HWND_BROADCAST 或者HWND_TOMPOST。官方参考https://learn.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues、https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagew。 wine中函数is_broadcast就是用于判断是否要发送的消息为broadcast 消息。
当确认发送的消息为broadcast消息时,那么如何具体对消息进行分发呢,这就是接下来分析的broadcast_message.
static BOOL broadcast_message(struct send_message_info *info, DWORD_PTR *res_ptr)
{
HWND * list;
if(is_message_broadcastable(info->msg) && (list = list_window_children(0, get_desktop_window(), NULL , 0)))
{
int i;
for(i = 0; list[i]; i++)
{
if(!is_window(list[i])) continue;
if((get_window_long(list[i], GW_STYLE) & (WS_POPUP | WS_CHILD)) == WS_CHILD)
continue;
switch(info->type)
{
case MSG_UNICODE:
case MSG_OTHER_PROCESS:
send_message_timeout(list[i], info->msg, info->wparam, info->lparam, info->flags, info->timeout, FALSE);
break;
case MSG_ASCII:
send_message_timeout(list[i], info->msg, info->wparam, info->lparam, info->flags, info->timeout, TRUE);
break;
case MSG_NOTIFY:
NtUserMessageCall(list[i], info->msg, info->wparam, info->lparam, 0, NtUserSendNotifyMessage, FALSE);
break;
case MSG_CALLBACK:
{
struct send_message_callback_params params =
{
.callback = info->callback, .data = info->data};
}
NtUserMessageCall(list[i], info->msg, info->wparam, info->lparam, ¶ms, NtUserSendMessageCallback, FALSE);
break;
}
case MSG_POSTED:
NtUserPostMessage(list[i], info->msg, info->wparam, info->lparam);
break;
default:
break;
}
}
free(list);
}
if(res_ptr) *res_ptr = 1;
return TRUE:
}
要广播消息是,首先需要确定以下几个问题
- 哪些消息可以被广播,或者说广播消息中都有哪些消息。
- 广播的消息发送给哪些窗口,是否是所有窗口都能接受广播消息。
对于第一个问题,具体哪些消息可以被广播,wine中使用的函数是is_message_broadcastable,即 message id < WM_USER 或者 message id >0xc000. 此范围内的消息不用于private window classes.详情参考:https://learn.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues#application-defined-messages。
对于第二个问题,list_window_children获取桌面窗口的所有子窗口。并调用get_window_long,获取窗口style,并将子窗口排除在能接受广播消息的窗口之外。
接下来就是根据消息类型调用不同函数将消息发送到各个窗口。