|
|
#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)-1); szExeName[sizeof(szExeName) -1] = 0; 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); }
|