xxxSendMessageBSM函数分析
BSM的意思:Broadcast Special Message
第一部分A:
//Broadcast Special Message Recipient list
#define BSM_ALLCOMPONENTS 0x00000000
#define BSM_VXDS 0x00000001
#define BSM_NETDRIVER 0x00000002
#define BSM_INSTALLABLEDRIVERS 0x00000004
#define BSM_APPLICATIONS 0x00000008
#define BSM_ALLDESKTOPS 0x00000010
#define BSM_COMPONENTS 0x0000000F ;internal
#define BSM_VALID 0x0000001F ;internal
//Broadcast Special Message Flags
#define BSF_QUERY 0x00000001
#define BSF_IGNORECURRENTTASK 0x00000002
#define BSF_FLUSHDISK 0x00000004
#define BSF_NOHANG 0x00000008
#define BSF_POSTMESSAGE 0x00000010
#define BSF_FORCEIFHUNG 0x00000020
#define BSF_NOTIMEOUTIFNOTHUNG 0x00000040
;begin_if_(_WIN32_WINNT)_500
#define BSF_ALLOWSFW 0x00000080
#define BSF_SENDNOTIFYMESSAGE 0x00000100
;end_if_(_WIN32_WINNT)_500
;begin_if_(_WIN32_WINNT)_501
#define BSF_RETURNHDESK 0x00000200
#define BSF_LUID 0x00000400
;end_if_(_WIN32_WINNT)_501
#define BSF_QUEUENOTIFYMESSAGE 0x20000000 ;internal
#define BSF_SYSTEMSHUTDOWN 0x40000000 ;internal
#define BSF_MSGSRV32OK 0x80000000 ;internal
#define BSF_VALID 0x000007FF ;internal
;begin_internal
#define BSF_ASYNC (BSF_POSTMESSAGE | BSF_SENDNOTIFYMESSAGE)
;end_internal
第一部分B:
typedef struct {
DWORD dwRecipients;
DWORD dwFlags;
BSMINFO;
} BROADCASTSYSTEMMSGPARAMS, *LPBROADCASTSYSTEMMSGPARAMS;
第一部分C:
typedef struct tagWND * KPTR_MODIFIER PWND;
typedef struct tagWND {
THRDESKHEAD head;
WW; // WOW-USER common fields. Defined in wowuserp.h
// The presence of "state" at the start of this structure is
// assumed by the STATEOFFSET macro.
PWND spwndNext; // Handle to the next window
PWND spwndPrev; // Handle to the previous window
PWND spwndParent; // Backpointer to the parent window.
PWND spwndChild; // Handle to child
PWND spwndOwner; // Popup window owner field
RECT rcWindow; // Window outer rectangle
RECT rcClient; // Client rectangle
WNDPROC_PWND lpfnWndProc; // Can be WOW address or standard address
PCLS pcls; // Pointer to window class
KHRGN hrgnUpdate; // Accumulated paint region
PPROPLIST ppropList; // Pointer to property list
PSBINFO pSBInfo; // Words used for scrolling
PMENU spmenuSys; // Handle to system menu
PMENU spmenu; // Menu handle or ID
KHRGN hrgnClip; // Clipping region for this window
LARGE_UNICODE_STRING strName;
int cbwndExtra; // Extra bytes in window
PWND spwndLastActive; // Last active in owner/ownee list
KHIMC hImc; // Associated input context handle
KERNEL_ULONG_PTR dwUserData; // Reserved for random application data
struct _ACTIVATION_CONTEXT * KPTR_MODIFIER pActCtx;
} WND;
第二部分:
BOOL xxxSendBSMtoDesktop(
PWND pwndDesk,
UINT message,
WPARAM wParam,
LPARAM lParam,
LPBROADCASTSYSTEMMSGPARAMS pbsmParams)
{
PBWL pbwl;
HWND *phwnd;
PWND pwnd;
TL tlpwnd;
BOOL fReturnValue = TRUE;
BOOL fFilterDriveMsg = FALSE;
PTHREADINFO ptiCurrent = PtiCurrent();
BOOL fPrivateMessage = (message >= WM_USER) && (message < MAXINTATOM);
DEV_BROADCAST_VOLUME dbv;
if (fPrivateMessage) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Attempt to broadcast a private message");
}
pbwl = BuildHwndList(pwndDesk->spwndChild, BWL_ENUMLIST, NULL);
if (pbwl == NULL)
return 0;
if (!(pbsmParams->dwFlags & BSF_POSTMESSAGE)) {
/*呼叫者想让接收者占据前台吗
在处理通知时?
* Does the caller want to allow the receivers to take the foreground
* while processing the notification?
*/
/*为了允许AppsHelp窗口进入
前台我们将ptiLastWoken设置为NULL,这将允许任何窗口
插入CD后,它会出现在前台。
* Bug 412159. In order to allow the AppsHelp window to come to the
* foreground we set ptiLastWoken to NULL, which will allow any window
* to come to the foreground after a CD's been inserted.
*/
if ((pbsmParams->dwFlags & BSF_ALLOWSFW) &&
(GETPDESK(pwndDesk) == grpdeskRitInput) &&
((ptiCurrent->TIF_flags & TIF_CSRSSTHREAD)
|| CanForceForeground(ptiCurrent->ppi FG_HOOKLOCK_PARAM(ptiCurrent)))) {
glinp.ptiLastWoken = NULL;
}
}
/*
* Determine if we need to filter the Drive Letter mask in fnINDEVICECHANGE
* WM_DEVICECHANGE message are sent synchronously
* LUID DosDevices maps must be enabled
*/
if ((gLUIDDeviceMapsEnabled == TRUE) &&
(message == WM_DEVICECHANGE) &&
((wParam == DBT_DEVICEREMOVECOMPLETE) || (wParam == DBT_DEVICEARRIVAL)) &&
(((struct _DEV_BROADCAST_HEADER *)lParam)->dbcd_devicetype == DBT_DEVTYP_VOLUME)
) {
LUID luidClient;
NTSTATUS Status;
if( ((DEV_BROADCAST_VOLUME *)lParam)->dbcv_unitmask & DBV_FILTER_MSG ) {
return 0;
}
else {
dbv = *((DEV_BROADCAST_VOLUME *)lParam);
dbv.dbcv_unitmask |= DBV_FILTER_MSG;
}
/*
* Caller must be LocalSystem and BSF_LUID is not specified
*/
if (!(pbsmParams->dwFlags & BSF_LUID)) {
Status = GetProcessLuid(NULL, &luidClient);
if (NT_SUCCESS(Status) &&
RtlEqualLuid(&luidClient, &luidSystem)) {
fFilterDriveMsg = TRUE;
}
}
}
for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
BOOL UseFilterLparam = FALSE;
/*确保这个hwnd还在
* Make sure this hwnd is still around.
*/
if ((pwnd = RevalidateHwnd(*phwnd)) == NULL)
continue;
if (pbsmParams->dwFlags & BSF_IGNORECURRENTTASK) {
// Don't deal with windows in the current task.不要在当前任务中处理窗口。
if (GETPTI(pwnd)->pq == ptiCurrent->pq)
continue;
}
if (pbsmParams->dwFlags & BSF_LUID) {
LUID luidWnd;
luidWnd.LowPart = luidWnd.HighPart = 0;
/*
* Now we have the window Luid LuidWindow
* Check to see if it is equal to the callers Luid or not
*/检查它是否等于呼叫者Luid
if (!NT_SUCCESS(GetWindowLuid(pwnd, &luidWnd)) ||
!RtlEqualLuid(&pbsmParams->luid, &luidWnd)) {
continue;
}
}
if (fFilterDriveMsg == TRUE) {
LUID luidWnd;
if (!NT_SUCCESS(GetWindowLuid(pwnd, &luidWnd))) {
continue;
}
/*由于LocalSystem使用Global DosDevices
* Since LocalSystem uses the Global DosDevices,
* don't filter for windows owned by LocalSystem
*/不筛选LocalSystem拥有的窗口
if(!RtlEqualLuid(&luidSystem, &luidWnd)) {
UseFilterLparam = TRUE;
}
}
/*确保此窗口可以处理广播消息
* Make sure this window can handle broadcast messages
*/
if (!fBroadcastProc(pwnd)) {
continue;
}
if (fPrivateMessage && TestWF(pwnd, WFWIN40COMPAT)) { // Don't broadcast
continue; // private message
} // to 4.0 apps.
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
//BSF_POSTMESSAGE,寄送消息。BroadcastSystemMessage
// Now, send message; This could be a query; so, remember the return value.
if (pbsmParams->dwFlags & BSF_POSTMESSAGE) {
_PostMessage(pwnd, message, wParam, lParam);
} else if (pbsmParams->dwFlags & BSF_SENDNOTIFYMESSAGE) {
/*
* We don't want to wait for an answer, but we don't want to use
* PostMessage either. This is useful if you need to maintain the
* order in which messages are delivered, but you only want to
* wait for some of them. See WM_POWERBROADCAST for an example.
*/
xxxSendNotifyMessage(pwnd, message, wParam, lParam);
} else if (pbsmParams->dwFlags & BSF_QUEUENOTIFYMESSAGE) {
/*
* We don't want to wait for an answer, but we don't want to use
* PostMessage either. This is useful if you need to maintain the
* order in which messages are delivered, but you only want to
* wait for some of them. See WM_POWERBROADCAST for an example.
*/
QueueNotifyMessage(pwnd, message, wParam, lParam);
} else {
/*
* pbsmParams->dwFlags can be changed while we loop here
* so we need to check it in every iteration.
*/
BOOL fNoHang = (BOOL)pbsmParams->dwFlags & BSF_NOHANG;
BOOL fForce = (BOOL)pbsmParams->dwFlags & BSF_FORCEIFHUNG;
DWORD dwTimeout;
ULONG_PTR dwResult = 0;
if (fNoHang)
dwTimeout = CMSWAITTOKILLTIMEOUT;
else
dwTimeout = 0;
if (xxxSendMessageTimeout(pwnd, message, wParam,
(UseFilterLparam ? (LPARAM)&dbv : lParam),
(fNoHang ? SMTO_ABORTIFHUNG : SMTO_NORMAL) |
((pbsmParams->dwFlags & BSF_NOTIMEOUTIFNOTHUNG) ? SMTO_NOTIMEOUTIFNOTHUNG : 0),
dwTimeout, &dwResult)) {
if (pbsmParams->dwFlags & BSF_QUERY) {
// For old messages, returning 0 means a deny
if(message == WM_QUERYENDSESSION)
fReturnValue = (dwResult != 0);
else
// For all new messages, returning BROADCAST_QUERY_DENY is
// the way to deny a query.
fReturnValue = (dwResult != BROADCAST_QUERY_DENY);
}
} else {
fReturnValue = fForce;
}
/*如果我们的查询被拒绝,请立即返回。
* If our query was denied, return immediately.
*/
if (fReturnValue == 0) {
// Store who denied the query.
pbsmParams->hwnd = HWq(pwnd);
if (pbsmParams->dwFlags & BSF_RETURNHDESK) {
NTSTATUS Status;
HDESK hdesk = NULL;
if (pwnd->head.rpdesk) {
Status = ObOpenObjectByPointer(pwnd->head.rpdesk,
0,
NULL,
EVENT_ALL_ACCESS,
NULL,
UserMode,
&hdesk);
if (!NT_SUCCESS(Status)) {
RIPMSG2(RIP_WARNING, "Could not get a handle for pdesk %#p Status %x",
pwnd->head.rpdesk, Status);
}
}
pbsmParams->hdesk = hdesk;
}
if (message == WM_POWERBROADCAST && wParam == PBT_APMQUERYSUSPEND) {
WCHAR wchTask[40];
ULONG cbTask;
/*
* Get the application name and log an error.
*/
cbTask = GetTaskName(GETPTI(pwnd), wchTask, sizeof(wchTask));
UserLogError(wchTask, cbTask, WARNING_POWER_QUERYSUSPEND_CANCELLED);
}
ThreadUnlock(&tlpwnd);
break;
}
}
ThreadUnlock(&tlpwnd);
}
FreeHwndList(pbwl);
return fReturnValue;
}
第三部分:
LONG xxxSendMessageBSM(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
LPBROADCASTSYSTEMMSGPARAMS pbsmParams)
{
PTHREADINFO ptiCurrent = PtiCurrent();
LONG lRet;
if (pbsmParams->dwRecipients & BSM_ALLDESKTOPS) {
PWINDOWSTATION pwinsta;
PDESKTOP pdesk;
TL tlpwinsta;
TL tlpdesk;
/*
* Walk through all windowstations and desktop looking for
* top-level windows.
*/
ThreadLockWinSta(ptiCurrent, NULL, &tlpwinsta);
ThreadLockDesktop(ptiCurrent, NULL, &tlpdesk, LDLT_FN_SENDMESSAGEBSM);
for (pwinsta = grpWinStaList; pwinsta != NULL; ) {
ThreadLockExchangeWinSta(ptiCurrent, pwinsta, &tlpwinsta);
for (pdesk = pwinsta->rpdeskList; pdesk != NULL; ) {
ThreadLockExchangeDesktop(ptiCurrent, pdesk, &tlpdesk, LDLT_FN_SENDMESSAGEBSM);
lRet = xxxSendBSMtoDesktop(pdesk->pDeskInfo->spwnd,
message, wParam, lParam, pbsmParams);
/*
* If our query was denied, return immediately.
*/
if ((lRet == 0) && (pbsmParams->dwFlags & BSF_QUERY)) {
ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_SENDMESSAGEBSM1);
ThreadUnlockWinSta(ptiCurrent, &tlpwinsta);
return 0;
}
pdesk = pdesk->rpdeskNext;
}
pwinsta = pwinsta->rpwinstaNext;
}
ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_SENDMESSAGEBSM2);
ThreadUnlockWinSta(ptiCurrent, &tlpwinsta);
} else {
lRet = xxxSendBSMtoDesktop(pwnd, message, wParam, lParam,
pbsmParams);
}
return lRet;
}
第四部分:
/core/ntuser/kernel/input.c:1244:BOOL _PostMessage(
/***************************************************************************\
* _PostMessage (API)
*
* Writes a message to the message queue for pwnd. If pwnd == -1, the message
* is broadcasted to all windows.
*
* History:
* 11-06-90 DavidPe Created.
\***************************************************************************/
BOOL _PostMessage(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PQMSG pqmsg;
BOOL fPwndUnlock;
BOOL fRet;
DWORD dwPostCode;
TL tlpwnd;
PTHREADINFO pti;
/*
* First check to see if this message takes DWORDs only. If it does not,
* fail the post. Cannot allow an app to post a message with pointers or
* handles in it - this can cause the server to fault and cause other
* problems - such as causing apps in separate address spaces to fault.
* (or even an app in the same address space to fault!)
*
* Block certain messages cross LUIDs to avoid security threats.
*/
if (TESTSYNCONLYMESSAGE(message, wParam) ||
BLOCKMESSAGECROSSLUID(message,
PpiCurrent(),
GETPTI(pwnd)->ppi)) {
RIPERR1(ERROR_MESSAGE_SYNC_ONLY,
RIP_WARNING,
"Invalid parameter \"message\" (%ld) to _PostMessage",
message);
return FALSE;
}
/*
* Is this a BroadcastMsg()?
*/
if (pwnd == PWND_BROADCAST) {
xxxBroadcastMessage(NULL, message, wParam, lParam, BMSG_POSTMSG, NULL);
return TRUE;
}
pti = PtiCurrent();
/*
* Is this posting to the current thread info?
*/
if (pwnd == NULL) {
return _PostThreadMessage(pti, message, wParam, lParam);
}
fPwndUnlock = FALSE;
if (message >= WM_DDE_FIRST && message <= WM_DDE_LAST) {
ThreadLockAlwaysWithPti(pti, pwnd, &tlpwnd);
dwPostCode = xxxDDETrackPostHook(&message, pwnd, wParam, &lParam, FALSE);
if (dwPostCode != DO_POST) {
ThreadUnlock(&tlpwnd);
return (BOOL)dwPostCode;
}
fPwndUnlock = TRUE;
}
pti = GETPTI(pwnd);
/*
* Check to see if this message is in the multimedia coalescing range.
* If so, see if it can be coalesced with the previous message.
*/
AdjustForCoalescing(&pti->mlPost, HWq(pwnd), message);
#ifdef GENERIC_INPUT
#if LOCK_HIDDATA
/*
* If someone is posting this message, we need to bump up the reference
* count of the HID data so it doesn't get freed too early.
*/
if (message == WM_INPUT) {
// lParam is an HRAWINPUT
PHIDDATA pHidData = HMValidateHandle((HANDLE)lParam, TYPE_HIDDATA);
TAGMSG1(DBGTAG_PNP, "_PostMessage: Got WM_INPUT pHidData=%p", pHidData);
if (pHidData != NULL) {
HMLockObject(pHidData);
} else {
RIPMSG1(RIP_WARNING, "_PostMessage: invalid handle %p for WM_INPUT", lParam);
return FALSE;
}
} else
#endif
#endif // GENERIC_INPUT
/*
* Allocate a key state update event if needed.
*/
if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
PostUpdateKeyStateEvent(pti->pq);
}
/*将此消息添加到“发送”队列中。重要。
* Put this message on the 'post' list.
*/
fRet = FALSE;
if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
/*设置QS_POSTMESSAGE位,以便线程知道它有消息。
* Set the QS_POSTMESSAGE bit so the thread knows it has a message.
*/
StoreQMessage(pqmsg, pwnd, message, wParam, lParam, 0, 0, 0);
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
/*
* If it's a hotkey, set the QS_HOTKEY bit since we have a separate
* bit for those messages.
*/
if (message == WM_HOTKEY)
SetWakeBit(pti, QS_HOTKEY);
fRet = TRUE;
}
/*
* Are we posting to the thread currently reading from the input queue?
* If so, update idSysLock with this pqmsg so that the input queue will
* not be unlocked until this message is read.
*/
if (pti == pti->pq->ptiSysLock)
pti->pq->idSysLock = (ULONG_PTR)pqmsg;
if (fPwndUnlock)
ThreadUnlock(&tlpwnd);
return fRet;
}
第五部分:
VOID StoreQMessage(
PQMSG pqmsg,
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
DWORD time,
DWORD dwQEvent,
ULONG_PTR dwExtraInfo)
{
CheckCritIn();
pqmsg->msg.hwnd = HW(pwnd);
pqmsg->msg.message = message;
pqmsg->msg.wParam = wParam;
pqmsg->msg.lParam = lParam;
pqmsg->msg.time = (time == 0) ? NtGetTickCount() : time;
#ifdef REDIRECTION
if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) {
pqmsg->msg.pt.x = LOWORD(lParam);
pqmsg->msg.pt.y = HIWORD(lParam);
} else {
pqmsg->msg.pt = gpsi->ptCursor;
}
#else
pqmsg->msg.pt = gpsi->ptCursor;
#endif
pqmsg->dwQEvent = dwQEvent;
pqmsg->ExtraInfo = dwExtraInfo;
}