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.

3651 lines
91 KiB

  1. #include "precomp.h"
  2. //
  3. // HOST.CPP
  4. // Hosting, local and remote
  5. //
  6. // Copyright(c) Microsoft 1997-
  7. //
  8. #define MLZ_FILE_ZONE ZONE_CORE
  9. //
  10. // HET_Init()
  11. //
  12. // Initialization for hosting
  13. // * window tracking
  14. // * capabilities
  15. // * host UI
  16. //
  17. BOOL HET_Init(void)
  18. {
  19. BOOL rc = FALSE;
  20. int property;
  21. UINT i;
  22. LOGFONT lf;
  23. DebugEntry(HET_Init);
  24. //
  25. // Initialize T.128 capabilities, whether we can host or not.
  26. //
  27. ZeroMemory(&g_cpcLocalCaps, sizeof(g_cpcLocalCaps));
  28. g_cpcLocalCaps.header.numCapabilities = PROTCAPS_COUNT;
  29. // PROTCAPS_GENERAL
  30. // Check for compression setting (useful to debug protocol)
  31. // You can set CT_PKZIP (1) or none (0) instead of persistent PKZIP,
  32. // which is the default.
  33. //
  34. g_cpcLocalCaps.general.header.capID = CAPS_ID_GENERAL;
  35. g_cpcLocalCaps.general.header.capSize = sizeof(g_cpcLocalCaps.general);
  36. COM_ReadProfInt(DBG_INI_SECTION_NAME, GDC_INI_COMPRESSION,
  37. GCT_DEFAULT, &property);
  38. g_cpcLocalCaps.general.genCompressionType = (TSHR_UINT16)property;
  39. g_cpcLocalCaps.general.genCompressionLevel = CAPS_GEN_COMPRESSION_LEVEL_1;
  40. g_cpcLocalCaps.general.OS = CAPS_WINDOWS;
  41. g_cpcLocalCaps.general.OSVersion = (g_asWin95 ? CAPS_WINDOWS_95 : CAPS_WINDOWS_NT);
  42. g_cpcLocalCaps.general.typeFlags = 0;
  43. if (g_asOptions & AS_SERVICE)
  44. {
  45. g_cpcLocalCaps.general.typeFlags |= AS_SERVICE;
  46. }
  47. if (g_asOptions & AS_UNATTENDED)
  48. {
  49. g_cpcLocalCaps.general.typeFlags |= AS_UNATTENDED;
  50. }
  51. g_cpcLocalCaps.general.version = CAPS_VERSION_CURRENT;
  52. g_cpcLocalCaps.general.supportsDOS6Compression = CAPS_UNSUPPORTED;
  53. g_cpcLocalCaps.general.supportsCapsUpdate = CAPS_SUPPORTED;
  54. g_cpcLocalCaps.general.supportsRemoteUnshare = CAPS_UNSUPPORTED;
  55. //
  56. // PROTCAPS_SCREEN
  57. //
  58. g_cpcLocalCaps.screen.header.capID = CAPS_ID_SCREEN;
  59. g_cpcLocalCaps.screen.header.capSize = sizeof(g_cpcLocalCaps.screen);
  60. g_cpcLocalCaps.screen.capsSupports1BPP = CAPS_UNSUPPORTED;
  61. g_cpcLocalCaps.screen.capsSupports4BPP = CAPS_SUPPORTED;
  62. g_cpcLocalCaps.screen.capsSupports8BPP = CAPS_SUPPORTED;
  63. g_cpcLocalCaps.screen.capsSupports24BPP = CAPS_SUPPORTED;
  64. g_cpcLocalCaps.screen.capsScreenWidth = (TSHR_UINT16)GetSystemMetrics(SM_CXSCREEN);
  65. g_cpcLocalCaps.screen.capsScreenHeight = (TSHR_UINT16)GetSystemMetrics(SM_CYSCREEN);
  66. g_cpcLocalCaps.screen.capsSupportsDesktopResize = CAPS_SUPPORTED;
  67. //
  68. // Set up the V1 and/or V2 Bitmap Compression capabilities. For the
  69. // V2.0 protocol, both are supported by default (supporting V1
  70. // compression allows for negotiation down to V1 protocol systems), but
  71. // can be overidden in the INI file.
  72. //
  73. g_cpcLocalCaps.screen.capsSupportsV1Compression = CAPS_UNSUPPORTED;
  74. g_cpcLocalCaps.screen.capsSupportsV2Compression = CAPS_SUPPORTED;
  75. g_cpcLocalCaps.screen.capsBPP = (TSHR_UINT16)g_usrScreenBPP;
  76. // PROTCAPS_SC
  77. g_cpcLocalCaps.share.header.capID = CAPS_ID_SC;
  78. g_cpcLocalCaps.share.header.capSize = sizeof(g_cpcLocalCaps.share);
  79. g_cpcLocalCaps.share.gccID = 0;
  80. // PROTCAPS_CM
  81. g_cpcLocalCaps.cursor.header.capID = CAPS_ID_CM;
  82. g_cpcLocalCaps.cursor.header.capSize = sizeof(g_cpcLocalCaps.cursor);
  83. g_cpcLocalCaps.cursor.capsSupportsColorCursors = CAPS_SUPPORTED;
  84. g_cpcLocalCaps.cursor.capsCursorCacheSize = TSHR_CM_CACHE_ENTRIES;
  85. // PROTCAPS_PM
  86. g_cpcLocalCaps.palette.header.capID = CAPS_ID_PM;
  87. g_cpcLocalCaps.palette.header.capSize = sizeof(g_cpcLocalCaps.palette);
  88. g_cpcLocalCaps.palette.capsColorTableCacheSize = TSHR_PM_CACHE_ENTRIES;
  89. //
  90. // PROTCAPS_BITMAPCACHE
  91. //
  92. g_cpcLocalCaps.bitmaps.header.capID = CAPS_ID_BITMAPCACHE;
  93. g_cpcLocalCaps.bitmaps.header.capSize = sizeof(g_cpcLocalCaps.bitmaps);
  94. //
  95. // SEND BITMAP CACHE
  96. //
  97. // The cache is now more in line with what the display driver is doing.
  98. // The memory size for medium/large is the same. But large bitmaps are
  99. // 4x bigger, so there are 1/4 as many. The # of small bitmaps is the
  100. // same as the # of medium bitmaps. Since small bitmaps are 1/4 the
  101. // size, only 1/4 as much memory is used.
  102. //
  103. if (g_sbcEnabled)
  104. {
  105. UINT maxSendBPP;
  106. ASSERT(g_asbcShuntBuffers[SBC_MEDIUM_TILE_INDEX]);
  107. ASSERT(g_asbcShuntBuffers[SBC_LARGE_TILE_INDEX]);
  108. g_cpcLocalCaps.bitmaps.sender.capsSmallCacheNumEntries =
  109. (TSHR_UINT16)g_asbcShuntBuffers[SBC_MEDIUM_TILE_INDEX]->numEntries;
  110. g_cpcLocalCaps.bitmaps.sender.capsMediumCacheNumEntries =
  111. (TSHR_UINT16)g_asbcShuntBuffers[SBC_MEDIUM_TILE_INDEX]->numEntries;
  112. g_cpcLocalCaps.bitmaps.sender.capsLargeCacheNumEntries =
  113. (TSHR_UINT16)g_asbcShuntBuffers[SBC_LARGE_TILE_INDEX]->numEntries;
  114. if (g_usrScreenBPP >= 24)
  115. {
  116. maxSendBPP = 24;
  117. }
  118. else
  119. {
  120. maxSendBPP = 8;
  121. }
  122. g_cpcLocalCaps.bitmaps.sender.capsSmallCacheCellSize =
  123. MP_CACHE_CELLSIZE(MP_SMALL_TILE_WIDTH, MP_SMALL_TILE_WIDTH,
  124. maxSendBPP);
  125. g_cpcLocalCaps.bitmaps.sender.capsMediumCacheCellSize =
  126. MP_CACHE_CELLSIZE(MP_MEDIUM_TILE_WIDTH, MP_MEDIUM_TILE_HEIGHT,
  127. maxSendBPP);
  128. g_cpcLocalCaps.bitmaps.sender.capsLargeCacheCellSize =
  129. MP_CACHE_CELLSIZE(MP_LARGE_TILE_WIDTH, MP_LARGE_TILE_HEIGHT,
  130. maxSendBPP);
  131. }
  132. else
  133. {
  134. //
  135. // We can't use sizes of zero, 2.x nodes will fail if we do. But
  136. // we can use a tiny number so they don't allocate huge hunks of
  137. // memory for no reason. And 3.0 will treat '1' like '0'.
  138. //
  139. g_cpcLocalCaps.bitmaps.sender.capsSmallCacheNumEntries = 1;
  140. g_cpcLocalCaps.bitmaps.sender.capsSmallCacheCellSize = 1;
  141. g_cpcLocalCaps.bitmaps.sender.capsMediumCacheNumEntries = 1;
  142. g_cpcLocalCaps.bitmaps.sender.capsMediumCacheCellSize = 1;
  143. g_cpcLocalCaps.bitmaps.sender.capsLargeCacheNumEntries = 1;
  144. g_cpcLocalCaps.bitmaps.sender.capsLargeCacheCellSize = 1;
  145. }
  146. TRACE_OUT(("SBC small cache: %d entries, size %d",
  147. g_cpcLocalCaps.bitmaps.sender.capsSmallCacheNumEntries,
  148. g_cpcLocalCaps.bitmaps.sender.capsSmallCacheCellSize));
  149. TRACE_OUT(("SBC medium cache: %d entries, size %d",
  150. g_cpcLocalCaps.bitmaps.sender.capsMediumCacheNumEntries,
  151. g_cpcLocalCaps.bitmaps.sender.capsMediumCacheCellSize));
  152. TRACE_OUT(("SBC large cache: %d entries, size %d",
  153. g_cpcLocalCaps.bitmaps.sender.capsLargeCacheNumEntries,
  154. g_cpcLocalCaps.bitmaps.sender.capsLargeCacheCellSize));
  155. //
  156. // RECEIVE caps are obsolete with 3.0; receivers simply look at the
  157. // sender's attributes. So just fill in the MAX possible. 2.x remotes
  158. // will take the min of themselves and everybody else's receiver caps.
  159. //
  160. g_cpcLocalCaps.bitmaps.receiver.capsSmallCacheNumEntries = 0x7FFF;
  161. g_cpcLocalCaps.bitmaps.receiver.capsSmallCacheCellSize = 0x7FFF;
  162. g_cpcLocalCaps.bitmaps.receiver.capsMediumCacheNumEntries = 0x7FFF;
  163. g_cpcLocalCaps.bitmaps.receiver.capsMediumCacheCellSize = 0x7FFF;
  164. g_cpcLocalCaps.bitmaps.receiver.capsLargeCacheNumEntries = 0x7FFF;
  165. g_cpcLocalCaps.bitmaps.receiver.capsLargeCacheCellSize = 0x7FFF;
  166. //
  167. // PROTCAPS_ORDERS
  168. //
  169. g_cpcLocalCaps.orders.header.capID = CAPS_ID_ORDERS;
  170. g_cpcLocalCaps.orders.header.capSize = sizeof(g_cpcLocalCaps.orders);
  171. //
  172. // Fill in the SaveBitmap capabilities.
  173. //
  174. g_cpcLocalCaps.orders.capsSaveBitmapSize = TSHR_SSI_BITMAP_SIZE;
  175. g_cpcLocalCaps.orders.capsSaveBitmapXGranularity = TSHR_SSI_BITMAP_X_GRANULARITY;
  176. g_cpcLocalCaps.orders.capsSaveBitmapYGranularity = TSHR_SSI_BITMAP_Y_GRANULARITY;
  177. g_cpcLocalCaps.orders.capsSendSaveBitmapSize = g_cpcLocalCaps.orders.capsSaveBitmapSize;
  178. g_cpcLocalCaps.orders.capsReceiveSaveBitmapSize = g_cpcLocalCaps.orders.capsSaveBitmapSize;
  179. //
  180. // We support
  181. // * R20 Signatures (cell heights, better matching)
  182. // * Aspect matching
  183. // * Charset/code page matching
  184. // * Baseline text orders
  185. // * Em Heights
  186. // * DeltaX arrays for simulation if font not on remote
  187. //
  188. //
  189. // BOGUS LAURABU BUGBUG
  190. //
  191. // Baseline text orders not yet supported in Win95. But that's OK,
  192. // we don't mark any orders we generate on that platform with
  193. // NF_BASELINE, so they aren't treated as such.
  194. //
  195. g_cpcLocalCaps.orders.capsfFonts = CAPS_FONT_R20_SIGNATURE |
  196. CAPS_FONT_ASPECT |
  197. CAPS_FONT_CODEPAGE |
  198. CAPS_FONT_ALLOW_BASELINE |
  199. CAPS_FONT_EM_HEIGHT |
  200. CAPS_FONT_OLD_NEED_X |
  201. CAPS_FONT_NEED_X_SOMETIMES;
  202. //
  203. // Fill in which orders we support.
  204. //
  205. for (i = 0; i < ORD_NUM_LEVEL_1_ORDERS; i++)
  206. {
  207. //
  208. // Order indices for desktop-scrolling and memblt variants are not
  209. // to be negotiated by this mechanism... these currently consume
  210. // 3 order indices which must be excluded from this negotiation.
  211. //
  212. if ( (i == ORD_RESERVED_INDEX ) ||
  213. (i == ORD_MEMBLT_R2_INDEX ) ||
  214. (i == ORD_UNUSED_INDEX ) ||
  215. (i == ORD_MEM3BLT_R2_INDEX) )
  216. {
  217. continue;
  218. }
  219. g_cpcLocalCaps.orders.capsOrders[i] = ORD_LEVEL_1_ORDERS;
  220. }
  221. g_cpcLocalCaps.orders.capsMaxOrderlevel = ORD_LEVEL_1_ORDERS;
  222. //
  223. // Fill in encoding capabilities
  224. //
  225. //
  226. // Keep the "encoding disabled" option, it's handy for using our
  227. // protocol analyzer
  228. //
  229. COM_ReadProfInt(DBG_INI_SECTION_NAME, OE2_INI_2NDORDERENCODING,
  230. CAPS_ENCODING_DEFAULT, &property);
  231. g_cpcLocalCaps.orders.capsEncodingLevel = (TSHR_UINT16)property;
  232. g_cpcLocalCaps.orders.capsfSendScroll = FALSE;
  233. //
  234. // Get the app and desktop icons, big and small
  235. //
  236. g_hetASIcon = LoadIcon(g_asInstance, MAKEINTRESOURCE(IDI_SHAREICON));
  237. if (!g_hetASIcon)
  238. {
  239. ERROR_OUT(("HET_Init: Failed to load app icon"));
  240. DC_QUIT;
  241. }
  242. g_hetDeskIcon = LoadIcon(g_asInstance, MAKEINTRESOURCE(IDI_DESKTOPICON));
  243. if (!g_hetDeskIcon)
  244. {
  245. ERROR_OUT(("HET_Init: failed to load desktop icon"));
  246. DC_QUIT;
  247. }
  248. // Get the small icon, created, that we paint on the window bar items
  249. g_hetASIconSmall = (HICON)LoadImage(g_asInstance, MAKEINTRESOURCE(IDI_SHAREICON),
  250. IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
  251. LR_DEFAULTCOLOR);
  252. if (!g_hetASIconSmall)
  253. {
  254. ERROR_OUT(("HET_Init: Failed to load app small icon"));
  255. DC_QUIT;
  256. }
  257. g_hetDeskIconSmall = (HICON)LoadImage(g_asInstance, MAKEINTRESOURCE(IDI_DESKTOPICON),
  258. IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
  259. LR_DEFAULTCOLOR);
  260. if (!g_hetDeskIconSmall)
  261. {
  262. ERROR_OUT(("HET_Init: Failed to load desktop small icon"));
  263. DC_QUIT;
  264. }
  265. //
  266. // Get the checkmark image
  267. //
  268. g_hetCheckBitmap = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECK));
  269. if (!g_hetCheckBitmap)
  270. {
  271. ERROR_OUT(("HET_Init: Failed to load checkmark bitmap"));
  272. DC_QUIT;
  273. }
  274. //
  275. // Create a bolded font for shared items in the host list
  276. //
  277. GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(lf), &lf);
  278. lf.lfWeight += FW_LIGHT;
  279. g_hetSharedFont = CreateFontIndirect(&lf);
  280. if (!g_hetSharedFont)
  281. {
  282. ERROR_OUT(("HET_Init: Failed to create shared item font"));
  283. DC_QUIT;
  284. }
  285. if (g_asCanHost && !(g_asPolicies & SHP_POLICY_NOSHARING))
  286. {
  287. HET_Clear();
  288. //
  289. // Create the host UI dialog.
  290. //
  291. ASSERT(!g_asSession.hwndHostUI);
  292. ASSERT(!g_asSession.fHostUI);
  293. ASSERT(!g_asSession.fHostUIFrozen);
  294. g_asSession.hwndHostUI = CreateDialogParam(g_asInstance,
  295. MAKEINTRESOURCE(IDD_HOSTUI), NULL, HostDlgProc, 0);
  296. if (!g_asSession.hwndHostUI)
  297. {
  298. ERROR_OUT(("Failed to create hosting UI dialog"));
  299. DC_QUIT;
  300. }
  301. }
  302. rc = TRUE;
  303. DC_EXIT_POINT:
  304. DebugExitBOOL(HET_Init, rc);
  305. return(rc);
  306. }
  307. //
  308. // HET_Term()
  309. //
  310. // Cleanup hosting objects
  311. //
  312. void HET_Term(void)
  313. {
  314. DebugEntry(HET_Term);
  315. if (g_asSession.hwndHostUI)
  316. {
  317. DestroyWindow(g_asSession.hwndHostUI);
  318. g_asSession.hwndHostUI = NULL;
  319. }
  320. g_asSession.fHostUIFrozen = FALSE;
  321. g_asSession.fHostUI = FALSE;
  322. if (g_hetSharedFont != NULL)
  323. {
  324. DeleteFont(g_hetSharedFont);
  325. g_hetSharedFont = NULL;
  326. }
  327. if (g_hetCheckBitmap != NULL)
  328. {
  329. DeleteBitmap(g_hetCheckBitmap);
  330. g_hetCheckBitmap = NULL;
  331. }
  332. if (g_hetDeskIconSmall != NULL)
  333. {
  334. DestroyIcon(g_hetDeskIconSmall);
  335. g_hetDeskIconSmall = NULL;
  336. }
  337. if (g_hetDeskIcon != NULL)
  338. {
  339. DestroyIcon(g_hetDeskIcon);
  340. g_hetDeskIcon = NULL;
  341. }
  342. if (g_hetASIconSmall != NULL)
  343. {
  344. DestroyIcon(g_hetASIconSmall);
  345. g_hetASIconSmall = NULL;
  346. }
  347. if (g_hetASIcon != NULL)
  348. {
  349. DestroyIcon(g_hetASIcon);
  350. g_hetASIcon = NULL;
  351. }
  352. DebugExitVOID(HET_Term);
  353. }
  354. //
  355. // HET_IsShellThread()
  356. // Returns TRUE if thread is one of shell's special threads
  357. //
  358. BOOL HET_IsShellThread(DWORD threadID)
  359. {
  360. BOOL rc;
  361. DebugEntry(HET_IsShellThread);
  362. if ((threadID == GetWindowThreadProcessId(HET_GetShellDesktop(), NULL)) ||
  363. (threadID == GetWindowThreadProcessId(HET_GetShellTray(), NULL)))
  364. {
  365. rc = TRUE;
  366. }
  367. else
  368. {
  369. rc = FALSE;
  370. }
  371. DebugExitBOOL(HET_IsShellThread, rc);
  372. return(rc);
  373. }
  374. //
  375. // HET_IsShellWindow()
  376. // Returns TRUE if window is in same thread as tray or desktop
  377. //
  378. BOOL HET_IsShellWindow(HWND hwnd)
  379. {
  380. BOOL rc;
  381. DWORD threadID;
  382. DebugEntry(HET_IsShellWindow);
  383. threadID = GetWindowThreadProcessId(hwnd, NULL);
  384. rc = HET_IsShellThread(threadID);
  385. DebugExitBOOL(HET_IsShellWindow, rc);
  386. return(rc);
  387. }
  388. //
  389. // HET_ShareApp()
  390. // This shares an app. We have 3 types of sharing, only two
  391. // of which are supported currently:
  392. // (1) By process (normal)
  393. // (2) By thread (ConsoleNT or possibly Explorer)
  394. // (3) By window <??>
  395. //
  396. // For the first two types, we enumerate all top level windows and share
  397. // them also.
  398. //
  399. void ASShare::HET_ShareApp
  400. (
  401. WPARAM uType,
  402. LPARAM dwID
  403. )
  404. {
  405. HET_SHARE_INFO si;
  406. DebugEntry(ASShare::HET_ShareApp);
  407. //
  408. // If we're sharing the desktop, ignore this.
  409. //
  410. if (m_pasLocal->hetCount == HET_DESKTOPSHARED)
  411. {
  412. WARNING_OUT(("Can't share app; already sharing desktop"));
  413. DC_QUIT;
  414. }
  415. si.cWnds = 0;
  416. si.uType = (UINT)uType;
  417. si.dwID = (DWORD)dwID;
  418. //
  419. // We need to get setup for sharing if we aren't hosting.
  420. //
  421. if (m_pasLocal->hetCount == 0)
  422. {
  423. if (!HETStartHosting(FALSE))
  424. {
  425. ERROR_OUT(("Can't start sharing"));
  426. DC_QUIT;
  427. }
  428. }
  429. if (uType == IAS_SHARE_BYWINDOW)
  430. {
  431. HETShareCallback((HWND)dwID, (LPARAM)&si);
  432. }
  433. else
  434. {
  435. EnumWindows(HETShareCallback, (LPARAM)&si);
  436. }
  437. if (!si.cWnds)
  438. {
  439. //
  440. // Nothing happened. We couldn't find any top level windows.
  441. //
  442. if (m_pasLocal->hetCount == 0)
  443. {
  444. HETStopHosting(FALSE);
  445. }
  446. }
  447. else
  448. {
  449. HETUpdateLocalCount(m_pasLocal->hetCount + si.cWnds);
  450. }
  451. DC_EXIT_POINT:
  452. DebugExitVOID(HET_ShareApp);
  453. }
  454. //
  455. // HETShareCallback()
  456. //
  457. // This is the enumerator callback from HETShareApp(). We look for windows
  458. // matching the thread/process.
  459. //
  460. BOOL CALLBACK HETShareCallback
  461. (
  462. HWND hwnd,
  463. LPARAM lParam
  464. )
  465. {
  466. LPHET_SHARE_INFO lpsi = (LPHET_SHARE_INFO)lParam;
  467. DWORD idProcess;
  468. DWORD idThread;
  469. UINT hostType;
  470. char szClass[HET_CLASS_NAME_SIZE];
  471. DebugEntry(HETShareCallback);
  472. ASSERT(!IsBadWritePtr(lpsi, sizeof(HET_SHARE_INFO)));
  473. //
  474. // Does this window match?
  475. //
  476. idThread = GetWindowThreadProcessId(hwnd, &idProcess);
  477. // NOTE: If the window is bogus now, dwThread/dwProcess will be zero,
  478. // and will not match the ones passed in.
  479. if (lpsi->uType == IAS_SHARE_BYPROCESS)
  480. {
  481. if (idProcess != lpsi->dwID)
  482. {
  483. DC_QUIT;
  484. }
  485. TRACE_OUT(("Found window 0x%08x on process 0x%08x", hwnd, idProcess));
  486. }
  487. else if (lpsi->uType == IAS_SHARE_BYTHREAD)
  488. {
  489. if (idThread != lpsi->dwID)
  490. {
  491. DC_QUIT;
  492. }
  493. TRACE_OUT(("Found window 0x%08x on thread 0x%08x", hwnd, idThread));
  494. }
  495. //
  496. // Always skip special shell thread windows (the tray, the desktop, etc.)
  497. //
  498. if (HET_IsShellThread(idThread))
  499. {
  500. TRACE_OUT(("Skipping shell threads"));
  501. DC_QUIT;
  502. }
  503. //
  504. // Always skip menus and system tooltips, those are temporarily shared
  505. // when shown then unshared when hidden. That's because USER creates
  506. // global windows that move threads/processes as needed to use them.
  507. //
  508. // New menus being created are different, those never change task and
  509. // are treating like other windows in a shared app.
  510. //
  511. if (!GetClassName(hwnd, szClass, sizeof(szClass)))
  512. {
  513. TRACE_OUT(("Can't get class name for window 0x%08x", hwnd));
  514. DC_QUIT;
  515. }
  516. if (!lstrcmp(szClass, HET_MENU_CLASS))
  517. {
  518. TRACE_OUT(("Skipping menu popup window 0x%08x", hwnd));
  519. DC_QUIT;
  520. }
  521. if (!lstrcmp(szClass, HET_TOOLTIPS98_CLASS) ||
  522. !lstrcmp(szClass, HET_TOOLTIPSNT5_CLASS))
  523. {
  524. TRACE_OUT(("Skipping system tooltip %08lx", hwnd));
  525. DC_QUIT;
  526. }
  527. if (HET_GetHosting(hwnd))
  528. {
  529. WARNING_OUT(("Window %08lx already shared", hwnd));
  530. DC_QUIT;
  531. }
  532. hostType = HET_HOSTED_PERMANENT;
  533. if (lpsi->uType == IAS_SHARE_BYPROCESS)
  534. {
  535. hostType |= HET_HOSTED_BYPROCESS;
  536. }
  537. else if (lpsi->uType == IAS_SHARE_BYTHREAD)
  538. {
  539. hostType |= HET_HOSTED_BYTHREAD;
  540. }
  541. else if (lpsi->uType == IAS_SHARE_BYWINDOW)
  542. {
  543. hostType |= HET_HOSTED_BYWINDOW;
  544. }
  545. //
  546. // See if we can share it. This returns TRUE if success.
  547. //
  548. if (OSI_ShareWindow(hwnd, hostType, TRUE, FALSE))
  549. {
  550. lpsi->cWnds++;
  551. }
  552. DC_EXIT_POINT:
  553. DebugExitBOOL(HET_ShareCallback, TRUE);
  554. return(TRUE);
  555. }
  556. //
  557. // HET_UnshareApp()
  558. // This unshares an app. We have 3 types of sharing, only two
  559. // of which are supported currently:
  560. // (1) By process (normal)
  561. // (2) By thread (ConsoleNT or possibly Explorer)
  562. // (3) By window (temporary)
  563. //
  564. // For the first two types, we enumerate all top level windows and share
  565. // them also.
  566. //
  567. void ASShare::HET_UnshareApp
  568. (
  569. WPARAM uType,
  570. LPARAM dwID
  571. )
  572. {
  573. HET_SHARE_INFO si;
  574. DebugEntry(ASShare::HET_UnshareApp);
  575. //
  576. // If we aren't sharing apps (not sharing anything or sharing the
  577. // dekstop), ignore this.
  578. //
  579. if ((m_pasLocal->hetCount == 0) || (m_pasLocal->hetCount == HET_DESKTOPSHARED))
  580. {
  581. WARNING_OUT(("Can't unshare app; not sharing any"));
  582. DC_QUIT;
  583. }
  584. si.cWnds = 0;
  585. si.uType = (UINT)uType;
  586. si.dwID = (DWORD)dwID;
  587. if (uType == IAS_SHARE_BYWINDOW)
  588. {
  589. //
  590. // No enumeration, just this window.
  591. //
  592. HETUnshareCallback((HWND)dwID, (LPARAM)&si);
  593. }
  594. else
  595. {
  596. //
  597. // Stop sharing all windows in it.
  598. //
  599. EnumWindows(HETUnshareCallback, (LPARAM)&si);
  600. }
  601. if (si.cWnds)
  602. {
  603. HETUpdateLocalCount(m_pasLocal->hetCount - si.cWnds);
  604. }
  605. DC_EXIT_POINT:
  606. DebugExitVOID(ASShare::HET_UnshareApp);
  607. }
  608. //
  609. // HETUnshareCallback()
  610. //
  611. // This is the enumerator callback from HET_UnshareApp(). We look for windows
  612. // matching the thread/process. In this case, we don't care about menus
  613. // or explorer windows, since we assume that, from the time we shared and it
  614. // was set up properly, the window/task tracking code did the right thing.
  615. // If not, we'll wipe it out here anyway.
  616. //
  617. BOOL CALLBACK HETUnshareCallback
  618. (
  619. HWND hwnd,
  620. LPARAM lParam
  621. )
  622. {
  623. LPHET_SHARE_INFO lpsi = (LPHET_SHARE_INFO)lParam;
  624. DWORD dwProcess;
  625. DWORD dwThread;
  626. DebugEntry(HETUnshareCallback);
  627. ASSERT(!IsBadWritePtr(lpsi, sizeof(HET_SHARE_INFO)));
  628. //
  629. // Does this window match? If by window, always.
  630. //
  631. if (lpsi->uType != IAS_SHARE_BYWINDOW)
  632. {
  633. dwThread = GetWindowThreadProcessId(hwnd, &dwProcess);
  634. // NOTE: If the window is bogus now, dwThread/dwProcess will be zero,
  635. // and will not match the ones passed in.
  636. if (lpsi->uType == IAS_SHARE_BYPROCESS)
  637. {
  638. if (dwProcess != lpsi->dwID)
  639. {
  640. DC_QUIT;
  641. }
  642. TRACE_OUT(("Found window 0x%08x on process 0x%08x", hwnd, dwProcess));
  643. }
  644. else if (lpsi->uType == IAS_SHARE_BYTHREAD)
  645. {
  646. if (dwThread != lpsi->dwID)
  647. {
  648. DC_QUIT;
  649. }
  650. TRACE_OUT(("Found window 0x%08x on thread 0x%08x", hwnd, dwThread));
  651. }
  652. }
  653. //
  654. // This returns TRUE if we unshared a shared window.
  655. //
  656. if (OSI_UnshareWindow(hwnd, FALSE))
  657. {
  658. lpsi->cWnds++;
  659. }
  660. DC_EXIT_POINT:
  661. DebugExitBOOL(HETUnshareCallback, TRUE);
  662. return(TRUE);
  663. }
  664. //
  665. // HET_ShareDesktop()
  666. //
  667. void ASShare::HET_ShareDesktop(void)
  668. {
  669. ASPerson * pasT;
  670. DebugEntry(ASShare:HET_ShareDesktop);
  671. //
  672. // If we're sharing apps, ignore this.
  673. //
  674. if (m_pasLocal->hetCount != 0)
  675. {
  676. WARNING_OUT(("Ignoring share desktop request, sharing apps"));
  677. DC_QUIT;
  678. }
  679. TRACE_OUT(("HET_ShareDesktop: starting share"));
  680. if (!HETStartHosting(TRUE))
  681. {
  682. ERROR_OUT(("HET_ShareDesktop cannot start sharing desktop"));
  683. DC_QUIT;
  684. }
  685. //
  686. // Update the count of hosted entities (ie user-hosted windows)
  687. //
  688. HETUpdateLocalCount(HET_DESKTOPSHARED);
  689. //
  690. // Get the desktop(s) repainted if anybody's viewing it.
  691. //
  692. ASSERT(m_pHost);
  693. m_pHost->HET_RepaintAll();
  694. DC_EXIT_POINT:
  695. DebugExitVOID(ASShare::HET_ShareDesktop);
  696. }
  697. //
  698. // HET_UnshareAll()
  699. // Unshares everything including the desktop. If we had been sharing
  700. // apps before, we will unshare them all.
  701. //
  702. void ASShare::HET_UnshareAll(void)
  703. {
  704. DebugEntry(ASShare::HET_UnshareAll);
  705. if (m_pasLocal->hetCount != 0)
  706. {
  707. HETUpdateLocalCount(0);
  708. }
  709. DebugExitVOID(ASShare::HET_UnshareAll);
  710. }
  711. //
  712. // HET_PartyJoiningShare()
  713. //
  714. BOOL ASShare::HET_PartyJoiningShare(ASPerson * pasPerson)
  715. {
  716. BOOL rc = TRUE;
  717. DebugEntry(ASShare::HET_PartyJoiningShare);
  718. HET_CalcViewers(NULL);
  719. DebugExitBOOL(ASShare::HET_PartyJoiningShare, rc);
  720. return(rc);
  721. }
  722. //
  723. // HET_PartyLeftShare()
  724. //
  725. void ASShare::HET_PartyLeftShare(ASPerson * pasPerson)
  726. {
  727. DebugEntry(ASShare::HET_PartyLeftShare);
  728. // This guy is leaving the share, cleanup if he was sharing.
  729. ValidatePerson(pasPerson);
  730. if (pasPerson->hetCount != 0)
  731. {
  732. // This person is hosting
  733. if (pasPerson == m_pasLocal)
  734. {
  735. HETUpdateLocalCount(0);
  736. }
  737. else
  738. {
  739. HETUpdateRemoteCount(pasPerson, 0);
  740. }
  741. }
  742. //
  743. // If we're hosting, stop viewing if this is the last person in the share.
  744. //
  745. HET_CalcViewers(pasPerson);
  746. DebugExitVOID(ASShare::HET_PartyLeftShare);
  747. }
  748. //
  749. // HET_CalcViewers()
  750. //
  751. // If we or a remote is viewing our shared stuff, then we must accumulate
  752. // graphic output. If not, don't other, but keep the app tracked as necessary.
  753. //
  754. // This is called when we start to host, when somebody joins, or somebody
  755. // leaves the conference.
  756. //
  757. void ASShare::HET_CalcViewers(ASPerson * pasLeaving)
  758. {
  759. BOOL fViewers;
  760. DebugEntry(ASShare::HET_CalcViewers);
  761. fViewers = FALSE;
  762. if (m_pHost)
  763. {
  764. if (m_scfViewSelf)
  765. {
  766. fViewers = TRUE;
  767. }
  768. else if (!pasLeaving)
  769. {
  770. //
  771. // Nobody is leaving, so just check if anybody else is in the
  772. // share.
  773. //
  774. if (m_pasLocal->pasNext)
  775. {
  776. fViewers = TRUE;
  777. }
  778. }
  779. else if (pasLeaving->pasNext || (m_pasLocal->pasNext != pasLeaving))
  780. {
  781. //
  782. // Sombody is leaving.
  783. // The person leaving isn't the only other one besides us in the
  784. // share, since there are others after it or before it in the
  785. // members linked list.
  786. //
  787. fViewers = TRUE;
  788. }
  789. }
  790. if (fViewers != m_hetViewers)
  791. {
  792. HET_VIEWER viewer;
  793. m_hetViewers = fViewers;
  794. viewer.viewersPresent = fViewers;
  795. OSI_FunctionRequest(HET_ESC_VIEWER, (LPOSI_ESCAPE_HEADER)&viewer,
  796. sizeof(viewer));
  797. }
  798. DebugExitVOID(ASShare::HET_CalcViewers);
  799. }
  800. //
  801. // HET_ReceivedPacket()
  802. //
  803. void ASShare::HET_ReceivedPacket
  804. (
  805. ASPerson * pasPerson,
  806. PS20DATAPACKET pPacket
  807. )
  808. {
  809. PHETPACKET pHETPacket;
  810. DebugEntry(ASShare:;HET_ReceivedPacket);
  811. ValidatePerson(pasPerson);
  812. pHETPacket = (PHETPACKET)pPacket;
  813. switch (pHETPacket->msg)
  814. {
  815. case HET_MSG_NUMHOSTED:
  816. HETUpdateRemoteCount(pasPerson, pHETPacket->hostState);
  817. break;
  818. default:
  819. ERROR_OUT(("Unknown HET packet type %u from [%d]", pHETPacket->msg,
  820. pasPerson->mcsID));
  821. break;
  822. }
  823. DebugExitVOID(ASShare::HET_ReceivedPacket);
  824. }
  825. //
  826. // HET_SyncCommon()
  827. //
  828. // Called when somebody joins a share, after it is fully joined. We repaint
  829. // all shared windows and send the current hosted top-level count.
  830. //
  831. // Also called when sharing, and somebody joins later.
  832. //
  833. // NOTE that some of the resets don't do anything when are just starting to
  834. // share. But all are quick and benign.
  835. //
  836. void ASHost::HET_SyncCommon(void)
  837. {
  838. OSI_ESCAPE_HEADER osi;
  839. DebugEntry(ASHost::HET_SyncCommon);
  840. m_upfSyncTokenRequired = TRUE;
  841. BA_SyncOutgoing();
  842. OE2_SyncOutgoing(); // To reset order encoding
  843. OA_SyncOutgoing(); // To clear pending orders
  844. SBC_SyncOutgoing(); // To clear bitmap cache
  845. PM_SyncOutgoing(); // To clear palette cache
  846. SSI_SyncOutgoing(); // To reset savebits orders
  847. SWL_SyncOutgoing(); // To reset shared window list
  848. AWC_SyncOutgoing(); // To send active window
  849. CM_SyncOutgoing(); // To send cursor shape/pos
  850. //
  851. // Tell the driver we are syncing
  852. //
  853. OSI_FunctionRequest(OSI_ESC_SYNC_NOW, &osi, sizeof(osi));
  854. DebugExitVOID(ASHost::HET_SyncCommon);
  855. }
  856. //
  857. // HET_SyncAlreadyHosting()
  858. // Called in a sync when we are already hosting and somebody joins call
  859. //
  860. void ASHost::HET_SyncAlreadyHosting(void)
  861. {
  862. DebugEntry(ASHost::HET_SyncAlreadyHosting);
  863. HET_RepaintAll();
  864. // Send out the current hosted count
  865. m_pShare->m_hetRetrySendState = TRUE;
  866. DebugExitVOID(ASHost::HET_SyncAlreadyHosting);
  867. }
  868. //
  869. // HET_RepaintAll()
  870. //
  871. // Repaints all shared stuff if there's at least two people in the share...
  872. //
  873. void ASHost::HET_RepaintAll(void)
  874. {
  875. DebugEntry(ASHost::HET_RepaintAll);
  876. ASSERT(m_pShare);
  877. ASSERT(m_pShare->m_pasLocal);
  878. if (m_pShare->m_hetViewers)
  879. {
  880. //
  881. // Only repaint if somebody's viewing
  882. //
  883. if (m_pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED)
  884. {
  885. // Desktop sharing, so repaint desktop(s)
  886. USR_RepaintWindow(NULL);
  887. OSI_RepaintDesktop(); //special repaint for winlogon desktop
  888. }
  889. else
  890. {
  891. // App sharing, so repaint shared apps
  892. EnumWindows(HETRepaintWindow, (LPARAM)m_pShare);
  893. }
  894. }
  895. DebugExitVOID(ASHost::HET_RepaintAll);
  896. }
  897. //
  898. // HET_Periodic()
  899. //
  900. void ASShare::HET_Periodic(void)
  901. {
  902. DebugEntry(ASShare::HET_Periodic);
  903. if (m_hetRetrySendState)
  904. {
  905. TRACE_OUT(( "Retry sending hosted count"));
  906. HETSendLocalCount();
  907. }
  908. DebugExitVOID(ASShare::HET_Periodic);
  909. }
  910. //
  911. // HET_WindowIsHosted - see het.h
  912. //
  913. BOOL ASShare::HET_WindowIsHosted(HWND hwnd)
  914. {
  915. BOOL rc = FALSE;
  916. HWND hwndParent;
  917. DebugEntry(ASShare::HET_WindowIsHosted);
  918. //
  919. // Desktop sharing: everything is shared
  920. //
  921. if (m_pasLocal->hetCount == HET_DESKTOPSHARED)
  922. {
  923. rc = TRUE;
  924. DC_QUIT;
  925. }
  926. if (!hwnd)
  927. {
  928. TRACE_OUT(("NULL window passed to HET_WindowIsHosted"));
  929. DC_QUIT;
  930. }
  931. //
  932. // Walk up to the top level window this one is part of
  933. //
  934. while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
  935. {
  936. hwndParent = GetParent(hwnd);
  937. if (hwndParent == GetDesktopWindow())
  938. break;
  939. hwnd = hwndParent;
  940. }
  941. rc = (BOOL)HET_GetHosting(hwnd);
  942. DC_EXIT_POINT:
  943. DebugExitBOOL(ASShare::HET_WindowIsHosted, rc);
  944. return(rc);
  945. }
  946. //
  947. // HET_HandleNewTopLevel()
  948. // Called when a shared top level window is shown or hidden. We update
  949. // our local top level count.
  950. //
  951. void ASShare::HET_HandleNewTopLevel(BOOL fShown)
  952. {
  953. DebugEntry(ASShare::HET_HandleNewTopLevel);
  954. //
  955. // If we aren't sharing any apps (not sharing at all or sharing the
  956. // desktop), ignore this.
  957. //
  958. if ((m_pasLocal->hetCount == 0) || (m_pasLocal->hetCount == HET_DESKTOPSHARED))
  959. {
  960. WARNING_OUT(("Ignoring new hosted notification; count is 0x%04x",
  961. m_pasLocal->hetCount));
  962. DC_QUIT;
  963. }
  964. if (fShown)
  965. HETUpdateLocalCount(m_pasLocal->hetCount + 1);
  966. else
  967. HETUpdateLocalCount(m_pasLocal->hetCount - 1);
  968. DC_EXIT_POINT:
  969. DebugExitVOID(ASShare::HET_HandleNewTopLevel);
  970. }
  971. //
  972. // HET_HandleRecountTopLevel()
  973. // Called when a massive change in the top level visible count occurs, so
  974. // that we can just set the new total at once, rather than handle
  975. // individual inc/dec messages.
  976. //
  977. void ASShare::HET_HandleRecountTopLevel(UINT uNewCount)
  978. {
  979. DebugEntry(ASShare::HET_HandleRecountTopLevel);
  980. //
  981. // If we aren't sharing any apps (not sharing at all or sharing the
  982. // desktop), ignore this.
  983. //
  984. if ((m_pasLocal->hetCount == 0) || (m_pasLocal->hetCount == HET_DESKTOPSHARED))
  985. {
  986. WARNING_OUT(("Ignoring new hosted notification; count is 0x%04x",
  987. m_pasLocal->hetCount));
  988. DC_QUIT;
  989. }
  990. HETUpdateLocalCount(uNewCount);
  991. DC_EXIT_POINT:
  992. DebugExitVOID(ASShare::HET_HandleRecountTopLevel);
  993. }
  994. //
  995. // HETStartHosting()
  996. //
  997. // Called when we are about to begin sharing windows. fDesktop is TRUE if
  998. // we are sharing the entire desktop, FALSE if just individual windows.
  999. //
  1000. BOOL ASShare::HETStartHosting(BOOL fDesktop)
  1001. {
  1002. BOOL rc = FALSE;
  1003. DebugEntry(ASShare::HETStartHosting);
  1004. //
  1005. // Create the hosting object
  1006. //
  1007. ASSERT(!m_pHost);
  1008. m_pHost = new ASHost;
  1009. if (!m_pHost)
  1010. {
  1011. ERROR_OUT(("HETStartHosting: couldn't create m_pHost"));
  1012. DC_QUIT;
  1013. }
  1014. ZeroMemory(m_pHost, sizeof(*(m_pHost)));
  1015. SET_STAMP(m_pHost, HOST);
  1016. //
  1017. // Init hosting
  1018. //
  1019. if (!m_pHost->HET_HostStarting(this))
  1020. {
  1021. ERROR_OUT(("Failed to init hosting for local person"));
  1022. DC_QUIT;
  1023. }
  1024. //
  1025. // Start tracking graphics/windows
  1026. //
  1027. if (fDesktop)
  1028. {
  1029. HET_SHARE_DESKTOP hdr;
  1030. //
  1031. // Shortcut directly to display driver. No need to track windows
  1032. // since everything will be shared.
  1033. //
  1034. if (!OSI_FunctionRequest(HET_ESC_SHARE_DESKTOP, (LPOSI_ESCAPE_HEADER)&hdr, sizeof(hdr)))
  1035. {
  1036. ERROR_OUT(("HET_ESC_SHARE_DESKTOP failed"));
  1037. DC_QUIT;
  1038. }
  1039. }
  1040. else
  1041. {
  1042. //
  1043. // Start tracking windows.
  1044. //
  1045. if (!OSI_StartWindowTracking())
  1046. {
  1047. ERROR_OUT(( "Failed to install window tracking hooks"));
  1048. DC_QUIT;
  1049. }
  1050. }
  1051. if (m_scfViewSelf && !HET_ViewStarting(m_pasLocal))
  1052. {
  1053. ERROR_OUT(("ViewSelf option is on, but can't create ASView data"));
  1054. DC_QUIT;
  1055. }
  1056. HET_CalcViewers(NULL);
  1057. rc = TRUE;
  1058. DC_EXIT_POINT:
  1059. //
  1060. // Return to caller
  1061. //
  1062. DebugExitBOOL(ASShare::HETStartHosting, rc);
  1063. return(rc);
  1064. }
  1065. //
  1066. //
  1067. // Name: HETStopHosting
  1068. //
  1069. // Description: Called when the last hosted window is unshared
  1070. // ALWAYS CALL THIS AFTER the "hethostedTopLevel" count is 0.
  1071. //
  1072. // Params: none
  1073. //
  1074. //
  1075. void ASShare::HETStopHosting(BOOL fDesktop)
  1076. {
  1077. DebugEntry(ASShare::HETStopHosting);
  1078. m_hetViewers = FALSE;
  1079. //
  1080. // Stop tracking graphics/windows. This will stop viewing, then uninstall
  1081. // hooks.
  1082. //
  1083. if (fDesktop)
  1084. {
  1085. HET_UNSHARE_DESKTOP hdr;
  1086. //
  1087. // There is no window tracking, just shortcut directly to the
  1088. // display driver.
  1089. //
  1090. OSI_FunctionRequest(HET_ESC_UNSHARE_DESKTOP, (LPOSI_ESCAPE_HEADER)&hdr, sizeof(hdr));
  1091. }
  1092. else
  1093. {
  1094. //
  1095. // Unshare any remaining shared windows
  1096. //
  1097. HET_Clear();
  1098. OSI_StopWindowTracking();
  1099. }
  1100. //
  1101. // Tell areas we are finished hosting
  1102. //
  1103. if (m_pHost)
  1104. {
  1105. //
  1106. // If we're viewing ourself, kill the view first
  1107. //
  1108. if (m_scfViewSelf)
  1109. {
  1110. HET_ViewEnded(m_pasLocal);
  1111. }
  1112. m_pHost->HET_HostEnded();
  1113. //
  1114. // Delete host object
  1115. //
  1116. delete m_pHost;
  1117. m_pHost = NULL;
  1118. }
  1119. //
  1120. // Return to caller
  1121. //
  1122. DebugExitVOID(ASShare::HETStopHosting);
  1123. }
  1124. //
  1125. // HETSendLocalCount()
  1126. // This sends the hosting count to remotes.
  1127. // * If zero, we are not sharing
  1128. // * If one, we are sharing apps
  1129. // * If 0xFFFF, we are sharing desktop
  1130. //
  1131. // Note that we used to send the real count of top level windows, so every
  1132. // time a new window came or went, we would broadcast a packet. But
  1133. // remotes only care when the value goes from zero to non-zero or back,
  1134. // and when non-zero if it's the special desktop value or not. So don't
  1135. // repeatedly broadcast values remotes don't care about!
  1136. //
  1137. void ASShare::HETSendLocalCount(void)
  1138. {
  1139. PHETPACKET pHETPacket;
  1140. #ifdef _DEBUG
  1141. UINT sentSize;
  1142. #endif // _DEBUG
  1143. DebugEntry(ASShare::HETSendLocalCount);
  1144. //
  1145. // Allocate a packet for the HET data.
  1146. //
  1147. pHETPacket = (PHETPACKET)SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID,
  1148. sizeof(HETPACKET));
  1149. if (!pHETPacket)
  1150. {
  1151. WARNING_OUT(("Failed to alloc HET host packet"));
  1152. m_hetRetrySendState = TRUE;
  1153. DC_QUIT;
  1154. }
  1155. //
  1156. // Packet successfully allocated. Fill in the data and send it.
  1157. //
  1158. pHETPacket->header.data.dataType = DT_HET;
  1159. pHETPacket->msg = HET_MSG_NUMHOSTED;
  1160. switch (m_pasLocal->hetCount)
  1161. {
  1162. case 0:
  1163. // Not hosting
  1164. pHETPacket->hostState = HET_NOTHOSTING;
  1165. break;
  1166. case HET_DESKTOPSHARED:
  1167. // Sharing desktop - 3.0 only
  1168. pHETPacket->header.data.dataType = DT_HET30;
  1169. pHETPacket->hostState = HET_DESKTOPSHARED;
  1170. break;
  1171. default:
  1172. // Sharing apps
  1173. pHETPacket->hostState = HET_APPSSHARED;
  1174. break;
  1175. }
  1176. //
  1177. // Compress and send the packet.
  1178. //
  1179. #ifdef _DEBUG
  1180. sentSize =
  1181. #endif // _DEBUG
  1182. DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID,
  1183. &(pHETPacket->header), sizeof(*pHETPacket));
  1184. TRACE_OUT(("HET packet size: %08d, sent %08d", sizeof(*pHETPacket), sentSize));
  1185. TRACE_OUT(("Sent new HET packet (%d)", m_pasLocal->hetCount));
  1186. m_hetRetrySendState = FALSE;
  1187. //
  1188. // Return to caller
  1189. //
  1190. DC_EXIT_POINT:
  1191. DebugExitVOID(ASShare::HETSendLocalCount);
  1192. }
  1193. //
  1194. // HETUpdateLocalCount()
  1195. //
  1196. void ASShare::HETUpdateLocalCount(UINT newCount)
  1197. {
  1198. UINT oldCount;
  1199. DebugEntry(ASShare::HETUpdateLocalCount);
  1200. oldCount = m_pasLocal->hetCount;
  1201. m_pasLocal->hetCount = newCount;
  1202. if ((oldCount == 0) && (newCount != 0))
  1203. {
  1204. SendMessage(g_asSession.hwndHostUI, HOST_MSG_HOSTSTART, 0, 0);
  1205. //
  1206. // Don't bother sending net packets if nobody is viewing
  1207. //
  1208. if (m_hetViewers)
  1209. {
  1210. HETSendLocalCount();
  1211. }
  1212. HETCheckSharing(TRUE);
  1213. }
  1214. else if ((oldCount != 0) && (newCount == 0))
  1215. {
  1216. if (m_hetViewers)
  1217. {
  1218. //
  1219. // Ending host, desktop or apps
  1220. //
  1221. HETSendLocalCount();
  1222. }
  1223. //
  1224. // The local guy is stopping sharing.
  1225. //
  1226. HETStopHosting(oldCount == HET_DESKTOPSHARED);
  1227. ASSERT(IsWindow(g_asSession.hwndHostUI));
  1228. SendMessage(g_asSession.hwndHostUI, HOST_MSG_HOSTEND, 0, 0);
  1229. HETCheckSharing(FALSE);
  1230. }
  1231. ASSERT(IsWindow(g_asSession.hwndHostUI));
  1232. SendMessage(g_asSession.hwndHostUI, HOST_MSG_UPDATELIST, 0, 0);
  1233. DebugExitVOID(ASShare::HETUpdateLocalCount);
  1234. }
  1235. //
  1236. // HETUpdateRemoteCount()
  1237. //
  1238. // Updates the count of shared top level windows from a remote, and notifies
  1239. // the UI on transition from/to zero if a remote. If local, kills the share.
  1240. //
  1241. void ASShare::HETUpdateRemoteCount
  1242. (
  1243. ASPerson * pasPerson,
  1244. UINT newCount
  1245. )
  1246. {
  1247. UINT oldCount;
  1248. DebugEntry(ASShare::HETUpdateRemoteCount);
  1249. ValidatePerson(pasPerson);
  1250. ASSERT(pasPerson != m_pasLocal);
  1251. oldCount = pasPerson->hetCount;
  1252. pasPerson->hetCount = newCount;
  1253. TRACE_OUT(("HETUpdateRemoteCount: Person [%d] old %d, new %d",
  1254. pasPerson->mcsID, oldCount, newCount));
  1255. //
  1256. // We generate events for remote people if
  1257. // * They were sharing but now they aren't
  1258. // * There weren't sharing but now they are
  1259. //
  1260. if ((oldCount == 0) && (newCount != 0))
  1261. {
  1262. //
  1263. // The remote dude started to share
  1264. //
  1265. if (!HET_ViewStarting(pasPerson))
  1266. {
  1267. ERROR_OUT(("HET_ViewStarting failed; pretending remote not sharing"));
  1268. pasPerson->hetCount = 0;
  1269. HET_ViewEnded(pasPerson);
  1270. }
  1271. else
  1272. {
  1273. HETCheckSharing(TRUE);
  1274. }
  1275. }
  1276. else if ((oldCount != 0) && (newCount == 0))
  1277. {
  1278. //
  1279. // The remote dude stopped sharing. Notify the UI also.
  1280. //
  1281. HET_ViewEnded(pasPerson);
  1282. HETCheckSharing(FALSE);
  1283. }
  1284. DebugExitVOID(ASShare::HETUpdateRemoteCount);
  1285. }
  1286. //
  1287. // HETCheckSharing()
  1288. // Called when any member of the conference (local or remote) transitions
  1289. // to/from sharing. When the first person has shared something, we notify
  1290. // the UI. When the last person has stopped sharing, we kill the share which
  1291. // will notify the UI.
  1292. //
  1293. void ASShare::HETCheckSharing(BOOL fStarting)
  1294. {
  1295. DebugEntry(ASShare::HETCheckSharing);
  1296. if (fStarting)
  1297. {
  1298. ++m_hetHostCount;
  1299. if (m_hetHostCount == 1)
  1300. {
  1301. // First host started
  1302. TRACE_OUT(("First person started hosting"));
  1303. DCS_NotifyUI(SH_EVT_SHARING_STARTED, 0, 0);
  1304. }
  1305. }
  1306. else
  1307. {
  1308. ASSERT(m_hetHostCount > 0);
  1309. --m_hetHostCount;
  1310. if (m_hetHostCount == 0)
  1311. {
  1312. //
  1313. // Last host stopped sharing -- end share if we're not cleaning
  1314. // up after the fact. But don't do it NOW, post a message.
  1315. // We may have come in here because the share is ending already.
  1316. //
  1317. PostMessage(g_asMainWindow, DCS_KILLSHARE_MSG, 0, 0);
  1318. }
  1319. }
  1320. DebugExitVOID(ASShare::HETCheckSharing);
  1321. }
  1322. //
  1323. // HET_HostStarting()
  1324. //
  1325. // Called when we start to host applications. This creates our host data
  1326. // then calls the component HostStarting() routines
  1327. //
  1328. BOOL ASHost::HET_HostStarting(ASShare * pShare)
  1329. {
  1330. BOOL rc = FALSE;
  1331. DebugEntry(ASHost::HET_HostStarting);
  1332. // Set back pointer to share
  1333. m_pShare = pShare;
  1334. //
  1335. // Turn effects off
  1336. //
  1337. HET_SetGUIEffects(FALSE, &m_hetEffects);
  1338. OSI_SetGUIEffects(FALSE);
  1339. //
  1340. // Now call HostStarting() routines
  1341. //
  1342. if (!USR_HostStarting())
  1343. {
  1344. ERROR_OUT(("USR_HostStarting failed"));
  1345. DC_QUIT;
  1346. }
  1347. if (!OE2_HostStarting())
  1348. {
  1349. ERROR_OUT(("OE2_HostStarting failed"));
  1350. DC_QUIT;
  1351. }
  1352. if (!SBC_HostStarting())
  1353. {
  1354. ERROR_OUT(("SBC_HostStarting failed"));
  1355. DC_QUIT;
  1356. }
  1357. if (!CM_HostStarting())
  1358. {
  1359. ERROR_OUT(("CM_HostStarting failed"));
  1360. DC_QUIT;
  1361. }
  1362. if (!SSI_HostStarting())
  1363. {
  1364. ERROR_OUT(("SSI_HostStarting failed"));
  1365. DC_QUIT;
  1366. }
  1367. if (!PM_HostStarting())
  1368. {
  1369. ERROR_OUT(("PM_HostStarting failed"));
  1370. DC_QUIT;
  1371. }
  1372. if (!SWL_HostStarting())
  1373. {
  1374. ERROR_OUT(("SWL_HostStarting failed"));
  1375. DC_QUIT;
  1376. }
  1377. if (!VIEW_HostStarting())
  1378. {
  1379. ERROR_OUT(("VIEW_HostStarting failed"));
  1380. DC_QUIT;
  1381. }
  1382. //
  1383. // Now reset OUTGOING info. 2.x nodes do not; that's why we have to
  1384. // hang on to RBC, OD2, CM, PM data for them. When 2.x compat is gone,
  1385. // we can move ASPerson data in to ASView, which is only around while
  1386. // the person is in fact hosting.
  1387. //
  1388. OA_LocalHostReset();
  1389. //
  1390. // Reset OUTGOING data.
  1391. // Note corresponding cleanup for 3.0 nodes
  1392. // in CM, OD2, RBC, and PM.
  1393. // Note that we don't need to reset SSI incoming goop, since we will
  1394. // clear all pending orders and are invalidating everything shared
  1395. // from scratch. There will be no reference to a previous savebits.
  1396. //
  1397. HET_SyncCommon();
  1398. rc = TRUE;
  1399. DC_EXIT_POINT:
  1400. DebugExitBOOL(ASHost::HET_HostStarting, rc);
  1401. return(rc);
  1402. }
  1403. //
  1404. // HET_HostEnded()
  1405. //
  1406. // Called when we stop hosting applications.
  1407. //
  1408. void ASHost::HET_HostEnded(void)
  1409. {
  1410. DebugEntry(ASHost::HET_HostEnded);
  1411. //
  1412. // Call HostEnded() routines
  1413. //
  1414. CA_HostEnded();
  1415. SWL_HostEnded();
  1416. PM_HostEnded();
  1417. CM_HostEnded();
  1418. SBC_HostEnded();
  1419. OE2_HostEnded();
  1420. USR_HostEnded();
  1421. //
  1422. // Restore windows animation.
  1423. //
  1424. HET_SetGUIEffects(TRUE, &m_hetEffects);
  1425. OSI_SetGUIEffects(TRUE);
  1426. DebugExitVOID(ASHost::HET_HostEnded);
  1427. }
  1428. //
  1429. // HET_ViewStarting()
  1430. //
  1431. // Called to create the data needed to view somebody who is hosting.
  1432. //
  1433. BOOL ASShare::HET_ViewStarting(ASPerson * pasPerson)
  1434. {
  1435. BOOL rc = FALSE;
  1436. DebugEntry(ASShare::HET_ViewStarting);
  1437. ValidatePerson(pasPerson);
  1438. //
  1439. // Create ASView object
  1440. //
  1441. ASSERT(!pasPerson->m_pView);
  1442. // Allocate VIEW structure
  1443. pasPerson->m_pView = new ASView;
  1444. if (!pasPerson->m_pView)
  1445. {
  1446. // Abject, total, failure.
  1447. ERROR_OUT(("HET_ViewStarting: Couldn't allocate ASView for [%d]", pasPerson->mcsID));
  1448. DC_QUIT;
  1449. }
  1450. ZeroMemory(pasPerson->m_pView, sizeof(*(pasPerson->m_pView)));
  1451. SET_STAMP(pasPerson->m_pView, VIEW);
  1452. //
  1453. // Now call ViewStarting routines
  1454. //
  1455. if (!USR_ViewStarting(pasPerson))
  1456. {
  1457. ERROR_OUT(("USR_ViewStarting failed"));
  1458. DC_QUIT;
  1459. }
  1460. if (!OD2_ViewStarting(pasPerson))
  1461. {
  1462. ERROR_OUT(("OD2_ViewStarting failed"));
  1463. DC_QUIT;
  1464. }
  1465. if (!OD_ViewStarting(pasPerson))
  1466. {
  1467. ERROR_OUT(("OD_ViewStarting failed"));
  1468. DC_QUIT;
  1469. }
  1470. if (!RBC_ViewStarting(pasPerson))
  1471. {
  1472. ERROR_OUT(("RBC_ViewStarting failed"));
  1473. DC_QUIT;
  1474. }
  1475. if (!CM_ViewStarting(pasPerson))
  1476. {
  1477. ERROR_OUT(("CM_ViewStarting failed"));
  1478. DC_QUIT;
  1479. }
  1480. if (!SSI_ViewStarting(pasPerson))
  1481. {
  1482. ERROR_OUT(("SSI_ViewStarting failed"));
  1483. DC_QUIT;
  1484. }
  1485. if (!PM_ViewStarting(pasPerson))
  1486. {
  1487. ERROR_OUT(("PM_ViewStarting failed"));
  1488. DC_QUIT;
  1489. }
  1490. if (!VIEW_ViewStarting(pasPerson))
  1491. {
  1492. ERROR_OUT(("VIEW_ViewStarting failed"));
  1493. DC_QUIT;
  1494. }
  1495. if (!CA_ViewStarting(pasPerson))
  1496. {
  1497. ERROR_OUT(("CA_ViewStarting failed"));
  1498. DC_QUIT;
  1499. }
  1500. rc = TRUE;
  1501. DC_EXIT_POINT:
  1502. DebugExitBOOL(ASShare::HET_ViewStarting, rc);
  1503. return(rc);
  1504. }
  1505. //
  1506. // HET_ViewEnded()
  1507. //
  1508. // Called when we stop viewing a host
  1509. //
  1510. void ASShare::HET_ViewEnded(ASPerson * pasPerson)
  1511. {
  1512. DebugEntry(ASShare::HET_ViewEnded);
  1513. ValidatePerson(pasPerson);
  1514. if (pasPerson->m_pView)
  1515. {
  1516. //
  1517. // Call the component ViewEnded routines
  1518. //
  1519. CA_ViewEnded(pasPerson);
  1520. VIEW_ViewEnded(pasPerson);
  1521. PM_ViewEnded(pasPerson);
  1522. SSI_ViewEnded(pasPerson);
  1523. CM_ViewEnded(pasPerson);
  1524. RBC_ViewEnded(pasPerson);
  1525. OD_ViewEnded(pasPerson);
  1526. OD2_ViewEnded(pasPerson);
  1527. USR_ViewEnded(pasPerson);
  1528. delete pasPerson->m_pView;
  1529. pasPerson->m_pView = NULL;
  1530. }
  1531. DebugExitVOID(ASShare::HET_ViewEnded);
  1532. }
  1533. //
  1534. // HETUnshareAllWindows()
  1535. // EnumWindows() callback, to make sure when you exit a share on the local
  1536. // machine, we aren't left with any properties on top level windows.
  1537. //
  1538. BOOL CALLBACK HETUnshareAllWindows(HWND hwnd, LPARAM lParam)
  1539. {
  1540. DebugEntry(HETUnshareAllWindows);
  1541. HET_ClearHosting(hwnd);
  1542. DebugExitVOID(HETUnshareAllWindows);
  1543. return(TRUE);
  1544. }
  1545. //
  1546. // HET_Clear()
  1547. //
  1548. void HET_Clear(void)
  1549. {
  1550. HET_UNSHARE_ALL req;
  1551. DebugEntry(HET_Clear);
  1552. ASSERT(g_asCanHost);
  1553. //
  1554. // Quick DD communication to wipe out the track list
  1555. //
  1556. OSI_FunctionRequest(HET_ESC_UNSHARE_ALL, (LPOSI_ESCAPE_HEADER)&req, sizeof(req));
  1557. //
  1558. // Enum all top level windows, and wipe out the property.
  1559. // if we can share.
  1560. //
  1561. EnumWindows(HETUnshareAllWindows, 0);
  1562. DebugExitVOID(HET_Clear);
  1563. }
  1564. //
  1565. // HETRepaintWindow()
  1566. // EnumWindows() callback to repaint each window, happens when somebody
  1567. // joins a share
  1568. //
  1569. BOOL CALLBACK HETRepaintWindow(HWND hwnd, LPARAM lParam)
  1570. {
  1571. ASShare * pShare = (ASShare *)lParam;
  1572. ASSERT(!IsBadWritePtr(pShare, sizeof(*pShare)));
  1573. if (pShare->HET_WindowIsHosted(hwnd))
  1574. {
  1575. USR_RepaintWindow(hwnd);
  1576. }
  1577. return(TRUE);
  1578. }
  1579. //
  1580. // HET_SetGUIEffects
  1581. //
  1582. // Turns various animations off/on when we start/stop hosting, to improve
  1583. // performance. Currently, we mess with
  1584. // * min animation
  1585. // * all of the effects in SPI_SETUIEFFECTS (tooltip fade, menu animation,
  1586. // etc.)
  1587. // * cursor shadows
  1588. //
  1589. // We don't turn off smooth scroll or full drag.
  1590. //
  1591. void HET_SetGUIEffects
  1592. (
  1593. BOOL fOn,
  1594. GUIEFFECTS * pEffects
  1595. )
  1596. {
  1597. DebugEntry(HET_SetGUIEffects);
  1598. ASSERT(!IsBadWritePtr(pEffects, sizeof(*pEffects)));
  1599. //
  1600. // NB. We deliberately do not track the state of animation whilst we
  1601. // are sharing. A determined user could, using some other app (such as
  1602. // the TweakUI control panel applet) reenable animation whilst in a
  1603. // share. We will respect this.
  1604. //
  1605. // We only affect the current 'in memory' setting - we do not write our
  1606. // temporary change to file.
  1607. //
  1608. if (fOn)
  1609. {
  1610. //
  1611. // If it was on before, restore it.
  1612. //
  1613. if (pEffects->hetAnimation.iMinAnimate)
  1614. {
  1615. pEffects->hetAnimation.cbSize = sizeof(pEffects->hetAnimation);
  1616. SystemParametersInfo(SPI_SETANIMATION, sizeof(pEffects->hetAnimation),
  1617. &pEffects->hetAnimation, 0);
  1618. }
  1619. if (pEffects->hetAdvanced)
  1620. {
  1621. SystemParametersInfo(SPI_SETUIEFFECTS, 0,
  1622. (LPVOID)pEffects->hetAdvanced, 0);
  1623. }
  1624. if (pEffects->hetCursorShadow)
  1625. {
  1626. SystemParametersInfo(SPI_SETCURSORSHADOW, 0,
  1627. (LPVOID)pEffects->hetCursorShadow, 0);
  1628. }
  1629. }
  1630. else
  1631. {
  1632. //
  1633. // Find out what animations are on.
  1634. //
  1635. ZeroMemory(&pEffects->hetAnimation, sizeof(pEffects->hetAnimation));
  1636. pEffects->hetAnimation.cbSize = sizeof(pEffects->hetAnimation);
  1637. SystemParametersInfo(SPI_GETANIMATION, sizeof(pEffects->hetAnimation),
  1638. &pEffects->hetAnimation, 0);
  1639. pEffects->hetAdvanced = FALSE;
  1640. SystemParametersInfo(SPI_GETUIEFFECTS, 0, &pEffects->hetAdvanced, 0);
  1641. pEffects->hetCursorShadow = FALSE;
  1642. SystemParametersInfo(SPI_GETCURSORSHADOW, 0, &pEffects->hetCursorShadow, 0);
  1643. //
  1644. // Turn off the animations which are on.
  1645. //
  1646. if (pEffects->hetAnimation.iMinAnimate)
  1647. {
  1648. //
  1649. // It's currently enabled, suppress it.
  1650. //
  1651. pEffects->hetAnimation.cbSize = sizeof(pEffects->hetAnimation);
  1652. pEffects->hetAnimation.iMinAnimate = FALSE;
  1653. SystemParametersInfo(SPI_SETANIMATION, sizeof(pEffects->hetAnimation),
  1654. &pEffects->hetAnimation, 0);
  1655. // SPI will wipe this out. Keep it set so we know to restore it.
  1656. pEffects->hetAnimation.iMinAnimate = TRUE;
  1657. }
  1658. if (pEffects->hetAdvanced)
  1659. {
  1660. SystemParametersInfo(SPI_SETUIEFFECTS, 0, FALSE, 0);
  1661. }
  1662. if (pEffects->hetCursorShadow)
  1663. {
  1664. SystemParametersInfo(SPI_SETCURSORSHADOW, 0, FALSE, 0);
  1665. }
  1666. }
  1667. DebugExitVOID(ASHost::HET_SetGUIEffects);
  1668. }
  1669. //
  1670. // HET_GetAppsList()
  1671. // Gets the list of shareable applications, the ones currently shared and
  1672. // the ones available for sharing.
  1673. //
  1674. // This routine does NOT check if we're in a call. The interface from the
  1675. // UI for the SDK does. This allows us to show the task list, disabled,
  1676. // always in the share host UI.
  1677. //
  1678. BOOL HET_GetAppsList(IAS_HWND_ARRAY ** ppArray)
  1679. {
  1680. BOOL rc = FALSE;
  1681. HOSTENUM hostEnum;
  1682. DebugEntry(HET_GetAppsList);
  1683. ASSERT(ppArray != NULL);
  1684. *ppArray = NULL;
  1685. //
  1686. // Generate a list of shareable apps
  1687. // This does NOT include the desktop.
  1688. //
  1689. ::COM_BasedListInit(&hostEnum.list);
  1690. hostEnum.count = 0;
  1691. hostEnum.countShared = 0;
  1692. ::EnumWindows(HostEnumProc, (LPARAM)&hostEnum);
  1693. //
  1694. // If there's nothing left in the list, but we know something is
  1695. // shared, it means there's a hidden/weird window the user can't
  1696. // see. Fake a catchall entry.
  1697. //
  1698. if (hostEnum.countShared && !hostEnum.count)
  1699. {
  1700. ::COM_SimpleListAppend(&hostEnum.list, HWND_BROADCAST);
  1701. hostEnum.count++;
  1702. }
  1703. *ppArray = (IAS_HWND_ARRAY *)new BYTE[sizeof(IAS_HWND_ARRAY) +
  1704. (hostEnum.count * sizeof(IAS_HWND))];
  1705. if (*ppArray != NULL)
  1706. {
  1707. (*ppArray)->cEntries = hostEnum.count;
  1708. (*ppArray)->cShared = hostEnum.countShared;
  1709. IAS_HWND * pEntry;
  1710. pEntry = (*ppArray)->aEntries;
  1711. while (! ::COM_BasedListIsEmpty(&hostEnum.list))
  1712. {
  1713. pEntry->hwnd = (HWND) ::COM_SimpleListRemoveHead(&hostEnum.list);
  1714. pEntry->fShared = (pEntry->hwnd == HWND_BROADCAST) ||
  1715. (HET_IsWindowShared(pEntry->hwnd));
  1716. pEntry++;
  1717. }
  1718. rc = TRUE;
  1719. }
  1720. else
  1721. {
  1722. WARNING_OUT(("HET_GetAppsList: can't allocate app array"));
  1723. }
  1724. DebugExitBOOL(HET_GetAppsList, rc);
  1725. return(rc);
  1726. }
  1727. //
  1728. // HET_FreeAppsList()
  1729. //
  1730. void HET_FreeAppsList(IAS_HWND_ARRAY * pArray)
  1731. {
  1732. ASSERT(!IsBadWritePtr(pArray, sizeof(*pArray)));
  1733. delete pArray;
  1734. }
  1735. //
  1736. // HostEnumProc()
  1737. //
  1738. // EnumWindows callback. This makes the shared/shareable task list.
  1739. //
  1740. BOOL CALLBACK HostEnumProc(HWND hwnd, LPARAM lParam)
  1741. {
  1742. PHOSTENUM phostEnum = (PHOSTENUM)lParam;
  1743. //
  1744. // We are only interested in windows which:
  1745. // - are shareable
  1746. // - have no owner. This should remove all top level windows
  1747. // except task windows
  1748. // - are not the front end itself, which should not be shared
  1749. // - are visible
  1750. // - are not shadowed or already hosted
  1751. //
  1752. // We are also only interested in already hosted or shadowed apps, but
  1753. // since only ASMaster knows our SHP_HANDLE, we let it test for that
  1754. // afterwards, since then we can use SHP_GetWindowStatus().
  1755. //
  1756. if (HET_IsWindowShared(hwnd))
  1757. {
  1758. phostEnum->countShared++;
  1759. }
  1760. HWND hwndOwner = ::GetWindow(hwnd, GW_OWNER);
  1761. //
  1762. // Note that we also want to skip windows with no title. There's not
  1763. // much point is showing <Untitled Application> in the Share menu since
  1764. // nobody will have a clue what it is.
  1765. //
  1766. if ( HET_IsWindowShareable(hwnd) &&
  1767. ((NULL == hwndOwner) || !::IsWindowVisible(hwndOwner)) &&
  1768. ::IsWindowVisible(hwnd) &&
  1769. ::GetWindowTextLength(hwnd)
  1770. )
  1771. {
  1772. ::COM_SimpleListAppend((PBASEDLIST)(&((PHOSTENUM)phostEnum)->list), (void *) hwnd);
  1773. phostEnum->count++;
  1774. }
  1775. //
  1776. // Return TRUE for the enumeration to continue
  1777. //
  1778. return TRUE;
  1779. }
  1780. //
  1781. // HET_IsWindowShared()
  1782. //
  1783. BOOL HET_IsWindowShared(HWND hwnd)
  1784. {
  1785. BOOL rc = FALSE;
  1786. UT_Lock(UTLOCK_AS);
  1787. if (g_asSession.pShare &&
  1788. g_asSession.pShare->m_pasLocal)
  1789. {
  1790. if (hwnd == GetDesktopWindow())
  1791. {
  1792. rc = (g_asSession.pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED);
  1793. }
  1794. else if (hwnd == HWND_BROADCAST)
  1795. {
  1796. rc = (g_asSession.pShare->m_pasLocal->hetCount != 0);
  1797. }
  1798. else
  1799. {
  1800. rc = (HET_GetHosting(hwnd) != 0);
  1801. }
  1802. }
  1803. UT_Unlock(UTLOCK_AS);
  1804. return(rc);
  1805. }
  1806. //
  1807. // HET_IsWindowShareable()
  1808. //
  1809. BOOL HET_IsWindowShareable(HWND hwnd)
  1810. {
  1811. BOOL rc = FALSE;
  1812. UT_Lock(UTLOCK_AS);
  1813. if (HET_IsWindowShared(hwnd))
  1814. {
  1815. // It's shared -- so it must be shareable (or was at the time)
  1816. rc = TRUE;
  1817. DC_QUIT;
  1818. }
  1819. //
  1820. // Now check the window against share restrictions
  1821. //
  1822. // if this is the desktop, check it
  1823. if (hwnd == ::GetDesktopWindow())
  1824. {
  1825. if (g_asPolicies & SHP_POLICY_NODESKTOPSHARE)
  1826. {
  1827. //
  1828. // Policy prevents desktop sharing
  1829. //
  1830. DC_QUIT;
  1831. }
  1832. }
  1833. else
  1834. {
  1835. DWORD idProcess;
  1836. char szClass[HET_CLASS_NAME_SIZE];
  1837. if (GetWindowThreadProcessId(hwnd, &idProcess) &&
  1838. (idProcess == GetCurrentProcessId()))
  1839. {
  1840. //
  1841. // We NEVER let you share windows in the caller's process
  1842. //
  1843. DC_QUIT;
  1844. }
  1845. if (HET_IsShellWindow(hwnd))
  1846. {
  1847. //
  1848. // We NEVER let you share the tray or the shell desktop
  1849. //
  1850. DC_QUIT;
  1851. }
  1852. if ((g_asPolicies & SHP_POLICY_SHAREMASK) &&
  1853. GetClassName(hwnd, szClass, sizeof(szClass)))
  1854. {
  1855. //
  1856. // Check for CMD prompt
  1857. //
  1858. if (!lstrcmpi(szClass, HET_CMD95_CLASS) ||
  1859. !lstrcmpi(szClass, HET_CMDNT_CLASS))
  1860. {
  1861. if (g_asPolicies & SHP_POLICY_NODOSBOXSHARE)
  1862. {
  1863. //
  1864. // Policy prevents cmd prompt sharing
  1865. //
  1866. DC_QUIT;
  1867. }
  1868. }
  1869. //
  1870. // Check for SHELL
  1871. //
  1872. if (!lstrcmpi(szClass, HET_EXPLORER_CLASS) ||
  1873. !lstrcmpi(szClass, HET_CABINET_CLASS))
  1874. {
  1875. if (g_asPolicies & SHP_POLICY_NOEXPLORERSHARE)
  1876. {
  1877. //
  1878. // Policy prevents shell sharing
  1879. //
  1880. DC_QUIT;
  1881. }
  1882. }
  1883. }
  1884. }
  1885. //
  1886. // Finally! It's OK to share this.
  1887. //
  1888. rc = TRUE;
  1889. DC_EXIT_POINT:
  1890. UT_Unlock(UTLOCK_AS);
  1891. return(rc);
  1892. }
  1893. //
  1894. // HostDlgProc()
  1895. //
  1896. // Handles the hosting UI dialog. This may or may not be visible. It can
  1897. // only actually share apps and change control state when in a call. But
  1898. // users may keep it up as a mini-taskman thing, so we need to dyanmically
  1899. // update its state.
  1900. //
  1901. INT_PTR CALLBACK HostDlgProc
  1902. (
  1903. HWND hwnd,
  1904. UINT uMsg,
  1905. WPARAM wParam,
  1906. LPARAM lParam
  1907. )
  1908. {
  1909. BOOL rc = TRUE;
  1910. DebugEntry(HostDlgProc);
  1911. switch (uMsg)
  1912. {
  1913. case WM_INITDIALOG:
  1914. {
  1915. HOST_InitDialog(hwnd);
  1916. rc = FALSE;
  1917. break;
  1918. }
  1919. case WM_DESTROY:
  1920. {
  1921. //
  1922. // Because NT4.x has bad WM_DELETEITEM bugs, we must clear out
  1923. // the listbox now, to avoid leaking the memory for the
  1924. // items.
  1925. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_RESETCONTENT, 0, 0);
  1926. rc = FALSE;
  1927. break;
  1928. }
  1929. case WM_INITMENU:
  1930. {
  1931. if (IsIconic(hwnd))
  1932. {
  1933. EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
  1934. EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_MINIMIZE, MF_BYCOMMAND | MF_GRAYED);
  1935. }
  1936. else
  1937. {
  1938. EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_RESTORE, MF_BYCOMMAND | MF_GRAYED);
  1939. EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
  1940. }
  1941. break;
  1942. }
  1943. case WM_SYSCOMMAND:
  1944. {
  1945. switch (wParam)
  1946. {
  1947. case CMD_TOPMOST:
  1948. {
  1949. if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
  1950. {
  1951. CheckMenuItem(GetSystemMenu(hwnd, FALSE),
  1952. CMD_TOPMOST, MF_BYCOMMAND | MF_UNCHECKED);
  1953. SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
  1954. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  1955. }
  1956. else
  1957. {
  1958. CheckMenuItem(GetSystemMenu(hwnd, FALSE),
  1959. CMD_TOPMOST, MF_BYCOMMAND | MF_CHECKED);
  1960. SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
  1961. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  1962. }
  1963. break;
  1964. }
  1965. default:
  1966. {
  1967. rc = FALSE;
  1968. break;
  1969. }
  1970. }
  1971. break;
  1972. }
  1973. case WM_COMMAND:
  1974. {
  1975. switch (GET_WM_COMMAND_ID(wParam, lParam))
  1976. {
  1977. case IDOK:
  1978. if (::GetFocus() == GetDlgItem(hwnd, CTRL_PROGRAM_LIST))
  1979. {
  1980. // Do same thing as double-click
  1981. HOST_ChangeShareState(hwnd, CHANGE_TOGGLE);
  1982. break;
  1983. }
  1984. // FALL THROUGH
  1985. case IDCANCEL:
  1986. SendMessage(hwnd, WM_CLOSE, 0, 0);
  1987. break;
  1988. case CTRL_PROGRAM_LIST:
  1989. {
  1990. // Double-click/Enter means to toggle sharing
  1991. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  1992. {
  1993. case LBN_SELCHANGE:
  1994. {
  1995. HOST_OnSelChange(hwnd);
  1996. break;
  1997. }
  1998. case LBN_DBLCLK:
  1999. {
  2000. HOST_ChangeShareState(hwnd, CHANGE_TOGGLE);
  2001. break;
  2002. }
  2003. }
  2004. break;
  2005. }
  2006. case CTRL_SHARE_BTN:
  2007. {
  2008. HOST_ChangeShareState(hwnd, CHANGE_SHARED);
  2009. break;
  2010. }
  2011. case CTRL_UNSHARE_BTN:
  2012. {
  2013. HOST_ChangeShareState(hwnd, CHANGE_UNSHARED);
  2014. break;
  2015. }
  2016. case CTRL_UNSHAREALL_BTN:
  2017. {
  2018. HOST_ChangeShareState(hwnd, CHANGE_ALLUNSHARED);
  2019. break;
  2020. }
  2021. case CTRL_ALLOWCONTROL_BTN:
  2022. {
  2023. // Turn on allow state.
  2024. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2025. {
  2026. case BN_CLICKED:
  2027. {
  2028. //
  2029. // CA_AllowControl() will send us a message back
  2030. // and cause us to change the button.
  2031. //
  2032. SendMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, TRUE, 0);
  2033. break;
  2034. }
  2035. }
  2036. break;
  2037. }
  2038. case CTRL_PREVENTCONTROL_BTN:
  2039. {
  2040. // Turn off allow state.
  2041. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2042. {
  2043. case BN_CLICKED:
  2044. {
  2045. //
  2046. // CA_AllowControl() will send us a message back
  2047. // and cause us to change the button.
  2048. //
  2049. SendMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0);
  2050. break;
  2051. }
  2052. }
  2053. break;
  2054. }
  2055. case CTRL_ENABLETRUECOLOR_CHECK:
  2056. {
  2057. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2058. {
  2059. case BN_CLICKED:
  2060. {
  2061. //
  2062. // This takes effect the next time something
  2063. // changes--somebody joins, somebody leaves,
  2064. // you stop/start hosting
  2065. //
  2066. if (IsDlgButtonChecked(hwnd, CTRL_ENABLETRUECOLOR_CHECK))
  2067. {
  2068. g_asSettings |= SHP_SETTING_TRUECOLOR;
  2069. }
  2070. else
  2071. {
  2072. g_asSettings &= ~SHP_SETTING_TRUECOLOR;
  2073. }
  2074. break;
  2075. }
  2076. }
  2077. break;
  2078. }
  2079. case CTRL_AUTOACCEPTCONTROL_CHECK:
  2080. {
  2081. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2082. {
  2083. case BN_CLICKED:
  2084. {
  2085. //
  2086. // This takes effect when the next control
  2087. // request comes in.
  2088. //
  2089. if (g_asSession.pShare && g_asSession.pShare->m_pHost)
  2090. {
  2091. g_asSession.pShare->m_pHost->m_caAutoAcceptRequests =
  2092. (IsDlgButtonChecked(hwnd, CTRL_AUTOACCEPTCONTROL_CHECK) != 0);
  2093. }
  2094. break;
  2095. }
  2096. }
  2097. break;
  2098. }
  2099. case CTRL_TEMPREJECTCONTROL_CHECK:
  2100. {
  2101. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2102. {
  2103. case BN_CLICKED:
  2104. {
  2105. //
  2106. // This takes effect when the next control
  2107. // request comes in.
  2108. //
  2109. // NOTE THAT IT TAKES PRECEDENCE OVER AUTO-ACCEPT.
  2110. // This allows you to keep auto-accept on, but then
  2111. // temporarily do not disturb.
  2112. //
  2113. if (g_asSession.pShare && g_asSession.pShare->m_pHost)
  2114. {
  2115. g_asSession.pShare->m_pHost->m_caTempRejectRequests =
  2116. (IsDlgButtonChecked(hwnd, CTRL_TEMPREJECTCONTROL_CHECK) != 0);
  2117. }
  2118. break;
  2119. }
  2120. }
  2121. break;
  2122. }
  2123. }
  2124. break;
  2125. }
  2126. case WM_MEASUREITEM:
  2127. {
  2128. rc = HOST_MeasureItem(hwnd, (LPMEASUREITEMSTRUCT)lParam);
  2129. break;
  2130. }
  2131. case WM_DELETEITEM:
  2132. {
  2133. rc = HOST_DeleteItem(hwnd, (LPDELETEITEMSTRUCT)lParam);
  2134. break;
  2135. }
  2136. case WM_DRAWITEM:
  2137. {
  2138. rc = HOST_DrawItem(hwnd, (LPDRAWITEMSTRUCT)lParam);
  2139. break;
  2140. }
  2141. case WM_TIMER:
  2142. {
  2143. if (wParam != IDT_REFRESH)
  2144. {
  2145. rc = FALSE;
  2146. }
  2147. else
  2148. {
  2149. ASSERT(IsWindowVisible(hwnd));
  2150. HOST_FillList(hwnd);
  2151. }
  2152. break;
  2153. }
  2154. case WM_ACTIVATE:
  2155. {
  2156. //
  2157. // When activating, kill timer. When deactivating, start
  2158. // timer. The theory is, there's nothing else going on when we
  2159. // are active, so why poll for updates? On sharing state
  2160. // changes, we update the list anyway.
  2161. //
  2162. if (IsWindowVisible(hwnd))
  2163. {
  2164. if (wParam)
  2165. {
  2166. KillTimer(hwnd, IDT_REFRESH);
  2167. HOST_FillList(hwnd);
  2168. }
  2169. else
  2170. {
  2171. SetTimer(hwnd, IDT_REFRESH, PERIOD_REFRESH, 0);
  2172. }
  2173. }
  2174. break;
  2175. }
  2176. //
  2177. // Private communication messages
  2178. //
  2179. case HOST_MSG_CALL:
  2180. {
  2181. HOST_OnCall(hwnd, (wParam != FALSE));
  2182. break;
  2183. }
  2184. case HOST_MSG_OPEN:
  2185. {
  2186. //
  2187. // If we are temporarily hidden, ignore all open requests.
  2188. //
  2189. if (!g_asSession.fHostUIFrozen)
  2190. {
  2191. if (!IsWindowVisible(hwnd))
  2192. {
  2193. //
  2194. // Note, we may end up updating the list twice, once here
  2195. // and once under activation.
  2196. HOST_FillList(hwnd);
  2197. ShowWindow(hwnd, SW_SHOW);
  2198. g_asSession.fHostUI = TRUE;
  2199. }
  2200. if (IsIconic(hwnd))
  2201. SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
  2202. SetForegroundWindow(hwnd);
  2203. }
  2204. break;
  2205. }
  2206. case WM_CLOSE:
  2207. case HOST_MSG_CLOSE:
  2208. {
  2209. if (IsWindowVisible(hwnd))
  2210. {
  2211. //
  2212. // Hiding the window will deactivate it. Deactivating it
  2213. // will kick off timer. So kill timer afterwards.
  2214. //
  2215. ShowWindow(hwnd, SW_HIDE);
  2216. KillTimer(hwnd, IDT_REFRESH);
  2217. g_asSession.fHostUI = FALSE;
  2218. }
  2219. break;
  2220. }
  2221. case HOST_MSG_UPDATELIST:
  2222. {
  2223. //
  2224. // We only do list stuff when the UI is up.
  2225. //
  2226. if (IsWindowVisible(hwnd))
  2227. {
  2228. HOST_FillList(hwnd);
  2229. //
  2230. // If timer is on, reset it. This is for case where you
  2231. // are hosting but this UI window is up in the background.
  2232. // There's no point in overlapping the updates. We want the
  2233. // list to update every time there's a top level shared
  2234. // window change OR PERIOD_REFRESH milliseconds have elapsed
  2235. // without a change.
  2236. //
  2237. if (hwnd != GetActiveWindow())
  2238. {
  2239. SetTimer(hwnd, IDT_REFRESH, PERIOD_REFRESH, 0);
  2240. }
  2241. }
  2242. break;
  2243. }
  2244. case HOST_MSG_HOSTSTART:
  2245. {
  2246. HOST_OnSharing(hwnd, TRUE);
  2247. break;
  2248. }
  2249. case HOST_MSG_HOSTEND:
  2250. {
  2251. HOST_OnSharing(hwnd, FALSE);
  2252. break;
  2253. }
  2254. case HOST_MSG_ALLOWCONTROL:
  2255. {
  2256. HOST_OnControllable(hwnd, (wParam != 0));
  2257. break;
  2258. }
  2259. case HOST_MSG_CONTROLLED:
  2260. {
  2261. if (wParam)
  2262. {
  2263. //
  2264. // Hide the window temporarily
  2265. //
  2266. ASSERT(!g_asSession.fHostUIFrozen);
  2267. g_asSession.fHostUIFrozen = TRUE;
  2268. if (IsWindowVisible(hwnd))
  2269. {
  2270. ASSERT(g_asSession.fHostUI);
  2271. SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE |
  2272. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE |
  2273. SWP_HIDEWINDOW);
  2274. }
  2275. }
  2276. else
  2277. {
  2278. //
  2279. // Put the window back in the state it was
  2280. //
  2281. if (g_asSession.fHostUIFrozen)
  2282. {
  2283. g_asSession.fHostUIFrozen = FALSE;
  2284. if (g_asSession.fHostUI)
  2285. {
  2286. SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE |
  2287. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE |
  2288. SWP_SHOWWINDOW);
  2289. }
  2290. }
  2291. }
  2292. break;
  2293. }
  2294. default:
  2295. rc = FALSE;
  2296. break;
  2297. }
  2298. DebugExitBOOL(HostDlgProc, rc);
  2299. return(rc);
  2300. }
  2301. //
  2302. // HOST_InitDialog()
  2303. //
  2304. // Initializes the host UI dialog
  2305. //
  2306. void HOST_InitDialog(HWND hwnd)
  2307. {
  2308. HMENU hMenu;
  2309. char szText[128];
  2310. MENUITEMINFO mi;
  2311. DebugEntry(HOST_InitDialog);
  2312. // Set title text
  2313. HOST_UpdateTitle(hwnd, IDS_NOTINCALL);
  2314. //
  2315. // Set window icon
  2316. //
  2317. SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)g_hetASIcon);
  2318. //
  2319. // Update system menu
  2320. //
  2321. hMenu = GetSystemMenu(hwnd, FALSE);
  2322. EnableMenuItem(hMenu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
  2323. EnableMenuItem(hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
  2324. // Append separator, always on top to system menu
  2325. ZeroMemory(&mi, sizeof(mi));
  2326. mi.cbSize = sizeof(mi);
  2327. mi.fMask = MIIM_TYPE;
  2328. mi.fType = MFT_SEPARATOR;
  2329. InsertMenuItem(hMenu, -1, TRUE, &mi);
  2330. mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
  2331. mi.fType = MFT_STRING;
  2332. mi.fState = MFS_ENABLED;
  2333. mi.wID = CMD_TOPMOST;
  2334. LoadString(g_asInstance, IDS_TOPMOST, szText, sizeof(szText));
  2335. mi.dwTypeData = szText;
  2336. mi.cch = lstrlen(szText);
  2337. InsertMenuItem(hMenu, -1, TRUE, &mi);
  2338. //
  2339. // Enable/disable true color sharing control. If a policy prevents it
  2340. // or our screen depth isn't capable, disable it.
  2341. //
  2342. HOST_EnableCtrl(hwnd, CTRL_ENABLETRUECOLOR_CHECK,
  2343. ((g_usrScreenBPP >= 24) && !(g_asPolicies & SHP_POLICY_NOTRUECOLOR)));
  2344. //
  2345. // Get text, control buttons set.
  2346. //
  2347. HOST_OnControllable(hwnd, TRUE);
  2348. HOST_OnControllable(hwnd, FALSE);
  2349. DebugExitVOID(HOST_InitDialog);
  2350. }
  2351. //
  2352. // HOST_UpdateTitle()
  2353. //
  2354. // Updates title bar of hosting UI
  2355. //
  2356. void HOST_UpdateTitle(HWND hwnd, UINT idState)
  2357. {
  2358. char szText[64];
  2359. char szFormat[128];
  2360. char szTitle[192];
  2361. DebugEntry(HOST_UpdateTitle);
  2362. LoadString(g_asInstance, IDS_SHARING_FORMAT, szFormat, sizeof(szFormat));
  2363. LoadString(g_asInstance, idState, szText, sizeof(szText));
  2364. wsprintf(szTitle, szFormat, szText);
  2365. SetWindowText(hwnd, szTitle);
  2366. DebugExitVOID(HOST_UpdateTitle);
  2367. }
  2368. //
  2369. // HOST_OnCall()
  2370. //
  2371. // Handles call start/stop
  2372. //
  2373. void HOST_OnCall(HWND hwnd, BOOL fCall)
  2374. {
  2375. DebugEntry(HOST_OnCall);
  2376. // Update title bar
  2377. HOST_UpdateTitle(hwnd, (fCall ? IDS_NOTHING : IDS_NOTINCALL));
  2378. HOST_EnableCtrl(hwnd, CTRL_PROGRAM_LIST, fCall);
  2379. if (IsWindowVisible(hwnd))
  2380. {
  2381. SendMessage(hwnd, HOST_MSG_UPDATELIST, 0, 0);
  2382. }
  2383. DebugExitVOID(HOST_OnCall);
  2384. }
  2385. //
  2386. // HOST_OnSharing()
  2387. //
  2388. // Handles sharing start/stop
  2389. //
  2390. void HOST_OnSharing(HWND hwnd, BOOL fSharing)
  2391. {
  2392. DebugEntry(HOST_OnSharing);
  2393. // Update title bar
  2394. if (fSharing)
  2395. {
  2396. HOST_UpdateTitle(hwnd,
  2397. (g_asSession.pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED) ?
  2398. IDS_DESKTOP : IDS_PROGRAMS);
  2399. }
  2400. else
  2401. {
  2402. HOST_UpdateTitle(hwnd, IDS_NOTHING);
  2403. }
  2404. //
  2405. // The ctrl button should always be Allow. When we stop hosting, we turn
  2406. // off allowing control first.
  2407. //
  2408. if (!(g_asPolicies & SHP_POLICY_NOCONTROL))
  2409. {
  2410. HOST_EnableCtrl(hwnd, CTRL_ALLOWCONTROL_BTN, fSharing);
  2411. }
  2412. HOST_EnableCtrl(hwnd, CTRL_UNSHAREALL_BTN, fSharing);
  2413. if ((g_usrScreenBPP >= 24) && !(g_asPolicies & SHP_POLICY_NOTRUECOLOR))
  2414. {
  2415. //
  2416. // Only dynamically change this checkbox if true color is available.
  2417. //
  2418. HOST_EnableCtrl(hwnd, CTRL_ENABLETRUECOLOR_CHECK, !fSharing);
  2419. }
  2420. DebugExitVOID(HOST_OnSharing);
  2421. }
  2422. //
  2423. // HOST_OnControllable()
  2424. //
  2425. // Updates the blurb, button text, and button ID when the controllable
  2426. // state changes.
  2427. //
  2428. void HOST_OnControllable(HWND hwnd, BOOL fControllable)
  2429. {
  2430. HWND hwndBtn;
  2431. TCHAR szText[256];
  2432. DebugEntry(HOST_OnControllable);
  2433. // Control blurb
  2434. LoadString(g_asInstance,
  2435. (fControllable ? IDS_MSG_TOPREVENTCONTROL : IDS_MSG_TOALLOWCONTROL),
  2436. szText, sizeof(szText));
  2437. SetDlgItemText(hwnd, CTRL_CONTROL_MSG, szText);
  2438. // Control button
  2439. if (fControllable)
  2440. {
  2441. hwndBtn = GetDlgItem(hwnd, CTRL_ALLOWCONTROL_BTN);
  2442. ASSERT(hwndBtn);
  2443. SetWindowLong(hwndBtn, GWL_ID, CTRL_PREVENTCONTROL_BTN);
  2444. LoadString(g_asInstance, IDS_PREVENTCONTROL, szText, sizeof(szText));
  2445. }
  2446. else
  2447. {
  2448. hwndBtn = GetDlgItem(hwnd, CTRL_PREVENTCONTROL_BTN);
  2449. ASSERT(hwndBtn);
  2450. SetWindowLong(hwndBtn, GWL_ID, CTRL_ALLOWCONTROL_BTN);
  2451. LoadString(g_asInstance, IDS_ALLOWCONTROL, szText, sizeof(szText));
  2452. }
  2453. SetWindowText(hwndBtn, szText);
  2454. // Enable/disable the control checkboxes, make sure they start unchecked.
  2455. HOST_EnableCtrl(hwnd, CTRL_TEMPREJECTCONTROL_CHECK, fControllable);
  2456. CheckDlgButton(hwnd, CTRL_TEMPREJECTCONTROL_CHECK, FALSE);
  2457. HOST_EnableCtrl(hwnd, CTRL_AUTOACCEPTCONTROL_CHECK, fControllable);
  2458. CheckDlgButton(hwnd, CTRL_AUTOACCEPTCONTROL_CHECK, FALSE);
  2459. DebugExitVOID(HOST_OnControllable);
  2460. }
  2461. //
  2462. // HOST_FillList()
  2463. //
  2464. // Fills the contents of the shared/unshared applications list
  2465. //
  2466. void HOST_FillList(HWND hwnd)
  2467. {
  2468. IAS_HWND_ARRAY * pArray;
  2469. int iItem;
  2470. PHOSTITEM pItem;
  2471. char szText[80];
  2472. UINT iWnd;
  2473. HICON hIcon;
  2474. BOOL fAppsAvailable;
  2475. HWND hwndSelect;
  2476. int iSelect;
  2477. int iTop;
  2478. int cxExtent;
  2479. RECT rc;
  2480. HFONT hfnT;
  2481. HFONT hfnControl;
  2482. HDC hdc;
  2483. //
  2484. // For the common case, remember what was selected and try to put that
  2485. // back.
  2486. //
  2487. // Save current top index
  2488. iTop = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETTOPINDEX, 0, 0);
  2489. // Save currently selected item
  2490. hwndSelect = HWND_BOTTOM;
  2491. iSelect = -1;
  2492. iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETCURSEL, 0, 0);
  2493. if (iItem != -1)
  2494. {
  2495. pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST,
  2496. LB_GETITEMDATA, iItem, 0);
  2497. if (pItem)
  2498. {
  2499. hwndSelect = pItem->hwnd;
  2500. }
  2501. }
  2502. //
  2503. // Turn off redraw and clear the apps list.
  2504. //
  2505. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, WM_SETREDRAW, FALSE, 0);
  2506. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_RESETCONTENT, 0, 0);
  2507. //
  2508. // We're going to calculate the horizontal extent since ownerdraw
  2509. // lists can't do that.
  2510. //
  2511. hdc = GetDC(hwnd);
  2512. hfnControl = (HFONT)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, WM_GETFONT, 0, 0);
  2513. cxExtent = 0;
  2514. //
  2515. // HET_GetAppsList() will fail if there's not enough memory to allocate
  2516. // the array. If we really can't allocate it, why add an item for the
  2517. // desktop?
  2518. //
  2519. if (HET_GetAppsList(&pArray))
  2520. {
  2521. ASSERT(pArray);
  2522. fAppsAvailable = TRUE;
  2523. //
  2524. // If desktop sharing is permitted, add desktop item.
  2525. //
  2526. if (!(g_asPolicies & SHP_POLICY_NODESKTOPSHARE))
  2527. {
  2528. pItem = new HOSTITEM;
  2529. if (!pItem)
  2530. {
  2531. ERROR_OUT(("Unable to alloc HOSTITEM for listbox"));
  2532. }
  2533. else
  2534. {
  2535. pItem->hwnd = GetDesktopWindow();
  2536. pItem->hIcon = g_hetDeskIconSmall;
  2537. LoadString(g_asInstance, IDS_DESKTOP, szText,
  2538. sizeof(szText));
  2539. pItem->fShared = (HET_IsWindowShared(pItem->hwnd) != FALSE);
  2540. if (pItem->fShared)
  2541. {
  2542. //
  2543. // When everything (the desktop) is shared, sharing
  2544. // individual apps makes no sense. We keep them in the
  2545. // list but draw them unavailable, same as if the list
  2546. // itself were completely disabled.
  2547. //
  2548. fAppsAvailable = FALSE;
  2549. pItem->fAvailable = TRUE;
  2550. }
  2551. else if (!pArray->cShared && g_asSession.callID &&
  2552. (g_asSession.attendeePermissions & NM_PERMIT_SHARE))
  2553. {
  2554. //
  2555. // No apps are shared, the desktop item is available.
  2556. //
  2557. pItem->fAvailable = TRUE;
  2558. }
  2559. else
  2560. {
  2561. //
  2562. // Apps are shared, sharing the entire desktop makes no
  2563. // sense.
  2564. //
  2565. pItem->fAvailable = FALSE;
  2566. }
  2567. iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST,
  2568. LB_ADDSTRING, 0, (LPARAM)szText);
  2569. if (iItem == -1)
  2570. {
  2571. ERROR_OUT(("Couldn't append item to list"));
  2572. delete pItem;
  2573. }
  2574. else
  2575. {
  2576. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETITEMDATA,
  2577. iItem, (LPARAM)pItem);
  2578. //
  2579. // Calculate width.
  2580. //
  2581. hfnT = SelectFont(hdc,
  2582. (pItem->fShared ? g_hetSharedFont : hfnControl));
  2583. SetRectEmpty(&rc);
  2584. DrawText(hdc, szText, lstrlen(szText), &rc,
  2585. DT_LEFT | DT_VCENTER | DT_EXTERNALLEADING | DT_NOPREFIX |
  2586. DT_SINGLELINE | DT_CALCRECT);
  2587. SelectFont(hdc, hfnT);
  2588. rc.right -= rc.left;
  2589. cxExtent = max(cxExtent, rc.right);
  2590. //
  2591. // If this desktop item were selected last time,
  2592. // remember so we select it again after.
  2593. //
  2594. if (pItem->hwnd == hwndSelect)
  2595. iSelect = iItem;
  2596. }
  2597. }
  2598. }
  2599. //
  2600. // Add items for apps.
  2601. //
  2602. for (iWnd = 0; iWnd < pArray->cEntries; iWnd++)
  2603. {
  2604. hIcon = NULL;
  2605. if (pArray->aEntries[iWnd].hwnd == HWND_BROADCAST)
  2606. {
  2607. LoadString(g_asInstance, IDS_HIDDEN_WINDOW, szText,
  2608. sizeof(szText));
  2609. hIcon = g_hetASIconSmall;
  2610. }
  2611. else
  2612. {
  2613. GetWindowText(pArray->aEntries[iWnd].hwnd, szText, sizeof(szText));
  2614. if (!szText[0])
  2615. continue;
  2616. // Try to get window small icon
  2617. SendMessageTimeout(pArray->aEntries[iWnd].hwnd, WM_GETICON, ICON_SMALL, 0,
  2618. SMTO_NORMAL, 1000, (DWORD_PTR*)&hIcon);
  2619. if (!hIcon)
  2620. {
  2621. hIcon = (HICON)GetClassLongPtr(pArray->aEntries[iWnd].hwnd, GCLP_HICON);
  2622. }
  2623. //
  2624. // Make a copy of the small icon, we can't just hang on to
  2625. // the application's, it could go away.
  2626. //
  2627. if (hIcon)
  2628. {
  2629. hIcon = (HICON)CopyImage(hIcon, IMAGE_ICON, 0, 0, 0);
  2630. }
  2631. if (!hIcon)
  2632. {
  2633. hIcon = g_hetASIconSmall;
  2634. }
  2635. }
  2636. //
  2637. // Add item to list
  2638. //
  2639. pItem = new HOSTITEM;
  2640. if (!pItem)
  2641. {
  2642. ERROR_OUT(("Unable to alloc HOSTITEM for listbox"));
  2643. }
  2644. else
  2645. {
  2646. pItem->hwnd = pArray->aEntries[iWnd].hwnd;
  2647. pItem->hIcon = hIcon;
  2648. pItem->fShared = pArray->aEntries[iWnd].fShared;
  2649. pItem->fAvailable = g_asSession.callID &&
  2650. (g_asSession.attendeePermissions & NM_PERMIT_SHARE) &&
  2651. (fAppsAvailable != FALSE);
  2652. iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST,
  2653. LB_ADDSTRING, 0, (LPARAM)szText);
  2654. if (iItem == -1)
  2655. {
  2656. ERROR_OUT(("Couldn't append item to list"));
  2657. delete pItem;
  2658. }
  2659. else
  2660. {
  2661. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETITEMDATA,
  2662. iItem, (LPARAM)pItem);
  2663. //
  2664. // Calculate width.
  2665. //
  2666. hfnT = SelectFont(hdc,
  2667. (pItem->fShared ? g_hetSharedFont : hfnControl));
  2668. SetRectEmpty(&rc);
  2669. DrawText(hdc, szText, lstrlen(szText), &rc,
  2670. DT_LEFT | DT_VCENTER | DT_EXTERNALLEADING | DT_NOPREFIX |
  2671. DT_SINGLELINE | DT_CALCRECT);
  2672. SelectFont(hdc, hfnT);
  2673. rc.right -= rc.left;
  2674. cxExtent = max(cxExtent, rc.right);
  2675. }
  2676. //
  2677. // If this app item were selected before, remember so we
  2678. // select it again when done.
  2679. //
  2680. if (pItem->hwnd == hwndSelect)
  2681. iSelect = iItem;
  2682. }
  2683. }
  2684. HET_FreeAppsList(pArray);
  2685. }
  2686. ReleaseDC(hwnd, hdc);
  2687. //
  2688. // Set cur sel, top index, update buttons
  2689. //
  2690. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETTOPINDEX, iTop, 0);
  2691. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETCURSEL, iSelect, 0);
  2692. HOST_OnSelChange(hwnd);
  2693. //
  2694. // Turn on redraw and repaint
  2695. //
  2696. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, WM_SETREDRAW, TRUE, 0);
  2697. //
  2698. // Set horizontal extent
  2699. //
  2700. if (cxExtent)
  2701. {
  2702. // Add on space for checkmark, icons
  2703. cxExtent += GetSystemMetrics(SM_CXMENUCHECK) + GetSystemMetrics(SM_CXSMICON) +
  2704. 3*GetSystemMetrics(SM_CXEDGE);
  2705. }
  2706. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETHORIZONTALEXTENT, cxExtent, 0);
  2707. InvalidateRect(GetDlgItem(hwnd, CTRL_PROGRAM_LIST), NULL, TRUE);
  2708. UpdateWindow(GetDlgItem(hwnd, CTRL_PROGRAM_LIST));
  2709. DebugExitVOID(HOST_FillList);
  2710. }
  2711. //
  2712. // HOST_MeasureItem()
  2713. //
  2714. // Calculates height of ownerdraw items in host list
  2715. //
  2716. BOOL HOST_MeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmi)
  2717. {
  2718. BOOL rc = FALSE;
  2719. UINT cy;
  2720. TEXTMETRIC tm;
  2721. HDC hdc;
  2722. HFONT hfnT;
  2723. DebugEntry(HOST_MeasureItem);
  2724. if (lpmi->CtlID != CTRL_PROGRAM_LIST)
  2725. {
  2726. // Not for us
  2727. DC_QUIT;
  2728. }
  2729. // Get height of bolded font
  2730. hdc = GetDC(hwnd);
  2731. hfnT = SelectFont(hdc, g_hetSharedFont);
  2732. GetTextMetrics(hdc, &tm);
  2733. SelectFont(hdc, hfnT);
  2734. ReleaseDC(hwnd, hdc);
  2735. //
  2736. // Height is max of default height (height of char in font),
  2737. // checkmark height, and small icon height, plus dotted rect.
  2738. //
  2739. cy = (UINT)tm.tmHeight;
  2740. lpmi->itemHeight = max(lpmi->itemHeight, cy);
  2741. cy = (UINT)GetSystemMetrics(SM_CYMENUCHECK);
  2742. lpmi->itemHeight = max(lpmi->itemHeight, cy);
  2743. cy = (UINT)GetSystemMetrics(SM_CYSMICON);
  2744. lpmi->itemHeight = max(lpmi->itemHeight, cy);
  2745. lpmi->itemHeight += GetSystemMetrics(SM_CYEDGE);
  2746. rc = TRUE;
  2747. DC_EXIT_POINT:
  2748. DebugExitBOOL(HOST_MeasureItem, rc);
  2749. return(rc);
  2750. }
  2751. //
  2752. // HOST_DeleteItem()
  2753. //
  2754. // Cleans up after an item is deleted from the list.
  2755. //
  2756. BOOL HOST_DeleteItem(HWND hwnd, LPDELETEITEMSTRUCT lpdi)
  2757. {
  2758. PHOSTITEM pItem;
  2759. BOOL rc = FALSE;
  2760. DebugEntry(HOST_DeleteItem);
  2761. if (lpdi->CtlID != CTRL_PROGRAM_LIST)
  2762. {
  2763. DC_QUIT;
  2764. }
  2765. pItem = (PHOSTITEM)lpdi->itemData;
  2766. if (!pItem)
  2767. {
  2768. //
  2769. // NT 4.x has a terrible bug where the item data is not passed
  2770. // in the DELETEITEMSTRUCT always. So try to obtain it if not.
  2771. //
  2772. pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETITEMDATA,
  2773. lpdi->itemID, 0);
  2774. }
  2775. if (pItem)
  2776. {
  2777. if ((pItem->hIcon != g_hetASIconSmall) && (pItem->hIcon != g_hetDeskIconSmall))
  2778. {
  2779. DestroyIcon(pItem->hIcon);
  2780. }
  2781. delete pItem;
  2782. }
  2783. rc = TRUE;
  2784. DC_EXIT_POINT:
  2785. DebugExitBOOL(HOST_DeleteItem, rc);
  2786. return(rc);
  2787. }
  2788. //
  2789. // HOST_DrawItem()
  2790. //
  2791. // Draws list item
  2792. //
  2793. BOOL HOST_DrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdi)
  2794. {
  2795. COLORREF clrBk;
  2796. COLORREF clrText;
  2797. HBRUSH hbr;
  2798. HFONT hfnT;
  2799. RECT rcItem;
  2800. char szText[80];
  2801. PHOSTITEM pItem;
  2802. BOOL rc = FALSE;
  2803. if (lpdi->CtlID != CTRL_PROGRAM_LIST)
  2804. {
  2805. DC_QUIT;
  2806. }
  2807. pItem = (PHOSTITEM)lpdi->itemData;
  2808. if (!pItem)
  2809. {
  2810. // Empty item for focus
  2811. rc = TRUE;
  2812. DC_QUIT;
  2813. }
  2814. rcItem = lpdi->rcItem;
  2815. //
  2816. // Set up colors
  2817. //
  2818. if (!pItem->fAvailable)
  2819. {
  2820. // No selection color
  2821. clrBk = GetSysColor(COLOR_WINDOW);
  2822. hbr = GetSysColorBrush(COLOR_WINDOW);
  2823. clrText = GetSysColor(COLOR_GRAYTEXT);
  2824. }
  2825. else if (lpdi->itemState & ODS_SELECTED)
  2826. {
  2827. clrBk = GetSysColor(COLOR_HIGHLIGHT);
  2828. hbr = GetSysColorBrush(COLOR_HIGHLIGHT);
  2829. clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  2830. }
  2831. else
  2832. {
  2833. clrBk = GetSysColor(COLOR_WINDOW);
  2834. hbr = GetSysColorBrush(COLOR_WINDOW);
  2835. clrText = GetSysColor(COLOR_WINDOWTEXT);
  2836. }
  2837. SetBkColor(lpdi->hDC, clrBk);
  2838. SetTextColor(lpdi->hDC, clrText);
  2839. // Erase background
  2840. FillRect(lpdi->hDC, &rcItem, hbr);
  2841. // Focus rect
  2842. if (lpdi->itemState & ODS_FOCUS)
  2843. {
  2844. DrawFocusRect(lpdi->hDC, &rcItem);
  2845. }
  2846. rcItem.left += GetSystemMetrics(SM_CXEDGE);
  2847. InflateRect(&rcItem, 0, -GetSystemMetrics(SM_CYBORDER));
  2848. //
  2849. // Draw checkmark and select bolded font
  2850. //
  2851. if (pItem->fShared)
  2852. {
  2853. HDC hdcT;
  2854. HBITMAP hbmpOld;
  2855. hdcT = CreateCompatibleDC(lpdi->hDC);
  2856. hbmpOld = SelectBitmap(hdcT, g_hetCheckBitmap);
  2857. SetTextColor(hdcT, clrText);
  2858. SetBkColor(hdcT, clrBk);
  2859. BitBlt(lpdi->hDC, rcItem.left,
  2860. (rcItem.top + rcItem.bottom - GetSystemMetrics(SM_CYMENUCHECK)) / 2,
  2861. GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK),
  2862. hdcT, 0, 0,
  2863. SRCCOPY);
  2864. SelectBitmap(hdcT, hbmpOld);
  2865. DeleteDC(hdcT);
  2866. hfnT = SelectFont(lpdi->hDC, g_hetSharedFont);
  2867. }
  2868. rcItem.left += GetSystemMetrics(SM_CXMENUCHECK) + GetSystemMetrics(SM_CXEDGE);
  2869. // Draw icon, centered vertically
  2870. DrawIconEx(lpdi->hDC, rcItem.left, (rcItem.top + rcItem.bottom -
  2871. GetSystemMetrics(SM_CYSMICON)) /2, pItem->hIcon,
  2872. GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
  2873. 0, NULL, DI_NORMAL);
  2874. rcItem.left += GetSystemMetrics(SM_CXSMICON) + GetSystemMetrics(SM_CXEDGE);
  2875. //
  2876. // Draw the text
  2877. //
  2878. szText[0] = 0;
  2879. SendMessage(lpdi->hwndItem, LB_GETTEXT, lpdi->itemID,
  2880. (LPARAM)szText);
  2881. DrawText(lpdi->hDC, szText, lstrlen(szText), &rcItem,
  2882. DT_LEFT | DT_VCENTER | DT_EXTERNALLEADING | DT_NOPREFIX | DT_SINGLELINE);
  2883. //
  2884. // Deselect bolded shared font
  2885. //
  2886. if (pItem->fShared)
  2887. {
  2888. SelectFont(lpdi->hDC, hfnT);
  2889. }
  2890. rc = TRUE;
  2891. DC_EXIT_POINT:
  2892. return(rc);
  2893. }
  2894. //
  2895. // HOST_ChangeShareState()
  2896. //
  2897. // Changes the sharing state of the currently selected item.
  2898. //
  2899. void HOST_ChangeShareState(HWND hwnd, UINT action)
  2900. {
  2901. int iItem;
  2902. PHOSTITEM pItem;
  2903. HWND hwndChange;
  2904. HCURSOR hcurT;
  2905. DebugEntry(HOST_ChangeShareState);
  2906. if (action == CHANGE_ALLUNSHARED)
  2907. {
  2908. hwndChange = HWND_BROADCAST;
  2909. action = CHANGE_UNSHARED;
  2910. goto ChangeState;
  2911. }
  2912. iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETCURSEL, 0, 0);
  2913. if (iItem != -1)
  2914. {
  2915. pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST,
  2916. LB_GETITEMDATA, iItem, 0);
  2917. if (pItem && pItem->fAvailable)
  2918. {
  2919. hwndChange = pItem->hwnd;
  2920. if (action == CHANGE_TOGGLE)
  2921. {
  2922. if (HET_IsWindowShared(hwndChange))
  2923. {
  2924. action = CHANGE_UNSHARED;
  2925. }
  2926. else
  2927. {
  2928. action = CHANGE_SHARED;
  2929. }
  2930. }
  2931. ChangeState:
  2932. ASSERT((action == CHANGE_SHARED) || (action == CHANGE_UNSHARED));
  2933. //
  2934. // Set wait cursor
  2935. //
  2936. hcurT = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2937. if (action == CHANGE_SHARED)
  2938. {
  2939. DCS_Share(hwndChange, IAS_SHARE_DEFAULT);
  2940. }
  2941. else
  2942. {
  2943. DCS_Unshare(hwndChange);
  2944. }
  2945. //
  2946. // Set wait cursor
  2947. //
  2948. SetCursor(hcurT);
  2949. }
  2950. }
  2951. DebugExitVOID(HOST_ChangeShareState);
  2952. }
  2953. //
  2954. // HOST_OnSelChange()
  2955. //
  2956. // Handles a selection change in the task list. We enable/disable
  2957. // buttons as appropriate, depending on whether item is available.
  2958. //
  2959. void HOST_OnSelChange(HWND hwnd)
  2960. {
  2961. int iItem;
  2962. PHOSTITEM pItem;
  2963. BOOL fShareBtn = FALSE;
  2964. BOOL fUnshareBtn = FALSE;
  2965. DebugEntry(HOST_OnSelChange);
  2966. //
  2967. // Get current selection, and decide what to do based off that.
  2968. //
  2969. iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETCURSEL, 0, 0);
  2970. if (iItem != -1)
  2971. {
  2972. pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST,
  2973. LB_GETITEMDATA, iItem, 0);
  2974. if (pItem)
  2975. {
  2976. if (pItem->fShared)
  2977. {
  2978. fUnshareBtn = TRUE;
  2979. }
  2980. else if (pItem->fAvailable)
  2981. {
  2982. ASSERT(g_asSession.callID);
  2983. fShareBtn = TRUE;
  2984. }
  2985. }
  2986. }
  2987. HOST_EnableCtrl(hwnd, CTRL_UNSHARE_BTN, fUnshareBtn);
  2988. HOST_EnableCtrl(hwnd, CTRL_SHARE_BTN, fShareBtn);
  2989. DebugExitVOID(HOST_OnSelChange);
  2990. }
  2991. //
  2992. // HOST_EnableCtrl()
  2993. //
  2994. // This enables/disables the child control. If disabling, and this control
  2995. // used to have the focus, we make sure the dialog resets the focus control
  2996. // so the keyboard keeps working. We know that the Close button is always
  2997. // available, so this won't die.
  2998. //
  2999. void HOST_EnableCtrl
  3000. (
  3001. HWND hwnd,
  3002. UINT ctrl,
  3003. BOOL fEnable
  3004. )
  3005. {
  3006. HWND hwndCtrl;
  3007. DebugEntry(HOST_EnableCtrl);
  3008. hwndCtrl = GetDlgItem(hwnd, ctrl);
  3009. ASSERT(hwndCtrl);
  3010. if (fEnable)
  3011. {
  3012. EnableWindow(hwndCtrl, TRUE);
  3013. }
  3014. else
  3015. {
  3016. if (GetFocus() == hwndCtrl)
  3017. {
  3018. // Advance the focus
  3019. SendMessage(hwnd, WM_NEXTDLGCTL, 0, 0);
  3020. }
  3021. EnableWindow(hwndCtrl, FALSE);
  3022. }
  3023. DebugExitVOID(HOST_EnableCtrl);
  3024. }