492 lines
9.6 KiB
C++
492 lines
9.6 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ForceKeepFocus.cpp
|
|
|
|
Abstract:
|
|
|
|
Some applications destroy windows that are topmost. In this case, focus
|
|
falls to the next topmost window. Of course, that window might be a window
|
|
from another application. If that is that case, then the app will
|
|
unexpectedly lose focus.
|
|
|
|
The fix is to make sure that another app window has focus before we destroy
|
|
the top one.
|
|
|
|
An additional fix is included in this shim: after a window is created, we
|
|
send a WM_TIMECHANGE message because Star Trek Generations blocked it's thread
|
|
waiting for a message. On Win9x a WM_COMMAND comes through, but I haven't been
|
|
able to repro this on other applications.
|
|
|
|
Notes:
|
|
|
|
This is a general purpose shim.
|
|
|
|
History:
|
|
|
|
06/09/2000 linstev Created
|
|
02/18/2002 robkenny Converted InitializeCriticalSection to InitializeCriticalSectionAndSpinCount
|
|
to ensure the critical section is fully initialized.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
IMPLEMENT_SHIM_BEGIN(ForceKeepFocus)
|
|
#include "ShimHookMacro.h"
|
|
|
|
APIHOOK_ENUM_BEGIN
|
|
APIHOOK_ENUM_ENTRY(CreateWindowExA)
|
|
APIHOOK_ENUM_ENTRY(CreateWindowExW)
|
|
APIHOOK_ENUM_ENTRY(CreateDialogParamA)
|
|
APIHOOK_ENUM_ENTRY(CreateDialogParamW)
|
|
APIHOOK_ENUM_ENTRY(CreateDialogIndirectParamA)
|
|
APIHOOK_ENUM_ENTRY(CreateDialogIndirectParamW)
|
|
APIHOOK_ENUM_ENTRY(CreateDialogIndirectParamAorW)
|
|
APIHOOK_ENUM_ENTRY(DestroyWindow)
|
|
APIHOOK_ENUM_END
|
|
|
|
//
|
|
// List of all app windows
|
|
//
|
|
|
|
struct HWNDITEM
|
|
{
|
|
HWND hWndParent;
|
|
HWND hWnd;
|
|
HWNDITEM *next;
|
|
};
|
|
HWNDITEM *g_hWndList = NULL;
|
|
|
|
//
|
|
// Critical section for list access
|
|
//
|
|
|
|
CRITICAL_SECTION g_csList;
|
|
|
|
/*++
|
|
|
|
Add a window to our list.
|
|
|
|
--*/
|
|
|
|
void
|
|
AddItem(
|
|
HWND hWndParent,
|
|
HWND hWnd
|
|
)
|
|
{
|
|
if (IsWindow(hWnd) && IsWindowVisible(hWndParent))
|
|
{
|
|
EnterCriticalSection(&g_csList);
|
|
|
|
HWNDITEM *hitem = (HWNDITEM *) malloc(sizeof(HWNDITEM));
|
|
|
|
if (hitem)
|
|
{
|
|
hitem->hWndParent = hWndParent;
|
|
hitem->hWnd = hWnd;
|
|
hitem->next = g_hWndList;
|
|
g_hWndList = hitem;
|
|
|
|
DPFN( eDbgLevelInfo, "Adding window %08lx with parent %08lx",
|
|
hWnd,
|
|
hWndParent);
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, "Failed to allocate list item");
|
|
}
|
|
|
|
LeaveCriticalSection(&g_csList);
|
|
}
|
|
|
|
//
|
|
// Some apps get stuck waiting for a message: not really part of this
|
|
// shim, but shouldn't be harmful
|
|
//
|
|
|
|
if (IsWindow(hWnd))
|
|
{
|
|
PostMessageA(hWnd, WM_TIMECHANGE, 0, 0);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Remove a window from the list and return another visible window that will
|
|
become the next top window.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
RemoveItem(
|
|
HWND hWnd
|
|
)
|
|
{
|
|
HWND hRet = NULL;
|
|
|
|
EnterCriticalSection(&g_csList);
|
|
|
|
//
|
|
// Remove the window and all it's children
|
|
//
|
|
|
|
HWNDITEM *hcurr = g_hWndList;
|
|
HWNDITEM *hprev = NULL;
|
|
|
|
while (hcurr)
|
|
{
|
|
if ((hcurr->hWndParent == hWnd) ||
|
|
(hcurr->hWnd == hWnd))
|
|
{
|
|
HWNDITEM *hfree;
|
|
|
|
DPFN( eDbgLevelInfo, "Removing %08lx", hcurr->hWnd);
|
|
|
|
if (hprev)
|
|
{
|
|
hprev->next = hcurr->next;
|
|
}
|
|
else
|
|
{
|
|
g_hWndList = hcurr->next;
|
|
}
|
|
|
|
hfree = hcurr;
|
|
hcurr = hcurr->next;
|
|
free(hfree);
|
|
continue;
|
|
}
|
|
hprev = hcurr;
|
|
hcurr = hcurr->next;
|
|
}
|
|
|
|
//
|
|
// Find another window to get focus
|
|
//
|
|
|
|
hcurr = g_hWndList;
|
|
while (hcurr)
|
|
{
|
|
if (IsWindowVisible(hcurr->hWnd))
|
|
{
|
|
hRet = hcurr->hWnd;
|
|
break;
|
|
}
|
|
hcurr = hcurr->next;
|
|
}
|
|
|
|
if (hRet)
|
|
{
|
|
DPFN( eDbgLevelInfo, "Giving focus to %08lx", hRet);
|
|
}
|
|
|
|
LeaveCriticalSection(&g_csList);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Track the created window and post a WM_COMMAND message.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
APIHOOK(CreateWindowExA)(
|
|
DWORD dwExStyle,
|
|
LPCSTR lpClassName,
|
|
LPCSTR lpWindowName,
|
|
DWORD dwStyle,
|
|
int x,
|
|
int y,
|
|
int nWidth,
|
|
int nHeight,
|
|
HWND hWndParent,
|
|
HMENU hMenu,
|
|
HINSTANCE hInstance,
|
|
LPVOID lpParam
|
|
)
|
|
{
|
|
HWND hRet;
|
|
|
|
hRet = ORIGINAL_API(CreateWindowExA)(
|
|
dwExStyle,
|
|
lpClassName,
|
|
lpWindowName,
|
|
dwStyle,
|
|
x,
|
|
y,
|
|
nWidth,
|
|
nHeight,
|
|
hWndParent,
|
|
hMenu,
|
|
hInstance,
|
|
lpParam);
|
|
|
|
AddItem(hWndParent, hRet);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Track the created window and post a WM_COMMAND message.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
APIHOOK(CreateWindowExW)(
|
|
DWORD dwExStyle,
|
|
LPCWSTR lpClassName,
|
|
LPCWSTR lpWindowName,
|
|
DWORD dwStyle,
|
|
int x,
|
|
int y,
|
|
int nWidth,
|
|
int nHeight,
|
|
HWND hWndParent,
|
|
HMENU hMenu,
|
|
HINSTANCE hInstance,
|
|
LPVOID lpParam
|
|
)
|
|
{
|
|
HWND hRet;
|
|
|
|
hRet = ORIGINAL_API(CreateWindowExW)(
|
|
dwExStyle,
|
|
lpClassName,
|
|
lpWindowName,
|
|
dwStyle,
|
|
x,
|
|
y,
|
|
nWidth,
|
|
nHeight,
|
|
hWndParent,
|
|
hMenu,
|
|
hInstance,
|
|
lpParam);
|
|
|
|
AddItem(hWndParent, hRet);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Track the created window and post a WM_COMMAND message.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
APIHOOK(CreateDialogParamA)(
|
|
HINSTANCE hInstance,
|
|
LPCSTR lpTemplateName,
|
|
HWND hWndParent,
|
|
DLGPROC lpDialogFunc,
|
|
LPARAM dwInitParam
|
|
)
|
|
{
|
|
HWND hRet;
|
|
|
|
hRet = ORIGINAL_API(CreateDialogParamA)(
|
|
hInstance,
|
|
lpTemplateName,
|
|
hWndParent,
|
|
lpDialogFunc,
|
|
dwInitParam);
|
|
|
|
AddItem(hWndParent, hRet);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Track the created window and post a WM_COMMAND message.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
APIHOOK(CreateDialogParamW)(
|
|
HINSTANCE hInstance,
|
|
LPCWSTR lpTemplateName,
|
|
HWND hWndParent,
|
|
DLGPROC lpDialogFunc,
|
|
LPARAM dwInitParam
|
|
)
|
|
{
|
|
HWND hRet;
|
|
|
|
hRet = ORIGINAL_API(CreateDialogParamW)(
|
|
hInstance,
|
|
lpTemplateName,
|
|
hWndParent,
|
|
lpDialogFunc,
|
|
dwInitParam);
|
|
|
|
AddItem(hWndParent, hRet);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Track the created window and post a WM_COMMAND message.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
APIHOOK(CreateDialogIndirectParamA)(
|
|
HINSTANCE hInstance,
|
|
LPCDLGTEMPLATE lpTemplate,
|
|
HWND hWndParent,
|
|
DLGPROC lpDialogFunc,
|
|
LPARAM lParamInit
|
|
)
|
|
{
|
|
HWND hRet;
|
|
|
|
hRet = ORIGINAL_API(CreateDialogIndirectParamA)(
|
|
hInstance,
|
|
lpTemplate,
|
|
hWndParent,
|
|
lpDialogFunc,
|
|
lParamInit);
|
|
|
|
AddItem(hWndParent, hRet);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Track the created window and post a WM_COMMAND message.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
APIHOOK(CreateDialogIndirectParamW)(
|
|
HINSTANCE hInstance,
|
|
LPCDLGTEMPLATE lpTemplate,
|
|
HWND hWndParent,
|
|
DLGPROC lpDialogFunc,
|
|
LPARAM lParamInit
|
|
)
|
|
{
|
|
HWND hRet;
|
|
|
|
hRet = ORIGINAL_API(CreateDialogIndirectParamW)(
|
|
hInstance,
|
|
lpTemplate,
|
|
hWndParent,
|
|
lpDialogFunc,
|
|
lParamInit);
|
|
|
|
AddItem(hWndParent, hRet);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Track the created window and post a WM_COMMAND message.
|
|
|
|
--*/
|
|
|
|
HWND
|
|
APIHOOK(CreateDialogIndirectParamAorW)(
|
|
HINSTANCE hInstance,
|
|
LPCDLGTEMPLATE lpTemplate,
|
|
HWND hWndParent,
|
|
DLGPROC lpDialogFunc,
|
|
LPARAM lParamInit
|
|
)
|
|
{
|
|
HWND hRet;
|
|
|
|
hRet = ORIGINAL_API(CreateDialogIndirectParamAorW)(
|
|
hInstance,
|
|
lpTemplate,
|
|
hWndParent,
|
|
lpDialogFunc,
|
|
lParamInit);
|
|
|
|
AddItem(hWndParent, hRet);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Destroy the window and make sure the focus falls to another app window,
|
|
rather than another app altogether.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
APIHOOK(DestroyWindow)(
|
|
HWND hWnd
|
|
)
|
|
{
|
|
HWND hWndNew = RemoveItem(hWnd);
|
|
|
|
if (hWndNew)
|
|
{
|
|
SetForegroundWindow(hWndNew);
|
|
}
|
|
|
|
BOOL bRet = ORIGINAL_API(DestroyWindow)(hWnd);
|
|
|
|
if (hWndNew)
|
|
{
|
|
SetForegroundWindow(hWndNew);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Register hooked functions
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
NOTIFY_FUNCTION(
|
|
DWORD fdwReason)
|
|
{
|
|
|
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
// If we fail to initialize the critical section, fail loading this shim.
|
|
return InitializeCriticalSectionAndSpinCount(&g_csList, 0x80000000);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HOOK_BEGIN
|
|
|
|
CALL_NOTIFY_FUNCTION
|
|
|
|
APIHOOK_ENTRY(USER32.DLL, CreateWindowExA)
|
|
APIHOOK_ENTRY(USER32.DLL, CreateWindowExW)
|
|
APIHOOK_ENTRY(USER32.DLL, CreateDialogParamA)
|
|
APIHOOK_ENTRY(USER32.DLL, CreateDialogParamW)
|
|
APIHOOK_ENTRY(USER32.DLL, CreateDialogIndirectParamA)
|
|
APIHOOK_ENTRY(USER32.DLL, CreateDialogIndirectParamW)
|
|
APIHOOK_ENTRY(USER32.DLL, CreateDialogIndirectParamAorW)
|
|
APIHOOK_ENTRY(USER32.DLL, DestroyWindow)
|
|
|
|
HOOK_END
|
|
|
|
|
|
|
|
IMPLEMENT_SHIM_END
|
|
|