Source code of Windows XP (NT5)
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.

1321 lines
32 KiB

  1. #include "precomp.h"
  2. //
  3. // HET.CPP
  4. // Window, task tracking hooks
  5. //
  6. // Copyright(c) Microsoft 1997-
  7. //
  8. //
  9. // Entry Point
  10. //
  11. int APIENTRY DllMain (HINSTANCE hInstance, DWORD reason, LPVOID plReserved)
  12. {
  13. //
  14. // DONT ADD ANY TRACING TO THIS FUNCTION OR ANY FUNCTIONS CALLED FROM
  15. // HERE - WE CANNOT GUARANTEE THAT THE TRACE DLL IS IN A FIT STATE TO
  16. // DO ANYTHING FROM HERE.
  17. //
  18. switch (reason)
  19. {
  20. case DLL_PROCESS_ATTACH:
  21. {
  22. #ifdef _DEBUG
  23. InitDebugModule(TEXT("MNMHOOK"));
  24. #endif // _DEBUG
  25. DBG_INIT_MEMORY_TRACKING(hInstance);
  26. HOOK_Load(hInstance);
  27. }
  28. break;
  29. case DLL_PROCESS_DETACH:
  30. {
  31. TRACE_OUT(("HOOK unloaded for app %s", GetCommandLine()));
  32. DBG_CHECK_MEMORY_TRACKING(hInstance);
  33. #ifdef _DEBUG
  34. //
  35. // NULL this out in debug to see if our hooks get called on
  36. // this process while we are exiting.
  37. //
  38. g_hookInstance = NULL;
  39. ExitDebugModule();
  40. #endif
  41. }
  42. break;
  43. case DLL_THREAD_ATTACH:
  44. {
  45. HOOK_NewThread();
  46. }
  47. break;
  48. }
  49. return(TRUE);
  50. }
  51. //
  52. // HOOK_Load()
  53. // This saves our instance handle and gets hold of various routines we
  54. // need for window tracking. We can not link to these functions directly
  55. // since some of them only exist in NT 4.0 SP-3, but we want you to be able
  56. // to view and control without it.
  57. //
  58. void HOOK_Load(HINSTANCE hInst)
  59. {
  60. DWORD dwExeType;
  61. LPSTR lpT;
  62. LPSTR lpNext;
  63. LPSTR lpLastPart;
  64. char szExeName[MAX_PATH+1];
  65. DebugEntry(HOOK_Load);
  66. //
  67. // Save our instance
  68. //
  69. g_hookInstance = hInst;
  70. //
  71. // (1) NtQueryInformationProcess() in NTDLL
  72. // (2) SetWinEventHook() in USER32
  73. // (3) UnhookWinEventHook() in USER32
  74. //
  75. // Get hold of NtQueryInformationProcess
  76. hInst = GetModuleHandle(NTDLL_DLL);
  77. g_hetNtQIP = (NTQIP) GetProcAddress(hInst, "NtQueryInformationProcess");
  78. // Get hold of the WinEvent routines
  79. hInst = GetModuleHandle(TEXT("USER32.DLL"));
  80. g_hetSetWinEventHook = (SETWINEVENTHOOK)GetProcAddress(hInst, "SetWinEventHook");
  81. g_hetUnhookWinEvent = (UNHOOKWINEVENT)GetProcAddress(hInst, "UnhookWinEvent");
  82. //
  83. // Figure out what type of app we are. We want to treat separate groupware
  84. // process applets and WOW16 apps specially.
  85. //
  86. GetModuleFileName(NULL, szExeName, sizeof(szExeName));
  87. ASSERT(*szExeName);
  88. TRACE_OUT(("HOOK loaded for app %s", szExeName));
  89. //
  90. // Start at the beginning, and work our way to the last part after the
  91. // last slash, if there is one. We know the path is fully qualified.
  92. //
  93. lpT = szExeName;
  94. lpLastPart = szExeName;
  95. while (*lpT)
  96. {
  97. lpNext = AnsiNext(lpT);
  98. if (*lpT == '\\')
  99. {
  100. //
  101. // This points to the next character AFTER the backwhack.
  102. // If we're at the end of the string somehow, *lpLastPart will
  103. // be zero, and worst that can happen is that our lstrcmpis fail.
  104. //
  105. lpLastPart = lpNext;
  106. }
  107. lpT = lpNext;
  108. }
  109. ASSERT(*lpLastPart);
  110. //
  111. // NOTE:
  112. // GetModuleFileName() dies sometimes for a WOW app--it doesn't always
  113. // NULL terminate. So we will do this on our own.
  114. //
  115. lpT = lpLastPart;
  116. //
  117. // Get to the '.' part of the 8.3 final file name
  118. //
  119. while (*lpT && (*lpT != '.'))
  120. {
  121. lpT = AnsiNext(lpT);
  122. }
  123. //
  124. // Skip past the next three chars
  125. //
  126. if (*lpT == '.')
  127. {
  128. lpT = AnsiNext(lpT);
  129. if (lpT && *lpT)
  130. lpT = AnsiNext(lpT);
  131. if (lpT && *lpT)
  132. lpT = AnsiNext(lpT);
  133. if (lpT && *lpT)
  134. lpT = AnsiNext(lpT);
  135. //
  136. // And null terminate after the 3rd char past the '.' extension.
  137. // This isn't great, but it covers .COM, .DLL, etc. dudes. The
  138. // worst that will happen is GetBinaryType() will fail and we won't
  139. // recognize a WOW app with some strange extension (not 3 chars)
  140. // starting up.
  141. //
  142. if (lpT)
  143. {
  144. if (*lpT != 0)
  145. {
  146. WARNING_OUT(("WOW GetModuleFileName() bug--didn't NULL terminate string"));
  147. }
  148. *lpT = 0;
  149. }
  150. }
  151. if (!lstrcmpi(lpLastPart, "WOWEXEC.EXE"))
  152. {
  153. TRACE_OUT(("New WOW VDM starting up"));
  154. //
  155. // A new WOW VDM is starting up. We don't want to share anything
  156. // in the first thread, the WOW service thread, because those windows
  157. // never go away.
  158. //
  159. g_appType = HET_WOWVDM_APP;
  160. }
  161. else if (!GetBinaryType(szExeName, &dwExeType))
  162. {
  163. ERROR_OUT(("Unable to determine binary type for %s", szExeName));
  164. }
  165. else if (dwExeType == SCS_WOW_BINARY)
  166. {
  167. TRACE_OUT(("New WOW APP in existing VDM starting up"));
  168. //
  169. // A new 16-bit app thread is starting in an existing WOW vdm.
  170. //
  171. g_idWOWApp = GetCurrentThreadId();
  172. g_fShareWOWApp = (BOOL)HET_GetHosting(GetForegroundWindow());
  173. TRACE_OUT(("For new WOW app %08ld, foreground is %s",
  174. g_idWOWApp, (g_fShareWOWApp ? "SHARED" : "not SHARED")));
  175. //
  176. // Remember who was really active when this WOW dude was started
  177. // up. On the first window create, we'll share him based on the
  178. // status of it.
  179. //
  180. }
  181. DebugExitVOID(HOOK_ProcessAttach);
  182. }
  183. //
  184. // HOOK_NewThread()
  185. // For WOW apps, each app is really a thread. The first thread created
  186. // in NTVDM is the WOW service thread. We don't want to share any windows
  187. // in it. Unfortunately, the first window created is a console window, so
  188. // that happens in CONF's context and we can't get any info. The next window
  189. // created in this thread is a WOW window (WOWEXEC.EXE). When that happens,
  190. // we want to go back and unshare the console window.
  191. //
  192. // If the WOW VDM is already running when another 16-bit app starts up,
  193. // we don't have these troubles.
  194. //
  195. void HOOK_NewThread(void)
  196. {
  197. DebugEntry(HOOK_NewThread);
  198. TRACE_OUT(("App thread %08ld starting", GetCurrentThreadId()));
  199. if (g_appType == HET_WOWVDM_APP)
  200. {
  201. TRACE_OUT(("Unsharing WOW service thread windows"));
  202. //
  203. // We want to go unshare the previously created WOW windows. We
  204. // never want to keep shared the dudes in the WOW service thread.
  205. //
  206. g_appType = 0;
  207. EnumWindows(HETUnshareWOWServiceWnds, GetCurrentProcessId());
  208. }
  209. // Update our "share windows on this thread" state.
  210. g_idWOWApp = GetCurrentThreadId();
  211. g_fShareWOWApp = (BOOL)HET_GetHosting(GetForegroundWindow());
  212. TRACE_OUT(("For new app thread %08ld, foreground is %s",
  213. g_idWOWApp, (g_fShareWOWApp ? "SHARED" : "not SHARED")));
  214. DebugExitVOID(HOOK_NewThread);
  215. }
  216. //
  217. // HETUnshareWOWServiceWnds()
  218. // This unshares any windows that accidentally got shared in the first
  219. // service thread in a WOW VDM. This can happen if a WOW app is launched
  220. // by a 32-bit app, and it's the first WOW app ever. The first window
  221. // created is a console window, and the notification happens in CONF's
  222. // process without the right styles that tell us it's in a WOW process.
  223. //
  224. BOOL CALLBACK HETUnshareWOWServiceWnds(HWND hwnd, LPARAM lParam)
  225. {
  226. DWORD idProcess;
  227. DebugEntry(HETUnshareWOWServiceWnds);
  228. if (GetWindowThreadProcessId(hwnd, &idProcess) &&
  229. (idProcess == (DWORD)lParam))
  230. {
  231. TRACE_OUT(("Unsharing WOW service window %08lx", hwnd));
  232. OSI_UnshareWindow(hwnd, TRUE);
  233. }
  234. DebugExitVOID(HETUnshareWOWServiceWnds);
  235. return(TRUE);
  236. }
  237. //
  238. // HOOK_Init()
  239. // This saves away the core window and atom used in the high level input
  240. // hooks and when sharing.
  241. //
  242. void WINAPI HOOK_Init(HWND hwndCore, ATOM atomTrack)
  243. {
  244. DebugEntry(HOOK_Init);
  245. g_asMainWindow = hwndCore;
  246. g_asHostProp = atomTrack;
  247. DebugExitVOID(HOOK_Init);
  248. }
  249. //
  250. // OSI_StartWindowTracking()
  251. // This installs our WinEvent hook so we can watch windows coming and going.
  252. //
  253. BOOL WINAPI OSI_StartWindowTracking(void)
  254. {
  255. BOOL rc = FALSE;
  256. DebugEntry(OSI_StartWindowTracking);
  257. ASSERT(!g_hetTrackHook);
  258. //
  259. // If we can't find the NTDLL + 2 USER32 routines we need, we can't
  260. // let you share.
  261. //
  262. if (!g_hetNtQIP || !g_hetSetWinEventHook || !g_hetUnhookWinEvent)
  263. {
  264. ERROR_OUT(("Wrong version of NT; missing NTDLL and USER32 routines needed to share"));
  265. DC_QUIT;
  266. }
  267. //
  268. // Install our hook.
  269. //
  270. g_hetTrackHook = g_hetSetWinEventHook(HET_MIN_WINEVENT, HET_MAX_WINEVENT,
  271. g_hookInstance, HETTrackProc, 0, 0,
  272. WINEVENT_INCONTEXT | WINEVENT_SKIPOWNPROCESS);
  273. if (!g_hetTrackHook)
  274. {
  275. ERROR_OUT(("SetWinEventHook failed"));
  276. DC_QUIT;
  277. }
  278. rc = TRUE;
  279. DC_EXIT_POINT:
  280. DebugExitBOOL(OSI_StartWindowTracking, rc);
  281. return(rc);
  282. }
  283. //
  284. // OSI_StopWindowTracking()
  285. // Removes our hooks for window/task spying, if installed.
  286. //
  287. void WINAPI OSI_StopWindowTracking(void)
  288. {
  289. DebugEntry(OSI_StopWindowTracking);
  290. if (g_hetTrackHook)
  291. {
  292. // Uninstall the WinEvent hook
  293. ASSERT((g_hetUnhookWinEvent != NULL));
  294. g_hetUnhookWinEvent(g_hetTrackHook);
  295. g_hetTrackHook = NULL;
  296. }
  297. DebugExitVOID(OSI_StopWindowTracking);
  298. }
  299. //
  300. // OSI_IsWindowScreenSaver()
  301. //
  302. // On NT the screensaver runs in a different desktop. We'll never get
  303. // an HWND for it.
  304. //
  305. BOOL WINAPI OSI_IsWindowScreenSaver(HWND hwnd)
  306. {
  307. #ifdef _DEBUG
  308. char className[HET_CLASS_NAME_SIZE];
  309. if (GetClassName(hwnd, className, sizeof(className)) > 0)
  310. {
  311. ASSERT(lstrcmp(className, HET_SCREEN_SAVER_CLASS));
  312. }
  313. #endif // _DEBUG
  314. return(FALSE);
  315. }
  316. //
  317. // OSI_IsWOWWindow()
  318. // Returns TRUE if the window is from a WOW (emulated 16-bit) application
  319. //
  320. BOOL WINAPI OSI_IsWOWWindow(HWND hwnd)
  321. {
  322. BOOL rc = FALSE;
  323. DWORD_PTR* pWOWWords;
  324. DebugEntry(OSI_IsWOWWindow);
  325. //
  326. // Get a pointer to the potential WOW words. We make use of an
  327. // undocumented field which is only valid for NT4.0.
  328. //
  329. pWOWWords = (DWORD_PTR*) GetClassLongPtr(hwnd, GCL_WOWWORDS);
  330. //
  331. // Check that we can use this as a pointer.
  332. //
  333. if (!pWOWWords || IsBadReadPtr(pWOWWords, sizeof(DWORD)))
  334. {
  335. DC_QUIT;
  336. }
  337. //
  338. // This is a valid pointer so try to dereference it.
  339. //
  340. if (0 == *pWOWWords)
  341. {
  342. DC_QUIT;
  343. }
  344. //
  345. // The value pointed at by <pWOWWords> is non-zero so this must be a
  346. // WOW app.
  347. //
  348. rc = TRUE;
  349. DC_EXIT_POINT:
  350. //
  351. // Let the world know what we've found.
  352. //
  353. TRACE_OUT(( "Window %#x is a %s window", hwnd, rc ? "WOW" : "Win32"));
  354. DebugExitBOOL(OSI_IsWOWWindow, rc);
  355. return(rc);
  356. }
  357. //
  358. // HETTrackProc()
  359. // Used to spy on window events
  360. // CREATE
  361. // DESTROY
  362. // SHOW
  363. // HIDE
  364. //
  365. void CALLBACK HETTrackProc
  366. (
  367. HWINEVENTHOOK hEvent,
  368. DWORD eventNotification,
  369. HWND hwnd,
  370. LONG idObject,
  371. LONG idChild,
  372. DWORD dwThreadId,
  373. DWORD dwmsEventTime
  374. )
  375. {
  376. DebugEntry(HETTrackProc);
  377. if ((idObject != OBJID_WINDOW) || (idChild != 0))
  378. {
  379. DC_QUIT;
  380. }
  381. //
  382. // Work around a bug in SP3 with ring transition callbacks, where this
  383. // proc gets called before the LoadLibrary is completed.
  384. //
  385. if (!g_hookInstance)
  386. {
  387. ERROR_OUT(( "WinEvent hook called before LoadLibrary completed!"));
  388. DC_QUIT;
  389. }
  390. switch (eventNotification)
  391. {
  392. case EVENT_OBJECT_CREATE:
  393. HETHandleCreate(hwnd);
  394. break;
  395. case EVENT_OBJECT_DESTROY:
  396. OSI_UnshareWindow(hwnd, TRUE);
  397. break;
  398. case EVENT_OBJECT_SHOW:
  399. // Only if this is a console window do we want to force a repaint.
  400. //
  401. // Only console apps cause events to occur in CONF's process (the one
  402. // that installed the hook)
  403. //
  404. HETHandleShow(hwnd, (g_hetTrackHook != NULL));
  405. break;
  406. case EVENT_OBJECT_HIDE:
  407. HETHandleHide(hwnd);
  408. break;
  409. case EVENT_OBJECT_PARENTCHANGE:
  410. HETCheckParentChange(hwnd);
  411. break;
  412. }
  413. DC_EXIT_POINT:
  414. DebugExitVOID(HETTrackProc);
  415. }
  416. //
  417. // HETHandleCreate()
  418. //
  419. // If the window isn't a real top level dude (not CHILD style or parent is
  420. // desktop) or is a menu, ignore it.
  421. //
  422. // Otherwise enum the top level windows and decide what to do:
  423. // * If at least one other in the thread/process is shared in a perm.
  424. // way, mark this the same
  425. //
  426. // * If this is the only one in the process, follow the ancestor chain
  427. // up.
  428. //
  429. void HETHandleCreate(HWND hwnd)
  430. {
  431. HET_TRACK_INFO hti;
  432. UINT hostType;
  433. #ifdef _DEBUG
  434. char szClass[HET_CLASS_NAME_SIZE];
  435. GetClassName(hwnd, szClass, sizeof(szClass));
  436. #endif
  437. DebugEntry(HETHandleCreate);
  438. //
  439. // Ignore child windows
  440. //
  441. if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
  442. {
  443. if (GetParent(hwnd) != GetDesktopWindow())
  444. {
  445. TRACE_OUT(("Skipping child window %08lx create", hwnd));
  446. DC_QUIT;
  447. }
  448. }
  449. hti.idThread = GetWindowThreadProcessId(hwnd, &hti.idProcess);
  450. if (!hti.idThread)
  451. {
  452. TRACE_OUT(("Window %08lx gone", hwnd));
  453. DC_QUIT;
  454. }
  455. //
  456. // Ignore special threads
  457. //
  458. if (HET_IsShellThread(hti.idThread))
  459. {
  460. TRACE_OUT(("Skipping shell thread window %08lx create", hwnd));
  461. DC_QUIT;
  462. }
  463. //
  464. // We don't need to ignore menus. Only when first shared do we skip
  465. // menus. The cached one we never want to share. The others will
  466. // go away almost immediately. From now on, we treat them the same
  467. // as other windows.
  468. //
  469. //
  470. // Figure out what to do.
  471. // NOTE:
  472. // We don't want to inadvertently share the other windows WOW creates.
  473. // The first thread in the WOW process has special classes, which aren't
  474. // WOW wrappers.
  475. //
  476. hti.hwndUs = hwnd;
  477. hti.fWOW = OSI_IsWOWWindow(hwnd);
  478. hti.cWndsApp = 0;
  479. hti.cWndsSharedThread = 0;
  480. hti.cWndsSharedProcess = 0;
  481. TRACE_OUT(("Create for %s window %08lx class %s process %08ld thread %08ld",
  482. (hti.fWOW ? "WOW" : "32-bit"), hwnd, szClass, hti.idProcess, hti.idThread));
  483. UpOneLevel:
  484. EnumWindows(HETShareEnum, (LPARAM)(LPHET_TRACK_INFO)&hti);
  485. if (hti.cWndsSharedThread)
  486. {
  487. TRACE_OUT(("Sharing window %08lx class %s by thread %08ld in process %08ld",
  488. hwnd, szClass, hti.idThread, hti.idProcess));
  489. hostType = HET_HOSTED_PERMANENT | HET_HOSTED_BYTHREAD;
  490. }
  491. else if (hti.cWndsSharedProcess)
  492. {
  493. TRACE_OUT(("Sharing window %08lx class %s by process %08ld in thread %08ld",
  494. hwnd, szClass, hti.idProcess, hti.idThread));
  495. hostType = HET_HOSTED_PERMANENT | HET_HOSTED_BYPROCESS;
  496. }
  497. else if (hti.cWndsApp)
  498. {
  499. //
  500. // There's another window in our app, but none are shared. So don't
  501. // share us either.
  502. //
  503. TRACE_OUT(("Not sharing window %08lx class %s; other unshared windows in thread %08ld process %08ld",
  504. hwnd, szClass, hti.idThread, hti.idProcess));
  505. DC_QUIT;
  506. }
  507. else if (hti.fWOW)
  508. {
  509. //
  510. // Task tracking code for WOW apps, which are really threads.
  511. //
  512. BOOL fShare;
  513. //
  514. // WOW apps are different. They are threads in the NTVDM process.
  515. // Therefore parent/child relationships aren't useful. Instead,
  516. // the best thing we can come up with is to use the status of the
  517. // foreground window. We assume that the currently active app at
  518. // the time the WOW app started up is the one that launched us.
  519. //
  520. // We can't just call GetForegroundWindow() here, because it is too
  521. // late.
  522. //
  523. if (hti.idThread == g_idWOWApp)
  524. {
  525. fShare = g_fShareWOWApp;
  526. g_fShareWOWApp = FALSE;
  527. g_idWOWApp = 0;
  528. }
  529. else
  530. {
  531. fShare = FALSE;
  532. }
  533. if (!fShare)
  534. {
  535. TRACE_OUT(("THREAD window %08lx class %s in thread %08ld not shared",
  536. hwnd, szClass, hti.idThread));
  537. DC_QUIT;
  538. }
  539. TRACE_OUT(("First window %08lx class %s of WOW app %08ld, shared since foreground is",
  540. hwnd, szClass, hti.idThread));
  541. hostType = HET_HOSTED_PERMANENT | HET_HOSTED_BYTHREAD;
  542. }
  543. else
  544. {
  545. //
  546. // Task tracking code for 32-bit apps.
  547. //
  548. DWORD idParentProcess;
  549. //
  550. // First window of a WIN32 app.
  551. //
  552. // Loop through our ancestor processes (no thread info at this point)
  553. HETGetParentProcessID(hti.idProcess, &idParentProcess);
  554. if (!idParentProcess)
  555. {
  556. TRACE_OUT(("Can't get parent of process %08ld", hti.idProcess));
  557. DC_QUIT;
  558. }
  559. //
  560. // We know if we got here that all our favorite fields are still
  561. // zero. So just loop! But NULL out idThread to avoid matching
  562. // anything while we look at our parent.
  563. //
  564. TRACE_OUT(("First window %08lx class %s in process %08ld %s, checking parent %08ld",
  565. hwnd, szClass, hti.idProcess, GetCommandLine(), idParentProcess));
  566. hti.idThread = 0;
  567. hti.idProcess = idParentProcess;
  568. goto UpOneLevel;
  569. }
  570. //
  571. // OK, we are going to share this. We do have to repaint console
  572. // windows--we get the notifications asynchronously. If the window isn't
  573. // visible yet, redrawing will do nothing. After this, the property is
  574. // set, and we will catch all ouput. If it has already become visible,
  575. // invalidating it now will still work, and we will ignore the queued
  576. // up show notification because the property is set.
  577. //
  578. OSI_ShareWindow(hwnd, hostType, (g_hetTrackHook != NULL), TRUE);
  579. DC_EXIT_POINT:
  580. DebugExitVOID(HETHandleCreate);
  581. }
  582. //
  583. // HETHandleShow()
  584. //
  585. void HETHandleShow
  586. (
  587. HWND hwnd,
  588. BOOL fForceRepaint
  589. )
  590. {
  591. UINT hostType;
  592. HET_TRACK_INFO hti;
  593. DebugEntry(HETHandleShow);
  594. hostType = (UINT)HET_GetHosting(hwnd);
  595. //
  596. // If this window is a real child, clear the hosting property. Usually
  597. // one isn't there. But in the case of a top level window becoming
  598. // a child of another, we want to wipe out junk.
  599. //
  600. if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
  601. {
  602. if (GetParent(hwnd) != GetDesktopWindow())
  603. {
  604. TRACE_OUT(("Skipping child window %08lx show", hwnd));
  605. if (hostType)
  606. {
  607. WARNING_OUT(("Unsharing shared child window 0x%08x from SHOW", hwnd));
  608. OSI_UnshareWindow(hwnd, TRUE);
  609. }
  610. DC_QUIT;
  611. }
  612. }
  613. //
  614. // Is this window already shared? Nothing to do if so. If it's a
  615. // console guy, we've seen it already on create.
  616. //
  617. if (hostType)
  618. {
  619. TRACE_OUT(("Window %08lx already shared, ignoring show", hwnd));
  620. DC_QUIT;
  621. }
  622. //
  623. // Here's where we also enumerate the top level windows and find a
  624. // match. But we DO not track across processes in this case. Instead
  625. // we look at the owner if there is one.
  626. //
  627. // This solves the create-as-a-child then change to a top level
  628. // window problem, like combo dropdowns.
  629. //
  630. hti.idThread = GetWindowThreadProcessId(hwnd, &hti.idProcess);
  631. if (!hti.idThread)
  632. {
  633. TRACE_OUT(("Window %08lx gone", hwnd));
  634. DC_QUIT;
  635. }
  636. //
  637. // Ignore special shell threads
  638. //
  639. if (HET_IsShellThread(hti.idThread))
  640. {
  641. TRACE_OUT(("Skipping shell thread window %08lx show", hwnd));
  642. DC_QUIT;
  643. }
  644. hti.hwndUs = hwnd;
  645. hti.fWOW = OSI_IsWOWWindow(hwnd);
  646. hti.cWndsApp = 0;
  647. hti.cWndsSharedThread = 0;
  648. hti.cWndsSharedProcess = 0;
  649. EnumWindows(HETShareEnum, (LPARAM)(LPHET_TRACK_INFO)&hti);
  650. //
  651. // These kinds of windows are always only temp shared. They don't
  652. // start out as top level windows that we saw from the beginning or
  653. // watched created. These are SetParent() or menu kinds of dudes, so
  654. // for a lot of reasons we're plain safer sharing these babies only
  655. // temporarily
  656. //
  657. //
  658. // Anything else shared on this thread/process, the decision is easy.
  659. // Otherwise, we look at the ownership trail.
  660. //
  661. if (!hti.cWndsSharedThread && !hti.cWndsSharedProcess)
  662. {
  663. HWND hwndOwner;
  664. //
  665. // Does it have an owner that is shared?
  666. //
  667. hwndOwner = hwnd;
  668. while (hwndOwner = GetWindow(hwndOwner, GW_OWNER))
  669. {
  670. if (HET_GetHosting(hwndOwner))
  671. {
  672. TRACE_OUT(("Found shared owner %08lx of window %08lx", hwndOwner, hwnd));
  673. break;
  674. }
  675. }
  676. if (!hwndOwner)
  677. {
  678. DC_QUIT;
  679. }
  680. }
  681. //
  682. // For console apps, we get notifications asynchronously posted to us,
  683. // in NM's process. The window may have painted already without our
  684. // seeing it. So force it to repaint just in case. The g_hetTrackHook
  685. // variable is only around when this is NM.
  686. //
  687. TRACE_OUT(("Sharing temporary window %08lx", hwnd));
  688. OSI_ShareWindow(hwnd, HET_HOSTED_BYWINDOW | HET_HOSTED_TEMPORARY,
  689. fForceRepaint, TRUE);
  690. DC_EXIT_POINT:
  691. DebugExitVOID(HETHandleShow);
  692. }
  693. //
  694. // HETHandleHide()
  695. // This handles a window being hidden. If it was temporary, it is unshared.
  696. // If it is permanent, it is marked as hidden.
  697. //
  698. void HETHandleHide(HWND hwnd)
  699. {
  700. UINT hostType;
  701. DebugEntry(HETHandleHide);
  702. hostType = (UINT)HET_GetHosting(hwnd);
  703. if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
  704. {
  705. if (GetParent(hwnd) != GetDesktopWindow())
  706. {
  707. TRACE_OUT(("Skipping child window %08lx hide", hwnd));
  708. if (hostType)
  709. {
  710. WARNING_OUT(("Unsharing shared child window 0x%08x from HIDE", hwnd));
  711. OSI_UnshareWindow(hwnd, TRUE);
  712. }
  713. DC_QUIT;
  714. }
  715. }
  716. if (!hostType)
  717. {
  718. //
  719. // Console apps give us notifications out of context. Make
  720. // sure the count is up to date.
  721. //
  722. if (g_hetTrackHook)
  723. {
  724. HETNewTopLevelCount();
  725. }
  726. else
  727. {
  728. TRACE_OUT(("Window %08lx not shared, ignoring hide", hwnd));
  729. }
  730. }
  731. else if (hostType & HET_HOSTED_TEMPORARY)
  732. {
  733. //
  734. // Temporarily shared window are only shared when visible.
  735. //
  736. TRACE_OUT(("Unsharing temporary window %08lx", hwnd));
  737. OSI_UnshareWindow(hwnd, TRUE);
  738. }
  739. else
  740. {
  741. ASSERT(hostType & HET_HOSTED_PERMANENT);
  742. // Nothing to do.
  743. TRACE_OUT(("Window %08lx permanently shared, ignoring hide", hwnd));
  744. }
  745. DC_EXIT_POINT:
  746. DebugExitVOID(HETHandleHide);
  747. }
  748. //
  749. // HETCheckParentChange()
  750. //
  751. // PARENTCHANGE is 100% reliable, compared to Win9x stuff.
  752. //
  753. void HETCheckParentChange(HWND hwnd)
  754. {
  755. DebugEntry(HETCheckParentChange);
  756. WARNING_OUT(("Got PARENTCHANGE for hwnd 0x%08x", hwnd));
  757. if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
  758. {
  759. if (GetParent(hwnd) != GetDesktopWindow())
  760. {
  761. UINT hostType;
  762. hostType = (UINT)HET_GetHosting(hwnd);
  763. if (hostType)
  764. {
  765. WARNING_OUT(("Unsharing shared child window 0x%08x from MOVE", hwnd));
  766. OSI_UnshareWindow(hwnd, TRUE);
  767. }
  768. }
  769. }
  770. DebugExitVOID(HETCheckParentChange);
  771. }
  772. //
  773. // OSI_ShareWindow
  774. // This shares a window, calling the display driver to add it to the visrgn
  775. // list. It is called when
  776. // * An app is shared
  777. // * A new window in a shared app is created
  778. // * A temporary window with a relationship to a shared window is shown
  779. //
  780. // This returns TRUE if it shared a window.
  781. //
  782. BOOL OSI_ShareWindow
  783. (
  784. HWND hwnd,
  785. UINT hostType,
  786. BOOL fRepaint,
  787. BOOL fUpdateCount
  788. )
  789. {
  790. BOOL rc = FALSE;
  791. HET_SHARE_WINDOW req;
  792. DebugEntry(OSI_ShareWindow);
  793. //
  794. // Set the property
  795. //
  796. if (!HET_SetHosting(hwnd, hostType))
  797. {
  798. ERROR_OUT(("Couldn't set shared property on window %08lx", hwnd));
  799. DC_QUIT;
  800. }
  801. //
  802. // Tell the display driver
  803. //
  804. req.winID = HandleToUlong(hwnd);
  805. req.result = 0;
  806. if (!OSI_FunctionRequest(HET_ESC_SHARE_WINDOW, (LPOSI_ESCAPE_HEADER)&req,
  807. sizeof(req)) ||
  808. !req.result)
  809. {
  810. ERROR_OUT(("Driver couldn't add window %08lx to list", hwnd));
  811. HET_ClearHosting(hwnd);
  812. DC_QUIT;
  813. }
  814. TRACE_OUT(("Shared window %08lx of type %08lx", hwnd, hostType));
  815. //
  816. // Repaint it
  817. //
  818. if (fRepaint)
  819. {
  820. USR_RepaintWindow(hwnd);
  821. }
  822. if (fUpdateCount)
  823. {
  824. PostMessage(g_asMainWindow, DCS_NEWTOPLEVEL_MSG, TRUE, 0);
  825. }
  826. rc = TRUE;
  827. DC_EXIT_POINT:
  828. DebugExitBOOL(OSI_ShareWindow, rc);
  829. return(rc);
  830. }
  831. //
  832. // OSI_UnshareWindow()
  833. // This unshares a window. This is called when
  834. // * An app is unshared
  835. // * A window is destroyed
  836. // * A temporarily shared window is hidden
  837. //
  838. // It returns TRUE if a shared window has been unshared.
  839. //
  840. BOOL OSI_UnshareWindow
  841. (
  842. HWND hwnd,
  843. BOOL fUpdateCount
  844. )
  845. {
  846. BOOL rc = FALSE;
  847. UINT hostType;
  848. HET_UNSHARE_WINDOW req;
  849. DebugEntry(OSI_UnshareWindow);
  850. //
  851. // This gets the old property and clears it in one step.
  852. //
  853. hostType = (UINT)HET_ClearHosting(hwnd);
  854. if (!hostType)
  855. {
  856. if (fUpdateCount && g_hetTrackHook)
  857. {
  858. //
  859. // We always get async notifications for console apps. In that
  860. // case, the window is really gone before this comes to us.
  861. // So redetermine the count now.
  862. //
  863. HETNewTopLevelCount();
  864. }
  865. DC_QUIT;
  866. }
  867. //
  868. // OK, stuff to do.
  869. //
  870. TRACE_OUT(("Unsharing window %08lx of type %08lx", hwnd, hostType));
  871. //
  872. // Tell the display driver
  873. //
  874. req.winID = HandleToUlong(hwnd);
  875. OSI_FunctionRequest(HET_ESC_UNSHARE_WINDOW, (LPOSI_ESCAPE_HEADER)&req, sizeof(req));
  876. //
  877. // Update the top level count
  878. //
  879. if (fUpdateCount)
  880. {
  881. PostMessage(g_asMainWindow, DCS_NEWTOPLEVEL_MSG, FALSE, 0);
  882. }
  883. rc = TRUE;
  884. DC_EXIT_POINT:
  885. DebugExitBOOL(OSI_UnshareWindow, rc);
  886. return(rc);
  887. }
  888. //
  889. // HETShareEnum()
  890. //
  891. // This is the EnumWindows() callback. We stop when we find the first
  892. // matching shared window (thread or process). We keep a running tally
  893. // of the count of all top level windows in our process (not shared by
  894. // thread or process) at the same time. This lets us do tracking.
  895. //
  896. BOOL CALLBACK HETShareEnum(HWND hwnd, LPARAM lParam)
  897. {
  898. LPHET_TRACK_INFO lphti = (LPHET_TRACK_INFO)lParam;
  899. DWORD idProcess;
  900. DWORD idThread;
  901. UINT hostType;
  902. BOOL rc = TRUE;
  903. DebugEntry(HETShareEnum);
  904. // Skip ourself.
  905. if (hwnd == lphti->hwndUs)
  906. {
  907. DC_QUIT;
  908. }
  909. // Skip if window is gone.
  910. idThread = GetWindowThreadProcessId(hwnd, &idProcess);
  911. if (!idThread)
  912. {
  913. DC_QUIT;
  914. }
  915. //
  916. // Do the apps match? If not, ignore this window.
  917. //
  918. if ((idProcess != lphti->idProcess) ||
  919. ((lphti->fWOW) && (idThread != lphti->idThread)))
  920. {
  921. DC_QUIT;
  922. }
  923. lphti->cWndsApp++;
  924. hostType = (UINT)HET_GetHosting(hwnd);
  925. if (!hostType)
  926. {
  927. DC_QUIT;
  928. }
  929. //
  930. // Now, if this window is shared by thread or process, do the right
  931. // thing.
  932. //
  933. if (hostType & HET_HOSTED_BYPROCESS)
  934. {
  935. // We have a match. We can return immediately.
  936. lphti->cWndsSharedProcess++;
  937. rc = FALSE;
  938. }
  939. else if (hostType & HET_HOSTED_BYTHREAD)
  940. {
  941. //
  942. // For WOW apps, we don't want this one, if in a separate thread, to
  943. // count. No matter what.
  944. //
  945. if (idThread == lphti->idThread)
  946. {
  947. lphti->cWndsSharedThread++;
  948. rc = FALSE;
  949. }
  950. }
  951. DC_EXIT_POINT:
  952. DebugExitBOOL(HETShareEnum, rc);
  953. return(rc);
  954. }
  955. //
  956. // HETNewTopLevelCount()
  957. // This does a quick new tally of the shared top level visible count
  958. //
  959. void HETNewTopLevelCount(void)
  960. {
  961. UINT newCount;
  962. DebugEntry(HETNewTopLevelCount);
  963. newCount = 0;
  964. EnumWindows(HETCountTopLevel, (LPARAM)&newCount);
  965. PostMessage(g_asMainWindow, DCS_RECOUNTTOPLEVEL_MSG, newCount, 0);
  966. DebugExitVOID(HETNewTopLevelCount);
  967. }
  968. //
  969. // HETCountTopLevel()
  970. // This counts shared windows
  971. //
  972. BOOL CALLBACK HETCountTopLevel(HWND hwnd, LPARAM lParam)
  973. {
  974. DebugEntry(HETCountTopLevel);
  975. if (HET_GetHosting(hwnd))
  976. {
  977. (*(LPUINT)lParam)++;
  978. }
  979. DebugExitBOOL(HETCountTopLevel, TRUE);
  980. return(TRUE);
  981. }
  982. //
  983. // HET_IsShellThread()
  984. // Returns TRUE if thread is one of shell's special threads
  985. //
  986. BOOL HET_IsShellThread(DWORD threadID)
  987. {
  988. BOOL rc;
  989. DebugEntry(HET_IsShellThread);
  990. if ((threadID == GetWindowThreadProcessId(HET_GetShellDesktop(), NULL)) ||
  991. (threadID == GetWindowThreadProcessId(HET_GetShellTray(), NULL)))
  992. {
  993. rc = TRUE;
  994. }
  995. else
  996. {
  997. rc = FALSE;
  998. }
  999. DebugExitBOOL(HET_IsShellThread, rc);
  1000. return(rc);
  1001. }
  1002. //
  1003. // HET_WindowIsHosted()
  1004. // This is called by the high level mouse hook. Unlike the version in
  1005. // MNMCPI32, it doesn't check (or know) if the whole desktop is shared.
  1006. //
  1007. // LAURABU BOGUS
  1008. // Note that this may need to be revised. The high level hooks are handy
  1009. // in desktop sharing also. For the keyboard, we track the toggle key
  1010. // states. For the mouse, we block messages to non-shared windows.
  1011. //
  1012. BOOL HET_WindowIsHosted(HWND hwnd)
  1013. {
  1014. BOOL rc = FALSE;
  1015. HWND hwndParent;
  1016. DebugEntry(HET_WindowIsHosted);
  1017. if (!hwnd)
  1018. DC_QUIT;
  1019. //
  1020. // Walk up to the top level window this one is inside of
  1021. //
  1022. while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
  1023. {
  1024. hwndParent = GetParent(hwnd);
  1025. if (hwndParent == GetDesktopWindow())
  1026. break;
  1027. hwnd = hwndParent;
  1028. }
  1029. rc = (BOOL)HET_GetHosting(hwnd);
  1030. DC_EXIT_POINT:
  1031. DebugExitBOOL(HET_WindowIsHosted, rc);
  1032. return(rc);
  1033. }
  1034. //
  1035. // HETGetParentProcessID()
  1036. // This gets the ID of the process which created the passed in one. Used
  1037. // for task tracking
  1038. //
  1039. void HETGetParentProcessID
  1040. (
  1041. DWORD processID,
  1042. LPDWORD pParentProcessID
  1043. )
  1044. {
  1045. HANDLE hProcess;
  1046. UINT intRC;
  1047. PROCESS_BASIC_INFORMATION basicInfo;
  1048. DebugEntry(HETGetParentProcessID);
  1049. *pParentProcessID = 0;
  1050. //
  1051. // Open a handle to the process. If we don't have security privileges,
  1052. // or it is gone, this will fail.
  1053. //
  1054. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
  1055. FALSE, processID);
  1056. if (NULL == hProcess)
  1057. {
  1058. WARNING_OUT(("Can't get process handle for ID %08lx", processID));
  1059. DC_QUIT;
  1060. }
  1061. //
  1062. // Get back an information block for this process, one item of which is
  1063. // the parent.
  1064. //
  1065. ASSERT(g_hetNtQIP);
  1066. intRC = g_hetNtQIP(hProcess, ProcessBasicInformation, &basicInfo,
  1067. sizeof(basicInfo), NULL);
  1068. if (!NT_SUCCESS(intRC))
  1069. {
  1070. ERROR_OUT(("Can't get info for process ID %08lx, handle %08lx -- error %u",
  1071. processID, hProcess, intRC));
  1072. }
  1073. else
  1074. {
  1075. *pParentProcessID = basicInfo.InheritedFromUniqueProcessId;
  1076. }
  1077. //
  1078. // Close the process handle
  1079. //
  1080. CloseHandle(hProcess);
  1081. DC_EXIT_POINT:
  1082. DebugExitVOID(HETGetParentProcessID);
  1083. }
  1084.