/****************************** 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 #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; }