最近正在研究不完整的Win2K源代码。<br>/***************************************************************************/<br>* xxxCallHook2<br>*<br>* When you have an actual HOOK structure to call, you'd use this function.<br>* It will check to see if the hook hasn't already been unhooked, and if<br>* is it will free it and keep looking until it finds a hook it can call<br>* or hits the end of the list. We also make sure any needed DLLs are loaded<br>* here. We also check to see if the HOOK was unhooked inside the call<br>* after we return.<br>*<br>* Note: Hooking server-side window procedures (such as the desktop and console<br>* windows) can only be done by sending the hook message to the hooking app.<br>* (This is because we must not load the hookproc DLL into the server process).<br>* The hook types this can be done with are currently WH_JOURNALRECORD,<br>* WH_JOURNALPLAYBACK, WH_KEYBOARD and WH_MOUSE : these are all marked as<br>* HKF_INTERSENDABLE. In order to prevent a global hooker from locking up the whole<br>* system, the hook message is sent with a timeout. To ensure minimal<br>* performance degradation, the hooker process is set to foreground priority,<br>* and prevented from being set back to background priority with the<br>* TIF_GLOBALHOOKER bit in hooking thread's pti-&gt;flags.<br>* Hooking emulated DOS apps is prevented with the TIF_DOSEMULATOR bit in the<br>* console thread: this is because these apps typically hog the CPU so much that<br>* the hooking app does not respond rapidly enough to the hook messsages sent<br>* to it. IanJa Nov 1994.<br>*<br>* History:<br>* 02-07-91 DavidPe Created.<br>* 1994 Nov 02 IanJa Hooking desktop and console windows.<br>/***************************************************************************/<br><br>LRESULT xxxCallHook2(<br> PHOOK phkCall,<br> int nCode,<br> WPARAM wParam,<br> LPARAM lParam,<br> LPBOOL lpbAnsiHook)<br>{<br> UINT iHook;<br> PHOOK phkSave;<br> LONG_PTR nRet;<br> PTHREADINFO ptiCurrent;<br> BOOL fLoadSuccess;<br> TL tlphkCall;<br> TL tlphkSave;<br> BYTE bHookFlags;<br> BOOL fMustIntersend;<br><br> CheckCritIn();<br><br> if (phkCall == NULL) {<br> return 0;<br> }<br><br> iHook = phkCall-&gt;iHook;<br><br> ptiCurrent = PtiCurrent();<br> /*<br> * Only low level hooks are allowed in the RIT context<br> * (This check used to be done in PhkFirstValid).<br> */<br> if (ptiCurrent == gptiRit) {<br> switch (iHook) {<br> case WH_MOUSE_LL:<br> case WH_KEYBOARD_LL:<br><br>#ifdef REDIRECTION<br> case WH_HITTEST:<br>#endif // REDIRECTION<br><br> break;<br><br> default:<br> return 0;<br> }<br> }<br><br> /*<br> * If this queue is in cleanup, exit: it has no business calling back<br> * a hook proc. Also check if hooks are disabled for the thread.<br> */<br> if ( ptiCurrent-&gt;TIF_flags & (TIF_INCLEANUP | TIF_DISABLEHOOKS) ||<br> ((ptiCurrent-&gt;rpdesk == NULL) && (phkCall-&gt;iHook != WH_MOUSE_LL))) {<br> return ampiHookError[iHook + 1];<br> }<br><br> /*<br> * Try to call each hook in the list until one is successful or<br> * we reach the end of the list.<br> */<br> do {<br> *lpbAnsiHook = phkCall-&gt;flags & HF_ANSI;<br> bHookFlags = abHookFlags[phkCall-&gt;iHook + 1];<br><br> /*<br> * Some WH_SHELL hook types can be called from console<br> * HSHELL_APPCOMMAND added for bug 346575 DefWindowProc invokes a shell hook<br> * for console windows if they don't handle the wm_appcommand message - we need the hook<br> * to go through for csrss.<br> */<br> if ((phkCall-&gt;iHook == WH_SHELL) && (ptiCurrent-&gt;TIF_flags & TIF_CSRSSTHREAD)) {<br> if ((nCode == HSHELL_LANGUAGE) || (nCode == HSHELL_WINDOWACTIVATED) ||<br> (nCode == HSHELL_APPCOMMAND)) {<br> bHookFlags |= HKF_INTERSENDABLE;<br> }<br> }<br><br> if ((phkCall-&gt;iHook == WH_SHELL) && (ptiCurrent-&gt;TIF_flags & TIF_SYSTEMTHREAD)) {<br> if ((nCode == HSHELL_ACCESSIBILITYSTATE) ) {<br> bHookFlags |= HKF_INTERSENDABLE;<br> }<br> }<br><br> fMustIntersend =<br> (GETPTI(phkCall) != ptiCurrent) &&<br> (<br> /*<br> * We always want to intersend journal hooks.<br> * CONSIDER (adams): Why? There's a performance hit by<br> * doing so, so if we haven't a reason, we shouldn't<br> * do it.<br> *<br> * we also need to intersend low level hooks. They can be called<br> * from the desktop thread, the raw input thread AND also from<br> * any thread that calls CallNextHookEx.<br> */<br> (bHookFlags & (HKF_JOURNAL | HKF_LOWLEVEL))<br><br> /*<br> * We must intersend if a 16bit app hooks a 32bit app<br> * because we can't load a 16bit dll into a 32bit process.<br> * We must also intersend if a 16bit app hooks another 16bit app<br> * in a different VDM, because we can't load a 16bit dll from<br> * one VDM into a 16bit app in another VDM (because that<br> * VDM is actually a 32bit process).<br> */<br> ||<br> ( GETPTI(phkCall)-&gt;TIF_flags & TIF_16BIT &&<br> ( !(ptiCurrent-&gt;TIF_flags & TIF_16BIT) ||<br> ptiCurrent-&gt;ppi != GETPTI(phkCall)-&gt;ppi))<br><br>#if defined(_WIN64)<br><br> /*<br> * Intersend if a 64bit app hooks a 32bit app or<br> * a 32bit app hooks a 64bit app.<br> * This is necessary since a hook DLL can not be loaded<br> * cross bit type.<br> */<br> ||<br> ( (GETPTI(phkCall)-&gt;TIF_flags & TIF_WOW64) !=<br> (ptiCurrent-&gt;TIF_flags & TIF_WOW64)<br>  
<br><br>#endif /* defined(_WIN64) */<br><br> /*<br> * We must intersend if a console or system thread is calling a hook<br> * that is not in the same console or the system process.<br> */<br> ||<br> ( ptiCurrent-&gt;TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD) &&<br> GETPTI(phkCall)-&gt;ppi != ptiCurrent-&gt;ppi)<br><br> /*<br> * If this is a global and non-journal hook, do a security<br> * check on the current desktop to see if we can call here.<br> * Note that we allow processes with the SYSTEM_LUID to hook<br> * other processes even if the other process says that it<br> * doesn't allow other accounts to hook them. We did this<br> * because there was a bug in NT 3.x that allowed it and some<br> * services were written to use it.<br> */<br> ||<br> ( phkCall-&gt;flags & HF_GLOBAL &&<br> !RtlEqualLuid(&GETPTI(phkCall)-&gt;ppi-&gt;luidSession, &ptiCurrent-&gt;ppi-&gt;luidSession) &&<br> !(ptiCurrent-&gt;TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK) &&<br> !RtlEqualLuid(&GETPTI(phkCall)-&gt;ppi-&gt;luidSession, &luidSystem))<br><br> /*<br> * We must intersend if the hooking thread is running in<br> * another process and is restricted.<br> */<br> ||<br> ( GETPTI(phkCall)-&gt;ppi != ptiCurrent-&gt;ppi &&<br> IsRestricted(GETPTI(phkCall)-&gt;pEThread))<br> );<br><br> /*<br> * We're calling back... make sure the hook doesn't go away while<br> * we're calling back. We've thread locked here: we must unlock before<br> * returning or enumerating the next hook in the chain.<br> */<br> ThreadLockAlwaysWithPti(ptiCurrent, phkCall, &tlphkCall);<br><br> if (!fMustIntersend) {<br> /*<br> * Make sure the DLL for this hook, if any, has been loaded<br> * for the current process.<br> */<br> if ((phkCall-&gt;ihmod != -1) &&<br> (TESTHMODLOADED(ptiCurrent, phkCall-&gt;ihmod) == 0)) {<br><br> BOOL bWx86KnownDll;<br><br> /*<br> * Try loading the library, since it isn't loaded in this processes<br> * context. First lock this hook so it doesn't go away while we're<br> * loading this library.<br> */<br> bWx86KnownDll = (phkCall-&gt;flags & HF_WX86KNOWNDLL) != 0;<br> fLoadSuccess = (xxxLoadHmodIndex(phkCall-&gt;ihmod, bWx86KnownDll) != NULL);<br><br> /*<br> * If the LoadLibrary() failed, skip to the next hook and try<br> * again.<br> */<br> if (!fLoadSuccess) {<br> goto LoopAgain;<br> }<br> }<br><br> /*<br> * Is WH_DEBUG installed? If we're not already calling it, do so.<br> */<br> if (IsHooked(ptiCurrent, WHF_DEBUG) && (phkCall-&gt;iHook != WH_DEBUG)) {<br> DEBUGHOOKINFO debug;<br><br> debug.idThread = TIDq(ptiCurrent);<br> debug.idThreadInstaller = 0; // TNND,微软就是可恶,非要给个0,就是不想让人知道钩子的安装者!<br> debug.code = nCode;<br> debug.wParam = wParam;<br> debug.lParam = lParam;<br><br> if (xxxCallHook(HC_ACTION, phkCall-&gt;iHook, (LPARAM)&debug, WH_DEBUG)) {<br> /*<br> * If WH_DEBUG returned non-zero, skip this hook and<br> * try the next one.<br> */<br> goto LoopAgain;<br> }<br> }<br><br> /*<br> * Make sure the hook is still around before we<br> * try and call it.<br> */<br> if (HMIsMarkDestroy(phkCall)) {<br> goto LoopAgain;<br> }<br><br> /*<br> * Time to call the hook! Lock it first so that it doesn't go away<br> * while we're using it. Thread lock right away in case the lock frees<br> * the previous contents.<br> */<br><br>#if DBG<br> if (phkCall-&gt;flags & HF_GLOBAL) {<br> UserAssert(phkCall-&gt;ptiHooked == NULL);<br> } else {<br> UserAssert(phkCall-&gt;ptiHooked == ptiCurrent);<br> }<br>#endif<br> phkSave = ptiCurrent-&gt;sphkCurrent;<br> ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave);<br><br> Lock(&ptiCurrent-&gt;sphkCurrent, phkCall);<br> if (ptiCurrent-&gt;pClientInfo)<br> ptiCurrent-&gt;pClientInfo-&gt;phkCurrent = phkCall;<br><br> nRet = xxxHkCallHook(phkCall, nCode, wParam, lParam);<br><br> Lock(&ptiCurrent-&gt;sphkCurrent, phkSave);<br> if (ptiCurrent-&gt;pClientInfo)<br> ptiCurrent-&gt;pClientInfo-&gt;phkCurrent = phkSave;<br><br> ThreadUnlock(&tlphkSave);<br><br> /*<br> * This hook proc faulted, so unhook it and try the next one.<br> */<br> if (phkCall-&gt;flags & HF_HOOKFAULTED) {<br> PHOOK phkFault;<br><br> phkCall = PhkNextValid(phkCall);<br> phkFault = ThreadUnlock(&tlphkCall);<br> if (phkFault != NULL) {<br> FreeHook(phkFault);<br> }<br><br> continue;<br> }<br><br> /*<br> * Lastly, we're done with this hook so it is ok to unlock it (it may<br> * get freed here!<br> */<br> ThreadUnlock(&tlphkCall);<br><br> return nRet;<br><br> } else if (bHookFlags & HKF_INTERSENDABLE) {<br><br> /*<br> * Receiving thread can access this structure since the<br> * sender thread's stack is locked down during xxxInterSendMsgEx<br> */<br> HOOKMSGSTRUCT hkmp;<br> int timeout = 200; // 1/5 second !!!<br><br> hkmp.lParam = lParam;<br> hkmp.phk = phkCall;<br> hkmp.nCode = nCode;<br><br> /*<br> * Thread lock right away in case the lock frees the previous contents<br> */<br> phkSave = ptiCurrent-&gt;sphkCurrent;<br><br> ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave);<br><br> Lock(&ptiCurrent-&gt;sphkCurrent, phkCall);<br> if (ptiCurrent-&gt;pClientInfo)<br> ptiCurrent-&gt;pClientInfo-&gt;phkCurrent = phkCall;<br><br> /*<br> * Make sure we don't get hung!<br> */<br> if (bHookFlags & HKF_LOWLEVEL)<br> timeout = gnllHooksTimeout;<br><br> /*<br> * CONSIDER(adams): Why should a journaling hook be allowed to<br> * hang the console or a system thread? Will that interfere with<br> * the user's ability to cancel journaling through Ctrl+Esc?<br> */<br> if (((bHookFlags & HKF_LOWLEVEL) == 0) &&<br> ( (bHookFlags & HKF_JOURNAL) ||<br> !(ptiCurrent-&gt;TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)))) {<br><br> nRet = xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam,<br> (LPARAM)&hkmp, ptiCurrent, GETPTI(phkCall), NULL);<br> } else {<br> /*<br> * We are a server thread (console/desktop) and we aren't<br> * journalling, so we can't allow the hookproc to hang us -<br> * we must use a timeout.<br> */<br> INTRSENDMSGEX ism;<br><br> ism.fuCall = ISM_TIMEOUT;<br> ism.fuSend = SMTO_ABORTIFHUNG | SMTO_NORMAL;<br> ism.uTimeout = timeout;<br> ism.lpdwResult = &nRet;<br><br> /*<br> * Don't hook DOS apps connected to the emulator - they often<br> * grab too much CPU for the callback to the hookproc to<br> * complete in a timely fashion, causing poor response.<br> */<br> if ((ptiCurrent-&gt;TIF_flags & TIF_DOSEMULATOR) ||<br> FHungApp(GETPTI(phkCall), CMSHUNGAPPTIMEOUT) ||<br> !xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam,<br> (LPARAM)&hkmp, ptiCurrent, GETPTI(phkCall), &ism)) {<br> nRet = ampiHookError[iHook + 1];<br> }<br><br> /*<br> * If the low-level hook is eaten, the app may wake up from<br> * MsgWaitForMultipleObjects, clear the wake mask, but not get<br> * anything in GetMessage / PeekMessage and we will think it's<br> * hung. This causes problems in DirectInput because then the<br> * app may miss some hooks if FHungApp returns true, see bug<br> * 430342 for more details on this.<br> */<br> if ((bHookFlags & HKF_LOWLEVEL) && nRet) {<br> SET_TIME_LAST_READ(GETPTI(phkCall));<br> }<br> }<br><br> Lock(&ptiCurrent-&gt;sphkCurrent, phkSave);<br> if (ptiCurrent-&gt;pClientInfo)<br> ptiCurrent-&gt;pClientInfo-&gt;phkCurrent = phkSave;<br><br> ThreadUnlock(&tlphkSave);<br> ThreadUnlock(&tlphkCall);<br> return nRet;<br> }<br> // fall-through<br><br>LoopAgain:<br> phkCall = PhkNextValid(phkCall);<br> ThreadUnlock(&tlphkCall);<br> } while (phkCall != NULL);<br><br> return ampiHookError[iHook + 1];<br>}<br>