You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3393 lines
113 KiB
3393 lines
113 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: sendmsg.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Contains SendMessage, xxxSendNotifyMessage, ReplyMessage, InSendMessage,
|
|
* RegisterWindowMessage and a few closely related functions.
|
|
*
|
|
* History:
|
|
* 10-19-90 darrinm Created.
|
|
* 02-04-91 IanJa Window handle revalidation added
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <dbt.h>
|
|
|
|
#pragma hdrstop
|
|
|
|
#define IsASwitchWnd( pw ) \
|
|
(gpsi->atomSysClass[ICLS_SWITCH] == pw->pcls->atomClassName)
|
|
|
|
#define IsOleMainThreadWnd( pw ) \
|
|
(gaOleMainThreadWndClass == pw->pcls->atomClassName)
|
|
|
|
VOID UnlinkSendListSms(PSMS, PSMS *);
|
|
VOID ReceiverDied(PSMS, PSMS *);
|
|
VOID SenderDied(PSMS, PSMS *);
|
|
NTSTATUS InitSMSLookaside(VOID);
|
|
|
|
#pragma alloc_text(INIT, InitSMSLookaside)
|
|
|
|
/*
|
|
* Globals local to this file only
|
|
*/
|
|
PPAGED_LOOKASIDE_LIST SMSLookaside;
|
|
|
|
/***************************************************************************\
|
|
* BroadcastProc
|
|
*
|
|
* Some windows need to be insulated from Broadcast messages.
|
|
* These include icon title windows, the switch window, all
|
|
* menu windows, etc. Before stuffing the message in the task's
|
|
* queue, check to see if it is one we want to trash.
|
|
*
|
|
* Notes: this procedure does not do exactly the same thing it does in
|
|
* windows 3.1. There it actually posts/Sends the message. For NT, it
|
|
* just returns TRUE if we SHOULD post the message, or FALSE other wise
|
|
*
|
|
* History:
|
|
* 25-Jun-1992 JonPa Ported from Windows 3.1 sources
|
|
\***************************************************************************/
|
|
#define fBroadcastProc(pwnd) \
|
|
(!(ISAMENU(pwnd) || IsASwitchWnd(pwnd) || IsOleMainThreadWnd(pwnd)))
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* StubAllocSMS / StubFreeSMS
|
|
*
|
|
* These are stub routines for SMS allocations. We need these to call
|
|
* our debug UserAlloc routines
|
|
*
|
|
* Dec-16-97 clupu Created.
|
|
\***************************************************************************/
|
|
PVOID StubAllocSMS(
|
|
POOL_TYPE PoolType,
|
|
SIZE_T uBytes,
|
|
ULONG iTag)
|
|
{
|
|
return UserAllocPool(uBytes, iTag);
|
|
|
|
UNREFERENCED_PARAMETER(PoolType);
|
|
}
|
|
|
|
VOID StubFreeSMS(
|
|
PVOID p)
|
|
{
|
|
UserFreePool(p);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitSMSLookaside
|
|
*
|
|
* Initializes the SMS entry lookaside list. This improves SMS entry locality
|
|
* by keeping SMS entries in a single page
|
|
*
|
|
* 09-09-93 Markl Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
InitSMSLookaside()
|
|
{
|
|
SMSLookaside = Win32AllocPoolNonPagedNS(sizeof(PAGED_LOOKASIDE_LIST),
|
|
TAG_LOOKASIDE);
|
|
if (SMSLookaside == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
ExInitializePagedLookasideList(SMSLookaside,
|
|
StubAllocSMS,
|
|
StubFreeSMS,
|
|
POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
|
|
sizeof(SMS),
|
|
TAG_SMS,
|
|
8);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* AllocSMS
|
|
*
|
|
* Allocates a message on a message list. DelSMS deletes a message
|
|
* on a message list.
|
|
*
|
|
* 10-22-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
PSMS AllocSMS(
|
|
VOID)
|
|
{
|
|
return ExAllocateFromPagedLookasideList(SMSLookaside);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FreeSMS
|
|
*
|
|
* Returns a qmsg to the lookaside buffer or free the memory.
|
|
*
|
|
* 10-26-93 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
void FreeSMS(
|
|
PSMS psms)
|
|
{
|
|
ExFreeToPagedLookasideList(SMSLookaside, psms);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _ReplyMessage (API)
|
|
*
|
|
* This function replies to a message sent from one thread to another, using
|
|
* the provided lRet value.
|
|
*
|
|
* The return value is TRUE if the calling thread is processing a SendMessage()
|
|
* and FALSE otherwise.
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
* 01-24-91 DavidPe Rewrote for Windows.
|
|
\***************************************************************************/
|
|
|
|
BOOL _ReplyMessage(
|
|
LRESULT lRet)
|
|
{
|
|
PTHREADINFO ptiCurrent;
|
|
PSMS psms;
|
|
|
|
CheckCritIn();
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Are we processing a SendMessage?
|
|
*/
|
|
psms = ptiCurrent->psmsCurrent;
|
|
if (psms == NULL)
|
|
return FALSE;
|
|
|
|
/*
|
|
* See if the reply has been made already.
|
|
*/
|
|
if (psms->flags & SMF_REPLY)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Blow off the rest of the call if the SMS came
|
|
* from xxxSendNotifyMessage(). Obviously there's
|
|
* no one around to reply to in the case.
|
|
*/
|
|
if (psms->ptiSender != NULL) {
|
|
|
|
/*
|
|
* Reply to this message. The sender should not free the SMS
|
|
* because the receiver still considers it valid. Thus we
|
|
* mark it with a special bit indicating it has been replied
|
|
* to. We wait until both the sender and receiver are done
|
|
* with the sms before we free it.
|
|
*/
|
|
psms->lRet = lRet;
|
|
psms->flags |= SMF_REPLY;
|
|
|
|
/*
|
|
* Wake up the sender.
|
|
* ??? why don't we test that psms == ptiSender->psmsSent?
|
|
*/
|
|
SetWakeBit(psms->ptiSender, QS_SMSREPLY);
|
|
} else if (psms->flags & SMF_CB_REQUEST) {
|
|
|
|
/*
|
|
* From SendMessageCallback REQUEST callback. Send the message
|
|
* back with a the REPLY value.
|
|
*/
|
|
TL tlpwnd;
|
|
INTRSENDMSGEX ism;
|
|
|
|
psms->flags |= SMF_REPLY;
|
|
|
|
if (!(psms->flags & SMF_SENDERDIED)) {
|
|
ism.fuCall = ISM_CALLBACK | ISM_REPLY;
|
|
if (psms->flags & SMF_CB_CLIENT)
|
|
ism.fuCall |= ISM_CB_CLIENT;
|
|
ism.lpResultCallBack = psms->lpResultCallBack;
|
|
ism.dwData = psms->dwData;
|
|
ism.lRet = lRet;
|
|
|
|
ThreadLockWithPti(ptiCurrent, psms->spwnd, &tlpwnd);
|
|
|
|
xxxInterSendMsgEx(psms->spwnd, psms->message, 0L, 0L,
|
|
NULL, psms->ptiCallBackSender, &ism );
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We have 4 conditions to satisfy:
|
|
*
|
|
* 16 - 16 : receiver yields if sender is waiting for this reply
|
|
* 32 - 16 : receiver yields if sender is waiting for this reply
|
|
* 16 - 32 : no yield required
|
|
* 32 - 32 : No yielding required.
|
|
*/
|
|
if (psms->ptiSender &&
|
|
(psms->ptiSender->TIF_flags & TIF_16BIT || ptiCurrent->TIF_flags & TIF_16BIT)) {
|
|
|
|
DirectedScheduleTask(ptiCurrent, psms->ptiSender, FALSE, psms);
|
|
if (ptiCurrent->TIF_flags & TIF_16BIT && psms->ptiSender->psmsSent == psms) {
|
|
xxxSleepTask(TRUE, NULL);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
UserLogError(
|
|
PCWSTR pwszError,
|
|
ULONG cbError,
|
|
NTSTATUS ErrorCode)
|
|
{
|
|
PIO_ERROR_LOG_PACKET perrLogEntry;
|
|
|
|
/*
|
|
* Allocate an error packet, fill it out, and write it to the log.
|
|
*/
|
|
perrLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(gpWin32kDriverObject,
|
|
(UCHAR)(cbError + sizeof(IO_ERROR_LOG_PACKET)));
|
|
if (perrLogEntry) {
|
|
perrLogEntry->ErrorCode = ErrorCode;
|
|
if (cbError) {
|
|
perrLogEntry->NumberOfStrings = 1;
|
|
perrLogEntry->StringOffset = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
|
|
RtlCopyMemory(perrLogEntry->DumpData, pwszError, cbError);
|
|
}
|
|
IoWriteErrorLogEntry(perrLogEntry);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
GetWindowLuid(
|
|
PWND pwnd,
|
|
PLUID pluidWnd
|
|
)
|
|
{
|
|
PACCESS_TOKEN pUserToken = NULL;
|
|
BOOLEAN fCopyOnOpen;
|
|
BOOLEAN fEffectiveOnly;
|
|
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
|
NTSTATUS Status;
|
|
PTHREADINFO pti = GETPTI(pwnd);
|
|
|
|
//
|
|
// Get the window's thread token
|
|
//
|
|
pUserToken = PsReferenceImpersonationToken(pti->pEThread,
|
|
&fCopyOnOpen, &fEffectiveOnly, &ImpersonationLevel);
|
|
|
|
if (pUserToken == NULL) {
|
|
|
|
//
|
|
// No thread token, go to the process
|
|
//
|
|
|
|
pUserToken = PsReferencePrimaryToken(pti->ppi->Process);
|
|
if (pUserToken == NULL)
|
|
return STATUS_NO_TOKEN;
|
|
}
|
|
|
|
Status = SeQueryAuthenticationIdToken(pUserToken, pluidWnd);
|
|
|
|
//
|
|
// We're finished with the token
|
|
//
|
|
|
|
ObDereferenceObject(pUserToken);
|
|
|
|
return Status;
|
|
}
|
|
|
|
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?
|
|
*/
|
|
/*
|
|
* 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;
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
if (!NT_SUCCESS(GetWindowLuid(pwnd, &luidWnd)) ||
|
|
!RtlEqualLuid(&pbsmParams->luid, &luidWnd)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (fFilterDriveMsg == TRUE) {
|
|
LUID luidWnd;
|
|
|
|
if (!NT_SUCCESS(GetWindowLuid(pwnd, &luidWnd))) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Since LocalSystem uses the Global DosDevices,
|
|
* don't filter for windows owned by 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);
|
|
|
|
// 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;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSendMessageFF
|
|
*
|
|
* We can't check for -1 in the thunks because that would allow all message
|
|
* thunk apis to take -1 erroneously. Since all message apis need to go through
|
|
* the message thunks, the message thunks can only do least-common-denominator
|
|
* hwnd validation (can't allow -1). So I made a special thunk that gets called
|
|
* when SendMessage(-1) gets called. This means the client side will do the
|
|
* special stuff to make sure the pwnd passed goes through thunk validation
|
|
* ok. I do it this way rather than doing validation in all message apis and
|
|
* not in the thunks (if I did it this way the code would be larger and
|
|
* inefficient in the common cases).
|
|
*
|
|
* 03-20-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
LRESULT xxxSendMessageFF(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
ULONG_PTR xParam)
|
|
{
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
|
|
/*
|
|
* Call xxxSendMessage() to do broadcasting rather than calling
|
|
* broadcast from here in case any internal code that calls
|
|
* sendmessage passes a -1 (that way the internal code doesn't
|
|
* need to know about this weird routine).
|
|
*/
|
|
if (xParam != 0L) {
|
|
/*
|
|
* SendMessageTimeout call
|
|
*/
|
|
return xxxSendMessageEx(PWND_BROADCAST, message, wParam, lParam, xParam);
|
|
} else {
|
|
/*
|
|
* Normal SendMessage call
|
|
*/
|
|
return xxxSendMessageTimeout(PWND_BROADCAST, message, wParam,
|
|
lParam, SMTO_NORMAL, 0, NULL );
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSendMessageEx
|
|
*
|
|
* The SendMessageTimeOut sends a pointer to struct that holds the extra
|
|
* params needed for the timeout call. Instead of chaning a bunch of things,
|
|
* we use the xParam to hold a ptr to a struct. So we change the client/srv
|
|
* entry point to hear so we can check for the extra param and extract the
|
|
* stuff we need if it's there.
|
|
*
|
|
*
|
|
* WARNING!!!! RETURN VALUE SWAPPED
|
|
*
|
|
* Only call this function from the thunks!
|
|
*
|
|
* our thunks are written for SendMessage where it returns the value of
|
|
* the message. This routine is used to dispatch SendMessageTimeout calls.
|
|
* SendMessageTimeout returns only TRUE or FALSE and returns the retval of
|
|
* the function in lpdwResult. So here the meanings are swapped and fixed
|
|
* up again in Client side SendMessageTimeout
|
|
*
|
|
*
|
|
* 08-10-92 ChrisBl Created.
|
|
\***************************************************************************/
|
|
|
|
LRESULT xxxSendMessageEx(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
ULONG_PTR xParam)
|
|
{
|
|
/*
|
|
* extract values from the xParam if from TimeOut call
|
|
* This should be the only way this function is ever
|
|
* called, but check it just in case...
|
|
*/
|
|
if (xParam != 0L) {
|
|
LRESULT lRet;
|
|
LRESULT lResult;
|
|
NTSTATUS Status;
|
|
SNDMSGTIMEOUT smto;
|
|
PETHREAD Thread = PsGetCurrentThread();
|
|
|
|
if (Thread == NULL)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Probe all read arguments
|
|
*/
|
|
try {
|
|
ProbeForWrite((PVOID)xParam, sizeof(smto), sizeof(ULONG));
|
|
smto = *(SNDMSGTIMEOUT *)xParam;
|
|
Status = STATUS_SUCCESS;
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
lRet = xxxSendMessageTimeout(pwnd, message, wParam, lParam,
|
|
smto.fuFlags, smto.uTimeout, &lResult);
|
|
|
|
/*
|
|
* put the result back into the client
|
|
*/
|
|
smto.lSMTOResult = lResult;
|
|
smto.lSMTOReturn = lRet;
|
|
|
|
try {
|
|
*(SNDMSGTIMEOUT *)xParam = smto;
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
lResult = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Return the lResult so our thunks are happy.
|
|
*/
|
|
return lResult;
|
|
}
|
|
|
|
return xxxSendMessageTimeout(pwnd, message, wParam,
|
|
lParam, SMTO_NORMAL, 0, NULL);
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* xxxSendMessage (API)
|
|
*
|
|
* This function synchronously sends a message to a window. The four
|
|
* parameters hwnd, message, wParam, and lParam are passed to the window
|
|
* procedure of the receiving window. If the window receiving the message
|
|
* belongs to the same queue as the current thread, the window proc is called
|
|
* directly. Otherwise, we set up an sms structure, wake the appropriate
|
|
* thread to receive the message and wait for a reply.
|
|
*
|
|
* Returns:
|
|
* the value returned by the window procedure, or NULL if there is an error
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
\***********************************************************************/
|
|
LRESULT xxxSendMessage(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return xxxSendMessageTimeout(pwnd, message, wParam, lParam,
|
|
SMTO_NORMAL, 0, NULL);
|
|
}
|
|
|
|
/***********************************************************************\
|
|
* xxxSendMessageToClient
|
|
*
|
|
* History:
|
|
* 04-22-98 GerardoB Extracted from xxxSendMessageTimeout, xxxSendMesageCallback
|
|
* and xxxReceiveMessage
|
|
* 05-12-00 JStall Changed from macro to inline function.
|
|
\***********************************************************************/
|
|
__inline void
|
|
xxxSendMessageToClient(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
PSMS psms,
|
|
BOOL fLock,
|
|
LRESULT * plRet)
|
|
{
|
|
DWORD dwSCMSFlags;
|
|
WORD fnid;
|
|
|
|
/*
|
|
* If the window has a client side worker proc and has
|
|
* not been subclassed, dispatch the message directly
|
|
* to the worker proc. Otherwise, dispatch it normally.
|
|
*/
|
|
dwSCMSFlags = TestWF((pwnd), WFANSIPROC) ? SCMS_FLAGS_ANSI : 0;
|
|
|
|
if (gihmodUserApiHook >= 0) {
|
|
/*
|
|
* UserApiHooks are installed, so we can't optimize the sending because
|
|
* the OverrideWndProc's needs to get the message.
|
|
*/
|
|
goto StandardSend;
|
|
}
|
|
|
|
fnid = GETFNID((pwnd));
|
|
if ((fnid >= FNID_CONTROLSTART && fnid <= FNID_CONTROLEND) &&
|
|
((ULONG_PTR)(pwnd)->lpfnWndProc == FNID_TO_CLIENT_PFNW(fnid) ||
|
|
(ULONG_PTR)(pwnd)->lpfnWndProc == FNID_TO_CLIENT_PFNA(fnid))) {
|
|
PWNDMSG pwm = &gSharedInfo.awmControl[fnid - FNID_START] ;
|
|
/*
|
|
* If this message is not processed by the control, call
|
|
* xxxDefWindowProc
|
|
*/
|
|
if (pwm->abMsgs && (((message) > pwm->maxMsgs) ||
|
|
!((pwm->abMsgs)[(message) / 8] & (1 << ((message) & 7))))) {
|
|
/*
|
|
* If this is a dialog window, we need to call the client because
|
|
* the app might want this message (eventhough DefDlgProc doesn't
|
|
* want it).
|
|
* If the dialog hasn't been marked as such, the app's DlgProc is
|
|
* not yet available so it's OK to ignore the message.
|
|
*/
|
|
if (TestWF((pwnd), WFDIALOGWINDOW)) {
|
|
*plRet = ScSendMessageSMS((pwnd), (message), (wParam), (lParam),
|
|
dwSCMSFlags, (PROC)(FNID_TO_CLIENT_PFNWORKER(fnid)),
|
|
dwSCMSFlags, (psms));
|
|
} else {
|
|
TL tlpwnd;
|
|
if (fLock) {
|
|
ThreadLock((pwnd), &tlpwnd);
|
|
}
|
|
*plRet = xxxDefWindowProc((pwnd), (message), (wParam), (lParam));
|
|
if (fLock) {
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
}
|
|
} else {
|
|
*plRet = ScSendMessageSMS((pwnd), (message), (wParam), (lParam),
|
|
dwSCMSFlags, (PROC)(FNID_TO_CLIENT_PFNWORKER(fnid)),
|
|
dwSCMSFlags, (psms));
|
|
}
|
|
} else {
|
|
StandardSend:
|
|
*plRet = ScSendMessageSMS((pwnd), (message), (wParam), (lParam),
|
|
(ULONG_PTR)(pwnd)->lpfnWndProc,
|
|
gpsi->apfnClientW.pfnDispatchMessage, dwSCMSFlags, (psms));
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* xxxSendMessageTimeout (API)
|
|
*
|
|
* This function synchronously sends a message to a window. The four
|
|
* parameters hwnd, message, wParam, and lParam are passed to the window
|
|
* procedure of the receiving window. If the window receiving the message
|
|
* belongs to the same queue as the current thread, the window proc is called
|
|
* directly. Otherwise, we set up an sms structure, wake the appropriate
|
|
* thread to receive the message and wait for a reply.
|
|
* If the thread is 'hung' or if the time-out value is exceeded, we will
|
|
* fail the request.
|
|
*
|
|
* lpdwResult = NULL if normal sendmessage, if !NULL then it's a timeout call
|
|
*
|
|
* Returns:
|
|
* the value returned by the window procedure, or NULL if there is an error
|
|
*
|
|
* History:
|
|
* 07-13-92 ChrisBl Created/extended from SendMessage
|
|
\***********************************************************************/
|
|
LRESULT xxxSendMessageTimeout(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
UINT fuFlags,
|
|
UINT uTimeout,
|
|
PLONG_PTR lpdwResult)
|
|
{
|
|
LRESULT lRet;
|
|
PTHREADINFO ptiCurrent;
|
|
ULONG_PTR uResult; // holder for DDE_INITIATE case
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* The timeout value is only respected if lpdwResult is non-NULL. This,
|
|
* however, is not obvious, and has caused multiple, hard to track down
|
|
* bugs. So let's assert that the call makes sense.
|
|
*/
|
|
UserAssert(uTimeout == 0 || lpdwResult != NULL);
|
|
|
|
if (lpdwResult != NULL) {
|
|
*lpdwResult = 0L;
|
|
}
|
|
|
|
/*
|
|
* Is this a BroadcastMsg()?
|
|
*/
|
|
if (pwnd == PWND_BROADCAST) {
|
|
BROADCASTMSG bcm;
|
|
PBROADCASTMSG pbcm = NULL;
|
|
UINT uCmd = BMSG_SENDMSG;
|
|
|
|
if (lpdwResult != NULL) {
|
|
uCmd = BMSG_SENDMSGTIMEOUT;
|
|
bcm.to.fuFlags = fuFlags;
|
|
bcm.to.uTimeout = uTimeout;
|
|
bcm.to.lpdwResult = lpdwResult;
|
|
pbcm = &bcm;
|
|
}
|
|
|
|
return xxxBroadcastMessage(NULL, message, wParam, lParam, uCmd, pbcm );
|
|
}
|
|
|
|
CheckLock(pwnd);
|
|
|
|
if (message >= WM_DDE_FIRST && message <= WM_DDE_LAST) {
|
|
/*
|
|
* Even though apps should only send WM_DDE_INITIATE or WM_DDE_ACK
|
|
* messages, we hook them all so DDESPY can monitor them.
|
|
*/
|
|
if (!xxxDDETrackSendHook(pwnd, message, wParam, lParam)) {
|
|
return 0;
|
|
}
|
|
if (message == WM_DDE_INITIATE && guDdeSendTimeout) {
|
|
/*
|
|
* This hack prevents DDE apps from locking up because some
|
|
* one in the system has a top level window and is not
|
|
* processing messages. guDdeSendTimeout is registry set.
|
|
*/
|
|
if (lpdwResult == NULL) {
|
|
lpdwResult = &uResult;
|
|
}
|
|
fuFlags |= SMTO_ABORTIFHUNG;
|
|
uTimeout = guDdeSendTimeout;
|
|
}
|
|
}
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Do inter-thread call if window queue differs from current queue
|
|
*/
|
|
if (ptiCurrent != GETPTI(pwnd)) {
|
|
INTRSENDMSGEX ism;
|
|
PINTRSENDMSGEX pism = NULL;
|
|
|
|
/*
|
|
* If this window is a zombie, don't allow inter-thread send messages
|
|
* to it.
|
|
*/
|
|
if (HMIsMarkDestroy(pwnd))
|
|
return xxxDefWindowProc(pwnd, message, wParam, lParam);
|
|
|
|
if ( lpdwResult != NULL ) {
|
|
/*
|
|
* fail if we think the thread is hung
|
|
*/
|
|
if ((fuFlags & SMTO_ABORTIFHUNG) && FHungApp(GETPTI(pwnd), CMSWAITTOKILLTIMEOUT))
|
|
return 0;
|
|
|
|
/*
|
|
* Setup for a InterSend time-out call
|
|
*/
|
|
ism.fuCall = ISM_TIMEOUT;
|
|
ism.fuSend = fuFlags;
|
|
ism.uTimeout = uTimeout;
|
|
ism.lpdwResult = lpdwResult;
|
|
pism = &ism;
|
|
}
|
|
|
|
lRet = xxxInterSendMsgEx(pwnd, message, wParam, lParam,
|
|
ptiCurrent, GETPTI(pwnd), pism );
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*
|
|
* Call WH_CALLWNDPROC if it's installed and the window is not marked
|
|
* as destroyed.
|
|
*/
|
|
if (IsHooked(ptiCurrent, WHF_CALLWNDPROC)) {
|
|
CWPSTRUCTEX cwps;
|
|
|
|
cwps.hwnd = HWq(pwnd);
|
|
cwps.message = message;
|
|
cwps.wParam = wParam;
|
|
cwps.lParam = lParam;
|
|
cwps.psmsSender = NULL;
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents.
|
|
*/
|
|
xxxCallHook(HC_ACTION, FALSE, (LPARAM)&cwps, WH_CALLWNDPROC);
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents. If this behavior reverts to
|
|
* Win3.1 semantics, we will need to copy the new parameters
|
|
* from cwps.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* If this window's proc is meant to be executed from the server side
|
|
* we'll just stay inside the semaphore and call it directly. Note
|
|
* how we don't convert the pwnd into an hwnd before calling the proc.
|
|
*/
|
|
if (TestWF(pwnd, WFSERVERSIDEPROC)) {
|
|
|
|
/*
|
|
* We have a number of places where we do recursion in User. This often goes
|
|
* through SendMessage (when we send a message to the parent for example) which
|
|
* can eat the amount of stack we have
|
|
*/
|
|
if ((IoGetRemainingStackSize() < KERNEL_STACK_MINIMUM_RESERVE)
|
|
#if defined(_IA64_)
|
|
|| (GET_CURRENT_BSTORE() < KERNEL_BSTORE_MINIMUM_RESERVE)
|
|
#endif
|
|
) {
|
|
RIPMSG1(RIP_ERROR, "SendMessage: Thread recursing in User with message %lX; failing", message);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
lRet = pwnd->lpfnWndProc(pwnd, message, wParam, lParam);
|
|
|
|
if ( lpdwResult == NULL ) {
|
|
return lRet;
|
|
} else { /* time-out call */
|
|
*lpdwResult = lRet;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call the client or xxxDefWindowProc. pwnd is already locked.
|
|
*/
|
|
xxxSendMessageToClient(pwnd, message, wParam, lParam, NULL, FALSE, &lRet);
|
|
|
|
/*
|
|
* Call WH_CALLWNDPROCRET if it's installed.
|
|
*/
|
|
if (IsHooked(ptiCurrent, WHF_CALLWNDPROCRET)) {
|
|
CWPRETSTRUCTEX cwps;
|
|
|
|
cwps.hwnd = HWq(pwnd);
|
|
cwps.message = message;
|
|
cwps.wParam = wParam;
|
|
cwps.lParam = lParam;
|
|
cwps.lResult = lRet;
|
|
cwps.psmsSender = NULL;
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents.
|
|
*/
|
|
xxxCallHook(HC_ACTION, FALSE, (LPARAM)&cwps, WH_CALLWNDPROCRET);
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents. If this behavior reverts to
|
|
* Win3.1 semantics, we will need to copy the new parameters
|
|
* from cwps.
|
|
*/
|
|
}
|
|
|
|
if ( lpdwResult != NULL ) { /* time-out call */
|
|
*lpdwResult = lRet;
|
|
return TRUE;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* QueueNotifyMessage
|
|
*
|
|
* This routine queues up a notify message *only*, and does NOT do any callbacks
|
|
* or any waits. This is for certain code that cannot do a callback for
|
|
* compatibility reasons, but still needs to send notify messages (normal
|
|
* notify messages actually do a callback if the calling thread created the
|
|
* pwnd. Also this will NOT callback any hooks (sorry!)
|
|
*
|
|
* 04-13-93 ScottLu Created.
|
|
\***************************************************************************/
|
|
void QueueNotifyMessage(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
TL tlpwnd;
|
|
BEGINATOMICCHECK();
|
|
|
|
/*
|
|
* We have to thread lock the window even though we don't leave
|
|
* the critical section or else xxxSendMessageCallback complains.
|
|
*/
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
xxxSendMessageCallback(pwnd, message, wParam, lParam, NULL, 1L, 0);
|
|
ThreadUnlock(&tlpwnd);
|
|
ENDATOMICCHECK();
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSystemBroadcastMessage
|
|
*
|
|
* Sends a message to all top-level windows in the system. To do this
|
|
* for messages with parameters that point to data structures in a way
|
|
* that won't block on a hung app, post an event message for
|
|
* each window that is to receive the real message. The real message
|
|
* will be sent when the event message is processed.
|
|
*
|
|
* History:
|
|
* 05-12-94 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxSystemBroadcastMessage(
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
UINT wCmd,
|
|
PBROADCASTMSG pbcm)
|
|
{
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
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_SYSTEMBROADCASTMESSAGE);
|
|
for (pwinsta = grpWinStaList; pwinsta != NULL; ) {
|
|
UINT wCmd1;
|
|
|
|
if ((wCmd == BMSG_SENDMSG) && (pwinsta != ptiCurrent->rpdesk->rpwinstaParent))
|
|
wCmd1 = BMSG_SENDNOTIFYMSG;
|
|
else
|
|
wCmd1 = wCmd;
|
|
|
|
ThreadLockExchangeWinSta(ptiCurrent, pwinsta, &tlpwinsta);
|
|
for (pdesk = pwinsta->rpdeskList; pdesk != NULL; ) {
|
|
|
|
ThreadLockExchangeDesktop(ptiCurrent, pdesk, &tlpdesk, LDLT_FN_SYSTEMBROADCASTMESSAGE);
|
|
|
|
/*
|
|
* Bug 276814. Don't recurse calling again xxxBroadcastMessage if there
|
|
* is no window on this desktop.
|
|
*/
|
|
if (pdesk->pDeskInfo->spwnd != NULL) {
|
|
xxxBroadcastMessage(pdesk->pDeskInfo->spwnd, message, wParam, lParam,
|
|
wCmd1, pbcm);
|
|
}
|
|
|
|
pdesk = pdesk->rpdeskNext;
|
|
}
|
|
pwinsta = pwinsta->rpwinstaNext;
|
|
}
|
|
ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_SYSTEMBROADCASTMESSAGE);
|
|
ThreadUnlockWinSta(ptiCurrent, &tlpwinsta);
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* xxxSendNotifyMessage (API)
|
|
*
|
|
* This function sends a message to the window proc associated with pwnd.
|
|
* The window proc is executed in the context of the thread which created
|
|
* pwnd. The function is identical to SendMessage() except that in the
|
|
* case of an inter-thread call, the send does not wait for a reply from
|
|
* the receiver, it simply returns a BOOL indicating success or failure.
|
|
* If the message is sent to a window on the current thread, then the
|
|
* function behaves just like SendMessage() and essentially does a
|
|
* subroutine call to pwnd's window procedure.
|
|
*
|
|
* History:
|
|
* 01-23-91 DavidPe Created.
|
|
* 07-14-92 ChrisBl Will return T/F if in same thread, as documented
|
|
\***********************************************************************/
|
|
|
|
BOOL xxxSendNotifyMessage(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
/*
|
|
* If this is a broadcast of one of the system
|
|
* notification messages, send it to all top-level
|
|
* windows in the system.
|
|
*/
|
|
if (pwnd == PWND_BROADCAST) {
|
|
switch (message) {
|
|
case WM_WININICHANGE:
|
|
case WM_DEVMODECHANGE:
|
|
case WM_SPOOLERSTATUS:
|
|
xxxSystemBroadcastMessage(message, wParam, lParam,
|
|
BMSG_SENDNOTIFYMSG, NULL);
|
|
return 1;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return xxxSendMessageCallback( pwnd, message, wParam, lParam,
|
|
NULL, 0L, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* xxxSendMessageCallback (API)
|
|
*
|
|
* This function synchronously sends a message to a window. The four
|
|
* parameters hwnd, message, wParam, and lParam are passed to the window
|
|
* procedure of the receiving window. If the window receiving the message
|
|
* belongs to the same queue as the current thread, the window proc is called
|
|
* directly. Otherwise, we set up an sms structure, wake the appropriate
|
|
* thread to receive the message and give him a call back function to send
|
|
* the result to.
|
|
*
|
|
* History:
|
|
* 07-13-92 ChrisBl Created/extended from SendNotifyMessage
|
|
\***********************************************************************/
|
|
|
|
BOOL xxxSendMessageCallback(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
SENDASYNCPROC lpResultCallBack,
|
|
ULONG_PTR dwData,
|
|
BOOL fClientRequest)
|
|
{
|
|
LRESULT lRet;
|
|
PTHREADINFO ptiCurrent;
|
|
BOOL fQueuedNotify;
|
|
|
|
/*
|
|
* See if this is a queued notify message.
|
|
*/
|
|
fQueuedNotify = FALSE;
|
|
if (lpResultCallBack == NULL && dwData == 1L)
|
|
fQueuedNotify = TRUE;
|
|
|
|
/*
|
|
* First check to see if this message takes DWORDs only. If it does not,
|
|
* fail the call. 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!)
|
|
*/
|
|
if (TESTSYNCONLYMESSAGE(message, wParam)) {
|
|
RIPERR1(ERROR_MESSAGE_SYNC_ONLY, RIP_WARNING,
|
|
"Trying to non-synchronously send a structure msg=%lX", message);
|
|
return FALSE;
|
|
}
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* Is this a BroadcastMsg()?
|
|
*/
|
|
if (pwnd == PWND_BROADCAST) {
|
|
BROADCASTMSG bcm;
|
|
PBROADCASTMSG pbcm = NULL;
|
|
UINT uCmd = BMSG_SENDNOTIFYMSG;
|
|
|
|
if (lpResultCallBack != NULL) {
|
|
uCmd = BMSG_SENDMSGCALLBACK;
|
|
bcm.cb.lpResultCallBack = lpResultCallBack;
|
|
bcm.cb.dwData = dwData;
|
|
bcm.cb.bClientRequest = fClientRequest;
|
|
pbcm = &bcm;
|
|
}
|
|
|
|
return xxxBroadcastMessage(NULL, message, wParam, lParam, uCmd, pbcm );
|
|
}
|
|
|
|
CheckLock(pwnd);
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Do inter-thread call if window thead differs from current thread.
|
|
* We pass NULL for ptiSender to tell xxxInterSendMsgEx() that this is
|
|
* a xxxSendNotifyMessage() and that there's no need for a reply.
|
|
*
|
|
* If this is a queued notify, always call InterSendMsgEx() so that
|
|
* we queue it up and return - we don't do callbacks here with queued
|
|
* notifies.
|
|
*/
|
|
if (fQueuedNotify || ptiCurrent != GETPTI(pwnd)) {
|
|
INTRSENDMSGEX ism;
|
|
PINTRSENDMSGEX pism = NULL;
|
|
|
|
if (lpResultCallBack != NULL) { /* CallBack request */
|
|
ism.fuCall = ISM_CALLBACK | (fClientRequest ? ISM_CB_CLIENT : 0);
|
|
ism.lpResultCallBack = lpResultCallBack;
|
|
ism.dwData = dwData;
|
|
pism = &ism;
|
|
}
|
|
return (BOOL)xxxInterSendMsgEx(pwnd, message, wParam, lParam,
|
|
NULL, GETPTI(pwnd), pism );
|
|
}
|
|
|
|
/*
|
|
* Call WH_CALLWNDPROC if it's installed.
|
|
*/
|
|
if (!fQueuedNotify && IsHooked(ptiCurrent, WHF_CALLWNDPROC)) {
|
|
CWPSTRUCTEX cwps;
|
|
|
|
cwps.hwnd = HWq(pwnd);
|
|
cwps.message = message;
|
|
cwps.wParam = wParam;
|
|
cwps.lParam = lParam;
|
|
cwps.psmsSender = NULL;
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents.
|
|
*/
|
|
xxxCallHook(HC_ACTION, FALSE, (LPARAM)&cwps, WH_CALLWNDPROC);
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents. If this behavior reverts to
|
|
* Win3.1 semantics, we will need to copy the new parameters
|
|
* from cwps.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* If this window's proc is meant to be executed from the server side
|
|
* we'll just stay inside the semaphore and call it directly. Note
|
|
* how we don't convert the pwnd into an hwnd before calling the proc.
|
|
*/
|
|
if (TestWF(pwnd, WFSERVERSIDEPROC)) {
|
|
lRet = pwnd->lpfnWndProc(pwnd, message, wParam, lParam);
|
|
} else {
|
|
/*
|
|
* Call the client or xxxDefWindowProc. pwnd is already locked
|
|
*/
|
|
xxxSendMessageToClient(pwnd, message, wParam, lParam, NULL, FALSE, &lRet);
|
|
}
|
|
|
|
if (lpResultCallBack != NULL) {
|
|
/*
|
|
* Call the callback funtion for the return value
|
|
*/
|
|
if (fClientRequest) {
|
|
/*
|
|
* The application-defined callback proc is neither Unicode/ANSI
|
|
*/
|
|
SET_FLAG(ptiCurrent->pcti->CTIF_flags, CTIF_INCALLBACKMESSAGE);
|
|
CallClientProcA(pwnd, message, dwData, lRet,
|
|
(ULONG_PTR)lpResultCallBack);
|
|
CLEAR_FLAG(ptiCurrent->pcti->CTIF_flags, CTIF_INCALLBACKMESSAGE);
|
|
} else {
|
|
(*lpResultCallBack)((HWND)pwnd, message, dwData, lRet);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call WH_CALLWNDPROCRET if it's installed.
|
|
*/
|
|
if (!fQueuedNotify && IsHooked(ptiCurrent, WHF_CALLWNDPROCRET)) {
|
|
CWPRETSTRUCTEX cwps;
|
|
|
|
cwps.hwnd = HWq(pwnd);
|
|
cwps.message = message;
|
|
cwps.wParam = wParam;
|
|
cwps.lParam = lParam;
|
|
cwps.lResult = lRet;
|
|
cwps.psmsSender = NULL;
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents.
|
|
*/
|
|
xxxCallHook(HC_ACTION, FALSE, (LPARAM)&cwps, WH_CALLWNDPROCRET);
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents. If this behavior reverts to
|
|
* Win3.1 semantics, we will need to copy the new parameters
|
|
* from cwps.
|
|
*/
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* xxxInterSendMsgEx
|
|
*
|
|
* This function does an inter-thread send message. If ptiSender is NULL,
|
|
* that means we're called from xxxSendNotifyMessage() and should act
|
|
* accordingly.
|
|
*
|
|
* History:
|
|
* 07-13-92 ChrisBl Created/extended from xxxInterSendMsg
|
|
\***********************************************************************/
|
|
|
|
#define NoString 0
|
|
#define IsAnsiString 1
|
|
#define IsUnicodeString 2
|
|
|
|
/*
|
|
* We will capture the an address in two cases
|
|
* 1- If the address is a user mode address or
|
|
* 2- The call is a SendNotifyMessafe or SendMessageCallback.
|
|
*
|
|
* #2 is true if ptiSender is NULL see xxxSendMessageCallback implementation.
|
|
* Why we do that?
|
|
* if we are in SendNotifyMessafe or SendMessageCallback then force capture.
|
|
* because these two APIs will not wait till the receiver thread handles the
|
|
* message, then any kernel stack memory will be invalid once we return from
|
|
* these two APIs.
|
|
*/
|
|
#define FORCE_CAPTURE(Addr) (!IS_SYSTEM_ADDRESS(Addr) || (ptiSender == NULL))
|
|
|
|
LRESULT xxxInterSendMsgEx(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
PTHREADINFO ptiSender,
|
|
PTHREADINFO ptiReceiver,
|
|
PINTRSENDMSGEX pism)
|
|
{
|
|
PSMS psms, *ppsms;
|
|
PSMS psmsSentSave;
|
|
LRESULT lRet = 0;
|
|
DWORD cbCapture, cbOutput;
|
|
PBYTE lpCapture;
|
|
PCOPYDATASTRUCT pcds;
|
|
PMDICREATESTRUCTEX pmdics;
|
|
LPHLP phlp;
|
|
LPHELPINFO phelpinfo;
|
|
LARGE_STRING str;
|
|
LPARAM lParamSave;
|
|
UINT fString = NoString;
|
|
BOOLEAN bWasSwapEnabled;
|
|
|
|
CheckCritIn();
|
|
|
|
|
|
/*
|
|
* If the sender is dying, fail the call
|
|
*/
|
|
if ((ptiSender != NULL) && (ptiSender->TIF_flags & TIF_INCLEANUP))
|
|
return 0;
|
|
|
|
/*
|
|
* Some messages cannot be sent across process because we don't know how to thunk them
|
|
* Fail attempts to read passwords across processes.
|
|
*/
|
|
if (pwnd && GETPTI(pwnd)->ppi != PpiCurrent()) {
|
|
switch (message) {
|
|
case EM_SETWORDBREAKPROC:
|
|
if (!RtlEqualLuid(&(GETPTI(pwnd)->ppi->luidSession), &(PpiCurrent()->luidSession))) {
|
|
RIPMSGF3(RIP_WARNING,
|
|
"Message cannot be sent across different LUID, pwnd: %p, message: 0x%x, target ppi: %p.",
|
|
pwnd,
|
|
message,
|
|
GETPTI(pwnd)->ppi);
|
|
return 0;
|
|
}
|
|
break;
|
|
case WM_INITDIALOG:
|
|
case WM_NOTIFY:
|
|
RIPMSG0(RIP_WARNING | RIP_THERESMORE, "xxxInterSendMsgEx: message cannot be sent across processes");
|
|
RIPMSG4(RIP_WARNING | RIP_THERESMORE, " pwnd:%#p message:%#x wParam:%#p lParam:%#p", pwnd, message, wParam, lParam);
|
|
return 0;
|
|
|
|
/*
|
|
* A change was introduced here to check with IS_EDIT macro instead of directly
|
|
* accessing FNID. The reason is to maintain conformity and not break
|
|
* comctl32 v6 password edits which can't set fnid field in pwnd.
|
|
*/
|
|
case WM_GETTEXT:
|
|
case EM_GETLINE:
|
|
case EM_SETPASSWORDCHAR:
|
|
if (IS_EDIT(pwnd) && TestWF(pwnd, EFPASSWORD)) {
|
|
RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Can't access protected edit control");
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Alloc SMS structure.
|
|
*/
|
|
psms = AllocSMS();
|
|
if (psms == NULL) {
|
|
|
|
/*
|
|
* Set to zero so xxxSendNotifyMessage would return FALSE.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Prepare to capture variable length data from client
|
|
* space. Addresses have already been probed. Fixed-length
|
|
* data is probed and captured in the message thunk.
|
|
*/
|
|
psms->pvCapture = NULL;
|
|
cbCapture = cbOutput = 0;
|
|
lpCapture = (LPBYTE)lParam;
|
|
|
|
/*
|
|
* If this is a reply message then wParam and lParam is equal NULL.
|
|
* No need to capture anything.
|
|
*/
|
|
if ((pism != NULL) && (pism->fuCall == (ISM_CALLBACK | ISM_REPLY))) {
|
|
goto REPLY_MSG;
|
|
}
|
|
|
|
/*
|
|
* For messages with indirect data, set cbCapture and lpCapture
|
|
* (if not lParam) as approp.
|
|
*/
|
|
try {
|
|
switch (message) {
|
|
case WM_COPYGLOBALDATA: // fnCOPYGLOBALDATA
|
|
cbCapture = (DWORD)wParam;
|
|
break;
|
|
|
|
case WM_COPYDATA: // fnCOPYDATA
|
|
pcds = (PCOPYDATASTRUCT)lParam;
|
|
if (pcds->lpData) {
|
|
cbCapture = sizeof(COPYDATASTRUCT) + pcds->cbData;
|
|
} else {
|
|
cbCapture = sizeof(COPYDATASTRUCT);
|
|
}
|
|
break;
|
|
|
|
case WM_CREATE: // fnINLPCREATESTRUCT
|
|
case WM_NCCREATE: // fnINLPCREATESTRUCT
|
|
RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Can't Intersend WM_CREATE or WM_NCCREATE message");
|
|
FreeSMS(psms);
|
|
return 0;
|
|
|
|
case WM_HELP: // fnINLPHELPINFOSTRUCT
|
|
phelpinfo = (LPHELPINFO)lParam;
|
|
cbCapture = phelpinfo->cbSize;
|
|
break;
|
|
|
|
case WM_WINHELP: // fnINLPHLPSTRUCT
|
|
phlp = (LPHLP)lParam;
|
|
cbCapture = phlp->cbData;
|
|
break;
|
|
|
|
case WM_MDICREATE: // fnINLPMDICREATESTRUCT
|
|
pmdics = (PMDICREATESTRUCTEX)lParam;
|
|
cbCapture = pmdics->strTitle.MaximumLength +
|
|
pmdics->strClass.MaximumLength;
|
|
UserAssert(pmdics->strClass.Buffer == NULL || pmdics->strClass.Buffer == pmdics->mdics.szClass);
|
|
if (pmdics->strTitle.Buffer)
|
|
UserAssert(pmdics->strTitle.Buffer == pmdics->mdics.szTitle);
|
|
break;
|
|
|
|
case LB_ADDSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case LB_INSERTSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case LB_SELECTSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case LB_FINDSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case LB_FINDSTRINGEXACT: // INLBOXSTRING calls fnINSTRING
|
|
/*
|
|
* See if the control is ownerdraw and does not have the LBS_HASSTRINGS
|
|
* style. If so, treat lParam as a DWORD.
|
|
*/
|
|
if (pwnd && !(pwnd->style & LBS_HASSTRINGS) &&
|
|
(pwnd->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))) {
|
|
/*
|
|
* Treat lParam as a dword.
|
|
*/
|
|
break;
|
|
} else {
|
|
goto fnINSTRINGThunk;
|
|
}
|
|
break;
|
|
|
|
case CB_ADDSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case CB_INSERTSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case CB_SELECTSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case CB_FINDSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case CB_FINDSTRINGEXACT: // INCBOXSTRING calls fnINSTRING
|
|
/*
|
|
* See if the control is ownerdraw and does not have the CBS_HASSTRINGS
|
|
* style. If so, treat lParam as a DWORD.
|
|
*/
|
|
if (pwnd && !(pwnd->style & CBS_HASSTRINGS) &&
|
|
(pwnd->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))) {
|
|
|
|
/*
|
|
* Treat lParam as a dword.
|
|
*/
|
|
break;
|
|
} else {
|
|
goto fnINSTRINGThunk;
|
|
}
|
|
break;
|
|
|
|
case EM_REPLACESEL: // fnINSTRINGNULL
|
|
case WM_SETTEXT: // fnINSTRINGNULL
|
|
case WM_WININICHANGE: // fnINSTRINGNULL
|
|
if (lParam == 0)
|
|
break;
|
|
|
|
/*
|
|
* Fall through
|
|
*/
|
|
|
|
case CB_DIR: // fnINSTRING
|
|
case LB_ADDFILE: // fnINSTRING
|
|
case LB_DIR: // fnINSTRING
|
|
case WM_DEVMODECHANGE: // fnINSTRING
|
|
fnINSTRINGThunk:
|
|
|
|
/*
|
|
* Only capture strings if they are not in system space or ptiSender
|
|
* is NULL (see FORCE_CAPTURE definition).
|
|
*
|
|
* Also we are going to capture the LARGE_STRING structure itself because
|
|
* it is (lParam) a stack memory.
|
|
*/
|
|
str = *(PLARGE_STRING)lParam;
|
|
|
|
if (FORCE_CAPTURE(str.Buffer))
|
|
cbCapture = str.Length + sizeof(WCHAR) + sizeof(LARGE_STRING);
|
|
break;
|
|
|
|
case WM_DEVICECHANGE:
|
|
if (lParam == 0)
|
|
break;
|
|
|
|
/*
|
|
* Only capture data if lParam is a pointer and
|
|
* the data is not in system space
|
|
*/
|
|
if ((wParam & 0x8000) != 0x8000)
|
|
break;
|
|
|
|
if (FORCE_CAPTURE((LPVOID)lParam)) {
|
|
cbCapture = *((DWORD *)lpCapture);
|
|
UserAssert(FALSE);
|
|
}
|
|
break;
|
|
|
|
case EM_SETTABSTOPS: // fnPOPTINLPUINT
|
|
case LB_SETTABSTOPS: // fnPOPTINLPUINT
|
|
case LB_GETSELITEMS: // fnPOUTLPINT
|
|
cbCapture = (UINT)wParam * sizeof(INT);
|
|
break;
|
|
|
|
case EM_GETLINE: // fnINCNTOUTSTRING
|
|
case WM_ASKCBFORMATNAME: // fnINCNTOUTSTRINGNULL
|
|
case WM_GETTEXT: // fnOUTSTRING
|
|
case LB_GETTEXT: // fnOUTLBOXSTRING
|
|
case CB_GETLBTEXT: // fnOUTCBOXSTRING
|
|
|
|
/*
|
|
* Only allocate output buffer if the real one is not in system space
|
|
*/
|
|
str = *(PLARGE_STRING)lParam;
|
|
/*
|
|
* Bug 18108. For WM_GETTEXT only copy the actual string and not the
|
|
* the maximum size into the output buffer
|
|
*/
|
|
if(str.bAnsi) {
|
|
fString = IsAnsiString ;
|
|
} else {
|
|
fString = IsUnicodeString ;
|
|
}
|
|
lParam = (LPARAM)&str;
|
|
if (FORCE_CAPTURE(str.Buffer))
|
|
cbCapture = str.MaximumLength;
|
|
break;
|
|
}
|
|
if (cbCapture &&
|
|
(psms->pvCapture = UserAllocPoolWithQuota(cbCapture, TAG_SMS_CAPTURE)) != NULL) {
|
|
|
|
lParamSave = lParam;
|
|
|
|
/*
|
|
* now actually copy memory from lpCapture to psms->pvCapture
|
|
* and fixup any references to the indirect data to point to
|
|
* psms->pvCapture.
|
|
*/
|
|
switch (message) {
|
|
case WM_COPYDATA: // fnCOPYDATA
|
|
{
|
|
PCOPYDATASTRUCT pcdsNew = (PCOPYDATASTRUCT)psms->pvCapture;
|
|
lParam = (LPARAM)pcdsNew;
|
|
RtlCopyMemory(pcdsNew, pcds, sizeof(COPYDATASTRUCT));
|
|
if (pcds->lpData) {
|
|
pcdsNew->lpData = (PVOID)((PBYTE)pcdsNew + sizeof(COPYDATASTRUCT));
|
|
RtlCopyMemory(pcdsNew->lpData, pcds->lpData, pcds->cbData);
|
|
}
|
|
}
|
|
break;
|
|
case WM_MDICREATE: // fnINLPMDICREATESTRUCT
|
|
if (pmdics->strClass.Buffer) {
|
|
RtlCopyMemory(psms->pvCapture, pmdics->strClass.Buffer,
|
|
pmdics->strClass.MaximumLength);
|
|
pmdics->mdics.szClass = (LPWSTR)psms->pvCapture;
|
|
}
|
|
if (pmdics->strTitle.Length) {
|
|
lpCapture = (PBYTE)psms->pvCapture + pmdics->strClass.MaximumLength;
|
|
RtlCopyMemory(lpCapture, pmdics->strTitle.Buffer,
|
|
pmdics->strTitle.MaximumLength);
|
|
pmdics->mdics.szTitle = (LPWSTR)lpCapture;
|
|
}
|
|
break;
|
|
|
|
case CB_DIR: // fnINSTRING
|
|
case LB_FINDSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case LB_FINDSTRINGEXACT: // INLBOXSTRING calls fnINSTRING
|
|
case CB_FINDSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case CB_FINDSTRINGEXACT: // INCBOXSTRING calls fnINSTRING
|
|
case LB_ADDFILE: // fnINSTRING
|
|
case LB_ADDSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case LB_INSERTSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case LB_SELECTSTRING: // INLBOXSTRING calls fnINSTRING
|
|
case CB_ADDSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case CB_INSERTSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case CB_SELECTSTRING: // INCBOXSTRING calls fnINSTRING
|
|
case LB_DIR: // fnINSTRING
|
|
case WM_DEVMODECHANGE: // fnINSTRING
|
|
case EM_REPLACESEL: // fnINSTRINGNULL
|
|
case WM_SETTEXT: // fnINSTRINGNULL
|
|
case WM_WININICHANGE: // fnINSTRINGNULL
|
|
{
|
|
PLARGE_STRING pstr = psms->pvCapture;
|
|
lParam = (LPARAM)pstr;
|
|
pstr->bAnsi = str.bAnsi;
|
|
pstr->Length = str.Length;
|
|
pstr->Buffer = (LPBYTE)pstr + sizeof(LARGE_STRING);
|
|
pstr->MaximumLength = cbCapture - sizeof(LARGE_STRING);
|
|
UserAssert(pstr->MaximumLength == pstr->Length + sizeof(WCHAR));
|
|
RtlCopyMemory(pstr->Buffer, str.Buffer, pstr->MaximumLength);
|
|
}
|
|
break;
|
|
|
|
case LB_GETSELITEMS:
|
|
cbOutput = cbCapture;
|
|
RtlCopyMemory(psms->pvCapture, lpCapture, cbCapture);
|
|
lParam = (LPARAM)psms->pvCapture;
|
|
break;
|
|
|
|
case EM_GETLINE: // fnINCNTOUTSTRING
|
|
*(WORD *)psms->pvCapture = *(WORD *)str.Buffer;
|
|
|
|
/*
|
|
* Fall through
|
|
*/
|
|
case WM_ASKCBFORMATNAME: // fnINCNTOUTSTRINGNULL
|
|
case WM_GETTEXT: // fnOUTSTRING
|
|
case LB_GETTEXT: // fnOUTLBOXSTRING
|
|
case CB_GETLBTEXT: // fnOUTCBOXSTRING
|
|
cbOutput = cbCapture;
|
|
lParamSave = (LPARAM)str.Buffer;
|
|
str.Buffer = psms->pvCapture;
|
|
break;
|
|
|
|
default:
|
|
RtlCopyMemory(psms->pvCapture, lpCapture, cbCapture);
|
|
lParam = (LPARAM)psms->pvCapture;
|
|
break;
|
|
}
|
|
}
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
if (psms->pvCapture != NULL)
|
|
UserFreePool(psms->pvCapture);
|
|
FreeSMS(psms);
|
|
return 0;
|
|
}
|
|
|
|
if (cbCapture && psms->pvCapture == NULL) {
|
|
FreeSMS(psms);
|
|
return 0;
|
|
}
|
|
REPLY_MSG:
|
|
/*
|
|
* Copy message parms
|
|
*/
|
|
psms->spwnd = NULL;
|
|
psms->psmsReceiveNext = NULL;
|
|
#if DBG
|
|
psms->psmsSendList = NULL;
|
|
psms->psmsSendNext = NULL;
|
|
#endif
|
|
Lock(&(psms->spwnd), pwnd);
|
|
psms->message = message;
|
|
psms->wParam = wParam;
|
|
psms->lParam = lParam;
|
|
psms->flags = 0;
|
|
|
|
/*
|
|
* Link into gpsmsList
|
|
*/
|
|
psms->psmsNext = gpsmsList;
|
|
gpsmsList = psms;
|
|
|
|
/*
|
|
* Time stamp message
|
|
*/
|
|
psms->tSent = NtGetTickCount();
|
|
|
|
/*
|
|
* Set queue fields
|
|
*/
|
|
psms->ptiReceiver = ptiReceiver;
|
|
psms->ptiSender = ptiSender;
|
|
psms->ptiCallBackSender = NULL;
|
|
|
|
if ((pism != NULL) && (pism->fuCall & ISM_CALLBACK)) {
|
|
/*
|
|
* Setup for a SendMessageCallback
|
|
*/
|
|
psms->flags |= (pism->fuCall & ISM_CB_CLIENT) ? SMF_CB_CLIENT : SMF_CB_SERVER;
|
|
psms->lpResultCallBack = pism->lpResultCallBack;
|
|
psms->dwData = pism->dwData;
|
|
|
|
if (pism->fuCall & ISM_REPLY) {
|
|
psms->flags |= SMF_CB_REPLY;
|
|
psms->lRet = pism->lRet;
|
|
} else { /* REQUEST */
|
|
psms->flags |= SMF_CB_REQUEST;
|
|
psms->ptiCallBackSender = PtiCurrent();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add SMS to the end of the ptiReceiver's receive list
|
|
*/
|
|
ppsms = &ptiReceiver->psmsReceiveList;
|
|
while (*ppsms != NULL) {
|
|
ppsms = &((*ppsms)->psmsReceiveNext);
|
|
}
|
|
*ppsms = psms;
|
|
|
|
/*
|
|
* Link this SMS into the SendMsg chain. Of course only do this if
|
|
* it's not from a xxxSendNotifyMessage() call.
|
|
*
|
|
* The psmsSendNext field implements a chain of messages being
|
|
* processed because of an initial SendMsg call. For example, if
|
|
* thread A sends message M1 to thread B, which causes B to send
|
|
* message M2 to thread C, the SendMsg chain is M1->M2. If the
|
|
* system hangs in this situation, the chain is traversed to find
|
|
* the offending thread (C).
|
|
*
|
|
* psms->psmsSendList always points to the head of this list so
|
|
* we can tell where to begin a list traversal.
|
|
*
|
|
* ptiSender->psmsCurrent is the last SMS in the chain.
|
|
*/
|
|
#if DBG
|
|
if (ptiSender != NULL && ptiSender->psmsCurrent != NULL) {
|
|
/*
|
|
* sending queue is currently processing a message sent to it,
|
|
* so append SMS to the chain. Link in the new sms because
|
|
* psmsSendNext may be pointing to a replied-to message.
|
|
*/
|
|
psms->psmsSendNext = ptiSender->psmsCurrent->psmsSendNext;
|
|
ptiSender->psmsCurrent->psmsSendNext = psms;
|
|
psms->psmsSendList = ptiSender->psmsCurrent->psmsSendList;
|
|
|
|
} else {
|
|
/*
|
|
* sending queue is initiating a send sequence, so put sms at
|
|
* the head of the chain
|
|
*/
|
|
psms->psmsSendList = psms;
|
|
}
|
|
#endif
|
|
|
|
if (ptiSender != NULL) {
|
|
/*
|
|
* ptiSender->psmsSent marks the most recent message sent from this
|
|
* thread that has not yet been replied to. Save the previous value
|
|
* on the stack so it can be restored when we get the reply.
|
|
*
|
|
* This way when an "older" SMS for this thread gets a reply before
|
|
* the "current" one does, the thread does get woken up.
|
|
*/
|
|
psmsSentSave = ptiSender->psmsSent;
|
|
ptiSender->psmsSent = psms;
|
|
} else {
|
|
|
|
/*
|
|
* Set SMF_RECEIVERFREE since we'll be returning to
|
|
* xxxSendNotifyMessage() right away and won't get a
|
|
* chance to free it.
|
|
*/
|
|
psms->flags |= SMF_RECEIVERFREE;
|
|
}
|
|
|
|
#ifdef DEBUG_SMS
|
|
ValidateSmsSendLists(psms);
|
|
#endif
|
|
|
|
/*
|
|
* If we're not being called from xxxSendNotifyMessage() or
|
|
* SendMessageCallback(), then sleep while we wait for the reply.
|
|
*/
|
|
if (ptiSender == NULL) {
|
|
/*
|
|
* Wake receiver for the sent message
|
|
*/
|
|
SetWakeBit(ptiReceiver, QS_SENDMESSAGE);
|
|
|
|
return (LONG)TRUE;
|
|
} else {
|
|
BOOL fTimeOut = FALSE;
|
|
UINT uTimeout = 0;
|
|
UINT uWakeMask = QS_SMSREPLY;
|
|
|
|
/*
|
|
* Wake up the receiver thread.
|
|
*/
|
|
SetWakeBit(ptiReceiver, QS_SENDMESSAGE);
|
|
|
|
/*
|
|
* We have 4 sending cases:
|
|
*
|
|
* 16 - 16 : yield to the 16 bit receiver
|
|
* 32 - 16 : no yielding required
|
|
* 16 - 32 : sender yields while receiver processes the message
|
|
* 32 - 32 : no yielding required.
|
|
*/
|
|
if (ptiSender->TIF_flags & TIF_16BIT || ptiReceiver->TIF_flags & TIF_16BIT) {
|
|
DirectedScheduleTask(ptiSender, ptiReceiver, TRUE, psms);
|
|
}
|
|
|
|
/*
|
|
* Put this thread to sleep until the reply arrives. First clear
|
|
* the QS_SMSREPLY bit, then leave the semaphore and go to sleep.
|
|
*
|
|
* IMPORTANT: The QS_SMSREPLY bit is not cleared once we get a
|
|
* reply because of the following case:
|
|
*
|
|
* We've recursed a second level into SendMessage() when the first level
|
|
* receiver thread dies, causing exit list processing to simulate
|
|
* a reply to the first message. When the second level send returns,
|
|
* SleepThread() is called again to get the first reply.
|
|
*
|
|
* Keeping QS_SMSREPLY set causes this call to SleepThread()
|
|
* to return without going to sleep to wait for the reply that has
|
|
* already happened.
|
|
*/
|
|
if ( pism != NULL ) {
|
|
if (pism->fuSend & SMTO_BLOCK) {
|
|
/*
|
|
* only wait for a return, all other events will
|
|
* be ignored until timeout or return
|
|
*/
|
|
uWakeMask |= QS_EXCLUSIVE;
|
|
}
|
|
|
|
uTimeout = pism->uTimeout;
|
|
}
|
|
|
|
|
|
/*
|
|
* Don't swap this guys stack while sleeping during a sendmessage
|
|
*/
|
|
if (ptiSender->cEnterCount == 0) {
|
|
bWasSwapEnabled = KeSetKernelStackSwapEnable(FALSE);
|
|
} else {
|
|
UserAssert(ptiSender->cEnterCount > 0);
|
|
}
|
|
ptiSender->cEnterCount++;
|
|
|
|
|
|
while ((psms->flags & SMF_REPLY) == 0 && !fTimeOut) {
|
|
PHOOK phk = NULL;
|
|
TL tl;
|
|
BOOLEAN fRememberTimeout = FALSE;
|
|
|
|
ptiSender->pcti->fsChangeBits &= ~QS_SMSREPLY;
|
|
|
|
if (message == WM_HOOKMSG && lParam && GetAppCompatFlags2ForPti(ptiReceiver, VER51)) {
|
|
phk = ((PHOOKMSGSTRUCT)lParam)->phk;
|
|
switch (phk->iHook) {
|
|
case WH_KEYBOARD_LL:
|
|
case WH_MOUSE_LL:
|
|
ThreadLock(phk, &tl);
|
|
fRememberTimeout = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If SendMessageTimeout, sleep for timeout amount, else wait
|
|
* forever. Since this is not technically a transition to an
|
|
* idle condition, indicate that this sleep is not going "idle".
|
|
*/
|
|
fTimeOut = !xxxSleepThread(uWakeMask, uTimeout, FALSE);
|
|
|
|
/*
|
|
* Windows bug 307738: EverQuest LL hook is virtually
|
|
* hung, blocking the DirectInput thread.
|
|
*/
|
|
if (fRememberTimeout) {
|
|
phk->fLastHookHung = fTimeOut;
|
|
ThreadUnlock(&tl);
|
|
}
|
|
|
|
/*
|
|
* If a timeout occurs, and the SMTO_NOTIMEOUTIFNOTHUNG bit is set,
|
|
* and the app is still calling GetMessage(), then just try again.
|
|
* This probably means that the receiver has put up some UI in
|
|
* response to this message but the user hasn't completed the
|
|
* interaction yet.
|
|
*/
|
|
if (fTimeOut && pism && (pism->fuSend & SMTO_NOTIMEOUTIFNOTHUNG) &&
|
|
!FHungApp(ptiReceiver, CMSHUNGAPPTIMEOUT)) {
|
|
fTimeOut = FALSE;
|
|
}
|
|
}
|
|
|
|
UserAssert(ptiSender->cEnterCount > 0);
|
|
if (--ptiSender->cEnterCount == 0) {
|
|
KeSetKernelStackSwapEnable(bWasSwapEnabled);
|
|
}
|
|
|
|
/*
|
|
* The reply bit should always be set! (even if we timed out). That
|
|
* is because if we're recursed into intersendmsg, we're going to
|
|
* return to the first intersendmsg's call to SleepThread() - and
|
|
* it needs to return back to intersendmsgex to see if its sms
|
|
* has been replied to.
|
|
*/
|
|
SetWakeBit(ptiSender, QS_SMSREPLY);
|
|
|
|
/*
|
|
* Copy out captured data. If cbOutput != 0 we know
|
|
* that the output buffer is in user-mode address
|
|
* space.
|
|
*/
|
|
if (!fTimeOut && cbOutput) {
|
|
PBYTE pbOutput;
|
|
INT len;
|
|
|
|
/*
|
|
* Probe output buffer if it is in the user's address space
|
|
*/
|
|
|
|
pbOutput = (PBYTE)lParamSave;
|
|
try {
|
|
if(fString == NoString) {
|
|
RtlCopyMemory((PBYTE)pbOutput, psms->pvCapture,
|
|
cbOutput);
|
|
} else if(fString == IsAnsiString) {
|
|
len = strncpycch((LPSTR)pbOutput,(LPCSTR)psms->pvCapture,
|
|
cbOutput);
|
|
#if DBG
|
|
len--; //Length includes terminating NULL char
|
|
if(len != psms->lRet) {
|
|
RIPMSG0(RIP_WARNING,
|
|
"Length of the copied string being returned is diffrent from the actual string length");
|
|
}
|
|
#endif
|
|
} else { //IsUnicodeString
|
|
len = wcsncpycch((LPWSTR)pbOutput,(LPCWSTR)psms->pvCapture,
|
|
cbOutput/sizeof(WCHAR));
|
|
#if DBG
|
|
len--;
|
|
if(len != psms->lRet) {
|
|
RIPMSG0(RIP_WARNING,
|
|
"Length of the copied string being returned is diffrent from the actual string length");
|
|
}
|
|
#endif
|
|
}
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
|
|
/*
|
|
* Return 0 to indicate an error.
|
|
*/
|
|
psms->lRet = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* we now have the reply -- restore psmsSent and save the return value
|
|
*/
|
|
ptiSender->psmsSent = psmsSentSave;
|
|
|
|
if (pism == NULL) {
|
|
lRet = psms->lRet;
|
|
} else {
|
|
/*
|
|
* save the values off for a SendMesssageTimeOut
|
|
*/
|
|
*pism->lpdwResult = psms->lRet;
|
|
lRet = (!fTimeOut) ? TRUE : FALSE; /* do this to ensure ret is T or F... */
|
|
|
|
/*
|
|
* If we did timeout and no reply was received, rely on
|
|
* the receiver to free the sms.
|
|
*/
|
|
if (!(psms->flags & SMF_REPLY))
|
|
psms->flags |= SMF_REPLY | SMF_RECEIVERFREE;
|
|
}
|
|
|
|
/*
|
|
* If the reply came while the receiver is still processing
|
|
* the sms, force the receiver to free the sms. This can occur
|
|
* via timeout, ReplyMessage or journal cancel.
|
|
*/
|
|
if ((psms->flags & (SMF_RECEIVERBUSY | SMF_RECEIVEDMESSAGE)) != SMF_RECEIVEDMESSAGE) {
|
|
psms->flags |= SMF_RECEIVERFREE;
|
|
}
|
|
|
|
/*
|
|
* Unlink the SMS structure from both the SendMsg chain and gpsmsList
|
|
* list and free it. This sms could be anywhere in the chain.
|
|
*
|
|
* If the SMS was replied to by a thread other than the receiver
|
|
* (ie. through ReplyMessage()), we don't free the SMS because the
|
|
* receiver is still processing it and will free it when done.
|
|
*/
|
|
if ((psms->flags & SMF_RECEIVERFREE) == 0) {
|
|
UnlinkSendListSms(psms, NULL);
|
|
}
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* xxxReceiveMessage
|
|
*
|
|
* This function receives a message sent from another thread. Physically,
|
|
* it gets the message, calls the window proc and then cleans up the
|
|
* fsWakeBits and sms stuctures.
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
* 01-23-91 DavidPe Add xxxSendNotifyMessage() support.
|
|
* 07-14-92 ChrisBl Added xxxSendMessageCallback support.
|
|
\***********************************************************************/
|
|
|
|
VOID xxxReceiveMessage(
|
|
PTHREADINFO ptiReceiver)
|
|
{
|
|
PSMS psms;
|
|
PSMS psmsCurrentSave;
|
|
PTHREADINFO ptiSender;
|
|
LRESULT lRet = 0;
|
|
TL tlpwnd;
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* Get the SMS and unlink it from the list of SMSs we've received
|
|
*/
|
|
psms = ptiReceiver->psmsReceiveList;
|
|
|
|
/*
|
|
* This can be NULL because an SMS can be removed in our cleanup
|
|
* code without clearing the QS_SENDMESSAGE bit.
|
|
*/
|
|
if (psms == NULL) {
|
|
ptiReceiver->pcti->fsWakeBits &= ~QS_SENDMESSAGE;
|
|
ptiReceiver->pcti->fsChangeBits &= ~QS_SENDMESSAGE;
|
|
return;
|
|
}
|
|
|
|
ptiReceiver->psmsReceiveList = psms->psmsReceiveNext;
|
|
psms->psmsReceiveNext = NULL;
|
|
|
|
/*
|
|
* We've taken the SMS off the receive list - mark the SMS with this
|
|
* information - used during cleanup.
|
|
*/
|
|
psms->flags |= SMF_RECEIVERBUSY | SMF_RECEIVEDMESSAGE;
|
|
|
|
/*
|
|
* Clear QS_SENDMESSAGE wakebit if list is now empty
|
|
*/
|
|
if (ptiReceiver->psmsReceiveList == NULL) {
|
|
ptiReceiver->pcti->fsWakeBits &= ~QS_SENDMESSAGE;
|
|
ptiReceiver->pcti->fsChangeBits &= ~QS_SENDMESSAGE;
|
|
}
|
|
|
|
ptiSender = psms->ptiSender;
|
|
|
|
if (psms->flags & SMF_CB_REPLY) {
|
|
/*
|
|
* From SendMessageCallback REPLY to callback. We need to call
|
|
* the call back function to give the return value.
|
|
* Don't process any this message, just mechanism for notification
|
|
* the sender's thread lock is already gone, so we need to re-lock here.
|
|
*/
|
|
if (ptiSender == NULL) {
|
|
ThreadLock(psms->spwnd, &tlpwnd);
|
|
}
|
|
|
|
if (psms->flags & SMF_CB_CLIENT) {
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
/*
|
|
* Application-defined callback proc is neither Unicode nor ANSI
|
|
*/
|
|
SET_FLAG(ptiCurrent->pcti->CTIF_flags, CTIF_INCALLBACKMESSAGE);
|
|
CallClientProcA(psms->spwnd, psms->message, psms->dwData,
|
|
psms->lRet, (ULONG_PTR)psms->lpResultCallBack);
|
|
CLEAR_FLAG(ptiCurrent->pcti->CTIF_flags, CTIF_INCALLBACKMESSAGE);
|
|
} else {
|
|
psms->lpResultCallBack(HW(psms->spwnd), psms->message,
|
|
psms->dwData, psms->lRet);
|
|
}
|
|
|
|
if (ptiSender == NULL) {
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
} else if (!(psms->flags & (SMF_REPLY | SMF_SENDERDIED | SMF_RECEIVERDIED))) {
|
|
/*
|
|
* Don't process message if it has been replied to already or
|
|
* if the sending or receiving thread has died
|
|
*/
|
|
|
|
/*
|
|
* Set new psmsCurrent for this queue, saving the current one
|
|
*/
|
|
psmsCurrentSave = ptiReceiver->psmsCurrent;
|
|
ptiReceiver->psmsCurrent = psms;
|
|
SET_FLAG(ptiReceiver->pcti->CTIF_flags, CTIF_INSENDMESSAGE);
|
|
|
|
/*
|
|
* If this SMS originated from a xxxSendNotifyMessage() or a
|
|
* xxxSendMessageCallback() call, the sender's thread lock is
|
|
* already gone, so we need to re-lock here.
|
|
*/
|
|
if (ptiSender == NULL) {
|
|
ThreadLock(psms->spwnd, &tlpwnd);
|
|
}
|
|
|
|
if (psms->message == WM_HOOKMSG) {
|
|
union {
|
|
EVENTMSG emsg; // WH_JOURNALRECORD/PLAYBACK
|
|
MOUSEHOOKSTRUCTEX mhs; // WH_MOUSE
|
|
KBDLLHOOKSTRUCT kbds; // WH_KEYBORD_LL
|
|
MSLLHOOKSTRUCT mslls;// WH_MOUSE_LL
|
|
#ifdef REDIRECTION
|
|
HTHOOKSTRUCT ht; // WH_HITTEST
|
|
#endif // REDIRECTION
|
|
} LocalData;
|
|
PVOID pSendersData;
|
|
PHOOKMSGSTRUCT phkmp;
|
|
int iHook;
|
|
BOOL bAnsiHook;
|
|
|
|
/*
|
|
* Some hook types (eg: WH_JOURNALPLAYBACK) pass pointers to
|
|
* data in the calling thread's stack. We must copy this to our
|
|
* own (called thread's) stack for safety because of the way this
|
|
* "message" is handled and in case the calling thread dies. #13577
|
|
*
|
|
* Originally only WH_JOURNALRECORD and WH_JOURNALPLAYBACK went
|
|
* through this code, but now all sorts of hooks do.
|
|
*/
|
|
phkmp = (PHOOKMSGSTRUCT)psms->lParam;
|
|
pSendersData = (PVOID)(phkmp->lParam);
|
|
iHook = phkmp->phk->iHook;
|
|
|
|
switch (iHook) {
|
|
case WH_JOURNALRECORD:
|
|
case WH_JOURNALPLAYBACK:
|
|
if (pSendersData)
|
|
LocalData.emsg = *(PEVENTMSG)pSendersData;
|
|
break;
|
|
|
|
case WH_MOUSE:
|
|
if (pSendersData)
|
|
LocalData.mhs = *(LPMOUSEHOOKSTRUCTEX)pSendersData;
|
|
break;
|
|
|
|
case WH_KEYBOARD_LL:
|
|
if (pSendersData)
|
|
LocalData.kbds = *(LPKBDLLHOOKSTRUCT)pSendersData;
|
|
break;
|
|
|
|
case WH_MOUSE_LL:
|
|
if (pSendersData)
|
|
LocalData.mslls = *(LPMSLLHOOKSTRUCT)pSendersData;
|
|
break;
|
|
|
|
#ifdef REDIRECTION
|
|
case WH_HITTEST:
|
|
if (pSendersData)
|
|
LocalData.ht = *(LPHTHOOKSTRUCT)pSendersData;
|
|
break;
|
|
#endif // REDIRECTION
|
|
|
|
case WH_KEYBOARD:
|
|
case WH_SHELL:
|
|
/*
|
|
* Fall thru...
|
|
*/
|
|
pSendersData = NULL;
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* No pointers: wParam & lParam can be sent as is.
|
|
*/
|
|
RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "Receive hook %d", iHook);
|
|
pSendersData = NULL;
|
|
break;
|
|
}
|
|
|
|
|
|
lRet = xxxCallHook2(phkmp->phk, phkmp->nCode, psms->wParam,
|
|
pSendersData ? (LPARAM)&LocalData : phkmp->lParam, &bAnsiHook);
|
|
|
|
/*
|
|
* Copy back data only if the sender hasn't died or timed out
|
|
* (timed out messages are marked SMF_REPLY by the sending thread)
|
|
*/
|
|
if (!(psms->flags & (SMF_SENDERDIED|SMF_REPLY)) && pSendersData) {
|
|
switch (iHook) {
|
|
case WH_JOURNALRECORD:
|
|
case WH_JOURNALPLAYBACK:
|
|
*(PEVENTMSG)pSendersData = LocalData.emsg;
|
|
break;
|
|
|
|
case WH_KEYBOARD_LL:
|
|
*(LPKBDLLHOOKSTRUCT)pSendersData = LocalData.kbds;
|
|
break;
|
|
|
|
case WH_MOUSE_LL:
|
|
*(LPMSLLHOOKSTRUCT)pSendersData = LocalData.mslls;
|
|
break;
|
|
|
|
case WH_MOUSE:
|
|
*(LPMOUSEHOOKSTRUCTEX)pSendersData = LocalData.mhs;
|
|
break;
|
|
|
|
#ifdef REDIRECTION
|
|
case WH_HITTEST:
|
|
*(LPHTHOOKSTRUCT)pSendersData = LocalData.ht;
|
|
break;
|
|
#endif // REDIRECTION
|
|
}
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* Call WH_CALLWNDPROC if it's installed and the window is not marked
|
|
* as destroyed.
|
|
*/
|
|
if (IsHooked(ptiReceiver, WHF_CALLWNDPROC)) {
|
|
CWPSTRUCTEX cwps;
|
|
|
|
cwps.hwnd = HW(psms->spwnd);
|
|
cwps.message = psms->message;
|
|
cwps.wParam = psms->wParam;
|
|
cwps.lParam = psms->lParam;
|
|
cwps.psmsSender = psms;
|
|
|
|
xxxCallHook(HC_ACTION, TRUE, (LPARAM)&cwps, WH_CALLWNDPROC);
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents. If this behavior reverts to
|
|
* Win3.1 semantics, we will need to copy the new parameters
|
|
* from cwps.
|
|
*/
|
|
}
|
|
|
|
if (!(psms->flags & (SMF_REPLY | SMF_SENDERDIED | SMF_RECEIVERDIED)) &&
|
|
psms->spwnd != NULL) {
|
|
if (TestWF(psms->spwnd, WFSERVERSIDEPROC)) {
|
|
TL tlpwndKernel;
|
|
|
|
ThreadLock(psms->spwnd, &tlpwndKernel);
|
|
/*
|
|
* If this window's proc is meant to be executed from the server side
|
|
* we'll just stay inside the semaphore and call it directly. Note
|
|
* how we don't convert the pwnd into an hwnd before calling the proc.
|
|
*/
|
|
lRet = psms->spwnd->lpfnWndProc(psms->spwnd, psms->message,
|
|
psms->wParam, psms->lParam);
|
|
|
|
ThreadUnlock(&tlpwndKernel);
|
|
} else {
|
|
/*
|
|
* Call the client or xxxDefWindowProc.
|
|
*/
|
|
xxxSendMessageToClient(psms->spwnd, psms->message, psms->wParam, psms->lParam,
|
|
psms, TRUE, &lRet);
|
|
}
|
|
|
|
/*
|
|
* Call WH_CALLWNDPROCRET if it's installed.
|
|
*/
|
|
if (IsHooked(ptiReceiver, WHF_CALLWNDPROCRET) &&
|
|
!(psms->flags & SMF_SENDERDIED)) {
|
|
CWPRETSTRUCTEX cwps;
|
|
|
|
cwps.hwnd = HW(psms->spwnd);
|
|
cwps.message = psms->message;
|
|
cwps.wParam = psms->wParam;
|
|
cwps.lParam = psms->lParam;
|
|
cwps.lResult = lRet;
|
|
cwps.psmsSender = psms;
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents.
|
|
*/
|
|
xxxCallHook(HC_ACTION, TRUE, (LPARAM)&cwps, WH_CALLWNDPROCRET);
|
|
|
|
/*
|
|
* Unlike Win3.1, NT and Win95 ignore any changes the app makes
|
|
* to the CWPSTRUCT contents. If this behavior reverts to
|
|
* Win3.1 semantics, we will need to copy the new parameters
|
|
* from cwps.
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((psms->flags & (SMF_CB_REQUEST | SMF_REPLY)) == SMF_CB_REQUEST) {
|
|
|
|
/*
|
|
* From SendMessageCallback REQUEST callback. Send the message
|
|
* back with a the REPLY value.
|
|
*/
|
|
INTRSENDMSGEX ism;
|
|
|
|
psms->flags |= SMF_REPLY;
|
|
|
|
if (!(psms->flags & SMF_SENDERDIED)) {
|
|
ism.fuCall = ISM_CALLBACK | ISM_REPLY;
|
|
if (psms->flags & SMF_CB_CLIENT)
|
|
ism.fuCall |= ISM_CB_CLIENT;
|
|
ism.lpResultCallBack = psms->lpResultCallBack;
|
|
ism.dwData = psms->dwData;
|
|
ism.lRet = lRet;
|
|
|
|
xxxInterSendMsgEx(psms->spwnd, psms->message, 0L, 0L,
|
|
NULL, psms->ptiCallBackSender, &ism );
|
|
}
|
|
}
|
|
|
|
if (ptiSender == NULL) {
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
/*
|
|
* Restore receiver's original psmsCurrent.
|
|
*/
|
|
ptiReceiver->psmsCurrent = psmsCurrentSave;
|
|
SET_OR_CLEAR_FLAG(ptiReceiver->pcti->CTIF_flags,
|
|
CTIF_INSENDMESSAGE,
|
|
ptiReceiver->psmsCurrent);
|
|
|
|
#ifdef DEBUG_SMS
|
|
ValidateSmsSendLists(psmsCurrentSave);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* We're done with this sms, so the appropriate thread
|
|
* can now free it.
|
|
*/
|
|
psms->flags &= ~SMF_RECEIVERBUSY;
|
|
|
|
/*
|
|
* Free the sms and return without reply if the
|
|
* SMF_RECEIVERFREE bit is set. Handily, this does just what we
|
|
* want for xxxSendNotifyMessage() since we set SMF_RECEIVERFREE
|
|
* in that case.
|
|
*/
|
|
if (psms->flags & SMF_RECEIVERFREE) {
|
|
UnlinkSendListSms(psms, NULL);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Set reply flag and return value if this message has not already
|
|
* been replied to with ReplyMessage().
|
|
*/
|
|
if (!(psms->flags & SMF_REPLY)) {
|
|
psms->lRet = lRet;
|
|
psms->flags |= SMF_REPLY;
|
|
|
|
/*
|
|
* Tell the sender, the reply is done
|
|
*/
|
|
if (ptiSender != NULL) {
|
|
/*
|
|
* Wake up the sender thread.
|
|
*/
|
|
SetWakeBit(ptiSender, QS_SMSREPLY);
|
|
|
|
/*
|
|
* We have 4 conditions to satisfy:
|
|
*
|
|
* 16 - 16 : yielding required, if sender is waiting for this reply
|
|
* 32 - 16 : yielding required, if sender is waiting for this reply
|
|
* 16 - 32 : no yielding required
|
|
* 32 - 32 : No yielding required.
|
|
*/
|
|
|
|
if (ptiSender->TIF_flags & TIF_16BIT || ptiReceiver->TIF_flags & TIF_16BIT) {
|
|
DirectedScheduleTask(ptiReceiver, ptiSender, FALSE, psms);
|
|
if (ptiReceiver->TIF_flags & TIF_16BIT &&
|
|
ptiSender->psmsSent == psms)
|
|
{
|
|
xxxSleepTask(TRUE, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* SendMsgCleanup
|
|
*
|
|
* This function cleans up sendmessage structures when the thread associated
|
|
* with a queue terminates. In the following, S is the sending thread,
|
|
* R the receiving thread.
|
|
*
|
|
* Case Table:
|
|
*
|
|
* single death:
|
|
* R no reply, S dies: mark that S died, R will free sms
|
|
* R no reply, R dies: fake reply for S
|
|
* R replied, S dies: free sms
|
|
* R replied, R dies: no problem
|
|
*
|
|
* double death:
|
|
* R no reply, S dies, R dies: free sms
|
|
* R no reply, R dies, S dies: free sms
|
|
* R replied, S dies, R dies: sms freed when S dies, as in single death
|
|
* R replied, R dies, S dies: sms freed when S dies, as in single death
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
\***********************************************************************/
|
|
|
|
VOID SendMsgCleanup(
|
|
PTHREADINFO ptiCurrent)
|
|
{
|
|
PSMS *ppsms;
|
|
PSMS psmsNext;
|
|
|
|
CheckCritIn();
|
|
|
|
for (ppsms = &gpsmsList; *ppsms; ) {
|
|
psmsNext = (*ppsms)->psmsNext;
|
|
|
|
if ((*ppsms)->ptiSender == ptiCurrent ||
|
|
(*ppsms)->ptiCallBackSender == ptiCurrent) {
|
|
SenderDied(*ppsms, ppsms);
|
|
} else if ((*ppsms)->ptiReceiver == ptiCurrent) {
|
|
ReceiverDied(*ppsms, ppsms);
|
|
}
|
|
|
|
/*
|
|
* If the message was not unlinked, go to the next one.
|
|
*/
|
|
if (*ppsms != psmsNext)
|
|
ppsms = &(*ppsms)->psmsNext;
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* ClearSendMessages
|
|
*
|
|
* This function marks messages destined for a given window as invalid.
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
\***********************************************************************/
|
|
|
|
VOID ClearSendMessages(
|
|
PWND pwnd)
|
|
{
|
|
PSMS psms, psmsNext;
|
|
PSMS *ppsms;
|
|
|
|
CheckCritIn();
|
|
|
|
psms = gpsmsList;
|
|
while (psms != NULL) {
|
|
/*
|
|
* Grab the next one beforehand in case we free the current one.
|
|
*/
|
|
psmsNext = psms->psmsNext;
|
|
|
|
if (psms->spwnd == pwnd) {
|
|
|
|
/*
|
|
* If the sender has died, then mark this receiver free so the
|
|
* receiver will destroy it in its processing.
|
|
*/
|
|
if (psms->flags & SMF_SENDERDIED) {
|
|
psms->flags |= SMF_REPLY | SMF_RECEIVERFREE;
|
|
} else {
|
|
/*
|
|
* The sender is alive. If the receiver hasn't replied to
|
|
* this yet, make a reply so the sender gets it. Make sure
|
|
* the receiver is the one free it so we don't have a race
|
|
* condition.
|
|
*/
|
|
if (!(psms->flags & SMF_REPLY)) {
|
|
|
|
/*
|
|
* The sms is either still on the receive list
|
|
* or is currently being received. Since the sender
|
|
* is alive, we want the sender to get the reply
|
|
* to this SMS. If it hasn't been received, take
|
|
* it off the receive list and reply to it. If it
|
|
* has been received, then just leave it alone:
|
|
* it'll get replied to normally.
|
|
*/
|
|
if (psms->flags & SMF_CB_REQUEST) {
|
|
/*
|
|
* From SendMessageCallback REQUEST callback. Send the
|
|
* message back with a the REPLY value.
|
|
*/
|
|
TL tlpwnd;
|
|
INTRSENDMSGEX ism;
|
|
|
|
psms->flags |= SMF_REPLY;
|
|
|
|
ism.fuCall = ISM_CALLBACK | ISM_REPLY;
|
|
if (psms->flags & SMF_CB_CLIENT)
|
|
ism.fuCall |= ISM_CB_CLIENT;
|
|
ism.lpResultCallBack = psms->lpResultCallBack;
|
|
ism.dwData = psms->dwData;
|
|
ism.lRet = 0L; /* null return */
|
|
|
|
ThreadLock(psms->spwnd, &tlpwnd);
|
|
|
|
xxxInterSendMsgEx(psms->spwnd, psms->message, 0L, 0L,
|
|
NULL, psms->ptiCallBackSender, &ism );
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
} else if (!(psms->flags & SMF_RECEIVERBUSY)) {
|
|
/*
|
|
* If there is no sender, this is a notification
|
|
* message (nobody to reply to). In this case,
|
|
* just set the SMF_REPLY bit (SMF_RECEIVERFREE
|
|
* is already set) and this'll cause ReceiveMessage
|
|
* to just free this SMS and return.
|
|
*/
|
|
if (psms->ptiSender == NULL) {
|
|
psms->flags |= SMF_REPLY;
|
|
} else {
|
|
/*
|
|
* There is a sender, and it wants a reply: take
|
|
* this SMS off the receive list, and reply
|
|
* to the sender.
|
|
*/
|
|
for (ppsms = &(psms->ptiReceiver->psmsReceiveList);
|
|
*ppsms != NULL;
|
|
ppsms = &((*ppsms)->psmsReceiveNext)) {
|
|
|
|
if (*ppsms == psms) {
|
|
*ppsms = psms->psmsReceiveNext;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Reply to this message so the sender
|
|
* wakes up.
|
|
*/
|
|
psms->flags |= SMF_REPLY;
|
|
psms->lRet = 0;
|
|
psms->psmsReceiveNext = NULL;
|
|
SetWakeBit(psms->ptiSender, QS_SMSREPLY);
|
|
|
|
/*
|
|
* 16 bit senders need to be notifed that sends completed
|
|
* otherwise it may wait for a very long time for the reply.
|
|
*/
|
|
if (psms->ptiSender->TIF_flags & TIF_16BIT) {
|
|
DirectedScheduleTask(psms->ptiReceiver, psms->ptiSender, FALSE, psms);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Unlock the pwnd from the SMS structure.
|
|
*/
|
|
Unlock(&psms->spwnd);
|
|
}
|
|
|
|
psms = psmsNext;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************\
|
|
* ReceiverDied
|
|
*
|
|
* This function cleans up the send message structures after a message
|
|
* receiver window or queue has died. It fakes a reply if one has not
|
|
* already been sent and the sender has not died. It frees the sms if
|
|
* the sender has died.
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
\***********************************************************************/
|
|
|
|
VOID ReceiverDied(
|
|
PSMS psms,
|
|
PSMS *ppsmsUnlink)
|
|
{
|
|
PSMS *ppsms;
|
|
PTHREADINFO ptiReceiver;
|
|
PTHREADINFO ptiSender;
|
|
|
|
/*
|
|
* mark that the receiver died
|
|
*/
|
|
ptiReceiver = psms->ptiReceiver;
|
|
psms->ptiReceiver = NULL;
|
|
psms->flags |= SMF_RECEIVERDIED;
|
|
|
|
/*
|
|
* Unlink sms from thread if it is not dying. We need to do
|
|
* this for journal cleanup.
|
|
*/
|
|
if (!(ptiReceiver->TIF_flags & TIF_INCLEANUP)) {
|
|
|
|
/*
|
|
* unlink sms from the receiver's receive list
|
|
*/
|
|
for (ppsms = &(ptiReceiver->psmsReceiveList); *ppsms != NULL;
|
|
ppsms = &((*ppsms)->psmsReceiveNext)) {
|
|
|
|
if (*ppsms == psms) {
|
|
*ppsms = psms->psmsReceiveNext;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* clear the QS_SENDMESSAGE bit if there are no more messages
|
|
*/
|
|
if (ptiReceiver->psmsReceiveList == NULL) {
|
|
ptiReceiver->pcti->fsWakeBits &= ~QS_SENDMESSAGE;
|
|
ptiReceiver->pcti->fsChangeBits &= ~QS_SENDMESSAGE;
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* The receiver thread is dying. Clear the received flag
|
|
* so that if there is a sender, it will free the sms.
|
|
*/
|
|
psms->flags &= ~SMF_RECEIVERBUSY;
|
|
}
|
|
|
|
psms->psmsReceiveNext = NULL;
|
|
|
|
/*
|
|
* Check if the sender died or if the receiver was marked to
|
|
* free the sms.
|
|
*/
|
|
if (psms->ptiSender == NULL) {
|
|
|
|
if (!(psms->flags & SMF_SENDERDIED) &&
|
|
(psms->flags & (SMF_CB_REQUEST | SMF_REPLY)) == SMF_CB_REQUEST) {
|
|
|
|
/*
|
|
* From SendMessageCallback REQUEST callback. Send the message
|
|
* back with a the REPLY value.
|
|
*/
|
|
TL tlpwnd;
|
|
INTRSENDMSGEX ism;
|
|
|
|
psms->flags |= SMF_REPLY;
|
|
|
|
ism.fuCall = ISM_CALLBACK | ISM_REPLY;
|
|
if (psms->flags & SMF_CB_CLIENT)
|
|
ism.fuCall |= ISM_CB_CLIENT;
|
|
ism.lpResultCallBack = psms->lpResultCallBack;
|
|
ism.dwData = psms->dwData;
|
|
ism.lRet = 0L; /* null return */
|
|
|
|
ThreadLock(psms->spwnd, &tlpwnd);
|
|
|
|
xxxInterSendMsgEx(psms->spwnd, psms->message, 0L, 0L,
|
|
NULL, psms->ptiCallBackSender, &ism );
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
/*
|
|
* If the receiver is not processing the message, free it.
|
|
*/
|
|
if (!(psms->flags & SMF_RECEIVERBUSY))
|
|
UnlinkSendListSms(psms, ppsmsUnlink);
|
|
return;
|
|
|
|
} else if (!(psms->flags & SMF_REPLY)) {
|
|
|
|
/*
|
|
* fake a reply
|
|
*/
|
|
psms->flags |= SMF_REPLY;
|
|
psms->lRet = 0;
|
|
psms->ptiReceiver = NULL;
|
|
|
|
/*
|
|
* wake the sender if he was waiting for us
|
|
*/
|
|
SetWakeBit(psms->ptiSender, QS_SMSREPLY);
|
|
} else {
|
|
/*
|
|
* There is a reply. We know the receiver is dying, so clear the
|
|
* SMF_RECEIVERFREE bit or the sender won't free this SMS!
|
|
* Although the sender's wake bit has already been set by the
|
|
* call to ClearSendMessages() earlier in the cleanup code,
|
|
* set it here again for safety.
|
|
*
|
|
* ??? Why would SMF_RECEIVERFREE be set?
|
|
*/
|
|
psms->flags &= ~SMF_RECEIVERFREE;
|
|
SetWakeBit(psms->ptiSender, QS_SMSREPLY);
|
|
}
|
|
|
|
/*
|
|
* If the sender is a WOW task, that task is now blocked in the non-
|
|
* preemptive scheduler waiting for a reply. DestroyTask will
|
|
* clean this up (even if ptiReceiver is 32-bit).
|
|
*/
|
|
ptiSender = psms->ptiSender;
|
|
if (ptiSender->TIF_flags & TIF_16BIT) {
|
|
DirectedScheduleTask(ptiReceiver, ptiSender, FALSE, psms);
|
|
}
|
|
|
|
/*
|
|
* Unlock this window from the sms: it is no longer needed, and will get
|
|
* rid of lock warnings.
|
|
*/
|
|
Unlock(&psms->spwnd);
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* SenderDied
|
|
*
|
|
* This function cleans up the send message structures after a message
|
|
* sender has died.
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
\***********************************************************************/
|
|
|
|
VOID SenderDied(
|
|
PSMS psms,
|
|
PSMS *ppsmsUnlink)
|
|
{
|
|
PTHREADINFO ptiSender;
|
|
BOOL fReply = FALSE;
|
|
|
|
/*
|
|
* mark the death
|
|
*/
|
|
if (psms->ptiSender != NULL)
|
|
ptiSender = psms->ptiSender;
|
|
else
|
|
ptiSender = psms->ptiCallBackSender;
|
|
psms->ptiSender = NULL;
|
|
psms->flags |= SMF_SENDERDIED;
|
|
|
|
/*
|
|
* There are two cases where we leave the sms alone so the receiver
|
|
* can handle the message and then free the sms itself.
|
|
*
|
|
* 1. When the receiver is processing the message.
|
|
*
|
|
* 2. When the message has not yet been received.
|
|
*/
|
|
|
|
/*
|
|
* If the receiver is processing the message, make it free the sms.
|
|
* Fake a reply for journal cancel.
|
|
*/
|
|
if (psms->flags & SMF_RECEIVERBUSY) {
|
|
psms->flags |= SMF_RECEIVERFREE;
|
|
fReply = TRUE;
|
|
}
|
|
|
|
/*
|
|
* This sms may be in the process of being sent, but has not yet
|
|
* been received. In so, fake a reply and wake the sender.
|
|
* The last thread to touch the sms, either the sender or
|
|
* receiver, will free the sms.
|
|
*/
|
|
if (ptiSender->psmsSent == psms)
|
|
fReply = TRUE;
|
|
|
|
/*
|
|
* If journalling is being cancelled and reply needs to be made,
|
|
* fake a reply and return.
|
|
*/
|
|
if (!(ptiSender->TIF_flags & TIF_INCLEANUP) && fReply) {
|
|
|
|
/*
|
|
* fake a reply
|
|
*/
|
|
psms->flags |= SMF_REPLY;
|
|
psms->lRet = 0;
|
|
|
|
/*
|
|
* wake the sender if he was waiting for us
|
|
*/
|
|
SetWakeBit(ptiSender, QS_SMSREPLY);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the receiver isn't dead, check to see if it has honestly replied to
|
|
* this SMS. If it has not replied, leave it alone so the receiver can
|
|
* reply to it (it'll then clean it up). If it has replied, then it's
|
|
* ok to free it.
|
|
*
|
|
* It is also ok to free it if the receiver is dead.
|
|
*/
|
|
if ((psms->flags & SMF_RECEIVERDIED) ||
|
|
(psms->flags & (SMF_REPLY | SMF_RECEIVERFREE)) == SMF_REPLY) {
|
|
UnlinkSendListSms(psms, ppsmsUnlink);
|
|
} else {
|
|
psms->flags |= SMF_RECEIVERFREE;
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* UnlinkSendListSms
|
|
*
|
|
* This function unlinks an sms structure from both its SendMsg chain and
|
|
* the global gpsmsList and frees it.
|
|
*
|
|
* History:
|
|
* 01-13-91 DavidPe Ported.
|
|
\***********************************************************************/
|
|
|
|
VOID UnlinkSendListSms(
|
|
PSMS psms,
|
|
PSMS *ppsmsUnlink)
|
|
{
|
|
#if DBG
|
|
PSMS psmsT;
|
|
BOOL fUpdateSendList;
|
|
PSMS *ppsms;
|
|
#endif
|
|
|
|
CheckCritIn();
|
|
|
|
#ifdef DEBUG_SMS
|
|
ValidateSmsSendLists(psms);
|
|
#endif
|
|
|
|
UserAssert(psms->psmsReceiveNext == NULL);
|
|
|
|
#if DBG
|
|
/*
|
|
* Remember ahead of time if the psms we're unlinking is also the
|
|
* head of the sms send list (so we know if we need to update this field
|
|
* member in every SMS in this list).
|
|
*/
|
|
fUpdateSendList = (psms == psms->psmsSendList);
|
|
|
|
/*
|
|
* Unlink sms from the sendlist chain. This effectively unlinks the SMS
|
|
* and updates psms->psmsSendList with the right head....
|
|
*/
|
|
ppsms = &(psms->psmsSendList);
|
|
while (*ppsms != NULL) {
|
|
if (*ppsms == psms) {
|
|
*ppsms = psms->psmsSendNext;
|
|
break;
|
|
}
|
|
ppsms = &(*ppsms)->psmsSendNext;
|
|
}
|
|
|
|
/*
|
|
* Update psmsSendList if necessary. psms->psmsSendList has been updated
|
|
* with the right sms send list head... distribute this head to all other
|
|
* sms's in this chain if this sms we're removing the current head.
|
|
*/
|
|
if (fUpdateSendList) {
|
|
for (psmsT = psms->psmsSendList; psmsT != NULL;
|
|
psmsT = psmsT->psmsSendNext) {
|
|
psmsT->psmsSendList = psms->psmsSendList;
|
|
}
|
|
}
|
|
|
|
psms->psmsSendList = NULL;
|
|
#endif
|
|
|
|
/*
|
|
* This unlinks an sms structure from the global gpsmsList and frees it.
|
|
*/
|
|
if (ppsmsUnlink == NULL) {
|
|
ppsmsUnlink = &gpsmsList;
|
|
|
|
while (*ppsmsUnlink && (*ppsmsUnlink != psms)) {
|
|
ppsmsUnlink = &((*ppsmsUnlink)->psmsNext);
|
|
}
|
|
}
|
|
|
|
UserAssert(*ppsmsUnlink);
|
|
|
|
*ppsmsUnlink = psms->psmsNext;
|
|
|
|
Unlock(&psms->spwnd);
|
|
|
|
#if DBG
|
|
UserAssert(!(psms == psms->psmsSendList && psms->psmsSendNext != NULL));
|
|
#endif
|
|
|
|
if (psms->pvCapture)
|
|
UserFreePool(psms->pvCapture);
|
|
|
|
FreeSMS(psms);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSendSizeMessages
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* 10-19-90 darrinm Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
void xxxSendSizeMessage(
|
|
PWND pwnd,
|
|
UINT cmdSize)
|
|
{
|
|
RECT rc;
|
|
CheckLock(pwnd);
|
|
|
|
// Added by Chicago: HACK ALERT:
|
|
// If the window is minimized then the real client width and height are
|
|
// zero. But, in win3.1 they were non-zero. Under Chicago, PrintShop
|
|
// Deluxe ver 1.2 hits a divide by zero. To fix this we fake the width
|
|
// and height for old apps to be non-zero values.
|
|
// GetClientRect does that job for us.
|
|
_GetClientRect(pwnd, &rc);
|
|
|
|
xxxSendMessage(pwnd, WM_SIZE, cmdSize,
|
|
MAKELONG(rc.right - rc.left, rc.bottom - rc.top));
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxProcessAsyncSendMessage
|
|
*
|
|
* Processes an event message posted by xxxSystemBroadcastMessage by
|
|
* sending a message to the window stored in the event.
|
|
*
|
|
* History:
|
|
* 05-12-94 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxProcessAsyncSendMessage(
|
|
PASYNCSENDMSG pmsg)
|
|
{
|
|
PWND pwnd;
|
|
TL tlpwndT;
|
|
WCHAR awchString[MAX_PATH];
|
|
ATOM Atom = 0;
|
|
LARGE_UNICODE_STRING str;
|
|
|
|
pwnd = RevalidateHwnd(pmsg->hwnd);
|
|
if (pwnd != NULL) {
|
|
ThreadLockAlways(pwnd, &tlpwndT);
|
|
switch (pmsg->message) {
|
|
case WM_WININICHANGE:
|
|
case WM_DEVMODECHANGE:
|
|
if (pmsg->lParam) {
|
|
if (UserGetAtomName((ATOM)pmsg->lParam, awchString, sizeof(awchString))) {
|
|
Atom = (ATOM)pmsg->lParam;
|
|
RtlInitLargeUnicodeString(&str, awchString, (UINT)-1);
|
|
pmsg->lParam = (LPARAM)&str;
|
|
} else {
|
|
UserAssert(FALSE);
|
|
pmsg->lParam = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
xxxSendMessage(pwnd, pmsg->message, pmsg->wParam, pmsg->lParam);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
if (Atom) {
|
|
UserDeleteAtom(Atom);
|
|
}
|
|
UserFreePool(pmsg);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxBroadcastMessage
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* 02-21-91 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
LONG xxxBroadcastMessage(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
UINT wCmd,
|
|
PBROADCASTMSG pbcm)
|
|
{
|
|
PBWL pbwl;
|
|
HWND *phwnd;
|
|
TL tlpwnd;
|
|
PASYNCSENDMSG pmsg;
|
|
PPROCESSINFO ppiCurrent;
|
|
LONG lRet = TRUE;
|
|
TL tlPool;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
BOOL fPrivateMessage = (message >= WM_USER) && (message < MAXINTATOM);
|
|
|
|
if (fPrivateMessage) {
|
|
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Attempt to broadcast a private message");
|
|
}
|
|
|
|
if (pwnd == NULL) {
|
|
LARGE_UNICODE_STRING str;
|
|
PLARGE_STRING pstr;
|
|
|
|
/*
|
|
* Handle special system-wide broadcasts.
|
|
*/
|
|
switch (message) {
|
|
case WM_SPOOLERSTATUS:
|
|
xxxSystemBroadcastMessage(message, wParam, lParam, wCmd, pbcm);
|
|
return 1;
|
|
|
|
case WM_WININICHANGE:
|
|
case WM_DEVMODECHANGE:
|
|
|
|
/*
|
|
* Probe and capture the string.
|
|
*/
|
|
if (lParam) {
|
|
UINT cbAlloc;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Allocate a temp buffer and convert
|
|
* the string to Unicode
|
|
*/
|
|
pstr = ((PLARGE_STRING)lParam);
|
|
if (pstr->bAnsi)
|
|
cbAlloc = (pstr->Length + 1) * sizeof(WCHAR);
|
|
else
|
|
cbAlloc = pstr->Length + sizeof(WCHAR);
|
|
str.Buffer = UserAllocPoolWithQuota(cbAlloc, TAG_SMS_STRING);
|
|
if (str.Buffer == NULL) {
|
|
return 0;
|
|
}
|
|
str.MaximumLength = cbAlloc;
|
|
str.bAnsi = FALSE;
|
|
try {
|
|
if (pstr->bAnsi) {
|
|
Status = RtlMultiByteToUnicodeN(
|
|
(PWCH)str.Buffer,
|
|
cbAlloc,
|
|
&cbAlloc,
|
|
(PCH)pstr->Buffer,
|
|
pstr->Length
|
|
);
|
|
str.Length = cbAlloc;
|
|
} else {
|
|
str.Length = pstr->Length;
|
|
RtlCopyMemory(str.Buffer, pstr->Buffer, str.Length);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
str.Buffer[str.Length / sizeof(WCHAR)] = 0;
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
UserFreePool(str.Buffer);
|
|
return 0;
|
|
}
|
|
pstr->Buffer = str.Buffer;
|
|
}
|
|
if (lParam) {
|
|
ThreadLockPool(ptiCurrent, str.Buffer, &tlPool);
|
|
}
|
|
xxxSystemBroadcastMessage(message, wParam,
|
|
lParam ? (LPARAM)&str : 0, wCmd, pbcm);
|
|
if (lParam)
|
|
ThreadUnlockAndFreePool(ptiCurrent, &tlPool);
|
|
return 1;
|
|
|
|
case WM_TIMECHANGE:
|
|
/*
|
|
* We automatically broadcast a WM_TIMECHANGE message whenever the
|
|
* kernel tells us the time has changed, so blow off any apps who
|
|
* are trying to do the same thing.
|
|
*/
|
|
if (!(ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD)) {
|
|
RIPMSG0(RIP_WARNING, "Only system should broadcast WM_TIMECHANGE");
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
UserAssert(ptiCurrent->rpdesk);
|
|
|
|
pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd;
|
|
|
|
if (pwnd == NULL) {
|
|
RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "sender must have an associated desktop");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
pbwl = BuildHwndList(pwnd->spwndChild, BWL_ENUMLIST, NULL);
|
|
if (pbwl == NULL)
|
|
return 0;
|
|
|
|
ppiCurrent = PpiCurrent();
|
|
|
|
for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
|
|
|
|
/*
|
|
* Make sure this hwnd is still around.
|
|
*/
|
|
if ((pwnd = RevalidateHwnd(*phwnd)) == NULL)
|
|
continue;
|
|
|
|
/*
|
|
* 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.
|
|
|
|
/*
|
|
* Don't bother sending palette messages to windows that are not
|
|
* visible on threads that are not palette aware.
|
|
*/
|
|
if ((message == WM_PALETTEISCHANGING || message == WM_PALETTECHANGED) &&
|
|
!TestWF(pwnd, WFVISIBLE) &&
|
|
!(GETPTI(pwnd)->TIF_flags & TIF_PALETTEAWARE)) {
|
|
continue;
|
|
}
|
|
|
|
ThreadLockAlways(pwnd, &tlpwnd);
|
|
|
|
switch (wCmd) {
|
|
case BMSG_SENDMSG:
|
|
xxxSendMessage(pwnd, message, wParam, lParam);
|
|
break;
|
|
|
|
case BMSG_SENDNOTIFYMSG:
|
|
{
|
|
ATOM Atom = 0;
|
|
|
|
switch (message) {
|
|
case WM_WININICHANGE:
|
|
case WM_DEVMODECHANGE:
|
|
if (lParam) {
|
|
PLARGE_STRING pstr = (PLARGE_STRING)lParam;
|
|
|
|
/*
|
|
* Convert strings to atoms for the post.
|
|
*/
|
|
if (pstr)
|
|
Atom = UserAddAtom(pstr->Buffer, FALSE);
|
|
if (!Atom) {
|
|
lRet = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* These messages need to be able to cross
|
|
* desktops so PostEvent 'em.
|
|
*/
|
|
pmsg = UserAllocPool(sizeof(ASYNCSENDMSG),
|
|
TAG_SMS_ASYNC);
|
|
if (pmsg == NULL) {
|
|
goto CleanupAtom;
|
|
}
|
|
|
|
pmsg->hwnd = *phwnd;
|
|
pmsg->message = message;
|
|
pmsg->wParam = wParam;
|
|
pmsg->lParam = Atom;
|
|
|
|
if (!PostEventMessage(GETPTI(pwnd), GETPTI(pwnd)->pq,
|
|
QEVENT_ASYNCSENDMSG,NULL, 0,
|
|
(WPARAM)pmsg, 0)) {
|
|
|
|
UserFreePool(pmsg);
|
|
CleanupAtom:
|
|
if (Atom) {
|
|
UserDeleteAtom(Atom);
|
|
}
|
|
lRet = FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* A regular kind of guy. No desktop crossing.
|
|
*/
|
|
xxxSendNotifyMessage(pwnd, message, wParam, lParam);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BMSG_SENDNOTIFYMSGPROCESS:
|
|
UserAssert(message != WM_WININICHANGE && message != WM_DEVMODECHANGE);
|
|
|
|
/*
|
|
* Intra-process messages are synchronous; 22238.
|
|
* WM_PALETTECHANGED was being sent after the WM_DESTROY
|
|
* but console thread must not be synchronous.
|
|
*/
|
|
if ((GETPTI(pwnd)->ppi == ppiCurrent) && !(GETPTI(pwnd)->TIF_flags & TIF_CSRSSTHREAD)) {
|
|
xxxSendMessage(pwnd, message, wParam, lParam);
|
|
} else {
|
|
xxxSendNotifyMessage(pwnd, message, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case BMSG_POSTMSG:
|
|
/*
|
|
* Don't broadcast-post to owned windows (Win3.1 compatiblilty)
|
|
*/
|
|
if (pwnd->spwndOwner == NULL)
|
|
_PostMessage(pwnd, message, wParam, lParam);
|
|
break;
|
|
|
|
case BMSG_SENDMSGCALLBACK:
|
|
xxxSendMessageCallback(pwnd, message, wParam, lParam,
|
|
pbcm->cb.lpResultCallBack, pbcm->cb.dwData, pbcm->cb.bClientRequest);
|
|
break;
|
|
|
|
case BMSG_SENDMSGTIMEOUT:
|
|
xxxSendMessageTimeout(pwnd, message, wParam, lParam,
|
|
pbcm->to.fuFlags, pbcm->to.uTimeout, pbcm->to.lpdwResult);
|
|
break;
|
|
}
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
FreeHwndList(pbwl);
|
|
|
|
/*
|
|
* Excel-Solver 3.0 expects a non-zero return value from a
|
|
* SendMessage(-1,WM_DDE_INITIATE,....); Because, we had
|
|
* FFFE_FARFRAME in 3.0, the DX register at this point always had
|
|
* a value of 0x102; But, because we removed it under Win3.1, we get
|
|
* a zero value in ax and dx; This makes solver think that the DDE has
|
|
* failed. So, to support the existing SOLVER, we make dx nonzero.
|
|
* Fix for Bug #6005 -- SANKAR -- 05-16-91 --
|
|
*/
|
|
return 1;
|
|
}
|