windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/hook/het.cpp

1322 lines
32 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include "precomp.h"
//
// HET.CPP
// Window, task tracking hooks
//
// Copyright(c) Microsoft 1997-
//
//
// Entry Point
//
int APIENTRY DllMain (HINSTANCE hInstance, DWORD reason, LPVOID plReserved)
{
//
// DONT ADD ANY TRACING TO THIS FUNCTION OR ANY FUNCTIONS CALLED FROM
// HERE - WE CANNOT GUARANTEE THAT THE TRACE DLL IS IN A FIT STATE TO
// DO ANYTHING FROM HERE.
//
switch (reason)
{
case DLL_PROCESS_ATTACH:
{
#ifdef _DEBUG
InitDebugModule(TEXT("MNMHOOK"));
#endif // _DEBUG
DBG_INIT_MEMORY_TRACKING(hInstance);
HOOK_Load(hInstance);
}
break;
case DLL_PROCESS_DETACH:
{
TRACE_OUT(("HOOK unloaded for app %s", GetCommandLine()));
DBG_CHECK_MEMORY_TRACKING(hInstance);
#ifdef _DEBUG
//
// NULL this out in debug to see if our hooks get called on
// this process while we are exiting.
//
g_hookInstance = NULL;
ExitDebugModule();
#endif
}
break;
case DLL_THREAD_ATTACH:
{
HOOK_NewThread();
}
break;
}
return(TRUE);
}
//
// HOOK_Load()
// This saves our instance handle and gets hold of various routines we
// need for window tracking. We can not link to these functions directly
// since some of them only exist in NT 4.0 SP-3, but we want you to be able
// to view and control without it.
//
void HOOK_Load(HINSTANCE hInst)
{
DWORD dwExeType;
LPSTR lpT;
LPSTR lpNext;
LPSTR lpLastPart;
char szExeName[MAX_PATH+1];
DebugEntry(HOOK_Load);
//
// Save our instance
//
g_hookInstance = hInst;
//
// (1) NtQueryInformationProcess() in NTDLL
// (2) SetWinEventHook() in USER32
// (3) UnhookWinEventHook() in USER32
//
// Get hold of NtQueryInformationProcess
hInst = GetModuleHandle(NTDLL_DLL);
g_hetNtQIP = (NTQIP) GetProcAddress(hInst, "NtQueryInformationProcess");
// Get hold of the WinEvent routines
hInst = GetModuleHandle(TEXT("USER32.DLL"));
g_hetSetWinEventHook = (SETWINEVENTHOOK)GetProcAddress(hInst, "SetWinEventHook");
g_hetUnhookWinEvent = (UNHOOKWINEVENT)GetProcAddress(hInst, "UnhookWinEvent");
//
// Figure out what type of app we are. We want to treat separate groupware
// process applets and WOW16 apps specially.
//
GetModuleFileName(NULL, szExeName, sizeof(szExeName));
ASSERT(*szExeName);
TRACE_OUT(("HOOK loaded for app %s", szExeName));
//
// Start at the beginning, and work our way to the last part after the
// last slash, if there is one. We know the path is fully qualified.
//
lpT = szExeName;
lpLastPart = szExeName;
while (*lpT)
{
lpNext = AnsiNext(lpT);
if (*lpT == '\\')
{
//
// This points to the next character AFTER the backwhack.
// If we're at the end of the string somehow, *lpLastPart will
// be zero, and worst that can happen is that our lstrcmpis fail.
//
lpLastPart = lpNext;
}
lpT = lpNext;
}
ASSERT(*lpLastPart);
//
// NOTE:
// GetModuleFileName() dies sometimes for a WOW app--it doesn't always
// NULL terminate. So we will do this on our own.
//
lpT = lpLastPart;
//
// Get to the '.' part of the 8.3 final file name
//
while (*lpT && (*lpT != '.'))
{
lpT = AnsiNext(lpT);
}
//
// Skip past the next three chars
//
if (*lpT == '.')
{
lpT = AnsiNext(lpT);
if (lpT && *lpT)
lpT = AnsiNext(lpT);
if (lpT && *lpT)
lpT = AnsiNext(lpT);
if (lpT && *lpT)
lpT = AnsiNext(lpT);
//
// And null terminate after the 3rd char past the '.' extension.
// This isn't great, but it covers .COM, .DLL, etc. dudes. The
// worst that will happen is GetBinaryType() will fail and we won't
// recognize a WOW app with some strange extension (not 3 chars)
// starting up.
//
if (lpT)
{
if (*lpT != 0)
{
WARNING_OUT(("WOW GetModuleFileName() bug--didn't NULL terminate string"));
}
*lpT = 0;
}
}
if (!lstrcmpi(lpLastPart, "WOWEXEC.EXE"))
{
TRACE_OUT(("New WOW VDM starting up"));
//
// A new WOW VDM is starting up. We don't want to share anything
// in the first thread, the WOW service thread, because those windows
// never go away.
//
g_appType = HET_WOWVDM_APP;
}
else if (!GetBinaryType(szExeName, &dwExeType))
{
ERROR_OUT(("Unable to determine binary type for %s", szExeName));
}
else if (dwExeType == SCS_WOW_BINARY)
{
TRACE_OUT(("New WOW APP in existing VDM starting up"));
//
// A new 16-bit app thread is starting in an existing WOW vdm.
//
g_idWOWApp = GetCurrentThreadId();
g_fShareWOWApp = (BOOL)HET_GetHosting(GetForegroundWindow());
TRACE_OUT(("For new WOW app %08ld, foreground is %s",
g_idWOWApp, (g_fShareWOWApp ? "SHARED" : "not SHARED")));
//
// Remember who was really active when this WOW dude was started
// up. On the first window create, we'll share him based on the
// status of it.
//
}
DebugExitVOID(HOOK_ProcessAttach);
}
//
// HOOK_NewThread()
// For WOW apps, each app is really a thread. The first thread created
// in NTVDM is the WOW service thread. We don't want to share any windows
// in it. Unfortunately, the first window created is a console window, so
// that happens in CONF's context and we can't get any info. The next window
// created in this thread is a WOW window (WOWEXEC.EXE). When that happens,
// we want to go back and unshare the console window.
//
// If the WOW VDM is already running when another 16-bit app starts up,
// we don't have these troubles.
//
void HOOK_NewThread(void)
{
DebugEntry(HOOK_NewThread);
TRACE_OUT(("App thread %08ld starting", GetCurrentThreadId()));
if (g_appType == HET_WOWVDM_APP)
{
TRACE_OUT(("Unsharing WOW service thread windows"));
//
// We want to go unshare the previously created WOW windows. We
// never want to keep shared the dudes in the WOW service thread.
//
g_appType = 0;
EnumWindows(HETUnshareWOWServiceWnds, GetCurrentProcessId());
}
// Update our "share windows on this thread" state.
g_idWOWApp = GetCurrentThreadId();
g_fShareWOWApp = (BOOL)HET_GetHosting(GetForegroundWindow());
TRACE_OUT(("For new app thread %08ld, foreground is %s",
g_idWOWApp, (g_fShareWOWApp ? "SHARED" : "not SHARED")));
DebugExitVOID(HOOK_NewThread);
}
//
// HETUnshareWOWServiceWnds()
// This unshares any windows that accidentally got shared in the first
// service thread in a WOW VDM. This can happen if a WOW app is launched
// by a 32-bit app, and it's the first WOW app ever. The first window
// created is a console window, and the notification happens in CONF's
// process without the right styles that tell us it's in a WOW process.
//
BOOL CALLBACK HETUnshareWOWServiceWnds(HWND hwnd, LPARAM lParam)
{
DWORD idProcess;
DebugEntry(HETUnshareWOWServiceWnds);
if (GetWindowThreadProcessId(hwnd, &idProcess) &&
(idProcess == (DWORD)lParam))
{
TRACE_OUT(("Unsharing WOW service window %08lx", hwnd));
OSI_UnshareWindow(hwnd, TRUE);
}
DebugExitVOID(HETUnshareWOWServiceWnds);
return(TRUE);
}
//
// HOOK_Init()
// This saves away the core window and atom used in the high level input
// hooks and when sharing.
//
void WINAPI HOOK_Init(HWND hwndCore, ATOM atomTrack)
{
DebugEntry(HOOK_Init);
g_asMainWindow = hwndCore;
g_asHostProp = atomTrack;
DebugExitVOID(HOOK_Init);
}
//
// OSI_StartWindowTracking()
// This installs our WinEvent hook so we can watch windows coming and going.
//
BOOL WINAPI OSI_StartWindowTracking(void)
{
BOOL rc = FALSE;
DebugEntry(OSI_StartWindowTracking);
ASSERT(!g_hetTrackHook);
//
// If we can't find the NTDLL + 2 USER32 routines we need, we can't
// let you share.
//
if (!g_hetNtQIP || !g_hetSetWinEventHook || !g_hetUnhookWinEvent)
{
ERROR_OUT(("Wrong version of NT; missing NTDLL and USER32 routines needed to share"));
DC_QUIT;
}
//
// Install our hook.
//
g_hetTrackHook = g_hetSetWinEventHook(HET_MIN_WINEVENT, HET_MAX_WINEVENT,
g_hookInstance, HETTrackProc, 0, 0,
WINEVENT_INCONTEXT | WINEVENT_SKIPOWNPROCESS);
if (!g_hetTrackHook)
{
ERROR_OUT(("SetWinEventHook failed"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(OSI_StartWindowTracking, rc);
return(rc);
}
//
// OSI_StopWindowTracking()
// Removes our hooks for window/task spying, if installed.
//
void WINAPI OSI_StopWindowTracking(void)
{
DebugEntry(OSI_StopWindowTracking);
if (g_hetTrackHook)
{
// Uninstall the WinEvent hook
ASSERT((g_hetUnhookWinEvent != NULL));
g_hetUnhookWinEvent(g_hetTrackHook);
g_hetTrackHook = NULL;
}
DebugExitVOID(OSI_StopWindowTracking);
}
//
// OSI_IsWindowScreenSaver()
//
// On NT the screensaver runs in a different desktop. We'll never get
// an HWND for it.
//
BOOL WINAPI OSI_IsWindowScreenSaver(HWND hwnd)
{
#ifdef _DEBUG
char className[HET_CLASS_NAME_SIZE];
if (GetClassName(hwnd, className, sizeof(className)) > 0)
{
ASSERT(lstrcmp(className, HET_SCREEN_SAVER_CLASS));
}
#endif // _DEBUG
return(FALSE);
}
//
// OSI_IsWOWWindow()
// Returns TRUE if the window is from a WOW (emulated 16-bit) application
//
BOOL WINAPI OSI_IsWOWWindow(HWND hwnd)
{
BOOL rc = FALSE;
DWORD_PTR* pWOWWords;
DebugEntry(OSI_IsWOWWindow);
//
// Get a pointer to the potential WOW words. We make use of an
// undocumented field which is only valid for NT4.0.
//
pWOWWords = (DWORD_PTR*) GetClassLongPtr(hwnd, GCL_WOWWORDS);
//
// Check that we can use this as a pointer.
//
if (!pWOWWords || IsBadReadPtr(pWOWWords, sizeof(DWORD)))
{
DC_QUIT;
}
//
// This is a valid pointer so try to dereference it.
//
if (0 == *pWOWWords)
{
DC_QUIT;
}
//
// The value pointed at by <pWOWWords> is non-zero so this must be a
// WOW app.
//
rc = TRUE;
DC_EXIT_POINT:
//
// Let the world know what we've found.
//
TRACE_OUT(( "Window %#x is a %s window", hwnd, rc ? "WOW" : "Win32"));
DebugExitBOOL(OSI_IsWOWWindow, rc);
return(rc);
}
//
// HETTrackProc()
// Used to spy on window events
// CREATE
// DESTROY
// SHOW
// HIDE
//
void CALLBACK HETTrackProc
(
HWINEVENTHOOK hEvent,
DWORD eventNotification,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwThreadId,
DWORD dwmsEventTime
)
{
DebugEntry(HETTrackProc);
if ((idObject != OBJID_WINDOW) || (idChild != 0))
{
DC_QUIT;
}
//
// Work around a bug in SP3 with ring transition callbacks, where this
// proc gets called before the LoadLibrary is completed.
//
if (!g_hookInstance)
{
ERROR_OUT(( "WinEvent hook called before LoadLibrary completed!"));
DC_QUIT;
}
switch (eventNotification)
{
case EVENT_OBJECT_CREATE:
HETHandleCreate(hwnd);
break;
case EVENT_OBJECT_DESTROY:
OSI_UnshareWindow(hwnd, TRUE);
break;
case EVENT_OBJECT_SHOW:
// Only if this is a console window do we want to force a repaint.
//
// Only console apps cause events to occur in CONF's process (the one
// that installed the hook)
//
HETHandleShow(hwnd, (g_hetTrackHook != NULL));
break;
case EVENT_OBJECT_HIDE:
HETHandleHide(hwnd);
break;
case EVENT_OBJECT_PARENTCHANGE:
HETCheckParentChange(hwnd);
break;
}
DC_EXIT_POINT:
DebugExitVOID(HETTrackProc);
}
//
// HETHandleCreate()
//
// If the window isn't a real top level dude (not CHILD style or parent is
// desktop) or is a menu, ignore it.
//
// Otherwise enum the top level windows and decide what to do:
// * If at least one other in the thread/process is shared in a perm.
// way, mark this the same
//
// * If this is the only one in the process, follow the ancestor chain
// up.
//
void HETHandleCreate(HWND hwnd)
{
HET_TRACK_INFO hti;
UINT hostType;
#ifdef _DEBUG
char szClass[HET_CLASS_NAME_SIZE];
GetClassName(hwnd, szClass, sizeof(szClass));
#endif
DebugEntry(HETHandleCreate);
//
// Ignore child windows
//
if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
{
if (GetParent(hwnd) != GetDesktopWindow())
{
TRACE_OUT(("Skipping child window %08lx create", hwnd));
DC_QUIT;
}
}
hti.idThread = GetWindowThreadProcessId(hwnd, &hti.idProcess);
if (!hti.idThread)
{
TRACE_OUT(("Window %08lx gone", hwnd));
DC_QUIT;
}
//
// Ignore special threads
//
if (HET_IsShellThread(hti.idThread))
{
TRACE_OUT(("Skipping shell thread window %08lx create", hwnd));
DC_QUIT;
}
//
// We don't need to ignore menus. Only when first shared do we skip
// menus. The cached one we never want to share. The others will
// go away almost immediately. From now on, we treat them the same
// as other windows.
//
//
// Figure out what to do.
// NOTE:
// We don't want to inadvertently share the other windows WOW creates.
// The first thread in the WOW process has special classes, which aren't
// WOW wrappers.
//
hti.hwndUs = hwnd;
hti.fWOW = OSI_IsWOWWindow(hwnd);
hti.cWndsApp = 0;
hti.cWndsSharedThread = 0;
hti.cWndsSharedProcess = 0;
TRACE_OUT(("Create for %s window %08lx class %s process %08ld thread %08ld",
(hti.fWOW ? "WOW" : "32-bit"), hwnd, szClass, hti.idProcess, hti.idThread));
UpOneLevel:
EnumWindows(HETShareEnum, (LPARAM)(LPHET_TRACK_INFO)&hti);
if (hti.cWndsSharedThread)
{
TRACE_OUT(("Sharing window %08lx class %s by thread %08ld in process %08ld",
hwnd, szClass, hti.idThread, hti.idProcess));
hostType = HET_HOSTED_PERMANENT | HET_HOSTED_BYTHREAD;
}
else if (hti.cWndsSharedProcess)
{
TRACE_OUT(("Sharing window %08lx class %s by process %08ld in thread %08ld",
hwnd, szClass, hti.idProcess, hti.idThread));
hostType = HET_HOSTED_PERMANENT | HET_HOSTED_BYPROCESS;
}
else if (hti.cWndsApp)
{
//
// There's another window in our app, but none are shared. So don't
// share us either.
//
TRACE_OUT(("Not sharing window %08lx class %s; other unshared windows in thread %08ld process %08ld",
hwnd, szClass, hti.idThread, hti.idProcess));
DC_QUIT;
}
else if (hti.fWOW)
{
//
// Task tracking code for WOW apps, which are really threads.
//
BOOL fShare;
//
// WOW apps are different. They are threads in the NTVDM process.
// Therefore parent/child relationships aren't useful. Instead,
// the best thing we can come up with is to use the status of the
// foreground window. We assume that the currently active app at
// the time the WOW app started up is the one that launched us.
//
// We can't just call GetForegroundWindow() here, because it is too
// late.
//
if (hti.idThread == g_idWOWApp)
{
fShare = g_fShareWOWApp;
g_fShareWOWApp = FALSE;
g_idWOWApp = 0;
}
else
{
fShare = FALSE;
}
if (!fShare)
{
TRACE_OUT(("THREAD window %08lx class %s in thread %08ld not shared",
hwnd, szClass, hti.idThread));
DC_QUIT;
}
TRACE_OUT(("First window %08lx class %s of WOW app %08ld, shared since foreground is",
hwnd, szClass, hti.idThread));
hostType = HET_HOSTED_PERMANENT | HET_HOSTED_BYTHREAD;
}
else
{
//
// Task tracking code for 32-bit apps.
//
DWORD idParentProcess;
//
// First window of a WIN32 app.
//
// Loop through our ancestor processes (no thread info at this point)
HETGetParentProcessID(hti.idProcess, &idParentProcess);
if (!idParentProcess)
{
TRACE_OUT(("Can't get parent of process %08ld", hti.idProcess));
DC_QUIT;
}
//
// We know if we got here that all our favorite fields are still
// zero. So just loop! But NULL out idThread to avoid matching
// anything while we look at our parent.
//
TRACE_OUT(("First window %08lx class %s in process %08ld %s, checking parent %08ld",
hwnd, szClass, hti.idProcess, GetCommandLine(), idParentProcess));
hti.idThread = 0;
hti.idProcess = idParentProcess;
goto UpOneLevel;
}
//
// OK, we are going to share this. We do have to repaint console
// windows--we get the notifications asynchronously. If the window isn't
// visible yet, redrawing will do nothing. After this, the property is
// set, and we will catch all ouput. If it has already become visible,
// invalidating it now will still work, and we will ignore the queued
// up show notification because the property is set.
//
OSI_ShareWindow(hwnd, hostType, (g_hetTrackHook != NULL), TRUE);
DC_EXIT_POINT:
DebugExitVOID(HETHandleCreate);
}
//
// HETHandleShow()
//
void HETHandleShow
(
HWND hwnd,
BOOL fForceRepaint
)
{
UINT hostType;
HET_TRACK_INFO hti;
DebugEntry(HETHandleShow);
hostType = (UINT)HET_GetHosting(hwnd);
//
// If this window is a real child, clear the hosting property. Usually
// one isn't there. But in the case of a top level window becoming
// a child of another, we want to wipe out junk.
//
if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
{
if (GetParent(hwnd) != GetDesktopWindow())
{
TRACE_OUT(("Skipping child window %08lx show", hwnd));
if (hostType)
{
WARNING_OUT(("Unsharing shared child window 0x%08x from SHOW", hwnd));
OSI_UnshareWindow(hwnd, TRUE);
}
DC_QUIT;
}
}
//
// Is this window already shared? Nothing to do if so. If it's a
// console guy, we've seen it already on create.
//
if (hostType)
{
TRACE_OUT(("Window %08lx already shared, ignoring show", hwnd));
DC_QUIT;
}
//
// Here's where we also enumerate the top level windows and find a
// match. But we DO not track across processes in this case. Instead
// we look at the owner if there is one.
//
// This solves the create-as-a-child then change to a top level
// window problem, like combo dropdowns.
//
hti.idThread = GetWindowThreadProcessId(hwnd, &hti.idProcess);
if (!hti.idThread)
{
TRACE_OUT(("Window %08lx gone", hwnd));
DC_QUIT;
}
//
// Ignore special shell threads
//
if (HET_IsShellThread(hti.idThread))
{
TRACE_OUT(("Skipping shell thread window %08lx show", hwnd));
DC_QUIT;
}
hti.hwndUs = hwnd;
hti.fWOW = OSI_IsWOWWindow(hwnd);
hti.cWndsApp = 0;
hti.cWndsSharedThread = 0;
hti.cWndsSharedProcess = 0;
EnumWindows(HETShareEnum, (LPARAM)(LPHET_TRACK_INFO)&hti);
//
// These kinds of windows are always only temp shared. They don't
// start out as top level windows that we saw from the beginning or
// watched created. These are SetParent() or menu kinds of dudes, so
// for a lot of reasons we're plain safer sharing these babies only
// temporarily
//
//
// Anything else shared on this thread/process, the decision is easy.
// Otherwise, we look at the ownership trail.
//
if (!hti.cWndsSharedThread && !hti.cWndsSharedProcess)
{
HWND hwndOwner;
//
// Does it have an owner that is shared?
//
hwndOwner = hwnd;
while (hwndOwner = GetWindow(hwndOwner, GW_OWNER))
{
if (HET_GetHosting(hwndOwner))
{
TRACE_OUT(("Found shared owner %08lx of window %08lx", hwndOwner, hwnd));
break;
}
}
if (!hwndOwner)
{
DC_QUIT;
}
}
//
// For console apps, we get notifications asynchronously posted to us,
// in NM's process. The window may have painted already without our
// seeing it. So force it to repaint just in case. The g_hetTrackHook
// variable is only around when this is NM.
//
TRACE_OUT(("Sharing temporary window %08lx", hwnd));
OSI_ShareWindow(hwnd, HET_HOSTED_BYWINDOW | HET_HOSTED_TEMPORARY,
fForceRepaint, TRUE);
DC_EXIT_POINT:
DebugExitVOID(HETHandleShow);
}
//
// HETHandleHide()
// This handles a window being hidden. If it was temporary, it is unshared.
// If it is permanent, it is marked as hidden.
//
void HETHandleHide(HWND hwnd)
{
UINT hostType;
DebugEntry(HETHandleHide);
hostType = (UINT)HET_GetHosting(hwnd);
if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
{
if (GetParent(hwnd) != GetDesktopWindow())
{
TRACE_OUT(("Skipping child window %08lx hide", hwnd));
if (hostType)
{
WARNING_OUT(("Unsharing shared child window 0x%08x from HIDE", hwnd));
OSI_UnshareWindow(hwnd, TRUE);
}
DC_QUIT;
}
}
if (!hostType)
{
//
// Console apps give us notifications out of context. Make
// sure the count is up to date.
//
if (g_hetTrackHook)
{
HETNewTopLevelCount();
}
else
{
TRACE_OUT(("Window %08lx not shared, ignoring hide", hwnd));
}
}
else if (hostType & HET_HOSTED_TEMPORARY)
{
//
// Temporarily shared window are only shared when visible.
//
TRACE_OUT(("Unsharing temporary window %08lx", hwnd));
OSI_UnshareWindow(hwnd, TRUE);
}
else
{
ASSERT(hostType & HET_HOSTED_PERMANENT);
// Nothing to do.
TRACE_OUT(("Window %08lx permanently shared, ignoring hide", hwnd));
}
DC_EXIT_POINT:
DebugExitVOID(HETHandleHide);
}
//
// HETCheckParentChange()
//
// PARENTCHANGE is 100% reliable, compared to Win9x stuff.
//
void HETCheckParentChange(HWND hwnd)
{
DebugEntry(HETCheckParentChange);
WARNING_OUT(("Got PARENTCHANGE for hwnd 0x%08x", hwnd));
if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
{
if (GetParent(hwnd) != GetDesktopWindow())
{
UINT hostType;
hostType = (UINT)HET_GetHosting(hwnd);
if (hostType)
{
WARNING_OUT(("Unsharing shared child window 0x%08x from MOVE", hwnd));
OSI_UnshareWindow(hwnd, TRUE);
}
}
}
DebugExitVOID(HETCheckParentChange);
}
//
// OSI_ShareWindow
// This shares a window, calling the display driver to add it to the visrgn
// list. It is called when
// * An app is shared
// * A new window in a shared app is created
// * A temporary window with a relationship to a shared window is shown
//
// This returns TRUE if it shared a window.
//
BOOL OSI_ShareWindow
(
HWND hwnd,
UINT hostType,
BOOL fRepaint,
BOOL fUpdateCount
)
{
BOOL rc = FALSE;
HET_SHARE_WINDOW req;
DebugEntry(OSI_ShareWindow);
//
// Set the property
//
if (!HET_SetHosting(hwnd, hostType))
{
ERROR_OUT(("Couldn't set shared property on window %08lx", hwnd));
DC_QUIT;
}
//
// Tell the display driver
//
req.winID = HandleToUlong(hwnd);
req.result = 0;
if (!OSI_FunctionRequest(HET_ESC_SHARE_WINDOW, (LPOSI_ESCAPE_HEADER)&req,
sizeof(req)) ||
!req.result)
{
ERROR_OUT(("Driver couldn't add window %08lx to list", hwnd));
HET_ClearHosting(hwnd);
DC_QUIT;
}
TRACE_OUT(("Shared window %08lx of type %08lx", hwnd, hostType));
//
// Repaint it
//
if (fRepaint)
{
USR_RepaintWindow(hwnd);
}
if (fUpdateCount)
{
PostMessage(g_asMainWindow, DCS_NEWTOPLEVEL_MSG, TRUE, 0);
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(OSI_ShareWindow, rc);
return(rc);
}
//
// OSI_UnshareWindow()
// This unshares a window. This is called when
// * An app is unshared
// * A window is destroyed
// * A temporarily shared window is hidden
//
// It returns TRUE if a shared window has been unshared.
//
BOOL OSI_UnshareWindow
(
HWND hwnd,
BOOL fUpdateCount
)
{
BOOL rc = FALSE;
UINT hostType;
HET_UNSHARE_WINDOW req;
DebugEntry(OSI_UnshareWindow);
//
// This gets the old property and clears it in one step.
//
hostType = (UINT)HET_ClearHosting(hwnd);
if (!hostType)
{
if (fUpdateCount && g_hetTrackHook)
{
//
// We always get async notifications for console apps. In that
// case, the window is really gone before this comes to us.
// So redetermine the count now.
//
HETNewTopLevelCount();
}
DC_QUIT;
}
//
// OK, stuff to do.
//
TRACE_OUT(("Unsharing window %08lx of type %08lx", hwnd, hostType));
//
// Tell the display driver
//
req.winID = HandleToUlong(hwnd);
OSI_FunctionRequest(HET_ESC_UNSHARE_WINDOW, (LPOSI_ESCAPE_HEADER)&req, sizeof(req));
//
// Update the top level count
//
if (fUpdateCount)
{
PostMessage(g_asMainWindow, DCS_NEWTOPLEVEL_MSG, FALSE, 0);
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(OSI_UnshareWindow, rc);
return(rc);
}
//
// HETShareEnum()
//
// This is the EnumWindows() callback. We stop when we find the first
// matching shared window (thread or process). We keep a running tally
// of the count of all top level windows in our process (not shared by
// thread or process) at the same time. This lets us do tracking.
//
BOOL CALLBACK HETShareEnum(HWND hwnd, LPARAM lParam)
{
LPHET_TRACK_INFO lphti = (LPHET_TRACK_INFO)lParam;
DWORD idProcess;
DWORD idThread;
UINT hostType;
BOOL rc = TRUE;
DebugEntry(HETShareEnum);
// Skip ourself.
if (hwnd == lphti->hwndUs)
{
DC_QUIT;
}
// Skip if window is gone.
idThread = GetWindowThreadProcessId(hwnd, &idProcess);
if (!idThread)
{
DC_QUIT;
}
//
// Do the apps match? If not, ignore this window.
//
if ((idProcess != lphti->idProcess) ||
((lphti->fWOW) && (idThread != lphti->idThread)))
{
DC_QUIT;
}
lphti->cWndsApp++;
hostType = (UINT)HET_GetHosting(hwnd);
if (!hostType)
{
DC_QUIT;
}
//
// Now, if this window is shared by thread or process, do the right
// thing.
//
if (hostType & HET_HOSTED_BYPROCESS)
{
// We have a match. We can return immediately.
lphti->cWndsSharedProcess++;
rc = FALSE;
}
else if (hostType & HET_HOSTED_BYTHREAD)
{
//
// For WOW apps, we don't want this one, if in a separate thread, to
// count. No matter what.
//
if (idThread == lphti->idThread)
{
lphti->cWndsSharedThread++;
rc = FALSE;
}
}
DC_EXIT_POINT:
DebugExitBOOL(HETShareEnum, rc);
return(rc);
}
//
// HETNewTopLevelCount()
// This does a quick new tally of the shared top level visible count
//
void HETNewTopLevelCount(void)
{
UINT newCount;
DebugEntry(HETNewTopLevelCount);
newCount = 0;
EnumWindows(HETCountTopLevel, (LPARAM)&newCount);
PostMessage(g_asMainWindow, DCS_RECOUNTTOPLEVEL_MSG, newCount, 0);
DebugExitVOID(HETNewTopLevelCount);
}
//
// HETCountTopLevel()
// This counts shared windows
//
BOOL CALLBACK HETCountTopLevel(HWND hwnd, LPARAM lParam)
{
DebugEntry(HETCountTopLevel);
if (HET_GetHosting(hwnd))
{
(*(LPUINT)lParam)++;
}
DebugExitBOOL(HETCountTopLevel, TRUE);
return(TRUE);
}
//
// HET_IsShellThread()
// Returns TRUE if thread is one of shell's special threads
//
BOOL HET_IsShellThread(DWORD threadID)
{
BOOL rc;
DebugEntry(HET_IsShellThread);
if ((threadID == GetWindowThreadProcessId(HET_GetShellDesktop(), NULL)) ||
(threadID == GetWindowThreadProcessId(HET_GetShellTray(), NULL)))
{
rc = TRUE;
}
else
{
rc = FALSE;
}
DebugExitBOOL(HET_IsShellThread, rc);
return(rc);
}
//
// HET_WindowIsHosted()
// This is called by the high level mouse hook. Unlike the version in
// MNMCPI32, it doesn't check (or know) if the whole desktop is shared.
//
// LAURABU BOGUS
// Note that this may need to be revised. The high level hooks are handy
// in desktop sharing also. For the keyboard, we track the toggle key
// states. For the mouse, we block messages to non-shared windows.
//
BOOL HET_WindowIsHosted(HWND hwnd)
{
BOOL rc = FALSE;
HWND hwndParent;
DebugEntry(HET_WindowIsHosted);
if (!hwnd)
DC_QUIT;
//
// Walk up to the top level window this one is inside of
//
while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
{
hwndParent = GetParent(hwnd);
if (hwndParent == GetDesktopWindow())
break;
hwnd = hwndParent;
}
rc = (BOOL)HET_GetHosting(hwnd);
DC_EXIT_POINT:
DebugExitBOOL(HET_WindowIsHosted, rc);
return(rc);
}
//
// HETGetParentProcessID()
// This gets the ID of the process which created the passed in one. Used
// for task tracking
//
void HETGetParentProcessID
(
DWORD processID,
LPDWORD pParentProcessID
)
{
HANDLE hProcess;
UINT intRC;
PROCESS_BASIC_INFORMATION basicInfo;
DebugEntry(HETGetParentProcessID);
*pParentProcessID = 0;
//
// Open a handle to the process. If we don't have security privileges,
// or it is gone, this will fail.
//
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, processID);
if (NULL == hProcess)
{
WARNING_OUT(("Can't get process handle for ID %08lx", processID));
DC_QUIT;
}
//
// Get back an information block for this process, one item of which is
// the parent.
//
ASSERT(g_hetNtQIP);
intRC = g_hetNtQIP(hProcess, ProcessBasicInformation, &basicInfo,
sizeof(basicInfo), NULL);
if (!NT_SUCCESS(intRC))
{
ERROR_OUT(("Can't get info for process ID %08lx, handle %08lx -- error %u",
processID, hProcess, intRC));
}
else
{
*pParentProcessID = basicInfo.InheritedFromUniqueProcessId;
}
//
// Close the process handle
//
CloseHandle(hProcess);
DC_EXIT_POINT:
DebugExitVOID(HETGetParentProcessID);
}