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

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