Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

hooks.c

Go to the documentation of this file.
00001 /****************************** Module Header ******************************\ 00002 * Module Name: hooks.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * This module contains the user hook APIs and support routines. 00007 * 00008 * History: 00009 * 01-28-91 DavidPe Created. 00010 * 08 Feb 1992 IanJa Unicode/ANSI aware & neutral 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 /* 00017 * This table is used to determine whether a particular hook 00018 * can be set for the system or a task, and other hook-ID specific things. 00019 */ 00020 #define HKF_SYSTEM 0x01 00021 #define HKF_TASK 0x02 00022 #define HKF_JOURNAL 0x04 // JOURNAL the mouse on set 00023 #define HKF_NZRET 0x08 // Always return NZ hook for <=3.0 compatibility 00024 #define HKF_INTERSENDABLE 0x10 // OK to call hookproc in context of hooking thread 00025 #define HKF_LOWLEVEL 0x20 // Low level hook 00026 00027 CONST int ampiHookError[CWINHOOKS] = { 00028 0, // WH_MSGFILTER (-1) 00029 0, // WH_JOURNALRECORD 0 00030 -1, // WH_JOURNALPLAYBACK 1 00031 0, // WH_KEYBOARD 2 00032 0, // WH_GETMESSAGE 3 00033 0, // WH_CALLWNDPROC 4 00034 0, // WH_CBT 5 00035 0, // WH_SYSMSGFILTER 6 00036 0, // WH_MOUSE 7 00037 0, // WH_HARDWARE 8 00038 0, // WH_DEBUG 9 00039 0, // WH_SHELL 10 00040 0, // WH_FOREGROUNDIDLE 11 00041 0, // WH_CALLWNDPROCRET 12 00042 0, // WH_KEYBOARD_LL 13 00043 0 // WH_MOUSE_LL 14 00044 #ifdef REDIRECTION 00045 ,0 // WH_HITTEST 00046 #endif // REDIRECTION 00047 }; 00048 00049 CONST BYTE abHookFlags[CWINHOOKS] = { 00050 HKF_SYSTEM | HKF_TASK | HKF_NZRET , // WH_MSGFILTER (-1) 00051 HKF_SYSTEM | HKF_JOURNAL | HKF_INTERSENDABLE , // WH_JOURNALRECORD 0 00052 HKF_SYSTEM | HKF_JOURNAL | HKF_INTERSENDABLE , // WH_JOURNALPLAYBACK 1 00053 HKF_SYSTEM | HKF_TASK | HKF_NZRET | HKF_INTERSENDABLE , // WH_KEYBOARD 2 00054 HKF_SYSTEM | HKF_TASK , // WH_GETMESSAGE 3 00055 HKF_SYSTEM | HKF_TASK , // WH_CALLWNDPROC 4 00056 HKF_SYSTEM | HKF_TASK , // WH_CBT 5 00057 HKF_SYSTEM , // WH_SYSMSGFILTER 6 00058 HKF_SYSTEM | HKF_TASK | HKF_INTERSENDABLE , // WH_MOUSE 7 00059 HKF_SYSTEM | HKF_TASK , // WH_HARDWARE 8 00060 HKF_SYSTEM | HKF_TASK , // WH_DEBUG 9 00061 HKF_SYSTEM | HKF_TASK , // WH_SHELL 10 00062 HKF_SYSTEM | HKF_TASK , // WH_FOREGROUNDIDLE 11 00063 HKF_SYSTEM | HKF_TASK , // WH_CALLWNDPROCRET 12 00064 HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE , // WH_KEYBOARD_LL 13 00065 HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE // WH_MOUSE_LL 14 00066 00067 #ifdef REDIRECTION 00068 ,HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE // WH_HITTEST 15 00069 #endif // REDIRECTION 00070 }; 00071 00072 00073 /* 00074 * HACK (hiroyama) see xxxCallJournalPlaybackHook() 00075 * Optimization: faster determination whether the message is one of 00076 * WM_[SYS][DEAD]CHAR. 00077 * Argument (msg) requires to be one of keyboard messages. Range check 00078 * should be done before calling IS_CHAR_MSG() macro. 00079 * 00080 * (i.e. WM_KEYFIRST <= msg < WM_KEYLAST) 00081 * 00082 * We expect bit 0x02 of all WM_*CHAR messages to be set. 00083 * and bit 0x02 of all WM_*KEY* messages to be clear 00084 * 00085 * WM_KEYDOWN 0x100 000 00086 * WM_KEYUP 0x101 001 00087 * WM_CHAR 0x102 010 00088 * WM_DEADCHAR 0x103 011 00089 * 00090 * WM_SYSKEYDOWN 0x104 100 00091 * WM_SYSKEYUP 0x105 101 00092 * WM_SYSCHAR 0x106 110 00093 * WM_SYSDEADCHAR 0x107 111 00094 * 00095 */ 00096 00097 /* 00098 */ 00099 #if (WM_KEYFIRST != 0x100) || \ 00100 (WM_KEYLAST != 0x108) || \ 00101 (WM_KEYDOWN & 0x2) || \ 00102 (WM_KEYUP & 0x2) || \ 00103 (WM_SYSKEYDOWN & 0x2) || \ 00104 (WM_SYSKEYUP & 0x2) || \ 00105 !(WM_CHAR & 0x02) || \ 00106 !(WM_DEADCHAR & 0x02) || \ 00107 !(WM_SYSCHAR & 0x02) || \ 00108 !(WM_SYSDEADCHAR & 0x02) 00109 #error "unexpected value in keyboard messages." 00110 #endif 00111 00112 00113 #if DBG 00114 00115 BOOL IsCharMsg(UINT msg) 00116 { 00117 UserAssert(msg >= WM_KEYFIRST && msg < WM_KEYLAST); 00118 00119 return msg & 0x02; 00120 } 00121 00122 #define IS_CHAR_MSG(msg) IsCharMsg(msg) 00123 00124 #else 00125 00126 #define IS_CHAR_MSG(msg) ((msg) & 0x02) 00127 00128 #endif 00129 00130 00131 00132 00133 void UnlinkHook(PHOOK phkFree); 00134 /***************************************************************************\ 00135 * DbgValidateThisHook 00136 * 00137 * Validates a hook structure and returns the start of its chain. 00138 * 00139 * History: 00140 * 03-25-97 GerardoB Created 00141 \***************************************************************************/ 00142 #if DBG 00143 PHOOK * DbgValidateThisHook (PHOOK phk, int iType, PTHREADINFO ptiHooked) 00144 { 00145 CheckCritIn(); 00146 /* 00147 * No bogus flags 00148 */ 00149 UserAssert(!(phk->flags & ~HF_DBGUSED)); 00150 /* 00151 * Type 00152 */ 00153 UserAssert(phk->iHook == iType); 00154 /* 00155 * HF_GLOBAL & ptiHooked. return the start of its hook chain. 00156 */ 00157 if (phk->flags & HF_GLOBAL) { 00158 UserAssert(phk->ptiHooked == NULL); 00159 if (phk->rpdesk != NULL) { 00160 UserAssert(GETPTI(phk) == gptiRit); 00161 return &phk->rpdesk->pDeskInfo->aphkStart[iType + 1]; 00162 } else { 00163 return &GETPTI(phk)->pDeskInfo->aphkStart[iType + 1]; 00164 } 00165 } else { 00166 UserAssert((phk->ptiHooked == ptiHooked) 00167 || (abHookFlags[iType + 1] & HKF_INTERSENDABLE)); 00168 00169 return &(phk->ptiHooked->aphkStart[iType + 1]); 00170 } 00171 } 00172 /***************************************************************************\ 00173 * DbgValidatefsHook 00174 * 00175 * Make sure that the fsHook bit masks are in sync. If the bits 00176 * are out of sync, some hook must have the HF_INCHECKWHF flag 00177 * (this means the bits are being adjusted right now) 00178 * 00179 * History: 00180 * 05-20-97 GerardoB Extracted from PhkFirst*Valid 00181 \***************************************************************************/ 00182 void DbgValidatefsHook(PHOOK phk, int nFilterType, PTHREADINFO pti, BOOL fGlobal) 00183 { 00184 CheckCritIn(); 00185 /* 00186 * If no pti is provided, figure out what it should be. 00187 * phk is expected to be NULL. 00188 */ 00189 if (pti == NULL) { 00190 fGlobal = (phk->flags & HF_GLOBAL); 00191 if (fGlobal) { 00192 pti = GETPTI(phk); 00193 } else { 00194 pti = phk->ptiHooked; 00195 UserAssert(pti != NULL); 00196 } 00197 } 00198 00199 if (fGlobal) { 00200 if ((phk != NULL) ^ IsGlobalHooked(pti, WHF_FROM_WH(nFilterType))) { 00201 PHOOK phkTemp = pti->pDeskInfo->aphkStart[nFilterType + 1]; 00202 while ((phkTemp != NULL) && !(phkTemp->flags & HF_INCHECKWHF)) { 00203 phkTemp = phkTemp->phkNext; 00204 } 00205 UserAssert(phkTemp != NULL); 00206 } 00207 } else { 00208 if ((phk != NULL) ^ IsHooked(pti, WHF_FROM_WH(nFilterType))) { 00209 PHOOK phkTemp = pti->aphkStart[nFilterType + 1]; 00210 while ((phkTemp != NULL) && !(phkTemp->flags & HF_INCHECKWHF)) { 00211 phkTemp = phkTemp->phkNext; 00212 } 00213 if (phkTemp == NULL) { 00214 phkTemp = pti->pDeskInfo->aphkStart[nFilterType + 1]; 00215 while ((phkTemp != NULL) && !(phkTemp->flags & HF_INCHECKWHF)) { 00216 phkTemp = phkTemp->phkNext; 00217 } 00218 } 00219 UserAssert(phkTemp != NULL); 00220 } 00221 } 00222 } 00223 /***************************************************************************\ 00224 * DbgValidateHooks 00225 * 00226 * This functions expects valid (not destroyed) and properly linked. 00227 * History: 00228 * 03-25-97 GerardoB Created 00229 \***************************************************************************/ 00230 void DbgValidateHooks (PHOOK phk, int iType) 00231 { 00232 PHOOK *pphkStart, *pphkNext; 00233 if (phk == NULL) { 00234 return; 00235 } 00236 /* 00237 * It shouldn't be destroyed 00238 */ 00239 UserAssert(!(phk->flags & (HF_DESTROYED | HF_FREED))); 00240 /* 00241 * Validate fsHooks 00242 */ 00243 DbgValidatefsHook(phk, iType, NULL, FALSE); 00244 /* 00245 * Validate this hook and get the beginning of the hook chain 00246 */ 00247 pphkStart = DbgValidateThisHook(phk, iType, phk->ptiHooked); 00248 /* 00249 * There must be at least one hook in the chain 00250 */ 00251 UserAssert(*pphkStart != NULL); 00252 /* 00253 * Validate the link. 00254 * And while your're at it, validate all hooks! 00255 */ 00256 pphkNext = pphkStart; 00257 while ((*pphkNext != phk) && (*pphkNext != NULL)) { 00258 UserAssert(pphkStart == DbgValidateThisHook(*pphkNext, iType, phk->ptiHooked)); 00259 pphkNext = &(*pphkNext)->phkNext; 00260 } 00261 /* 00262 * Verify that we found it. 00263 */ 00264 UserAssert(*pphkNext == phk); 00265 /* 00266 * Walk until the end of the chain 00267 */ 00268 while (*pphkNext != NULL) { 00269 UserAssert(pphkStart == DbgValidateThisHook(*pphkNext, iType, phk->ptiHooked)); 00270 pphkNext = &(*pphkNext)->phkNext; 00271 } 00272 } 00273 #else 00274 #define DbgValidatefsHook(phk, nFilterType, pti, fGlobal) 00275 #endif /* DBG */ 00276 /***************************************************************************\ 00277 * zzzJournalAttach 00278 * 00279 * This attaches/detaches threads to one input queue so input is synchronized. 00280 * Journalling requires this. 00281 * 00282 * 12-10-92 ScottLu Created. 00283 \***************************************************************************/ 00284 00285 BOOL zzzJournalAttach( 00286 PTHREADINFO pti, 00287 BOOL fAttach) 00288 { 00289 PTHREADINFO ptiT; 00290 PQ pq; 00291 PLIST_ENTRY pHead, pEntry; 00292 00293 /* 00294 * If we're attaching, calculate the pqAttach for all threads journalling. 00295 * If we're unattaching, just call zzzReattachThreads() and it will calculate 00296 * the non-journalling queues to attach to. 00297 */ 00298 if (fAttach) { 00299 if ((pq = AllocQueue(pti, NULL)) == NULL) 00300 return FALSE; 00301 00302 pHead = &pti->rpdesk->PtiList; 00303 for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) { 00304 ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink); 00305 00306 /* 00307 * This is the Q to attach to for all threads that will do 00308 * journalling. 00309 */ 00310 if (!(ptiT->TIF_flags & (TIF_DONTJOURNALATTACH | TIF_INCLEANUP))) { 00311 ptiT->pqAttach = pq; 00312 ptiT->pqAttach->cThreads++; 00313 } 00314 } 00315 } 00316 00317 return zzzReattachThreads(fAttach); 00318 } 00319 /***************************************************************************\ 00320 * InterQueueMsgCleanup 00321 * 00322 * Walk gpsmsList looking for inter queue messages with a hung receiver; 00323 * if one is found and it's a message that would have been an async event or 00324 * intra queue if not journalling, then it cleans it up. 00325 * 00326 * While Journalling most threads are attached to the same queue. This causes 00327 * activation and input stuff to be synchronous; if a thread hangs or dies, 00328 * any other thread sending a message to the hung/dead thread will be 00329 * blocked for good. 00330 * This is critical when the blocked thread is cssr; this can happen with 00331 * console windows or when some one requests a hard error box, specially 00332 * during window activation. 00333 * 00334 * This function must be called when all queues have been detached (unless previously attached), 00335 * so we can take care of hung/dead receivers with pending SMSs. 00336 * 00337 * 03-28-96 GerardoB Created 00338 \***************************************************************************/ 00339 void InterQueueMsgCleanup (DWORD dwTimeFromLastRead) 00340 { 00341 PSMS *ppsms; 00342 PSMS psmsNext; 00343 00344 CheckCritIn(); 00345 00346 /* 00347 * Walk gpsmsList 00348 */ 00349 for (ppsms = &gpsmsList; *ppsms; ) { 00350 psmsNext = (*ppsms)->psmsNext; 00351 /* 00352 * If this is an inter queue message 00353 */ 00354 if (((*ppsms)->ptiSender != NULL) 00355 && ((*ppsms)->ptiReceiver != NULL) 00356 && ((*ppsms)->ptiSender->pq != (*ppsms)->ptiReceiver->pq)) { 00357 /* 00358 * If the receiver has been hung for a while 00359 */ 00360 if (FHungApp ((*ppsms)->ptiReceiver, dwTimeFromLastRead)) { 00361 00362 switch ((*ppsms)->message) { 00363 /* 00364 * Activation messages 00365 */ 00366 case WM_NCACTIVATE: 00367 case WM_ACTIVATEAPP: 00368 case WM_ACTIVATE: 00369 case WM_SETFOCUS: 00370 case WM_KILLFOCUS: 00371 case WM_QUERYNEWPALETTE: 00372 /* 00373 * Sent to spwndFocus, which now can be in a different queue 00374 */ 00375 case WM_INPUTLANGCHANGE: 00376 RIPMSG3 (RIP_WARNING, "InterQueueMsgCleanup: ptiSender:%#p ptiReceiver:%#p message:%#lx", 00377 (*ppsms)->ptiSender, (*ppsms)->ptiReceiver, (*ppsms)->message); 00378 ReceiverDied(*ppsms, ppsms); 00379 break; 00380 00381 } /* switch */ 00382 00383 } /* If hung receiver */ 00384 00385 } /* If inter queue message */ 00386 00387 /* 00388 * If the message was not unlinked, go to the next one. 00389 */ 00390 if (*ppsms != psmsNext) 00391 ppsms = &(*ppsms)->psmsNext; 00392 00393 } /* for */ 00394 } 00395 /***************************************************************************\ 00396 * zzzCancelJournalling 00397 * 00398 * Journalling is cancelled with control-escape is pressed, or when the desktop 00399 * is switched. 00400 * 00401 * 01-27-93 ScottLu Created. 00402 \***************************************************************************/ 00403 00404 void zzzCancelJournalling(void) 00405 { 00406 PTHREADINFO ptiCancelJournal; 00407 PHOOK phook; 00408 PHOOK phookNext; 00409 00410 /* 00411 * Mouse buttons sometimes get stuck down due to hardware glitches, 00412 * usually due to input concentrator switchboxes or faulty serial 00413 * mouse COM ports, so clear the global button state here just in case, 00414 * otherwise we may not be able to change focus with the mouse. 00415 * Also do this in Alt-Tab processing. 00416 */ 00417 #if DBG 00418 if (gwMouseOwnerButton) 00419 RIPMSG1(RIP_WARNING, 00420 "gwMouseOwnerButton=%x, being cleared forcibly\n", 00421 gwMouseOwnerButton); 00422 #endif 00423 gwMouseOwnerButton = 0; 00424 00425 /* 00426 * Remove journal hooks. This'll cause threads to associate with 00427 * different queues. 00428 * DeferWinEventNotify() so we can traverse the phook list safely 00429 */ 00430 DeferWinEventNotify(); 00431 UserAssert(gptiRit->pDeskInfo == grpdeskRitInput->pDeskInfo); 00432 phook = PhkFirstGlobalValid(gptiRit, WH_JOURNALPLAYBACK); 00433 while (phook != NULL) { 00434 ptiCancelJournal = phook->head.pti; 00435 00436 if (ptiCancelJournal != NULL) { 00437 /* 00438 * Let the thread that set the journal hook know this is happening. 00439 */ 00440 _PostThreadMessage(ptiCancelJournal, WM_CANCELJOURNAL, 0, 0); 00441 00442 /* 00443 * If there was an app waiting for a response back from the journal 00444 * application, cancel that request so the app can continue running 00445 * (for example, we don't want winlogon or console to wait for an 00446 * app that may be hung!) 00447 */ 00448 SendMsgCleanup(ptiCancelJournal); 00449 } 00450 00451 phookNext = PhkNextValid(phook); 00452 zzzUnhookWindowsHookEx(phook); // May free phook memory 00453 phook = phookNext; 00454 } 00455 zzzEndDeferWinEventNotify(); 00456 00457 /* 00458 * DeferWinEventNotify() so we can traverse the phook list safely 00459 */ 00460 DeferWinEventNotify(); 00461 UserAssert(gptiRit->pDeskInfo == grpdeskRitInput->pDeskInfo); 00462 phook = PhkFirstGlobalValid(gptiRit, WH_JOURNALRECORD); 00463 while (phook != NULL) { 00464 ptiCancelJournal = phook->head.pti; 00465 00466 if (ptiCancelJournal != NULL) { 00467 /* 00468 * Let the thread that set the journal hook know this is happening. 00469 */ 00470 _PostThreadMessage(ptiCancelJournal, WM_CANCELJOURNAL, 0, 0); 00471 00472 /* 00473 * If there was an app waiting for a response back from the journal 00474 * application, cancel that request so the app can continue running 00475 * (for example, we don't want winlogon or console to wait for an 00476 * app that may be hung!) 00477 */ 00478 SendMsgCleanup(ptiCancelJournal); 00479 } 00480 00481 phookNext = PhkNextValid(phook); 00482 zzzUnhookWindowsHookEx(phook); // May free phook memory 00483 phook = phookNext; 00484 } 00485 zzzEndDeferWinEventNotify(); 00486 00487 00488 /* 00489 * Make sure journalling ssync mode didn't hose any one 00490 */ 00491 InterQueueMsgCleanup(CMSWAITTOKILLTIMEOUT); 00492 00493 /* 00494 * Unlock SetForegroundWindow (if locked) 00495 */ 00496 gppiLockSFW = NULL; 00497 00498 /* 00499 * NT5's last minute hack for evil applications, who disables the desktop window 00500 * (perhaps by accidents though) leaving the system pretty unusable. 00501 * See Raid #423704. 00502 */ 00503 if (grpdeskRitInput && grpdeskRitInput->pDeskInfo) { 00504 PWND pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd; 00505 00506 if (pwndDesktop && TestWF(pwndDesktop, WFDISABLED)) { 00507 ClrWF(pwndDesktop, WFDISABLED); 00508 } 00509 } 00510 } 00511 00512 /***************************************************************************\ 00513 * zzzSetWindowsHookAW (API) 00514 * 00515 * This is the Win32 version of the SetWindowsHook() call. It has the 00516 * same characteristics as far as return values, but only sets 'local' 00517 * hooks. This is because we weren't provided a DLL we can load into 00518 * other processes. Because of this WH_SYSMSGFILTER is no longer a 00519 * valid hook. Apps will either need to call with WH_MSGFILTER or call 00520 * the new API SetWindowsHookEx(). Essentially this API is obsolete and 00521 * everyone should call SetWindowsHookEx(). 00522 * 00523 * History: 00524 * 10-Feb-1991 DavidPe Created. 00525 * 30-Jan-1992 IanJa Added bAnsi parameter 00526 \***************************************************************************/ 00527 00528 PROC zzzSetWindowsHookAW( 00529 int nFilterType, 00530 PROC pfnFilterProc, 00531 DWORD dwFlags) 00532 { 00533 PHOOK phk; 00534 00535 phk = zzzSetWindowsHookEx(NULL, NULL, PtiCurrent(), 00536 nFilterType, pfnFilterProc, dwFlags); 00537 00538 /* 00539 * If we get an error from zzzSetWindowsHookEx() then we return 00540 * -1 to be compatible with older version of Windows. 00541 */ 00542 if (phk == NULL) { 00543 return (PROC)-1; 00544 } 00545 00546 /* 00547 * Handle the backwards compatibility return value cases for 00548 * SetWindowsHook. If this was the first hook in the chain, 00549 * then return NULL, else return something non-zero. HKF_NZRET 00550 * is a special case where SetWindowsHook would always return 00551 * something because there was a default hook installed. Some 00552 * apps relied on a non-zero return value in those cases. 00553 */ 00554 if ((phk->phkNext != NULL) || (abHookFlags[nFilterType + 1] & HKF_NZRET)) { 00555 return (PROC)phk; 00556 } 00557 00558 return NULL; 00559 } 00560 00561 00562 /***************************************************************************\ 00563 * zzzSetWindowsHookEx 00564 * 00565 * SetWindowsHookEx() is the updated version of SetWindowsHook(). It allows 00566 * applications to set hooks on specific threads or throughout the entire 00567 * system. The function returns a hook handle to the application if 00568 * successful and NULL if a failure occured. 00569 * 00570 * History: 00571 * 28-Jan-1991 DavidPe Created. 00572 * 15-May-1991 ScottLu Changed to work client/server. 00573 * 30-Jan-1992 IanJa Added bAnsi parameter 00574 \***************************************************************************/ 00575 00576 PHOOK zzzSetWindowsHookEx( 00577 HANDLE hmod, 00578 PUNICODE_STRING pstrLib, 00579 PTHREADINFO ptiThread, 00580 int nFilterType, 00581 PROC pfnFilterProc, 00582 DWORD dwFlags) 00583 { 00584 ACCESS_MASK amDesired; 00585 PHOOK phkNew; 00586 TL tlphkNew; 00587 PHOOK *pphkStart; 00588 PTHREADINFO ptiCurrent; 00589 00590 /* 00591 * Check to see if filter type is valid. 00592 */ 00593 if ((nFilterType < WH_MIN) || (nFilterType > WH_MAX)) { 00594 RIPERR0(ERROR_INVALID_HOOK_FILTER, RIP_VERBOSE, ""); 00595 return NULL; 00596 } 00597 00598 /* 00599 * Check to see if filter proc is valid. 00600 */ 00601 if (pfnFilterProc == NULL) { 00602 RIPERR0(ERROR_INVALID_FILTER_PROC, RIP_VERBOSE, ""); 00603 return NULL; 00604 } 00605 00606 ptiCurrent = PtiCurrent(); 00607 00608 if (ptiThread == NULL) { 00609 /* 00610 * Is the app trying to set a global hook without a library? 00611 * If so return an error. 00612 */ 00613 if (hmod == NULL) { 00614 RIPERR0(ERROR_HOOK_NEEDS_HMOD, RIP_VERBOSE, ""); 00615 return NULL; 00616 } 00617 } else { 00618 /* 00619 * Is the app trying to set a local hook that is global-only? 00620 * If so return an error. 00621 */ 00622 if (!(abHookFlags[nFilterType + 1] & HKF_TASK)) { 00623 RIPERR0(ERROR_GLOBAL_ONLY_HOOK, RIP_VERBOSE, ""); 00624 return NULL; 00625 } 00626 00627 /* 00628 * Can't hook outside our own desktop. 00629 */ 00630 if (ptiThread->rpdesk != ptiCurrent->rpdesk) { 00631 RIPERR0(ERROR_ACCESS_DENIED, 00632 RIP_WARNING, 00633 "Access denied to desktop in zzzSetWindowsHookEx - can't hook other desktops"); 00634 00635 return NULL; 00636 } 00637 00638 if (ptiCurrent->ppi != ptiThread->ppi) { 00639 /* 00640 * Is the app trying to set hook in another process without a library? 00641 * If so return an error. 00642 */ 00643 if (hmod == NULL) { 00644 RIPERR0(ERROR_HOOK_NEEDS_HMOD, RIP_VERBOSE, ""); 00645 return NULL; 00646 } 00647 00648 /* 00649 * Is the app hooking another user without access? 00650 * If so return an error. Note that this check is done 00651 * for global hooks every time the hook is called. 00652 */ 00653 if ((!RtlEqualLuid(&ptiThread->ppi->luidSession, 00654 &ptiCurrent->ppi->luidSession)) && 00655 !(ptiThread->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK)) { 00656 00657 RIPERR0(ERROR_ACCESS_DENIED, 00658 RIP_WARNING, 00659 "Access denied to other user in zzzSetWindowsHookEx"); 00660 00661 return NULL; 00662 } 00663 00664 if ((ptiThread->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)) && 00665 !(abHookFlags[nFilterType + 1] & HKF_INTERSENDABLE)) { 00666 00667 /* 00668 * Can't hook console or GUI system thread if inter-thread 00669 * calling isn't implemented for this hook type. 00670 */ 00671 RIPERR1(ERROR_HOOK_TYPE_NOT_ALLOWED, 00672 RIP_WARNING, 00673 "nFilterType (%ld) not allowed in zzzSetWindowsHookEx", 00674 nFilterType); 00675 00676 return NULL; 00677 } 00678 } 00679 } 00680 00681 /* 00682 * Check if this thread has access to hook its desktop. 00683 */ 00684 switch( nFilterType ) { 00685 case WH_JOURNALRECORD: 00686 amDesired = DESKTOP_JOURNALRECORD; 00687 break; 00688 00689 case WH_JOURNALPLAYBACK: 00690 amDesired = DESKTOP_JOURNALPLAYBACK; 00691 break; 00692 00693 default: 00694 amDesired = DESKTOP_HOOKCONTROL; 00695 break; 00696 } 00697 00698 if (!RtlAreAllAccessesGranted(ptiCurrent->amdesk, amDesired)) { 00699 RIPERR0(ERROR_ACCESS_DENIED, 00700 RIP_WARNING, 00701 "Access denied to desktop in zzzSetWindowsHookEx"); 00702 00703 return NULL; 00704 } 00705 00706 if (amDesired != DESKTOP_HOOKCONTROL && 00707 (ptiCurrent->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO)) { 00708 RIPERR0(ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION, 00709 RIP_WARNING, 00710 "Journal hooks invalid on a desktop belonging to a non-interactive WindowStation."); 00711 00712 return NULL; 00713 } 00714 00715 #if 0 00716 /* 00717 * Is this a journal hook? 00718 */ 00719 if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) { 00720 /* 00721 * Is a journal hook of this type already installed? 00722 * If so it's an error. 00723 * If this code is enabled, use PhkFirstGlobalValid instead 00724 * of checking phkStart directly 00725 */ 00726 if (ptiCurrent->pDeskInfo->asphkStart[nFilterType + 1] != NULL) { 00727 RIPERR0(ERROR_JOURNAL_HOOK_SET, RIP_VERBOSE, ""); 00728 return NULL; 00729 } 00730 } 00731 #endif 00732 00733 /* 00734 * Allocate the new HOOK structure. 00735 */ 00736 phkNew = (PHOOK)HMAllocObject(ptiCurrent, ptiCurrent->rpdesk, 00737 TYPE_HOOK, sizeof(HOOK)); 00738 if (phkNew == NULL) { 00739 return NULL; 00740 } 00741 00742 /* 00743 * If a DLL is required for this hook, register the library with 00744 * the library management routines so we can assure it's loaded 00745 * into all the processes necessary. 00746 */ 00747 phkNew->ihmod = -1; 00748 if (hmod != NULL) { 00749 00750 #if defined(WX86) 00751 00752 phkNew->flags |= (dwFlags & HF_WX86KNOWNDLL); 00753 00754 #endif 00755 00756 phkNew->ihmod = GetHmodTableIndex(pstrLib); 00757 00758 if (phkNew->ihmod == -1) { 00759 RIPERR0(ERROR_MOD_NOT_FOUND, RIP_VERBOSE, ""); 00760 HMFreeObject((PVOID)phkNew); 00761 return NULL; 00762 } 00763 00764 /* 00765 * Add a dependency on this module - meaning, increment a count 00766 * that simply counts the number of hooks set into this module. 00767 */ 00768 if (phkNew->ihmod >= 0) { 00769 AddHmodDependency(phkNew->ihmod); 00770 } 00771 } 00772 00773 /* 00774 * Depending on whether we're setting a global or local hook, 00775 * get the start of the appropriate linked-list of HOOKs. Also 00776 * set the HF_GLOBAL flag if it's a global hook. 00777 */ 00778 if (ptiThread != NULL) { 00779 pphkStart = &ptiThread->aphkStart[nFilterType + 1]; 00780 00781 /* 00782 * Set the WHF_* in the THREADINFO so we know it's hooked. 00783 */ 00784 ptiThread->fsHooks |= WHF_FROM_WH(nFilterType); 00785 00786 /* 00787 * Set the flags in the thread's TEB 00788 */ 00789 if (ptiThread->pClientInfo) { 00790 BOOL fAttached; 00791 00792 /* 00793 * If the thread being hooked is in another process, attach 00794 * to that process so that we can access its ClientInfo. 00795 */ 00796 if (ptiThread->ppi != ptiCurrent->ppi) { 00797 KeAttachProcess(&ptiThread->ppi->Process->Pcb); 00798 fAttached = TRUE; 00799 } else 00800 fAttached = FALSE; 00801 00802 ptiThread->pClientInfo->fsHooks = ptiThread->fsHooks; 00803 00804 if (fAttached) 00805 KeDetachProcess(); 00806 } 00807 00808 /* 00809 * Remember which thread we're hooking. 00810 */ 00811 phkNew->ptiHooked = ptiThread; 00812 00813 } else { 00814 pphkStart = &ptiCurrent->pDeskInfo->aphkStart[nFilterType + 1]; 00815 phkNew->flags |= HF_GLOBAL; 00816 00817 /* 00818 * Set the WHF_* in the SERVERINFO so we know it's hooked. 00819 */ 00820 ptiCurrent->pDeskInfo->fsHooks |= WHF_FROM_WH(nFilterType); 00821 00822 phkNew->ptiHooked = NULL; 00823 } 00824 00825 /* 00826 * Does the hook function expect ANSI or Unicode text? 00827 */ 00828 phkNew->flags |= (dwFlags & HF_ANSI); 00829 00830 /* 00831 * Initialize the HOOK structure. Unreferenced parameters are assumed 00832 * to be initialized to zero by LocalAlloc(). 00833 */ 00834 phkNew->iHook = nFilterType; 00835 00836 /* 00837 * Libraries are loaded at different linear addresses in different 00838 * process contexts. For this reason, we need to convert the filter 00839 * proc address into an offset while setting the hook, and then convert 00840 * it back to a real per-process function pointer when calling a 00841 * hook. Do this by subtracting the 'hmod' (which is a pointer to the 00842 * linear and contiguous .exe header) from the function index. 00843 */ 00844 phkNew->offPfn = ((ULONG_PTR)pfnFilterProc) - ((ULONG_PTR)hmod); 00845 00846 #ifdef HOOKBATCH 00847 phkNew->cEventMessages = 0; 00848 phkNew->iCurrentEvent = 0; 00849 phkNew->CacheTimeOut = 0; 00850 phkNew->aEventCache = NULL; 00851 #endif //HOOKBATCH 00852 00853 /* 00854 * Link this hook into the front of the hook-list. 00855 */ 00856 phkNew->phkNext = *pphkStart; 00857 *pphkStart = phkNew; 00858 00859 /* 00860 * If this is a journal hook, setup synchronized input processing 00861 * AFTER we set the hook - so this synchronization can be cancelled 00862 * with control-esc. 00863 */ 00864 if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) { 00865 /* 00866 * Attach everyone to us so journal-hook processing 00867 * will be synchronized. 00868 * No need to DeferWinEventNotify() here, since we lock phkNew. 00869 */ 00870 ThreadLockAlwaysWithPti(ptiCurrent, phkNew, &tlphkNew); 00871 if (!zzzJournalAttach(ptiCurrent, TRUE)) { 00872 RIPMSG1(RIP_WARNING, "zzzJournalAttach failed, so abort hook %#p", phkNew); 00873 if (ThreadUnlock(&tlphkNew) != NULL) { 00874 zzzUnhookWindowsHookEx(phkNew); 00875 } 00876 return NULL; 00877 } 00878 if ((phkNew = ThreadUnlock(&tlphkNew)) == NULL) { 00879 return NULL; 00880 } 00881 } 00882 00883 UserAssert(phkNew != NULL); 00884 00885 /* 00886 * Later 5.0 GerardoB: The old code just to check this but 00887 * I think it's some left over stuff from server side days. 00888 .* Let's assert on it for a while 00889 * Also, I added the assertions in the else's below because I reorganized 00890 * the code and want to make sure we don't change behavior 00891 */ 00892 UserAssert(ptiCurrent->pEThread && THREAD_TO_PROCESS(ptiCurrent->pEThread)); 00893 00894 /* 00895 * Can't allow a process that has set a global hook that works 00896 * on server-side winprocs to run at background priority! Bump 00897 * up it's dynamic priority and mark it so it doesn't get reset. 00898 */ 00899 if ((phkNew->flags & HF_GLOBAL) && 00900 (abHookFlags[nFilterType + 1] & HKF_INTERSENDABLE)) { 00901 00902 ptiCurrent->TIF_flags |= TIF_GLOBALHOOKER; 00903 KeSetPriorityThread(&ptiCurrent->pEThread->Tcb, LOW_REALTIME_PRIORITY-2); 00904 00905 if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) { 00906 ThreadLockAlwaysWithPti(ptiCurrent, phkNew, &tlphkNew); 00907 /* 00908 * If we're changing the journal hooks, jiggle the mouse. 00909 * This way the first event will always be a mouse move, which 00910 * will ensure that the cursor is set properly. 00911 */ 00912 zzzSetFMouseMoved(); 00913 phkNew = ThreadUnlock(&tlphkNew); 00914 /* 00915 * If setting a journal playback hook, this process is the input 00916 * provider. This gives it the right to call SetForegroundWindow 00917 */ 00918 if (nFilterType == WH_JOURNALPLAYBACK) { 00919 gppiInputProvider = ptiCurrent->ppi; 00920 } 00921 } else { 00922 UserAssert(nFilterType != WH_JOURNALPLAYBACK); 00923 } 00924 } else { 00925 UserAssert(!(abHookFlags[nFilterType + 1] & HKF_JOURNAL)); 00926 UserAssert(nFilterType != WH_JOURNALPLAYBACK); 00927 } 00928 00929 00930 00931 00932 /* 00933 * Return pointer to our internal hook structure so we know 00934 * which hook to call next in CallNextHookEx(). 00935 */ 00936 DbgValidateHooks(phkNew, phkNew->iHook); 00937 return phkNew; 00938 } 00939 00940 00941 /***************************************************************************\ 00942 * xxxCallNextHookEx 00943 * 00944 * In the new world DefHookProc() is a bit deceptive since SetWindowsHook() 00945 * isn't returning the actual address of the next hook to call, but instead 00946 * a hook handle. CallNextHookEx() is a slightly clearer picture of what's 00947 * going on so apps don't get tempted to try and call the value we return. 00948 * 00949 * As a side note we don't actually use the hook handle passed in. We keep 00950 * track of which hooks is currently being called on a thread in the Q 00951 * structure and use that. This is because SetWindowsHook() will sometimes 00952 * return NULL to be compatible with the way it used to work, but even though 00953 * we may be dealing with the last 'local' hook, there may be further 'global' 00954 * hooks we need to call. PhkNext() is smart enough to jump over to the 00955 * 'global' hook chain if it reaches the end of the 'local' hook chain. 00956 * 00957 * History: 00958 * 01-30-91 DavidPe Created. 00959 \***************************************************************************/ 00960 00961 LRESULT xxxCallNextHookEx( 00962 int nCode, 00963 WPARAM wParam, 00964 LPARAM lParam) 00965 { 00966 BOOL bAnsiHook; 00967 00968 if (PtiCurrent()->sphkCurrent == NULL) { 00969 return 0; 00970 } 00971 00972 return xxxCallHook2(PhkNextValid(PtiCurrent()->sphkCurrent), nCode, wParam, lParam, &bAnsiHook); 00973 } 00974 00975 00976 /***************************************************************************\ 00977 * CheckWHFBits 00978 * 00979 * This routine checks to see if any hooks for nFilterType exist, and clear 00980 * the appropriate WHF_ in the THREADINFO and SERVERINFO. 00981 * 00982 * History: 00983 * 08-17-92 DavidPe Created. 00984 \***************************************************************************/ 00985 00986 VOID CheckWHFBits( 00987 PTHREADINFO pti, 00988 int nFilterType) 00989 { 00990 BOOL fClearThreadBits; 00991 BOOL fClearDesktopBits; 00992 PHOOK phook; 00993 00994 00995 /* 00996 * Assume we're are going to clear local(thread) and 00997 * global(desktop) bits. 00998 */ 00999 fClearThreadBits = TRUE; 01000 fClearDesktopBits = TRUE; 01001 /* 01002 * Get the first valid hook for this thread 01003 */ 01004 phook = PhkFirstValid(pti, nFilterType); 01005 if (phook != NULL) { 01006 /* 01007 * If it found a global hook, don't clear the desktop bits 01008 * (that would mean that there are no local(thread) hooks 01009 * so we fall through to clear the thread bits) 01010 */ 01011 if (phook->flags & HF_GLOBAL) { 01012 fClearDesktopBits = FALSE; 01013 } else { 01014 /* 01015 * It found a thread hook so don't clear the thread bits 01016 */ 01017 fClearThreadBits = FALSE; 01018 /* 01019 * Check for global hooks now. If there is one, don't 01020 * clear the desktop bits 01021 */ 01022 phook = PhkFirstGlobalValid(pti, nFilterType); 01023 fClearDesktopBits = (phook == NULL); 01024 } 01025 } /* if (phook != NULL) */ 01026 01027 if (fClearThreadBits) { 01028 pti->fsHooks &= ~(WHF_FROM_WH(nFilterType)); 01029 /* 01030 * Set the flags in the thread's TEB 01031 */ 01032 if (pti->pClientInfo) { 01033 BOOL fAttached; 01034 /* 01035 * If the hooked thread is in another process, attach 01036 * to that process to access its address space. 01037 */ 01038 if (pti->ppi != PpiCurrent()) { 01039 KeAttachProcess(&pti->ppi->Process->Pcb); 01040 fAttached = TRUE; 01041 } else 01042 fAttached = FALSE; 01043 01044 pti->pClientInfo->fsHooks = pti->fsHooks; 01045 01046 if (fAttached) 01047 KeDetachProcess(); 01048 } 01049 } 01050 01051 if (fClearDesktopBits) { 01052 pti->pDeskInfo->fsHooks &= ~(WHF_FROM_WH(nFilterType)); 01053 } 01054 } 01055 01056 01057 /***************************************************************************\ 01058 * zzzUnhookWindowsHook (API) 01059 * 01060 * This is the old version of the Unhook API. It does the same thing as 01061 * zzzUnhookWindowsHookEx(), but takes a filter-type and filter-proc to 01062 * identify which hook to unhook. 01063 * 01064 * History: 01065 * 01-28-91 DavidPe Created. 01066 \***************************************************************************/ 01067 01068 BOOL zzzUnhookWindowsHook( 01069 int nFilterType, 01070 PROC pfnFilterProc) 01071 { 01072 PHOOK phk; 01073 PTHREADINFO ptiCurrent; 01074 01075 if ((nFilterType < WH_MIN) || (nFilterType > WH_MAX)) { 01076 RIPERR0(ERROR_INVALID_HOOK_FILTER, RIP_VERBOSE, ""); 01077 return FALSE; 01078 } 01079 01080 ptiCurrent = PtiCurrent(); 01081 01082 for (phk = PhkFirstValid(ptiCurrent, nFilterType); phk != NULL; phk = PhkNextValid(phk)) { 01083 /* 01084 * Is this the hook we're looking for? 01085 */ 01086 if (PFNHOOK(phk) == pfnFilterProc) { 01087 01088 /* 01089 * Are we on the thread that set the hook? 01090 * If not return an error. 01091 */ 01092 if (GETPTI(phk) != ptiCurrent) { 01093 RIPERR0(ERROR_ACCESS_DENIED, 01094 RIP_WARNING, 01095 "Access denied in zzzUnhookWindowsHook: " 01096 "this thread is not the same as that which set the hook"); 01097 01098 return FALSE; 01099 } 01100 01101 return zzzUnhookWindowsHookEx( phk ); 01102 } 01103 } 01104 01105 /* 01106 * Didn't find the hook we were looking for so return FALSE. 01107 */ 01108 RIPERR0(ERROR_HOOK_NOT_INSTALLED, RIP_VERBOSE, ""); 01109 return FALSE; 01110 } 01111 01112 01113 /***************************************************************************\ 01114 * zzzUnhookWindowsHookEx (API) 01115 * 01116 * Applications call this API to 'unhook' a hook. First we check if someone 01117 * is currently calling this hook. If no one is we go ahead and free the 01118 * HOOK structure now. If someone is then we simply clear the filter-proc 01119 * in the HOOK structure. In xxxCallHook2() we check for this and if by 01120 * that time no one is calling the hook in question we free it there. 01121 * 01122 * History: 01123 * 01-28-91 DavidPe Created. 01124 \***************************************************************************/ 01125 01126 BOOL zzzUnhookWindowsHookEx( 01127 PHOOK phkFree) 01128 { 01129 PTHREADINFO pti; 01130 01131 pti = GETPTI(phkFree); 01132 01133 /* 01134 * If this hook is already destroyed, bail 01135 */ 01136 if (phkFree->flags & HF_DESTROYED) { 01137 RIPMSG1(RIP_WARNING, "_UnhookWindowsHookEx(%#p) already destroyed", phkFree); 01138 return FALSE; 01139 } 01140 01141 /* 01142 * Clear the journaling flags in all the queues. 01143 */ 01144 if (abHookFlags[phkFree->iHook + 1] & HKF_JOURNAL) { 01145 zzzJournalAttach(pti, FALSE); 01146 /* 01147 * If someone got stuck because of the hook, let him go 01148 * 01149 * I want to get some performance numbers before checking this in. 01150 * MSTest hooks and unhooks all the time when running a script. 01151 * This code has never been in. 5/22/96. GerardoB 01152 */ 01153 // InterQueueMsgCleanup(3 * CMSWAITTOKILLTIMEOUT); 01154 } 01155 01156 /* 01157 * If no one is currently calling this hook, 01158 * go ahead and free it now. 01159 */ 01160 FreeHook(phkFree); 01161 01162 /* 01163 * If this thread has no more global hooks that are able to hook 01164 * server-side window procs, we must clear it's TIF_GLOBALHOOKER bit. 01165 */ 01166 if (pti->TIF_flags & TIF_GLOBALHOOKER) { 01167 int iHook; 01168 PHOOK phk; 01169 for (iHook = WH_MIN ; iHook <= WH_MAX ; ++iHook) { 01170 /* 01171 * Ignore those that can't hook server-side winprocs 01172 */ 01173 if (!(abHookFlags[iHook + 1] & HKF_INTERSENDABLE)) { 01174 continue; 01175 } 01176 01177 /* 01178 * Scan the global hooks 01179 */ 01180 for (phk = PhkFirstGlobalValid(pti, iHook); 01181 phk != NULL; phk = PhkNextValid(phk)) { 01182 01183 if (GETPTI(phk) == pti) { 01184 goto StillHasGlobalHooks; 01185 } 01186 } 01187 } 01188 pti->TIF_flags &= ~TIF_GLOBALHOOKER; 01189 } 01190 01191 StillHasGlobalHooks: 01192 /* 01193 * Success, return TRUE. 01194 */ 01195 return TRUE; 01196 } 01197 01198 01199 /***************************************************************************\ 01200 * _CallMsgFilter (API) 01201 * 01202 * CallMsgFilter() allows applications to call the WH_*MSGFILTER hooks. 01203 * If there's a sysmodal window we return FALSE right away. WH_MSGFILTER 01204 * isn't called if WH_SYSMSGFILTER returned non-zero. 01205 * 01206 * History: 01207 * 01-29-91 DavidPe Created. 01208 \***************************************************************************/ 01209 01210 BOOL _CallMsgFilter( 01211 LPMSG pmsg, 01212 int nCode) 01213 { 01214 PTHREADINFO pti; 01215 01216 pti = PtiCurrent(); 01217 01218 /* 01219 * First call WH_SYSMSGFILTER. If it returns non-zero, don't 01220 * bother calling WH_MSGFILTER, just return TRUE. Otherwise 01221 * return what WH_MSGFILTER gives us. 01222 */ 01223 if (IsHooked(pti, WHF_SYSMSGFILTER) && xxxCallHook(nCode, 0, (LPARAM)pmsg, 01224 WH_SYSMSGFILTER)) { 01225 return TRUE; 01226 } 01227 01228 if (IsHooked(pti, WHF_MSGFILTER)) { 01229 return (BOOL)xxxCallHook(nCode, 0, (LPARAM)pmsg, WH_MSGFILTER); 01230 } 01231 01232 return FALSE; 01233 } 01234 01235 01236 /***************************************************************************\ 01237 * xxxCallHook 01238 * 01239 * User code calls this function to call the first hook of a specific 01240 * type. 01241 * 01242 * History: 01243 * 01-29-91 DavidPe Created. 01244 \***************************************************************************/ 01245 01246 int xxxCallHook( 01247 int nCode, 01248 WPARAM wParam, 01249 LPARAM lParam, 01250 int iHook) 01251 { 01252 BOOL bAnsiHook; 01253 01254 return (int)xxxCallHook2(PhkFirstValid(PtiCurrent(), iHook), nCode, wParam, lParam, &bAnsiHook); 01255 } 01256 01257 01258 /***************************************************************************\ 01259 * xxxCallHook2 01260 * 01261 * When you have an actual HOOK structure to call, you'd use this function. 01262 * It will check to see if the hook hasn't already been unhooked, and if 01263 * is it will free it and keep looking until it finds a hook it can call 01264 * or hits the end of the list. We also make sure any needed DLLs are loaded 01265 * here. We also check to see if the HOOK was unhooked inside the call 01266 * after we return. 01267 * 01268 * Note: Hooking server-side window procedures (such as the desktop and console 01269 * windows) can only be done by sending the hook message to the hooking app. 01270 * (This is because we must not load the hookproc DLL into the server process). 01271 * The hook types this can be done with are currently WH_JOURNALRECORD, 01272 * WH_JOURNALPLAYBACK, WH_KEYBOARD and WH_MOUSE : these are all marked as 01273 * HKF_INTERSENDABLE. In order to prevent a global hooker from locking up the whole 01274 * system, the hook message is sent with a timeout. To ensure minimal 01275 * performance degradation, the hooker process is set to foreground priority, 01276 * and prevented from being set back to background priority with the 01277 * TIF_GLOBALHOOKER bit in hooking thread's pti->flags. 01278 * Hooking emulated DOS apps is prevented with the TIF_DOSEMULATOR bit in the 01279 * console thread: this is because these apps typically hog the CPU so much that 01280 * the hooking app does not respond rapidly enough to the hook messsages sent 01281 * to it. IanJa Nov 1994. 01282 * 01283 * History: 01284 * 02-07-91 DavidPe Created. 01285 * 1994 Nov 02 IanJa Hooking desktop and console windows. 01286 \***************************************************************************/ 01287 01288 LRESULT xxxCallHook2( 01289 PHOOK phkCall, 01290 int nCode, 01291 WPARAM wParam, 01292 LPARAM lParam, 01293 LPBOOL lpbAnsiHook) 01294 { 01295 UINT iHook; 01296 PHOOK phkSave; 01297 LONG_PTR nRet; 01298 PTHREADINFO ptiCurrent; 01299 BOOL fLoadSuccess; 01300 TL tlphkCall; 01301 TL tlphkSave; 01302 BYTE bHookFlags; 01303 BOOL fMustIntersend; 01304 01305 CheckCritIn(); 01306 01307 if (phkCall == NULL) { 01308 return 0; 01309 } 01310 01311 iHook = phkCall->iHook; 01312 01313 ptiCurrent = PtiCurrent(); 01314 /* 01315 * Only low level hooks are allowed in the RIT context 01316 * (This check used to be done in PhkFirstValid). 01317 */ 01318 if (ptiCurrent == gptiRit) { 01319 switch (iHook) { 01320 case WH_MOUSE_LL: 01321 case WH_KEYBOARD_LL: 01322 01323 #ifdef REDIRECTION 01324 case WH_HITTEST: 01325 #endif // REDIRECTION 01326 01327 break; 01328 01329 default: 01330 return 0; 01331 } 01332 } 01333 01334 /* 01335 * If this queue is in cleanup, exit: it has no business calling back 01336 * a hook proc. Also check if hooks are disabled for the thread. 01337 */ 01338 if ( ptiCurrent->TIF_flags & (TIF_INCLEANUP | TIF_DISABLEHOOKS) || 01339 ((ptiCurrent->rpdesk == NULL) && (phkCall->iHook != WH_MOUSE_LL))) { 01340 return ampiHookError[iHook + 1]; 01341 } 01342 01343 /* 01344 * Try to call each hook in the list until one is successful or 01345 * we reach the end of the list. 01346 */ 01347 do { 01348 *lpbAnsiHook = phkCall->flags & HF_ANSI; 01349 bHookFlags = abHookFlags[phkCall->iHook + 1]; 01350 01351 /* 01352 * Some WH_SHELL hook types can be called from console 01353 * HSHELL_APPCOMMAND added for bug 346575 DefWindowProc invokes a shell hook 01354 * for console windows if they don't handle the wm_appcommand message - we need the hook 01355 * to go through for csrss. 01356 */ 01357 if ((phkCall->iHook == WH_SHELL) && (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD)) { 01358 if ((nCode == HSHELL_LANGUAGE) || (nCode == HSHELL_WINDOWACTIVATED) || 01359 (nCode == HSHELL_APPCOMMAND)) { 01360 bHookFlags |= HKF_INTERSENDABLE; 01361 } 01362 } 01363 01364 if ((phkCall->iHook == WH_SHELL) && (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD)) { 01365 if ((nCode == HSHELL_ACCESSIBILITYSTATE) ) { 01366 bHookFlags |= HKF_INTERSENDABLE; 01367 } 01368 } 01369 01370 fMustIntersend = 01371 (GETPTI(phkCall) != ptiCurrent) && 01372 ( 01373 /* 01374 * We always want to intersend journal hooks. 01375 * CONSIDER (adams): Why? There's a performance hit by 01376 * doing so, so if we haven't a reason, we shouldn't 01377 * do it. 01378 * 01379 * we also need to intersend low level hooks. They can be called 01380 * from the desktop thread, the raw input thread AND also from 01381 * any thread that calls CallNextHookEx. 01382 */ 01383 (bHookFlags & (HKF_JOURNAL | HKF_LOWLEVEL)) 01384 01385 /* 01386 * We must intersend if a 16bit app hooks a 32bit app 01387 * because we can't load a 16bit dll into a 32bit process. 01388 * We must also intersend if a 16bit app hooks another 16bit app 01389 * in a different VDM, because we can't load a 16bit dll from 01390 * one VDM into a 16bit app in another VDM (because that 01391 * VDM is actually a 32bit process). 01392 */ 01393 || 01394 ( GETPTI(phkCall)->TIF_flags & TIF_16BIT && 01395 ( !(ptiCurrent->TIF_flags & TIF_16BIT) || 01396 ptiCurrent->ppi != GETPTI(phkCall)->ppi)) 01397 01398 #if defined(_WIN64) 01399 01400 /* 01401 * Intersend if a 64bit app hooks a 32bit app or 01402 * a 32bit app hooks a 64bit app. 01403 * This is necessary since a hook DLL can not be loaded 01404 * cross bit type. 01405 */ 01406 || 01407 ( (GETPTI(phkCall)->TIF_flags & TIF_WOW64) != 01408 (ptiCurrent->TIF_flags & TIF_WOW64) 01409 ) 01410 01411 #endif /* defined(_WIN64) */ 01412 01413 /* 01414 * We must intersend if a console or system thread is calling a hook 01415 * that is not in the same console or the system process. 01416 */ 01417 || 01418 ( ptiCurrent->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD) && 01419 GETPTI(phkCall)->ppi != ptiCurrent->ppi) 01420 01421 /* 01422 * If this is a global and non-journal hook, do a security 01423 * check on the current desktop to see if we can call here. 01424 * Note that we allow processes with the SYSTEM_LUID to hook 01425 * other processes even if the other process says that it 01426 * doesn't allow other accounts to hook them. We did this 01427 * because there was a bug in NT 3.x that allowed it and some 01428 * services were written to use it. 01429 */ 01430 || 01431 ( phkCall->flags & HF_GLOBAL && 01432 !RtlEqualLuid(&GETPTI(phkCall)->ppi->luidSession, &ptiCurrent->ppi->luidSession) && 01433 !(ptiCurrent->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK) && 01434 !RtlEqualLuid(&GETPTI(phkCall)->ppi->luidSession, &luidSystem)) 01435 01436 /* 01437 * We must intersend if the hooking thread is running in 01438 * another process and is restricted. 01439 */ 01440 || 01441 ( GETPTI(phkCall)->ppi != ptiCurrent->ppi && 01442 IsRestricted(GETPTI(phkCall)->pEThread)) 01443 ); 01444 01445 /* 01446 * We're calling back... make sure the hook doesn't go away while 01447 * we're calling back. We've thread locked here: we must unlock before 01448 * returning or enumerating the next hook in the chain. 01449 */ 01450 ThreadLockAlwaysWithPti(ptiCurrent, phkCall, &tlphkCall); 01451 01452 if (!fMustIntersend) { 01453 /* 01454 * Make sure the DLL for this hook, if any, has been loaded 01455 * for the current process. 01456 */ 01457 if ((phkCall->ihmod != -1) && 01458 (TESTHMODLOADED(ptiCurrent, phkCall->ihmod) == 0)) { 01459 01460 BOOL bWx86KnownDll; 01461 01462 /* 01463 * Try loading the library, since it isn't loaded in this processes 01464 * context. First lock this hook so it doesn't go away while we're 01465 * loading this library. 01466 */ 01467 bWx86KnownDll = (phkCall->flags & HF_WX86KNOWNDLL) != 0; 01468 fLoadSuccess = (xxxLoadHmodIndex(phkCall->ihmod, bWx86KnownDll) != NULL); 01469 01470 /* 01471 * If the LoadLibrary() failed, skip to the next hook and try 01472 * again. 01473 */ 01474 if (!fLoadSuccess) { 01475 goto LoopAgain; 01476 } 01477 } 01478 01479 /* 01480 * Is WH_DEBUG installed? If we're not already calling it, do so. 01481 */ 01482 if (IsHooked(ptiCurrent, WHF_DEBUG) && (phkCall->iHook != WH_DEBUG)) { 01483 DEBUGHOOKINFO debug; 01484 01485 debug.idThread = TIDq(ptiCurrent); 01486 debug.idThreadInstaller = 0; 01487 debug.code = nCode; 01488 debug.wParam = wParam; 01489 debug.lParam = lParam; 01490 01491 if (xxxCallHook(HC_ACTION, phkCall->iHook, (LPARAM)&debug, WH_DEBUG)) { 01492 /* 01493 * If WH_DEBUG returned non-zero, skip this hook and 01494 * try the next one. 01495 */ 01496 goto LoopAgain; 01497 } 01498 } 01499 01500 /* 01501 * Make sure the hook is still around before we 01502 * try and call it. 01503 */ 01504 if (HMIsMarkDestroy(phkCall)) { 01505 goto LoopAgain; 01506 } 01507 01508 /* 01509 * Time to call the hook! Lock it first so that it doesn't go away 01510 * while we're using it. Thread lock right away in case the lock frees 01511 * the previous contents. 01512 */ 01513 01514 #if DBG 01515 if (phkCall->flags & HF_GLOBAL) { 01516 UserAssert(phkCall->ptiHooked == NULL); 01517 } else { 01518 UserAssert(phkCall->ptiHooked == ptiCurrent); 01519 } 01520 #endif 01521 phkSave = ptiCurrent->sphkCurrent; 01522 ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave); 01523 01524 Lock(&ptiCurrent->sphkCurrent, phkCall); 01525 if (ptiCurrent->pClientInfo) 01526 ptiCurrent->pClientInfo->phkCurrent = phkCall; 01527 01528 nRet = xxxHkCallHook(phkCall, nCode, wParam, lParam); 01529 01530 Lock(&ptiCurrent->sphkCurrent, phkSave); 01531 if (ptiCurrent->pClientInfo) 01532 ptiCurrent->pClientInfo->phkCurrent = phkSave; 01533 01534 ThreadUnlock(&tlphkSave); 01535 01536 /* 01537 * This hook proc faulted, so unhook it and try the next one. 01538 */ 01539 if (phkCall->flags & HF_HOOKFAULTED) { 01540 PHOOK phkFault; 01541 01542 phkCall = PhkNextValid(phkCall); 01543 phkFault = ThreadUnlock(&tlphkCall); 01544 if (phkFault != NULL) { 01545 FreeHook(phkFault); 01546 } 01547 01548 continue; 01549 } 01550 01551 /* 01552 * Lastly, we're done with this hook so it is ok to unlock it (it may 01553 * get freed here! 01554 */ 01555 ThreadUnlock(&tlphkCall); 01556 01557 return nRet; 01558 01559 } else if (bHookFlags & HKF_INTERSENDABLE) { 01560 01561 /* 01562 * Receiving thread can access this structure since the 01563 * sender thread's stack is locked down during xxxInterSendMsgEx 01564 */ 01565 HOOKMSGSTRUCT hkmp; 01566 int timeout = 200; // 1/5 second !!! 01567 01568 hkmp.lParam = lParam; 01569 hkmp.phk = phkCall; 01570 hkmp.nCode = nCode; 01571 01572 /* 01573 * Thread lock right away in case the lock frees the previous contents 01574 */ 01575 phkSave = ptiCurrent->sphkCurrent; 01576 01577 ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave); 01578 01579 Lock(&ptiCurrent->sphkCurrent, phkCall); 01580 if (ptiCurrent->pClientInfo) 01581 ptiCurrent->pClientInfo->phkCurrent = phkCall; 01582 01583 /* 01584 * Make sure we don't get hung! 01585 */ 01586 if (bHookFlags & HKF_LOWLEVEL) 01587 timeout = gnllHooksTimeout; 01588 01589 /* 01590 * CONSIDER(adams): Why should a journaling hook be allowed to 01591 * hang the console or a system thread? Will that interfere with 01592 * the user's ability to cancel journaling through Ctrl+Esc? 01593 */ 01594 if (((bHookFlags & HKF_LOWLEVEL) == 0) && 01595 ( (bHookFlags & HKF_JOURNAL) || 01596 !(ptiCurrent->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)))) { 01597 01598 nRet = xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam, 01599 (LPARAM)&hkmp, ptiCurrent, GETPTI(phkCall), NULL); 01600 } else { 01601 /* 01602 * We are a server thread (console/desktop) and we aren't 01603 * journalling, so we can't allow the hookproc to hang us - 01604 * we must use a timeout. 01605 */ 01606 INTRSENDMSGEX ism; 01607 01608 ism.fuCall = ISM_TIMEOUT; 01609 ism.fuSend = SMTO_ABORTIFHUNG | SMTO_NORMAL; 01610 ism.uTimeout = timeout; 01611 ism.lpdwResult = &nRet; 01612 01613 /* 01614 * Don't hook DOS apps connected to the emulator - they often 01615 * grab too much CPU for the callback to the hookproc to 01616 * complete in a timely fashion, causing poor response. 01617 */ 01618 if ((ptiCurrent->TIF_flags & TIF_DOSEMULATOR) || 01619 FHungApp(GETPTI(phkCall), CMSHUNGAPPTIMEOUT) || 01620 !xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam, 01621 (LPARAM)&hkmp, ptiCurrent, GETPTI(phkCall), &ism)) { 01622 nRet = ampiHookError[iHook + 1]; 01623 } 01624 01625 /* 01626 * If the low-level hook is eaten, the app may wake up from 01627 * MsgWaitForMultipleObjects, clear the wake mask, but not get 01628 * anything in GetMessage / PeekMessage and we will think it's 01629 * hung. This causes problems in DirectInput because then the 01630 * app may miss some hooks if FHungApp returns true, see bug 01631 * 430342 for more details on this. 01632 */ 01633 if ((bHookFlags & HKF_LOWLEVEL) && nRet) { 01634 SET_TIME_LAST_READ(GETPTI(phkCall)); 01635 } 01636 } 01637 01638 Lock(&ptiCurrent->sphkCurrent, phkSave); 01639 if (ptiCurrent->pClientInfo) 01640 ptiCurrent->pClientInfo->phkCurrent = phkSave; 01641 01642 ThreadUnlock(&tlphkSave); 01643 ThreadUnlock(&tlphkCall); 01644 return nRet; 01645 } 01646 // fall-through 01647 01648 LoopAgain: 01649 phkCall = PhkNextValid(phkCall); 01650 ThreadUnlock(&tlphkCall); 01651 } while (phkCall != NULL); 01652 01653 return ampiHookError[iHook + 1]; 01654 } 01655 01656 /***************************************************************************\ 01657 * xxxCallMouseHook 01658 * 01659 * This is a helper routine that packages up a MOUSEHOOKSTRUCTEX and calls 01660 * the WH_MOUSE hook. 01661 * 01662 * History: 01663 * 02-09-91 DavidPe Created. 01664 \***************************************************************************/ 01665 01666 BOOL xxxCallMouseHook( 01667 UINT message, 01668 PMOUSEHOOKSTRUCTEX pmhs, 01669 BOOL fRemove) 01670 { 01671 BOOL bAnsiHook; 01672 01673 /* 01674 * Call the mouse hook. 01675 */ 01676 if (xxxCallHook2(PhkFirstValid(PtiCurrent(), WH_MOUSE), fRemove ? 01677 HC_ACTION : HC_NOREMOVE, (DWORD)message, (LPARAM)pmhs, &bAnsiHook)) { 01678 return TRUE; 01679 } 01680 01681 return FALSE; 01682 } 01683 01684 01685 /***************************************************************************\ 01686 * xxxCallJournalRecordHook 01687 * 01688 * This is a helper routine that packages up an EVENTMSG and calls 01689 * the WH_JOURNALRECORD hook. 01690 * 01691 * History: 01692 * 02-28-91 DavidPe Created. 01693 \***************************************************************************/ 01694 01695 void xxxCallJournalRecordHook( 01696 PQMSG pqmsg) 01697 { 01698 EVENTMSG emsg; 01699 BOOL bAnsiHook; 01700 01701 /* 01702 * Setup the EVENTMSG structure. 01703 */ 01704 emsg.message = pqmsg->msg.message; 01705 emsg.time = pqmsg->msg.time; 01706 01707 if (RevalidateHwnd(pqmsg->msg.hwnd)) { 01708 emsg.hwnd = pqmsg->msg.hwnd; 01709 } else { 01710 emsg.hwnd = NULL; 01711 } 01712 01713 if ((emsg.message >= WM_MOUSEFIRST) && (emsg.message <= WM_MOUSELAST)) { 01714 emsg.paramL = (UINT)pqmsg->msg.pt.x; 01715 emsg.paramH = (UINT)pqmsg->msg.pt.y; 01716 01717 } else if ((emsg.message >= WM_KEYFIRST) && (emsg.message <= WM_KEYLAST)) { 01718 BYTE bScanCode = LOBYTE(HIWORD(pqmsg->msg.lParam)); 01719 /* 01720 * Build up a Win 3.1 compatible journal record key 01721 * Win 3.1 ParamL 00 00 SC VK (SC=scan code VK=virtual key) 01722 * Also set ParamH 00 00 00 SC to be compatible with our Playback 01723 * 01724 * If WM_*CHAR messages ever come this way we would have a problem 01725 * because we would lose the top byte of the Unicode character. We'd 01726 * We'd get ParamL 00 00 SC CH (SC=scan code, CH = low byte of WCHAR) 01727 * 01728 */ 01729 if ((LOWORD(pqmsg->msg.wParam) == VK_PACKET) && (bScanCode == 0)) { 01730 /* 01731 * If we have an injected Unicode char (from SendInput), the 01732 * character value was cached, let's give that to them too. 01733 */ 01734 emsg.paramL = (UINT)MAKELONG(pqmsg->msg.wParam, PtiCurrent()->wchInjected); 01735 } else { 01736 emsg.paramL = MAKELONG(MAKEWORD(pqmsg->msg.wParam, bScanCode),0); 01737 } 01738 emsg.paramH = bScanCode; 01739 01740 UserAssert((emsg.message != WM_CHAR) && 01741 (emsg.message != WM_DEADCHAR) && 01742 (emsg.message != WM_SYSCHAR) && 01743 (emsg.message != WM_SYSDEADCHAR)); 01744 /* 01745 * Set extended-key bit. 01746 */ 01747 if (pqmsg->msg.lParam & 0x01000000) { 01748 emsg.paramH |= 0x8000; 01749 } 01750 01751 } else { 01752 RIPMSG2(RIP_WARNING, 01753 "Bad journal record message!\n" 01754 " message = 0x%08lx\n" 01755 " dwQEvent = 0x%08lx", 01756 pqmsg->msg.message, 01757 pqmsg->dwQEvent); 01758 } 01759 01760 /* 01761 * Call the journal recording hook. 01762 */ 01763 xxxCallHook2(PhkFirstGlobalValid(PtiCurrent(), WH_JOURNALRECORD), HC_ACTION, 0, 01764 (LPARAM)&emsg, &bAnsiHook); 01765 01766 /* 01767 * Write the MSG parameters back because the app may have modified it. 01768 * AfterDark's screen saver password actually zero's out the keydown 01769 * chars. 01770 * 01771 * If it was a mouse message patch up the mouse point. If it was a 01772 * WM_KEYxxx message convert the Win 3.1 compatible journal record key 01773 * back into a half backed WM_KEYxxx format. Only the VK and SC fields 01774 * where initialized at this point. 01775 * 01776 * wParam 00 00 00 VK lParam 00 SC 00 00 01777 */ 01778 if ((pqmsg->msg.message >= WM_MOUSEFIRST) && (pqmsg->msg.message <= WM_MOUSELAST)) { 01779 pqmsg->msg.pt.x = emsg.paramL; 01780 pqmsg->msg.pt.y = emsg.paramH; 01781 01782 } else if ((pqmsg->msg.message >= WM_KEYFIRST) && (pqmsg->msg.message <= WM_KEYLAST)) { 01783 (BYTE)pqmsg->msg.wParam = (BYTE)emsg.paramL; 01784 ((PBYTE)&pqmsg->msg.lParam)[2] = HIBYTE(LOWORD(emsg.paramL)); 01785 } 01786 } 01787 01788 01789 /***************************************************************************\ 01790 * xxxCallJournalPlaybackHook 01791 * 01792 * 01793 * History: 01794 * 03-01-91 DavidPe Created. 01795 \***************************************************************************/ 01796 01797 DWORD xxxCallJournalPlaybackHook( 01798 PQMSG pqmsg) 01799 { 01800 EVENTMSG emsg; 01801 LONG dt; 01802 PWND pwnd; 01803 WPARAM wParam; 01804 LPARAM lParam; 01805 POINT pt; 01806 PTHREADINFO ptiCurrent; 01807 BOOL bAnsiHook; 01808 PHOOK phkCall; 01809 TL tlphkCall; 01810 01811 UserAssert(IsWinEventNotifyDeferredOK()); 01812 01813 TryNextEvent: 01814 01815 /* 01816 * Initialized to the current time for compatibility with 01817 * <= 3.0. 01818 */ 01819 emsg.time = NtGetTickCount(); 01820 ptiCurrent = PtiCurrent(); 01821 pwnd = NULL; 01822 01823 phkCall = PhkFirstGlobalValid(ptiCurrent, WH_JOURNALPLAYBACK); 01824 ThreadLockWithPti(ptiCurrent, phkCall, &tlphkCall); 01825 01826 dt = (DWORD)xxxCallHook2(phkCall, HC_GETNEXT, 0, (LPARAM)&emsg, &bAnsiHook); 01827 01828 /* 01829 * -1 means some error occured. Return -1 for error. 01830 */ 01831 if (dt == 0xFFFFFFFF) { 01832 ThreadUnlock(&tlphkCall); 01833 return dt; 01834 } 01835 01836 /* 01837 * Update the message id. Need this if we decide to sleep. 01838 */ 01839 pqmsg->msg.message = emsg.message; 01840 01841 if (dt > 0) { 01842 if (ptiCurrent->TIF_flags & TIF_IGNOREPLAYBACKDELAY) { 01843 /* 01844 * This flag tells us to ignore the requested delay (set in mnloop) 01845 * We clear it to indicate that we did so. 01846 */ 01847 RIPMSG1(RIP_WARNING, "Journal Playback delay ignored (%lx)", emsg.message); 01848 ptiCurrent->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY; 01849 dt = 0; 01850 } else { 01851 ThreadUnlock(&tlphkCall); 01852 return dt; 01853 } 01854 } 01855 01856 /* 01857 * The app is ready to be asked for the next event 01858 */ 01859 01860 if ((emsg.message >= WM_MOUSEFIRST) && (emsg.message <= WM_MOUSELAST)) { 01861 01862 pt.x = (int)emsg.paramL; 01863 pt.y = (int)emsg.paramH; 01864 01865 lParam = MAKELONG(LOWORD(pt.x), LOWORD(pt.y)); 01866 wParam = 0; 01867 01868 /* 01869 * If the message has changed the mouse position, 01870 * update the cursor. 01871 */ 01872 if (pt.x != gpsi->ptCursor.x || pt.y != gpsi->ptCursor.y) { 01873 zzzInternalSetCursorPos(pt.x, pt.y); 01874 } 01875 01876 } else if ((emsg.message >= WM_KEYFIRST) && (emsg.message <= WM_KEYLAST)) { 01877 UINT wExtraStuff = 0; 01878 01879 if ((emsg.message == WM_KEYUP) || (emsg.message == WM_SYSKEYUP)) { 01880 wExtraStuff |= 0x8000; 01881 } 01882 01883 if ((emsg.message == WM_SYSKEYUP) || (emsg.message == WM_SYSKEYDOWN)) { 01884 wExtraStuff |= 0x2000; 01885 } 01886 01887 if (emsg.paramH & 0x8000) { 01888 wExtraStuff |= 0x0100; 01889 } 01890 01891 if (TestKeyStateDown(ptiCurrent->pq, (BYTE)emsg.paramL)) { 01892 wExtraStuff |= 0x4000; 01893 } 01894 lParam = MAKELONG(1, (UINT)((emsg.paramH & 0xFF) | wExtraStuff)); 01895 01896 if ((LOWORD(emsg.paramL) == VK_PACKET) && (LOBYTE(emsg.paramH) == 0)) { 01897 /* 01898 * We are playing back an injected Unicode char (see SendInput) 01899 * save the character for TranslateMessage to pick up. 01900 */ 01901 ptiCurrent->wchInjected = HIWORD(emsg.paramL); 01902 } else { 01903 /* 01904 * Raid# 65331 01905 * WM_KEY* and WM_SYSKEY* messages should only contain 8bit Virtual Keys. 01906 * Some applications passes scan code in HIBYTE and could screw up 01907 * the system. E.g. Tab Keydown, paramL: 0x0f09 where 0f is scan code 01908 */ 01909 DWORD dwMask = 0xff; 01910 01911 /* 01912 * There are old ANSI apps that only fill in the byte for when 01913 * they generate journal playback so we used to strip everything 01914 * else off. That however breaks unicode journalling; 22645 01915 * (Yes, some apps apparently do Playback WM_*CHAR msgs!) 01916 * 01917 */ 01918 if (!bAnsiHook || IS_DBCS_ENABLED()) { 01919 if (IS_CHAR_MSG(emsg.message)) { 01920 RIPMSG1(RIP_VERBOSE, "Unusual char message(%x) passed through JournalPlayback.", emsg.message); 01921 /* 01922 * Don't mask off HIBYTE(LOWORD(paramL)) for DBCS and UNICODE. 01923 */ 01924 dwMask = 0xffff; 01925 } 01926 } 01927 01928 wParam = emsg.paramL & dwMask; 01929 } 01930 01931 } else if (emsg.message == WM_QUEUESYNC) { 01932 if (emsg.paramL == 0) { 01933 pwnd = ptiCurrent->pq->spwndActive; 01934 } else { 01935 if ((pwnd = RevalidateHwnd((HWND)IntToPtr( emsg.paramL ))) == NULL) 01936 pwnd = ptiCurrent->pq->spwndActive; 01937 } 01938 01939 } else { 01940 /* 01941 * This event doesn't match up with what we're looking 01942 * for. If the hook is still valid, then skip this message 01943 * and try the next. 01944 */ 01945 if (phkCall == NULL || phkCall->offPfn == 0L) { 01946 /* Hook is nolonger valid, return -1 */ 01947 ThreadUnlock(&tlphkCall); 01948 return 0xFFFFFFFF; 01949 } 01950 01951 RIPMSG1(RIP_WARNING, 01952 "Bad journal playback message=0x%08lx", 01953 emsg.message); 01954 01955 xxxCallHook(HC_SKIP, 0, 0, WH_JOURNALPLAYBACK); 01956 ThreadUnlock(&tlphkCall); 01957 goto TryNextEvent; 01958 } 01959 01960 StoreQMessage(pqmsg, pwnd, emsg.message, wParam, lParam, 0, 0, 0); 01961 01962 ThreadUnlock(&tlphkCall); 01963 return 0; 01964 } 01965 01966 /***************************************************************************\ 01967 * FreeHook 01968 * 01969 * Free hook unlinks the HOOK structure from its hook-list and removes 01970 * any hmod dependencies on this hook. It also frees the HOOK structure. 01971 * 01972 * History: 01973 * 01-31-91 DavidPe Created. 01974 \***************************************************************************/ 01975 01976 VOID FreeHook( 01977 PHOOK phkFree) 01978 { 01979 /* 01980 * Paranoia... 01981 */ 01982 UserAssert(!(phkFree->flags & HF_FREED)); 01983 /* 01984 * BUGBUG 01985 * Unless we come from zzzUnhookWindowsHook, if this was a journaling hook 01986 * we don't unattach the threads. We should reconsider this one day. 01987 * MCostea Jan 21, 99 01988 */ 01989 01990 /* 01991 * Clear fsHooks bits the first time around (and mark it as destroyed). 01992 */ 01993 if (!(phkFree->flags & HF_DESTROYED)) { 01994 DbgValidateHooks (phkFree, phkFree->iHook); 01995 phkFree->flags |= HF_DESTROYED; 01996 /* 01997 * This hook has been marked as destroyed so CheckWHSBits 01998 * won't take it into account when updating the fsHooks bits. 01999 * However, this means that right at this moment fsHooks is 02000 * out of sync. So we need a flag to make the assertion freaks 02001 * (i.e., me) happy. 02002 */ 02003 #if DBG 02004 phkFree->flags |= HF_INCHECKWHF; 02005 #endif 02006 UserAssert((phkFree->ptiHooked != NULL) || (phkFree->flags & HF_GLOBAL)); 02007 CheckWHFBits(phkFree->ptiHooked != NULL 02008 ? phkFree->ptiHooked 02009 : GETPTI(phkFree), 02010 phkFree->iHook); 02011 #if DBG 02012 phkFree->flags &= ~HF_INCHECKWHF; 02013 #endif 02014 } 02015 /* 02016 * Mark it for destruction. If it the object is locked it can't 02017 * be freed right now. 02018 */ 02019 if (!HMMarkObjectDestroy((PVOID)phkFree)) { 02020 return; 02021 } 02022 /* 02023 * We're going to free this hook so get it off the list. 02024 */ 02025 UnlinkHook(phkFree); 02026 /* 02027 * Now remove the hmod dependency and free the 02028 * HOOK structure. 02029 */ 02030 if (phkFree->ihmod >= 0) { 02031 RemoveHmodDependency(phkFree->ihmod); 02032 } 02033 02034 #ifdef HOOKBATCH 02035 /* 02036 * Free the cached Events 02037 */ 02038 if (phkFree->aEventCache) { 02039 UserFreePool(phkFree->aEventCache); 02040 phkFree->aEventCache = NULL; 02041 } 02042 #endif //HOOKBATCH 02043 02044 #if DBG 02045 phkFree->flags |= HF_FREED; 02046 #endif 02047 02048 HMFreeObject((PVOID)phkFree); 02049 return; 02050 } 02051 /***************************************************************************\ 02052 * UnlinkHook 02053 * 02054 * Gets a hook out of its chain. Note that FreeThreadsWindowHooks unlinks 02055 * some hooks but don't free them. So this function doesn't assume that 02056 * the hook is going away. 02057 * 02058 * History: 02059 * 04-25-97 GerardoB Added Header 02060 \***************************************************************************/ 02061 void UnlinkHook( 02062 PHOOK phkFree) 02063 { 02064 PHOOK *pphkNext; 02065 PTHREADINFO ptiT; 02066 02067 CheckCritIn(); 02068 /* 02069 * Since we have the HOOK structure, we can tell if this a global 02070 * or local hook and start on the right list. 02071 */ 02072 if (phkFree->flags & HF_GLOBAL) { 02073 pphkNext = &GETPTI(phkFree)->pDeskInfo->aphkStart[phkFree->iHook + 1]; 02074 } else { 02075 ptiT = phkFree->ptiHooked; 02076 if (ptiT == NULL) { 02077 /* 02078 * Already unlinked (by FreeThreadsWindowHooks) 02079 */ 02080 return; 02081 } else { 02082 /* 02083 * Clear ptiHooked so we won't try to unlink it again. 02084 */ 02085 phkFree->ptiHooked = NULL; 02086 } 02087 pphkNext = &(ptiT->aphkStart[phkFree->iHook + 1]); 02088 /* 02089 * There must be at least one hook in the chain 02090 */ 02091 UserAssert(*pphkNext != NULL); 02092 } 02093 /* 02094 * Find the address of the phkNext pointing to phkFree 02095 */ 02096 while ((*pphkNext != phkFree) && (*pphkNext != NULL)) { 02097 pphkNext = &(*pphkNext)->phkNext; 02098 } 02099 /* 02100 * If we haven't found it, it must be global hook whose owner is gone or 02101 * has switched desktops. 02102 */ 02103 if (*pphkNext == NULL) { 02104 UserAssert(phkFree->flags & HF_GLOBAL); 02105 /* 02106 * if we saved a pdesk, use it. Else use the one we allocated it from 02107 */ 02108 if (phkFree->rpdesk != NULL) { 02109 UserAssert(GETPTI(phkFree) == gptiRit); 02110 UserAssert(phkFree->rpdesk != NULL); 02111 UserAssert(phkFree->rpdesk->pDeskInfo != gptiRit->pDeskInfo); 02112 02113 pphkNext = &phkFree->rpdesk->pDeskInfo->aphkStart[phkFree->iHook + 1]; 02114 } else { 02115 UserAssert(GETPTI(phkFree)->pDeskInfo != phkFree->head.rpdesk->pDeskInfo); 02116 pphkNext = &phkFree->head.rpdesk->pDeskInfo->aphkStart[phkFree->iHook + 1]; 02117 } 02118 02119 UserAssert(*pphkNext != NULL); 02120 while ((*pphkNext != phkFree) && (*pphkNext != NULL)) { 02121 pphkNext = &(*pphkNext)->phkNext; 02122 } 02123 } 02124 /* 02125 * We're supposed to find it 02126 */ 02127 UserAssert(*pphkNext == phkFree); 02128 /* 02129 * Unlink it 02130 */ 02131 *pphkNext = phkFree->phkNext; 02132 phkFree->phkNext = NULL; 02133 /* 02134 * If we had a desktop, unlock it 02135 */ 02136 if (phkFree->rpdesk != NULL) { 02137 UserAssert(phkFree->flags & HF_GLOBAL); 02138 UserAssert(GETPTI(phkFree) == gptiRit); 02139 UnlockDesktop(&phkFree->rpdesk, LDU_HOOK_DESK, 0); 02140 } 02141 } 02142 02143 /***************************************************************************\ 02144 * PhkFirstGlobalValid 02145 * 02146 * Returns the first not-destroyed hook on the given desktop info. 02147 * 02148 * History: 02149 * 03/24/97 GerardoB Created 02150 \***************************************************************************/ 02151 PHOOK PhkFirstGlobalValid(PTHREADINFO pti, int nFilterType) 02152 { 02153 PHOOK phk; 02154 02155 CheckCritIn(); 02156 phk = pti->pDeskInfo->aphkStart[nFilterType + 1]; 02157 /* 02158 * Return the first hook that it's not destroyed (i.e, the 02159 * first valid one). 02160 */ 02161 if ((phk != NULL) && (phk->flags & HF_DESTROYED)) { 02162 phk = PhkNextValid(phk); 02163 } 02164 /* 02165 * Good place to check fsHooks. If the bits are out of sync, 02166 * someone must be adjusting them. 02167 */ 02168 DbgValidatefsHook(phk, nFilterType, pti, TRUE); 02169 DbgValidateHooks(phk, nFilterType); 02170 return phk; 02171 } 02172 02173 /***************************************************************************\ 02174 * PhkFirstValid 02175 * 02176 * Given a filter-type PhkFirstValid() returns the first hook, if any, of the 02177 * specified type. 02178 * 02179 * History: 02180 * 02-10-91 DavidPe Created. 02181 \***************************************************************************/ 02182 02183 PHOOK PhkFirstValid( 02184 PTHREADINFO pti, 02185 int nFilterType) 02186 { 02187 PHOOK phk; 02188 CheckCritIn(); 02189 /* 02190 * Grab the first hook off the local hook-list 02191 * for the current queue. 02192 */ 02193 phk = pti->aphkStart[nFilterType + 1]; 02194 /* 02195 * If there aren't any local hooks, try the global hooks. 02196 */ 02197 if (phk == NULL) { 02198 phk = pti->pDeskInfo->aphkStart[nFilterType + 1]; 02199 } 02200 /* 02201 * Return the first hook that it's not destroyed (i.e, the 02202 * first valid one). 02203 */ 02204 if ((phk != NULL) && (phk->flags & HF_DESTROYED)) { 02205 phk = PhkNextValid(phk); 02206 } 02207 /* 02208 * Good place to check fsHooks. If the bits are out of sync, 02209 * someone must be adjusting them. 02210 */ 02211 02212 DbgValidatefsHook(phk, nFilterType, pti, FALSE); 02213 DbgValidateHooks(phk, nFilterType); 02214 return phk; 02215 } 02216 02217 /***************************************************************************\ 02218 * FreeThreadsWindowHooks 02219 * 02220 * During 'exit-list' processing this function is called to free any hooks 02221 * created on, or set for the current queue. 02222 * 02223 * History: 02224 * 02-10-91 DavidPe Created. 02225 \***************************************************************************/ 02226 02227 VOID FreeThreadsWindowHooks(VOID) 02228 { 02229 int iHook; 02230 PHOOK phk, phkNext; 02231 PTHREADINFO ptiCurrent = PtiCurrent(); 02232 02233 /* 02234 * If there is not thread info, there are not hooks to worry about 02235 */ 02236 if (ptiCurrent == NULL || ptiCurrent->rpdesk == NULL) { 02237 return; 02238 } 02239 /* 02240 * In case we have a hook locked in as the current hook unlock it 02241 * so it can be freed 02242 */ 02243 Unlock(&ptiCurrent->sphkCurrent); 02244 02245 UserAssert(ptiCurrent->TIF_flags & TIF_INCLEANUP); 02246 // Why bother doing this? We won't be calling back to user mode again! 02247 // if (ptiCurrent->pClientInfo) { 02248 // ptiCurrent->pClientInfo->phkCurrent = NULL; 02249 // } 02250 02251 /* 02252 * Loop through all the hook types. 02253 */ 02254 for (iHook = WH_MIN ; iHook <= WH_MAX ; ++iHook) { 02255 /* 02256 * Loop through all the hooks of this type, including the 02257 * ones already marked as destroyed (so don't call 02258 * PhkFirstValid and PhkNextValid). 02259 */ 02260 phk = ptiCurrent->aphkStart[iHook + 1]; 02261 if (phk == NULL) { 02262 phk = ptiCurrent->pDeskInfo->aphkStart[iHook + 1]; 02263 UserAssert((phk == NULL) || (phk->flags & HF_GLOBAL)); 02264 } 02265 02266 while (phk != NULL) { 02267 /* 02268 * We might free phk below, so grab the next now 02269 * If at end of local chain, jump to the global chain 02270 */ 02271 phkNext = phk->phkNext; 02272 if ((phkNext == NULL) && !(phk->flags & HF_GLOBAL)) { 02273 phkNext = ptiCurrent->pDeskInfo->aphkStart[iHook + 1]; 02274 UserAssert((phkNext == NULL) || (phkNext->flags & HF_GLOBAL)); 02275 } 02276 /* 02277 * If this is a local(thread) hook, unlink it and mark it as 02278 * destroyed so we won't call it anymore. We want to do 02279 * this even if not calling FreeHook; also note that 02280 * FreeHook won't unlink it if locked so we do it here anyway. 02281 */ 02282 if (!(phk->flags & HF_GLOBAL)) { 02283 UserAssert(ptiCurrent == phk->ptiHooked); 02284 UnlinkHook(phk); 02285 phk->flags |= HF_DESTROYED; 02286 phk->phkNext = NULL; 02287 } 02288 /* 02289 * If this hook was created by this thread, free it 02290 */ 02291 if (GETPTI(phk) == ptiCurrent) { 02292 FreeHook(phk); 02293 } 02294 02295 phk = phkNext; 02296 } 02297 /* 02298 * All local hooks should be unlinked 02299 */ 02300 UserAssert(ptiCurrent->aphkStart[iHook + 1] == NULL); 02301 } /* for (iHook = WH_MIN....*/ 02302 02303 /* 02304 * Keep fsHooks in sync. 02305 */ 02306 ptiCurrent->fsHooks = 0; 02307 } 02308 02309 /***************************************************************************\ 02310 * zzzRegisterSystemThread: Private API 02311 * 02312 * Used to set various attributes pertaining to a thread. 02313 * 02314 * History: 02315 * 21-Jun-1994 from Chicago Created. 02316 \***************************************************************************/ 02317 02318 VOID zzzRegisterSystemThread (DWORD dwFlags, DWORD dwReserved) 02319 { 02320 PTHREADINFO ptiCurrent; 02321 02322 UserAssert(dwReserved == 0); 02323 02324 if (dwReserved != 0) 02325 return; 02326 02327 ptiCurrent = PtiCurrent(); 02328 02329 if (dwFlags & RST_DONTATTACHQUEUE) 02330 ptiCurrent->TIF_flags |= TIF_DONTATTACHQUEUE; 02331 02332 if (dwFlags & RST_DONTJOURNALATTACH) { 02333 ptiCurrent->TIF_flags |= TIF_DONTJOURNALATTACH; 02334 02335 /* 02336 * If we are already journaling, then this queue was already 02337 * journal attached. We need to unattach and reattach journaling 02338 * so that we are removed from the journal attached queues. 02339 */ 02340 if (FJOURNALPLAYBACK() || FJOURNALRECORD()) { 02341 zzzJournalAttach(ptiCurrent, FALSE); 02342 zzzJournalAttach(ptiCurrent, TRUE); 02343 } 02344 } 02345 } 02346 02347 #ifdef REDIRECTION 02348 02349 /***************************************************************************\ 02350 * xxxGetCursorPos 02351 * 02352 \***************************************************************************/ 02353 02354 BOOL 02355 xxxGetCursorPos( 02356 LPPOINT lpPt) 02357 { 02358 POINT pt; 02359 02360 CheckCritIn(); 02361 02362 /* 02363 * If there is no CBT hook installed bail out. 02364 */ 02365 if (!IsHooked(PtiCurrent(), WHF_CBT)) 02366 return TRUE; 02367 02368 try { 02369 ProbeForWrite(lpPt, sizeof(RECT), DATAALIGN); 02370 RtlCopyMemory(&pt, lpPt, sizeof(RECT)); 02371 } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { 02372 return FALSE; 02373 } 02374 02375 xxxCallHook(HCBT_GETCURSORPOS, 0, (LPARAM)&pt, WH_CBT); 02376 02377 try { 02378 RtlCopyMemory(lpPt, &pt, sizeof(RECT)); 02379 } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { 02380 return FALSE; 02381 } 02382 02383 return TRUE; 02384 } 02385 02386 #endif // REDIRECTION 02387

Generated on Sat May 15 19:40:18 2004 for test by doxygen 1.3.7