Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

850 lines
25 KiB

/****************************** Module Header ******************************\
* Module Name: hotkeys.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* This module contains the core functions of hotkey processing.
*
* History:
* 12-04-90 DavidPe Created.
* 02-12-91 JimA Added access checks
* 13-Feb-1991 mikeke Added Revalidation code (None)
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
static PHOTKEY gphkHashTable[128];
/*
* This is the hash function for vks. The vast majority of hotkeys will have
* vk values < 128, so we limit our table to that size. Worst case (all vks
* > 128) we'll have the same perf (essentially) as the old, linked list
* code.
*/
__inline BYTE HKHashVK(
UINT vk)
{
return (BYTE)(vk & (ARRAY_SIZE(gphkHashTable) - 1));
}
/***************************************************************************\
* HKGetHashHead
*
* This routine returns the start of the bucket keyed by the specified vk.
*
* History:
* 08-13-2002 JasonSch Created.
\***************************************************************************/
PHOTKEY HKGetHashHead(
UINT vk)
{
return gphkHashTable[HKHashVK(vk)];
}
/***************************************************************************\
* HKInsertHashElement
*
* Inserts a HOTKEY structure into the hash table.
*
* History:
* 08-13-2002 JasonSch Created.
\***************************************************************************/
VOID HKInsertHashElement(
PHOTKEY phk)
{
BYTE index = HKHashVK(phk->vk);
PHOTKEY phkT;
phkT = gphkHashTable[index];
phk->phkNext = phkT;
gphkHashTable[index] = phk;
}
/***************************************************************************\
* SetDebugHotKeys
*
* This routine registers the default system hotkeys for debugging.
*
* History:
* 12-04-90 DavidPe Created.
\***************************************************************************/
VOID SetDebugHotKeys(
VOID)
{
UINT VkDebug;
FastGetProfileDwordW(NULL, PMAP_AEDEBUG, L"UserDebuggerHotkey", 0, &VkDebug, 0);
if (VkDebug == 0) {
if (ENHANCED_KEYBOARD(gKeyboardInfo.KeyboardIdentifier)) {
VkDebug = VK_F12;
} else {
VkDebug = VK_SUBTRACT;
}
} else {
UserAssert((0xFFFFFF00 & VkDebug) == 0);
}
_UnregisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUG);
_UnregisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUGSERVER);
_RegisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUG, 0, VkDebug);
_RegisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUGSERVER, MOD_SHIFT, VkDebug);
}
/***************************************************************************\
* DestroyThreadsHotKeys
*
* History:
* 26-Feb-1991 mikeke Created.
\***************************************************************************/
VOID DestroyThreadsHotKeys(
VOID)
{
PHOTKEY *pphk, phk;
PTHREADINFO ptiCurrent = PtiCurrent();
int i = 0;
for (; i < ARRAY_SIZE(gphkHashTable); ++i) {
pphk = &gphkHashTable[i];
while (*pphk) {
if ((*pphk)->pti == ptiCurrent) {
phk = *pphk;
*pphk = (*pphk)->phkNext;
/*
* Unlock the object stored here.
*/
if (phk->spwnd != PWND_FOCUS && phk->spwnd != PWND_INPUTOWNER) {
Unlock(&phk->spwnd);
}
UserFreePool(phk);
} else {
pphk = &((*pphk)->phkNext);
}
}
}
}
/***************************************************************************\
* DestroyWindowsHotKeys
*
* Frees hotkeys associated with the specified pwnd that were not explicitly
* unregistered by the app.
*
* History:
* 23-Sep-1992 IanJa Created.
\***************************************************************************/
VOID DestroyWindowsHotKeys(
PWND pwnd)
{
PHOTKEY *pphk, phk;
int i = 0;
for (; i < ARRAY_SIZE(gphkHashTable); ++i) {
pphk = &gphkHashTable[i];
while (*pphk) {
if ((*pphk)->spwnd == pwnd) {
phk = *pphk;
*pphk = (*pphk)->phkNext;
Unlock(&phk->spwnd);
UserFreePool(phk);
} else {
pphk = &((*pphk)->phkNext);
}
}
}
}
/***************************************************************************\
* _RegisterHotKey (API)
*
* This API registers the hotkey specified. If the specified key sequence has
* already been registered we return FALSE. If the specified hwnd and id have
* already been registered, fsModifiers and vk are reset for the HOTKEY.
*
* History:
* 12-04-90 DavidPe Created.
* 02-12-91 JimA Added access check
\***************************************************************************/
BOOL _RegisterHotKey(
PWND pwnd,
int id,
UINT fsModifiers,
UINT vk)
{
PHOTKEY phk;
BOOL fKeysExist, bSAS;
PTHREADINFO ptiCurrent;
WORD wFlags;
wFlags = fsModifiers & MOD_SAS;
fsModifiers &= ~MOD_SAS;
ptiCurrent = PtiCurrent();
/*
* Blow it off if the caller is not the windowstation init thread
* and doesn't have the proper access rights
*/
if (PsGetCurrentProcess() != gpepCSRSS) {
if (grpWinStaList && !CheckWinstaWriteAttributesAccess()) {
return FALSE;
}
}
/*
* If VK_PACKET is specified, just bail out, since VK_PACKET is
* not a real keyboard input.
*/
if (vk == VK_PACKET) {
return FALSE;
}
/*
* If this is the SAS check that winlogon is the one registering it.
*/
if ((wFlags & MOD_SAS) != 0 && PsGetCurrentProcessId() == gpidLogon) {
bSAS = TRUE;
} else {
bSAS = FALSE;
}
/*
* Can't register hotkey for a window of another queue.
*/
if (pwnd != PWND_FOCUS && pwnd != PWND_INPUTOWNER) {
if (GETPTI(pwnd) != ptiCurrent) {
RIPERR1(ERROR_WINDOW_OF_OTHER_THREAD,
RIP_WARNING,
"hwnd 0x%x belongs to a different thread",
HWq(pwnd));
return FALSE;
}
}
phk = FindHotKey(ptiCurrent, pwnd, id, fsModifiers, vk, FALSE, &fKeysExist);
/*
* If the keys have already been registered, return FALSE.
*/
if (fKeysExist) {
RIPERR0(ERROR_HOTKEY_ALREADY_REGISTERED,
RIP_WARNING,
"Hotkey already exists");
return FALSE;
}
if (phk == NULL) {
/*
* This hotkey doesn't exist yet.
*/
phk = (PHOTKEY)UserAllocPool(sizeof(HOTKEY), TAG_HOTKEY);
if (phk == NULL) {
return FALSE;
}
phk->pti = ptiCurrent;
if (pwnd != PWND_FOCUS && pwnd != PWND_INPUTOWNER) {
phk->spwnd = NULL;
Lock(&phk->spwnd, pwnd);
} else {
phk->spwnd = pwnd;
}
phk->fsModifiers = (WORD)fsModifiers;
phk->wFlags = wFlags;
phk->vk = vk;
phk->id = id;
/*
* Add the new hotkey to our global hash.
*/
HKInsertHashElement(phk);
} else {
/*
* Hotkey already exists, reset the keys.
*/
phk->fsModifiers = (WORD)fsModifiers;
phk->wFlags = wFlags;
phk->vk = vk;
}
if (bSAS) {
/*
* Store the SAS on the terminal.
*/
gvkSAS = vk;
gfsSASModifiers = fsModifiers;
}
return TRUE;
}
/***************************************************************************\
* _UnregisterHotKey (API)
*
* This API will unregister the specified hwnd/id hotkey so that the
* WM_HOTKEY message will not be generated for it.
*
* History:
* 12-04-90 DavidPe Created.
\***************************************************************************/
BOOL _UnregisterHotKey(
PWND pwnd,
int id)
{
PHOTKEY phk;
BOOL fKeysExist;
PTHREADINFO ptiCurrent = PtiCurrent();
phk = FindHotKey(ptiCurrent, pwnd, id, 0, 0, TRUE, &fKeysExist);
if (phk == NULL) {
RIPERR2(ERROR_HOTKEY_NOT_REGISTERED,
(pwnd == PWND_INPUTOWNER) ? RIP_VERBOSE : RIP_WARNING,
"Hotkey 0x%x on pwnd 0x%p does not exist",
id,
pwnd);
return FALSE;
}
return TRUE;
}
/***************************************************************************\
* FindHotKey
*
* Both RegisterHotKey() and UnregisterHotKey() call this function to search
* for hotkeys that already exist. If a HOTKEY is found that matches
* fsModifiers and vk, *pfKeysExist is set to TRUE. If a HOTKEY is found that
* matches pwnd and id, a pointer to it is returned.
*
* If fUnregister is TRUE, we remove the HOTKEY from the list if we find
* one that matches pwnd and id and return (PHOTKEY)1.
*
* History:
* 12-04-90 DavidPe Created.
\***************************************************************************/
PHOTKEY FindHotKey(
PTHREADINFO ptiCurrent,
PWND pwnd,
int id,
UINT fsModifiers,
UINT vk,
BOOL fUnregister,
PBOOL pfKeysExist)
{
PHOTKEY phk, phkRet, phkPrev;
BYTE index = HKHashVK(vk);
UserAssert(!fUnregister || vk == 0);
/*
* Initialize out 'return' values.
*/
*pfKeysExist = FALSE;
phkRet = NULL;
phk = gphkHashTable[index];
hashloop:
while (phk) {
/*
* If all this matches up then we've found it.
*/
if (phk->pti == ptiCurrent && phk->spwnd == pwnd && phk->id == id) {
if (fUnregister) {
/*
* Unlink the HOTKEY from the list.
*/
if (phk == gphkHashTable[index]) {
gphkHashTable[index] = phk->phkNext;
} else {
phkPrev->phkNext = phk->phkNext;
}
if (pwnd != PWND_FOCUS && pwnd != PWND_INPUTOWNER) {
Unlock(&phk->spwnd);
}
UserFreePool(phk);
return (PHOTKEY)1;
}
phkRet = phk;
}
/*
* If the key is already registered, set the exists flag so the app
* knows it can't use this hotkey sequence.
*/
if (phk->fsModifiers == (WORD)fsModifiers && phk->vk == vk) {
/*
* In the case of PWND_FOCUS, we need to check that the queues
* are the same since PWND_FOCUS is local to the queue it was
* registered under.
*/
if (phk->spwnd == PWND_FOCUS) {
if (phk->pti == ptiCurrent) {
*pfKeysExist = TRUE;
}
} else {
*pfKeysExist = TRUE;
}
}
phkPrev = phk;
phk = phk->phkNext;
}
/*
* This is needed because when called from unregister we specify 0 as
* the VK so the hash is always 0 and we need to index through the
* entire hash table to try to find it.
*/
if (fUnregister && ++index < ARRAY_SIZE(gphkHashTable)) {
phk = gphkHashTable[index];
goto hashloop;
}
return phkRet;
}
/***************************************************************************\
* IsSAS
*
* Checks the physical state of keyboard modifiers that would effect SAS.
\***************************************************************************/
BOOL IsSAS(
BYTE vk,
UINT *pfsModifiers)
{
CheckCritIn();
if (gvkSAS != vk) {
return FALSE;
}
/*
* Special case for SAS - examine real physical modifier-key state!
*
* An evil daemon process can fool convincingly pretend to be winlogon
* by registering Alt+Del as a hotkey, and spinning another thread that
* continually calls keybd_event() to send the Ctrl key up: when the
* user types Ctrl+Alt+Del, only Alt+Del will be seen by the system,
* the evil daemon will get woken by WM_HOTKEY and can pretend to be
* winlogon. So look at gfsSASModifiersDown in this case, to see what keys
* were physically pressed.
* NOTE: If hotkeys are ever made to work under journal playback, make
* sure they don't affect the gfsSASModifiersDown! - IanJa.
*/
if (gfsSASModifiersDown == gfsSASModifiers) {
*pfsModifiers = gfsSASModifiersDown;
return TRUE;
}
return FALSE;
}
/*
* The below two states are used by xxxDoHotKeyStuff().
* Originally function-static variables, but as it's required
* to clear those flags after the system wakes up from hybernation,
* they are made global,
*/
UINT gfsModifiers;
UINT gfsModOnlyCandidate;
VOID ClearCachedHotkeyModifiers(
VOID)
{
/*
* Clear the cached modifiers.
*/
gfsModifiers = 0;
gfsModOnlyCandidate = 0;
/*
* Clear the special modifier cache for the Ctrl+Alt+Del recognition.
* (See comments in IsSAS()).
*/
gfsSASModifiersDown = 0;
}
/***************************************************************************\
* xxxDoHotKeyStuff
*
* This function gets called for every key event from low-level input
* processing. It keeps track of the current state of modifier keys
* and when gfsModifiers and vk match up with one of the registered
* hotkeys, a WM_HOTKEY message is generated. DoHotKeyStuff() will
* tell the input system to eat both the make and break for the 'vk'
* event. This prevents apps from getting input that wasn't really
* intended for them. DoHotKeyStuff() returns TRUE if it wants to 'eat'
* the event, FALSE if the system can pass on the event like it normally
* would.
*
* A Note on Modifier-Only Hotkeys
* Some hotkeys involve VK_SHIFT, VK_CONTROL, VK_MENU and/or VK_WINDOWS only.
* These are called Modifier-Only hotkeys.
* In order to distinguish hotkeys such as Alt-Shift-S and and Alt-Shift alone,
* modifier-only hotkeys must operate on a break, not a make.
* In order to prevent Alt-Shift-S from activating the Alt-Shift hotkey when
* the keys are released, modifier-only hotkeys are only activated when a
* modifier keyup (break) was immediately preceded by a modifier keydown (break)
* This also lets Alt-Shift,Shift,Shift activate the Alt-Shift hotkey 3 times.
*
* History:
* 12-05-90 DavidPe Created.
* 4-15-93 Sanfords Added code to return TRUE for Ctrl-Alt-Del events.
\***************************************************************************/
BOOL xxxDoHotKeyStuff(
UINT vk,
BOOL fBreak,
DWORD fsReserveKeys)
{
UINT fsModOnlyHotkey;
UINT fs;
PHOTKEY phk;
BOOL fCancel;
BOOL fEatDebugKeyBreak;
PWND pwnd;
BOOL bSAS;
CheckCritIn();
UserAssert(IsWinEventNotifyDeferredOK());
if (gfInNumpadHexInput & NUMPAD_HEXMODE_LL) {
RIPMSGF0(RIP_VERBOSE,
"Since we're in gfInNumpadHexInput, just bail out.");
return FALSE;
}
/*
* Update gfsModifiers.
*/
fs = 0;
fsModOnlyHotkey = 0;
switch (vk) {
case VK_SHIFT:
fs = MOD_SHIFT;
break;
case VK_CONTROL:
fs = MOD_CONTROL;
break;
case VK_MENU:
fs = MOD_ALT;
break;
case VK_LWIN:
case VK_RWIN:
fs = MOD_WIN;
break;
default:
/*
* A non-modifier key rules out Modifier-Only hotkeys
*/
gfsModOnlyCandidate = 0;
break;
}
if (fBreak) {
gfsModifiers &= ~fs;
/*
* If a modifier key is coming up, the current modifier only hotkey
* candidate must be tested to see if it is a hotkey. Store this
* in fsModOnlyHotkey, and prevent the next key release from
* being a candidate by clearing fsModOnlyCandidate.
*/
if (fs != 0) {
fsModOnlyHotkey = gfsModOnlyCandidate;
gfsModOnlyCandidate = 0;
}
} else {
gfsModifiers |= fs;
/*
* If a modifier key is going down, we have a modifier-only hotkey
* candidate. Save current modifier state until the following break.
*/
if (fs != 0) {
gfsModOnlyCandidate = gfsModifiers;
}
}
/*
* We look at the physical state for the modifiers because they cannot be
* manipulated and this prevents someone from writing a trojan winlogon
* look alike (see comment in AreModifiersIndicatingSAS).
*/
bSAS = IsSAS((BYTE)vk, &gfsModifiers);
/*
* If the key is not a hotkey then we're done but first check if the
* key is an Alt-Escape if so we need to cancel journalling.
*
* NOTE: Support for Alt+Esc to cancel journalling dropped in NT 4.0
*/
if (fsModOnlyHotkey && fBreak) {
/*
* A hotkey involving only VK_SHIFT, VK_CONTROL, VK_MENU or VK_WINDOWS
* must only operate on a key release.
*/
if ((phk = IsHotKey(fsModOnlyHotkey, VK_NONE)) == NULL) {
return FALSE;
}
} else if ((phk = IsHotKey(gfsModifiers, vk)) == NULL) {
return FALSE;
}
/*
* If we tripped a SAS hotkey, but it's not really the SAS, don't do it.
*/
if ((phk->wFlags & MOD_SAS) && !bSAS) {
return FALSE;
}
#ifdef GENERIC_INPUT
if (gpqForeground && TestRawInputMode(PtiKbdFromQ(gpqForeground), NoHotKeys) &&
(phk->wFlags & MOD_SAS) == 0) {
/*
* NOTE:
* If the foreground thread does not want the hotkey handling,
* just bail out.
*
* Exception: Ctrl+Alt+Del should be strictly handled by the system.
*/
return FALSE;
}
#endif
if (phk->id == IDHOT_WINDOWS) {
pwnd = GETDESKINFO(PtiCurrent())->spwndShell;
if (pwnd != NULL) {
gfsModOnlyCandidate = 0; /* Make it return TRUE */
goto PostTaskListSysCmd;
}
}
if (phk->id == IDHOT_DEBUG || phk->id == IDHOT_DEBUGSERVER) {
if (!fBreak) {
/*
* The DEBUG key has been pressed. Break the appropriate thread
* into the debugger. We won't need phk after this callback
* because we return immediately.
*/
fEatDebugKeyBreak = xxxActivateDebugger(phk->fsModifiers);
} else {
fEatDebugKeyBreak = FALSE;
}
/*
* This'll eat the debug key down and break if we broke into the
* debugger on the server only on the down.
*/
return fEatDebugKeyBreak;
}
/*
* Don't allow hotkeys (except for ones owned by the logon process) if
* the window station is locked.
*/
if (((grpdeskRitInput->rpwinstaParent->dwWSF_Flags & WSF_SWITCHLOCK) != 0) &&
(PsGetThreadProcessId(phk->pti->pEThread) != gpidLogon)) {
RIPMSG0(RIP_WARNING, "Ignoring hotkey because Workstation locked");
return FALSE;
}
if (fsModOnlyHotkey == 0 && fBreak) {
/*
* Do Modifier-Only hotkeys on break events, else return here.
*/
return FALSE;
}
/*
* Unhook hooks if a control-escape, alt-escape, or control-alt-del
* comes through, so the user can cancel if the system seems hung.
*
* Note the hook may be locked so even if the unhook succeeds it
* won't remove the hook from the global asphkStart array. So
* we have to walk the list manually. This code works because
* we are in the critical section and we know other hooks won't
* be deleted.
*
* Once we've unhooked, post a WM_CANCELJOURNAL message to the app
* that set the hook so it knows we did this.
*
* NOTE: Support for Alt+Esc to cancel journalling dropped in NT 4.0
*/
fCancel = FALSE;
if (vk == VK_ESCAPE && (gfsModifiers == MOD_CONTROL)) {
fCancel = TRUE;
}
if (bSAS) {
fCancel = TRUE;
}
if (fCancel) {
zzzCancelJournalling(); // BUG BUG phk might go away IANJA
}
/*
* See if the key is reserved by a console window. If it is,
* return FALSE so the key will be passed to the console.
*/
if (fsReserveKeys != 0) {
switch (vk) {
case VK_TAB:
if ((fsReserveKeys & CONSOLE_ALTTAB) &&
((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
case VK_ESCAPE:
if ((fsReserveKeys & CONSOLE_ALTESC) &&
((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
if ((fsReserveKeys & CONSOLE_CTRLESC) &&
((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_CONTROL)) {
return FALSE;
}
break;
case VK_RETURN:
if ((fsReserveKeys & CONSOLE_ALTENTER) &&
((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
case VK_SNAPSHOT:
if ((fsReserveKeys & CONSOLE_PRTSC) &&
((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == 0)) {
return FALSE;
}
if ((fsReserveKeys & CONSOLE_ALTPRTSC) &&
((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
case VK_SPACE:
if ((fsReserveKeys & CONSOLE_ALTSPACE) &&
((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
}
}
/*
* If this is the task-list hotkey, go ahead and set foreground
* status to the task-list queue right now. This prevents problems
* where the user hits ctrl-esc and types-ahead before the task-list
* processes the hotkey and brings up the task-list window.
*/
if ((gfsModifiers == MOD_CONTROL) && (vk == VK_ESCAPE) && !fBreak) {
PWND pwndSwitch;
TL tlpwndSwitch;
if (ghwndSwitch != NULL) {
pwndSwitch = PW(ghwndSwitch);
ThreadLock(pwndSwitch, &tlpwndSwitch);
xxxSetForegroundWindow2(pwndSwitch, NULL, 0); // BUG BUG phk might go away IANJA
ThreadUnlock(&tlpwndSwitch);
}
}
/*
* Get the hot key contents.
*/
if (phk->spwnd == NULL) {
_PostThreadMessage(phk->pti,
WM_HOTKEY,
phk->id,
MAKELONG(gfsModifiers, vk));
/*
* Since this hotkey is for this guy, he owns the last input.
*/
glinp.ptiLastWoken = phk->pti;
} else {
if (phk->spwnd == PWND_INPUTOWNER) {
if (gpqForeground != NULL) {
pwnd = gpqForeground->spwndFocus;
} else {
return FALSE;
}
} else {
pwnd = phk->spwnd;
}
if (pwnd) {
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndShell && phk->id == SC_TASKLIST) {
PostTaskListSysCmd:
_PostMessage(pwnd, WM_SYSCOMMAND, SC_TASKLIST, 0);
} else {
_PostMessage(pwnd, WM_HOTKEY, phk->id, MAKELONG(gfsModifiers, vk));
}
/*
* Since this hotkey is for this guy, he owns the last input.
*/
glinp.ptiLastWoken = GETPTI(pwnd);
}
}
/*
* If this is a Modifier-Only hotkey, let the modifier break through
* by returning FALSE, otherwise we will have modifier keys stuck down.
*/
return (fsModOnlyHotkey == 0);
}
/***************************************************************************\
* IsHotKey
*
*
* History:
* 03-10-91 DavidPe Created.
\***************************************************************************/
PHOTKEY IsHotKey(
UINT fsModifiers,
UINT vk)
{
PHOTKEY phk;
CheckCritIn();
phk = HKGetHashHead(vk);
while (phk != NULL) {
/*
* Do the modifiers and vk for this hotkey match the current state?
*/
if (phk->fsModifiers == fsModifiers && phk->vk == vk) {
return phk;
}
phk = phk->phkNext;
}
return phk;
}