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

2747 lines
74 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // TaskMan - NT TaskManager
  4. // Copyright (C) Microsoft
  5. //
  6. // File: userpage.cpp
  7. //
  8. // History: 07-July-1999 BradG Created
  9. //
  10. //--------------------------------------------------------------------------
  11. #include "precomp.h"
  12. #include "userdlgs.h"
  13. #define IsActiveConsoleSession() (USER_SHARED_DATA->ActiveConsoleId == NtCurrentPeb()->SessionId)
  14. CUserColSelectDlg ColSelectDlg;
  15. //
  16. // The following arrays map WTS sessions state codes into strings
  17. //
  18. #define MAX_STAT_STRINGS 4
  19. #define FIRST_STAT_STRING IDS_STAT_ACTIVE
  20. LPTSTR g_pszStatString[MAX_STAT_STRINGS];
  21. const int g_aWTSStateToString[] = {
  22. (IDS_STAT_ACTIVE - FIRST_STAT_STRING), // WTSActive
  23. (IDS_STAT_UNKNOWN - FIRST_STAT_STRING), // WTSConnected
  24. (IDS_STAT_UNKNOWN - FIRST_STAT_STRING), // WTSConnectQuery
  25. (IDS_STAT_SHADOW - FIRST_STAT_STRING), // WTSShadow
  26. (IDS_STAT_DISCONNECT - FIRST_STAT_STRING), // WTSDisconnected
  27. (IDS_STAT_UNKNOWN - FIRST_STAT_STRING), // Waiting for client to connect
  28. (IDS_STAT_UNKNOWN - FIRST_STAT_STRING), // WinStation is listening for connection
  29. (IDS_STAT_UNKNOWN - FIRST_STAT_STRING), // WinStation is being reset
  30. (IDS_STAT_UNKNOWN - FIRST_STAT_STRING), // WinStation is down due to error
  31. (IDS_STAT_UNKNOWN - FIRST_STAT_STRING) // WinStation in initialization
  32. };
  33. /*++ class CUserInfo
  34. Class Description:
  35. Represents the last known information about a running task
  36. Arguments:
  37. Return Value:
  38. Revision History:
  39. Nov-29-95 BradG Created
  40. Mar-23-00 a-skuzin Revised
  41. --*/
  42. class CUserInfo
  43. {
  44. public:
  45. DWORD m_dwSessionId;
  46. BOOL m_fShowDomainName;
  47. WCHAR m_szUserName[USERNAME_LENGTH + 1];
  48. WCHAR m_szDomainName[DOMAIN_LENGTH + 1];
  49. WCHAR m_szClientName[CLIENTNAME_LENGTH + 1 ];
  50. LPTSTR m_pszWinStaName;
  51. WTS_CONNECTSTATE_CLASS m_wtsState;
  52. LARGE_INTEGER m_uPassCount;
  53. //
  54. // This is a union of which attribute is dirty. You can look at
  55. // or set any particular column's bit, or just inspect m_fDirty
  56. // to see if anyone at all is dirty. Used to optimize listview
  57. // painting
  58. //
  59. #pragma warning(disable:4201) // Nameless struct or union
  60. union
  61. {
  62. DWORD m_fDirty;
  63. struct
  64. {
  65. DWORD m_fDirty_COL_USERNAME :1;
  66. DWORD m_fDirty_COL_USERSESSIONID :1;
  67. DWORD m_fDirty_COL_SESSIONSTATUS :1;
  68. DWORD m_fDirty_COL_CLIENTNAME :1;
  69. DWORD m_fDirty_COL_WINSTANAME :1;
  70. };
  71. };
  72. #pragma warning(default:4201) // Nameless struct or union
  73. HRESULT SetData(
  74. LPTSTR lpszClientName,
  75. LPTSTR lpszWinStaName,
  76. WTS_CONNECTSTATE_CLASS wtsState,
  77. BOOL fShowDomainName,
  78. LARGE_INTEGER uPassCount,
  79. BOOL fUpdateOnly);
  80. CUserInfo()
  81. {
  82. ZeroMemory(this, sizeof(*this));
  83. }
  84. ~CUserInfo()
  85. {
  86. if (m_pszWinStaName)
  87. {
  88. LocalFree(m_pszWinStaName);
  89. m_pszWinStaName = NULL;
  90. }
  91. }
  92. INT Compare(CUserInfo * pOther);
  93. private:
  94. //
  95. // Column ID on which to sort in the listview, and for
  96. // compares in general
  97. //
  98. static USERCOLUMNID m_iUserSortColumnID;
  99. static INT m_iUserSortDirection; // 1 = asc, -1 = desc
  100. public:
  101. static void SetUserSortColumnID(USERCOLUMNID id)
  102. {
  103. m_iUserSortColumnID = id;
  104. m_iUserSortDirection = 1;
  105. }
  106. static USERCOLUMNID GetUserSortColumnID()
  107. {
  108. return m_iUserSortColumnID;
  109. }
  110. static void SwitchUserSortDirection()
  111. {
  112. m_iUserSortDirection *= -1;
  113. }
  114. };
  115. USERCOLUMNID CUserInfo::m_iUserSortColumnID = USR_COL_USERSNAME;
  116. INT CUserInfo::m_iUserSortDirection = 1; // 1 = asc, -1 = desc
  117. void Shadow(HWND, CUserInfo * );
  118. /*++ class CUserInfo::Compare
  119. Class Description:
  120. Compares this CUserInfo object to another, and returns its ranking
  121. based on the g_iUserSortColumnID field.
  122. Arguments:
  123. pOther - the CUserInfo object to compare this to
  124. Return Value:
  125. < 0 - This CUserInfo is "less" than the other
  126. 0 - Equal (Can't happen, since HWND is secondary sort)
  127. > 0 - This CUserInfo is "greater" than the other
  128. Revision History:
  129. Nov-29-95 BradG Created
  130. --*/
  131. INT CUserInfo::Compare(CUserInfo * pOther)
  132. {
  133. INT iRet;
  134. switch (m_iUserSortColumnID)
  135. {
  136. case USR_COL_USERSNAME:
  137. if (g_Options.m_fShowDomainNames)
  138. {
  139. iRet = lstrcmpi(this->m_szDomainName, pOther->m_szDomainName);
  140. if (iRet != 0)
  141. break;
  142. }
  143. iRet = lstrcmpi(this->m_szUserName, pOther->m_szUserName);
  144. break;
  145. case USR_COL_USERSESSION_ID:
  146. iRet = Compare64(this->m_dwSessionId, pOther->m_dwSessionId);
  147. break;
  148. case USR_COL_SESSION_STATUS:
  149. Assert(g_pszStatString[g_aWTSStateToString[this->m_wtsState]]);
  150. Assert(g_pszStatString[g_aWTSStateToString[pOther->m_wtsState]]);
  151. iRet = lstrcmpi(
  152. g_pszStatString[g_aWTSStateToString[this->m_wtsState]],
  153. g_pszStatString[g_aWTSStateToString[pOther->m_wtsState]]
  154. );
  155. break;
  156. case USR_COL_WINSTA_NAME:
  157. iRet = lstrcmpi(
  158. (this->m_pszWinStaName) ? this->m_pszWinStaName : TEXT(""),
  159. (pOther->m_pszWinStaName) ? pOther->m_pszWinStaName : TEXT("")
  160. );
  161. break;
  162. case USR_COL_CLIENT_NAME:
  163. iRet = lstrcmpi(
  164. (this->m_wtsState != WTSDisconnected) ? this->m_szClientName : TEXT(""),
  165. (pOther->m_wtsState != WTSDisconnected) ? pOther->m_szClientName : TEXT("")
  166. );
  167. break;
  168. default:
  169. Assert(0 && "Invalid task sort column");
  170. iRet = 0;
  171. break;
  172. }
  173. return (iRet * m_iUserSortDirection);
  174. }
  175. /*++ InsertIntoSortedArray
  176. Class Description:
  177. Sticks a CUserInfo ptr into the ptrarray supplied at the
  178. appropriate location based on the current sort column (which
  179. is used by the Compare member function)
  180. Arguments:
  181. pArray - The CPtrArray to add to
  182. pProc - The CUserInfo object to add to the array
  183. Return Value:
  184. TRUE if successful, FALSE if fails
  185. Revision History:
  186. Nov-20-95 BradG Created
  187. --*/
  188. BOOL InsertIntoSortedArray(CPtrArray * pArray, CUserInfo * pUser)
  189. {
  190. INT cItems = pArray->GetSize();
  191. for (INT iIndex = 0; iIndex < cItems; iIndex++)
  192. {
  193. CUserInfo * pTmp = (CUserInfo *) pArray->GetAt(iIndex);
  194. if (pUser->Compare(pTmp) < 0)
  195. {
  196. return pArray->InsertAt(iIndex, pUser);
  197. }
  198. }
  199. return pArray->Add(pUser);
  200. }
  201. /*++ ResortUserArray
  202. Function Description:
  203. Creates a new ptr array sorted in the current sort order based
  204. on the old array, and then replaces the old with the new
  205. Arguments:
  206. ppArray - The CPtrArray to resort
  207. Return Value:
  208. TRUE if successful, FALSE if fails
  209. Revision History:
  210. Nov-21-95 BradG Created
  211. --*/
  212. BOOL ResortUserArray(CPtrArray ** ppArray)
  213. {
  214. // Create a new array which will be sorted in the new
  215. // order and used to replace the existing array
  216. CPtrArray * pNew = new CPtrArray(GetProcessHeap());
  217. if (NULL == pNew)
  218. {
  219. return FALSE;
  220. }
  221. // Insert each of the existing items in the old array into
  222. // the new array in the correct spot
  223. INT cItems = (*ppArray)->GetSize();
  224. for (int i = 0; i < cItems; i++)
  225. {
  226. CUserInfo * pItem = (CUserInfo *) (*ppArray)->GetAt(i);
  227. if (FALSE == InsertIntoSortedArray(pNew, pItem))
  228. {
  229. delete pNew;
  230. return FALSE;
  231. }
  232. }
  233. // Kill off the old array, replace it with the new
  234. delete (*ppArray);
  235. (*ppArray) = pNew;
  236. return TRUE;
  237. }
  238. //*****************************************************************************
  239. //class CUserPage
  240. //*****************************************************************************
  241. //
  242. //
  243. //
  244. CUserPage::~CUserPage()
  245. {
  246. RemoveAllUsers();
  247. delete m_pUserArray;
  248. }
  249. //
  250. //
  251. //
  252. void CUserPage::RemoveAllUsers()
  253. {
  254. if (m_pUserArray)
  255. {
  256. INT c = m_pUserArray->GetSize();
  257. while (c)
  258. {
  259. delete (CUserInfo *) (m_pUserArray->GetAt(c - 1));
  260. c--;
  261. }
  262. }
  263. }
  264. /*++ CUserPage::UpdateUserListview
  265. Class Description:
  266. Walks the listview and checks to see if each line in the
  267. listview matches the corresponding entry in our process
  268. array. Those which differe by HWND are replaced, and those
  269. that need updating are updated.
  270. Items are also added and removed to/from the tail of the
  271. listview as required.
  272. Arguments:
  273. Return Value:
  274. HRESULT
  275. Revision History:
  276. Nov-29-95 BradG Created
  277. --*/
  278. HRESULT CUserPage::UpdateUserListview()
  279. {
  280. HWND hListView = GetDlgItem(m_hPage, IDC_USERLIST);
  281. //
  282. // Stop repaints while we party on the listview
  283. //
  284. SendMessage(hListView, WM_SETREDRAW, FALSE, 0);
  285. INT cListViewItems = ListView_GetItemCount(hListView);
  286. INT CUserArrayItems = m_pUserArray->GetSize();
  287. //
  288. // Walk the existing lines in the listview and replace/update
  289. // them as needed
  290. //
  291. for (INT iCurrent = 0;
  292. iCurrent < cListViewItems && iCurrent < CUserArrayItems;
  293. iCurrent++)
  294. {
  295. LV_ITEM lvitem = { 0 };
  296. WCHAR szDisplayName[ USERNAME_LENGTH + 1 + DOMAIN_LENGTH + 1 ];
  297. lvitem.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
  298. lvitem.iItem = iCurrent;
  299. if (FALSE == ListView_GetItem(hListView, &lvitem))
  300. {
  301. SendMessage(hListView, WM_SETREDRAW, TRUE, 0);
  302. return E_FAIL;
  303. }
  304. CUserInfo * pTmp = (CUserInfo *) lvitem.lParam;
  305. CUserInfo * pUser = (CUserInfo *) m_pUserArray->GetAt(iCurrent);
  306. if (pTmp != pUser || pUser->m_fDirty)
  307. {
  308. // If the objects aren't the same, we need to replace this line
  309. if (g_Options.m_fShowDomainNames)
  310. {
  311. // UI only - don't care if it gets truncated.
  312. StringCchCopy( szDisplayName, ARRAYSIZE(szDisplayName), pUser->m_szDomainName );
  313. StringCchCat( szDisplayName, ARRAYSIZE(szDisplayName), L"\\" );
  314. }
  315. else
  316. {
  317. szDisplayName[ 0 ] = L'\0';
  318. }
  319. // UI only - don't care if it gets truncated.
  320. StringCchCat( szDisplayName, ARRAYSIZE(szDisplayName), pUser->m_szUserName );
  321. lvitem.pszText = szDisplayName;
  322. lvitem.lParam = (LPARAM) pUser;
  323. if (g_dwMySessionId == pUser->m_dwSessionId)
  324. {
  325. lvitem.iImage = m_iCurrentUserIcon;
  326. }
  327. else
  328. {
  329. lvitem.iImage = m_iUserIcon;
  330. }
  331. ListView_SetItem(hListView, &lvitem);
  332. ListView_RedrawItems(hListView, iCurrent, iCurrent);
  333. pUser->m_fDirty = 0;
  334. }
  335. }
  336. //
  337. // We've either run out of listview items or run out of User array
  338. // entries, so remove/add to the listview as appropriate
  339. //
  340. while (iCurrent < cListViewItems)
  341. {
  342. // Extra items in the listview (processes gone away), so remove them
  343. ListView_DeleteItem(hListView, iCurrent);
  344. cListViewItems--;
  345. }
  346. while (iCurrent < CUserArrayItems)
  347. {
  348. // Need to add new items to the listview (new user appeared)
  349. CUserInfo * pUser = (CUserInfo *)m_pUserArray->GetAt(iCurrent);
  350. LV_ITEM lvitem = { 0 };
  351. WCHAR szDisplayName[ USERNAME_LENGTH + 1 + DOMAIN_LENGTH + 1 ];
  352. lvitem.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
  353. lvitem.iItem = iCurrent;
  354. lvitem.lParam = (LPARAM) pUser;
  355. if (g_Options.m_fShowDomainNames)
  356. {
  357. // UI only - don't care if it gets truncated.
  358. StringCchCopy( szDisplayName, ARRAYSIZE(szDisplayName), pUser->m_szDomainName );
  359. StringCchCat( szDisplayName, ARRAYSIZE(szDisplayName), L"\\" );
  360. }
  361. else
  362. {
  363. szDisplayName[0] = L'\0';
  364. }
  365. // UI only - don't care if it gets truncated.
  366. StringCchCat( szDisplayName, ARRAYSIZE(szDisplayName), pUser->m_szUserName );
  367. lvitem.pszText = szDisplayName;
  368. if (g_dwMySessionId == pUser->m_dwSessionId)
  369. {
  370. lvitem.iImage = m_iCurrentUserIcon;
  371. }
  372. else
  373. {
  374. lvitem.iImage = m_iUserIcon;
  375. }
  376. // The first item added (actually, every 0 to 1 count transition) gets
  377. // selected and focused
  378. if (iCurrent == 0)
  379. {
  380. lvitem.state = LVIS_SELECTED | LVIS_FOCUSED;
  381. lvitem.stateMask = lvitem.state;
  382. lvitem.mask |= LVIF_STATE;
  383. }
  384. ListView_InsertItem(hListView, &lvitem);
  385. pUser->m_fDirty = 0;
  386. iCurrent++;
  387. }
  388. //
  389. // Let the listview paint again
  390. //
  391. SendMessage(hListView, WM_SETREDRAW, TRUE, 0);
  392. return S_OK;
  393. }
  394. /*++ CUserPage::GetSelectedUsers
  395. Routine Description:
  396. Returns a CPtrArray of the selected tasks
  397. Arguments:
  398. Return Value:
  399. CPtrArray on success, NULL on failure
  400. Revision History:
  401. Dec-01-95 BradG Created
  402. --*/
  403. CPtrArray * CUserPage::GetSelectedUsers()
  404. {
  405. BOOL fSuccess = TRUE;
  406. //
  407. // Get the count of selected items
  408. //
  409. HWND hUserList = GetDlgItem(m_hPage, IDC_USERLIST);
  410. INT cItems = ListView_GetSelectedCount(hUserList);
  411. if (0 == cItems)
  412. {
  413. return NULL;
  414. }
  415. //
  416. // Create a CPtrArray to hold the task items
  417. //
  418. CPtrArray * pArray = new CPtrArray(GetProcessHeap());
  419. if (NULL == pArray)
  420. {
  421. return NULL;
  422. }
  423. INT iLast = -1;
  424. for (INT i = 0; i < cItems; i++)
  425. {
  426. //
  427. // Get the Nth selected item
  428. //
  429. INT iItem = ListView_GetNextItem(hUserList, iLast, LVNI_SELECTED);
  430. if (-1 == iItem)
  431. {
  432. fSuccess = FALSE;
  433. break;
  434. }
  435. iLast = iItem;
  436. //
  437. // Pull the item from the listview and add it to the selected array
  438. //
  439. LV_ITEM lvitem = { LVIF_PARAM };
  440. lvitem.iItem = iItem;
  441. if (ListView_GetItem(hUserList, &lvitem))
  442. {
  443. LPVOID pUser = (LPVOID) (lvitem.lParam);
  444. if (FALSE == pArray->Add(pUser))
  445. {
  446. fSuccess = FALSE;
  447. break;
  448. }
  449. }
  450. else
  451. {
  452. fSuccess = FALSE;
  453. break;
  454. }
  455. }
  456. //
  457. // Any errors, clean up the array and bail. We don't release the
  458. // tasks in the array, since they are owned by the listview.
  459. //
  460. if (FALSE == fSuccess && NULL != pArray)
  461. {
  462. delete pArray;
  463. return NULL;
  464. }
  465. return pArray;
  466. }
  467. /*++ CProcPage::HandleUserListContextMenu
  468. Routine Description:
  469. Handles right-clicks (context menu) in the task list
  470. Arguments:
  471. xPos, yPos - coords of where the click occurred
  472. Return Value:
  473. Revision History:
  474. Dec-01-95 BradG Created
  475. --*/
  476. void CUserPage::HandleUserListContextMenu(INT xPos, INT yPos)
  477. {
  478. HWND hUserList = GetDlgItem(m_hPage, IDC_USERLIST);
  479. CPtrArray * pArray = GetSelectedUsers();
  480. if (pArray)
  481. {
  482. // If non-mouse-based context menu, use the currently selected
  483. // item as the coords
  484. if (0xFFFF == LOWORD(xPos) && 0xFFFF == LOWORD(yPos))
  485. {
  486. int iSel = ListView_GetNextItem(hUserList, -1, LVNI_SELECTED);
  487. RECT rcItem;
  488. ListView_GetItemRect(hUserList, iSel, &rcItem, LVIR_ICON);
  489. MapWindowRect(hUserList, NULL, &rcItem);
  490. xPos = rcItem.right;
  491. yPos = rcItem.bottom;
  492. }
  493. HMENU hPopup = LoadPopupMenu(g_hInstance, IDR_USER_CONTEXT);
  494. if (hPopup)
  495. {
  496. SetMenuDefaultItem(hPopup, IDM_SENDMESSAGE, FALSE);
  497. //
  498. //If our current session is a console session,
  499. //we cannot do remote control
  500. //
  501. if(IsActiveConsoleSession())
  502. {
  503. EnableMenuItem(hPopup, IDM_REMOTECONTROL, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  504. }
  505. //
  506. // If multiple-selection, disable the items that require single
  507. // selections to make sense
  508. //
  509. if (pArray->GetSize() > 1)
  510. {
  511. EnableMenuItem(hPopup, IDM_REMOTECONTROL, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  512. EnableMenuItem(hPopup, IDM_CONNECT, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  513. }
  514. //
  515. // See if we have our own session selected
  516. //
  517. for (int i = 0; i < pArray->GetSize(); i++)
  518. {
  519. CUserInfo * pUser = (CUserInfo *) pArray->GetAt(i);
  520. if (g_dwMySessionId == pUser->m_dwSessionId)
  521. {
  522. //
  523. // The current session is in the list
  524. //
  525. EnableMenuItem(hPopup, IDM_REMOTECONTROL, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  526. EnableMenuItem(hPopup, IDM_CONNECT, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  527. if (SHRestricted(REST_NODISCONNECT))
  528. {
  529. EnableMenuItem(hPopup, IDM_DISCONNECT, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  530. }
  531. if (pArray->GetSize() == 1)
  532. {
  533. //
  534. // My session is the only one selected
  535. //
  536. EnableMenuItem(hPopup, IDM_SENDMESSAGE, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  537. }
  538. }
  539. if (pUser->m_wtsState == WTSDisconnected)
  540. {
  541. // EnableMenuItem(hPopup, IDM_REMOTECONTROL, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  542. EnableMenuItem(hPopup, IDM_DISCONNECT, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  543. }
  544. }
  545. Pause();
  546. g_fInPopup = TRUE;
  547. TrackPopupMenuEx(hPopup, 0, xPos, yPos, m_hPage, NULL);
  548. g_fInPopup = FALSE;
  549. // Note that we don't "unpause" until one of the menu commands (incl CANCEL) is
  550. // selected or the menu is dismissed
  551. DestroyMenu(hPopup);
  552. }
  553. delete pArray;
  554. }
  555. else
  556. {
  557. // Nothing is selected
  558. }
  559. }
  560. /*++ CUserPage::UpdateUIState
  561. Routine Description:
  562. Updates the enabled/disabled states, etc., of the task UI
  563. Arguments:
  564. Return Value:
  565. Revision History:
  566. Dec-04-95 BradG Created
  567. --*/
  568. // Controls which are enabled only for any selection
  569. static const UINT g_aUserSingleIDs[] =
  570. {
  571. IDM_DISCONNECT,
  572. IDM_LOGOFF,
  573. IDM_SENDMESSAGE
  574. };
  575. void CUserPage::UpdateUIState()
  576. {
  577. INT i;
  578. //
  579. // Set the state for controls which require a selection (1 or more items)
  580. //
  581. for (i = 0; i < ARRAYSIZE(g_aUserSingleIDs); i++)
  582. {
  583. EnableWindow(GetDlgItem(m_hPage, g_aUserSingleIDs[i]), m_cSelected > 0);
  584. }
  585. CPtrArray * pArray = GetSelectedUsers();
  586. if (pArray)
  587. {
  588. //
  589. // See if we have our own session selected
  590. //
  591. for (int i = 0; i < pArray->GetSize(); i++)
  592. {
  593. CUserInfo * pUser = (CUserInfo *) pArray->GetAt(i);
  594. if (g_dwMySessionId == pUser->m_dwSessionId)
  595. {
  596. if (SHRestricted(REST_NODISCONNECT))
  597. {
  598. EnableWindow(GetDlgItem(m_hPage, IDM_DISCONNECT), FALSE);
  599. }
  600. if (pArray->GetSize() == 1)
  601. {
  602. //
  603. // My session is the only one selected
  604. //
  605. EnableWindow(GetDlgItem(m_hPage, IDM_SENDMESSAGE), FALSE);
  606. }
  607. }
  608. if (pUser->m_wtsState == WTSDisconnected)
  609. {
  610. EnableWindow(GetDlgItem(m_hPage, IDM_DISCONNECT), FALSE);
  611. }
  612. }
  613. delete pArray;
  614. }
  615. }
  616. /*++ CUserPage::HandleUserPageNotify
  617. Routine Description:
  618. Processes WM_NOTIFY messages received by the taskpage dialog
  619. Arguments:
  620. hWnd - Control that generated the WM_NOTIFY
  621. pnmhdr - Ptr to the NMHDR notification stucture
  622. Return Value:
  623. BOOL "did we handle it" code
  624. Revision History:
  625. Nov-29-95 BradG Created
  626. --*/
  627. INT CUserPage::HandleUserPageNotify(LPNMHDR pnmhdr)
  628. {
  629. switch(pnmhdr->code)
  630. {
  631. case LVN_ITEMCHANGED:
  632. {
  633. //
  634. // If the (selection) state of an item is changing, see if
  635. // the count has changed, and if so, update the UI
  636. //
  637. const NM_LISTVIEW * pnmv = (const NM_LISTVIEW *) pnmhdr;
  638. if (pnmv->uChanged & LVIF_STATE)
  639. {
  640. UINT cSelected = ListView_GetSelectedCount(GetDlgItem(m_hPage, IDC_USERLIST));
  641. if (cSelected != m_cSelected)
  642. {
  643. m_cSelected = cSelected;
  644. UpdateUIState();
  645. }
  646. }
  647. }
  648. break;
  649. case LVN_COLUMNCLICK:
  650. {
  651. // User clicked a header control, so set the sort column. If its the
  652. // same as the current sort column, just invert the sort direction in
  653. // the column. Then resort the task array
  654. const NM_LISTVIEW * pnmv = (const NM_LISTVIEW *) pnmhdr;
  655. LV_COLUMN lvcolumn;
  656. lvcolumn.mask = LVCF_SUBITEM;
  657. if(!ListView_GetColumn(GetDlgItem(m_hPage, IDC_USERLIST), pnmv->iSubItem, &lvcolumn))
  658. {
  659. break;
  660. }
  661. if (CUserInfo::GetUserSortColumnID() == lvcolumn.iSubItem)
  662. {
  663. CUserInfo::SwitchUserSortDirection();
  664. }
  665. else
  666. {
  667. CUserInfo::SetUserSortColumnID((USERCOLUMNID)lvcolumn.iSubItem);
  668. }
  669. ResortUserArray(&m_pUserArray);
  670. TimerEvent();
  671. }
  672. break;
  673. case LVN_GETDISPINFO:
  674. {
  675. LV_ITEM * plvitem = &(((LV_DISPINFO *) pnmhdr)->item);
  676. //
  677. // Listview needs a text string
  678. //
  679. if (plvitem->mask & LVIF_TEXT)
  680. {
  681. LV_COLUMN lvcolumn;
  682. lvcolumn.mask = LVCF_SUBITEM;
  683. if(!ListView_GetColumn(GetDlgItem(m_hPage, IDC_USERLIST),
  684. plvitem->iSubItem, &lvcolumn))
  685. {
  686. break;
  687. }
  688. USERCOLUMNID columnid = (USERCOLUMNID) lvcolumn.iSubItem;
  689. const CUserInfo * pUserInfo = (const CUserInfo *) plvitem->lParam;
  690. switch(columnid)
  691. {
  692. case USR_COL_USERSNAME:
  693. plvitem->mask |= LVIF_DI_SETITEM;
  694. if (g_Options.m_fShowDomainNames)
  695. {
  696. // UI only - don't care if it gets truncated
  697. StringCchCopy(plvitem->pszText, plvitem->cchTextMax, pUserInfo->m_szDomainName );
  698. StringCchCat(plvitem->pszText, plvitem->cchTextMax, L"\\" );
  699. StringCchCat(plvitem->pszText, plvitem->cchTextMax, pUserInfo->m_szUserName );
  700. }
  701. else
  702. {
  703. // UI only - don't care if it gets truncated
  704. StringCchCopy(plvitem->pszText, plvitem->cchTextMax, pUserInfo->m_szUserName );
  705. }
  706. break;
  707. case USR_COL_USERSESSION_ID:
  708. // UI only - don't care if it gets truncated
  709. StringCchPrintf(plvitem->pszText, plvitem->cchTextMax, L"%d", (ULONG) (pUserInfo->m_dwSessionId));
  710. break;
  711. case USR_COL_SESSION_STATUS:
  712. if ( NULL != g_pszStatString[ g_aWTSStateToString[ pUserInfo->m_wtsState ] ] )
  713. {
  714. // UI only - don't care if it gets truncated
  715. StringCchCopy( plvitem->pszText
  716. , plvitem->cchTextMax
  717. , g_pszStatString[ g_aWTSStateToString[ pUserInfo->m_wtsState ] ]
  718. );
  719. }
  720. else
  721. {
  722. // UI only - don't care if it gets truncated
  723. StringCchCopy( plvitem->pszText, plvitem->cchTextMax, L"" );
  724. }
  725. break;
  726. case USR_COL_CLIENT_NAME:
  727. if ( pUserInfo->m_wtsState != WTSDisconnected )
  728. {
  729. // UI only - don't care if it gets truncated
  730. StringCchCopy( plvitem->pszText, plvitem->cchTextMax, pUserInfo->m_szClientName );
  731. }
  732. else
  733. {
  734. // UI only - don't care if it gets truncated
  735. StringCchCopy( plvitem->pszText, plvitem->cchTextMax, L"" );
  736. }
  737. break;
  738. case USR_COL_WINSTA_NAME:
  739. if ( NULL != pUserInfo->m_pszWinStaName )
  740. {
  741. // UI only - don't care if it gets truncated
  742. StringCchCopy( plvitem->pszText, plvitem->cchTextMax, pUserInfo->m_pszWinStaName );
  743. }
  744. else
  745. {
  746. // UI only - don't care if it gets truncated
  747. StringCchCopy( plvitem->pszText, plvitem->cchTextMax, L"" );
  748. }
  749. break;
  750. default:
  751. Assert( 0 && "Unknown listview subitem" );
  752. break;
  753. } // end switch(columnid)
  754. } // end LVIF_TEXT case
  755. } // end LVN_GETDISPINFO case
  756. break;
  757. } // end switch(pnmhdr->code)
  758. return 1;
  759. }
  760. /*++ CUserPage::TimerEvent
  761. Routine Description:
  762. Called by main app when the update time fires. Walks every window
  763. in the system (on every desktop, in every windowstation) and adds
  764. or updates it in the task array, then removes any stale processes,
  765. and filters the results into the listview
  766. Arguments:
  767. Return Value:
  768. Revision History:
  769. Nov-29-95 BradG Created
  770. --*/
  771. VOID CUserPage::TimerEvent()
  772. {
  773. //
  774. // If this page is paused (ie: it has a context menu up, etc), we do
  775. // nothing
  776. //
  777. if (m_fPaused)
  778. {
  779. return;
  780. }
  781. static LARGE_INTEGER uPassCount = {0, 0};
  782. //
  783. // TODO: BRADG
  784. // This should be done on a separate thread
  785. //
  786. PWTS_SESSION_INFO pSession;
  787. DWORD nSessions;
  788. DWORD dwSize;
  789. LPTSTR pszClientName;
  790. LPTSTR pszUserName;
  791. LPTSTR pszDomainName;
  792. HRESULT hr;
  793. BOOL b;
  794. BOOL bDelete;
  795. INT i;
  796. DWORD j;
  797. CUserInfo *pNewUser;
  798. b = WTSEnumerateSessions(
  799. WTS_CURRENT_SERVER_HANDLE,
  800. 0,
  801. 1,
  802. &pSession,
  803. &nSessions);
  804. if ( b )
  805. {
  806. i = 0;
  807. while (i < m_pUserArray->GetSize())
  808. {
  809. CUserInfo * pUserInfo = (CUserInfo *)(m_pUserArray->GetAt(i));
  810. ASSERT(pUserInfo);
  811. //
  812. // See if this item has a matching session. If so, update it.
  813. //
  814. bDelete = FALSE;
  815. for (j = 0; j < nSessions; j++)
  816. {
  817. if (pUserInfo->m_dwSessionId == pSession[j].SessionId)
  818. {
  819. break;
  820. }
  821. }
  822. if (j < nSessions)
  823. {
  824. //
  825. // This session is still alive. See what it's doing
  826. //
  827. switch( pSession[j].State )
  828. {
  829. case WTSActive:
  830. case WTSDisconnected:
  831. case WTSShadow:
  832. //
  833. //If we log off user from disconnected session 0
  834. //it does not change its state but remains disconnected
  835. //we must not display disconnected session 0 if nobody
  836. //is logged on to it.
  837. //
  838. b = WTSQuerySessionInformation(
  839. WTS_CURRENT_SERVER_HANDLE,
  840. pSession[j].SessionId,
  841. WTSUserName,
  842. &pszUserName,
  843. &dwSize
  844. );
  845. if (!b || pszUserName == NULL)
  846. {
  847. bDelete = TRUE;
  848. pSession[j].State = WTSIdle; //see "MAJOR HACK" below
  849. break;
  850. }
  851. //
  852. //pszUserName[0] == 0 - means that nobody is logged on to session 0.
  853. //It might also happen, though very unlikely, that session number got
  854. //reused by some other user while taskmgr was busy doing something.
  855. //In this case we'll treat this session as a new one
  856. //
  857. if(lstrcmp(pUserInfo->m_szUserName,pszUserName))
  858. {
  859. bDelete = TRUE;
  860. if(pszUserName[0] == 0)
  861. {
  862. pSession[j].State = WTSIdle; //see "MAJOR HACK" below
  863. }
  864. }
  865. WTSFreeMemory(pszUserName);
  866. pszUserName = NULL;
  867. if(bDelete)
  868. {
  869. break;
  870. }
  871. //
  872. // It's still doing something interesting, so go and
  873. // update the item's status
  874. //
  875. pszClientName = NULL;
  876. b = WTSQuerySessionInformation(
  877. WTS_CURRENT_SERVER_HANDLE,
  878. pSession[j].SessionId,
  879. WTSClientName,
  880. &pszClientName,
  881. &dwSize
  882. );
  883. hr = pUserInfo->SetData(
  884. (pszClientName == NULL) ? TEXT("") : pszClientName,
  885. pSession[j].pWinStationName,
  886. pSession[j].State,
  887. g_Options.m_fShowDomainNames,
  888. uPassCount,
  889. TRUE
  890. );
  891. if (pszClientName)
  892. {
  893. // Free the ClientName buffer
  894. WTSFreeMemory(pszClientName);
  895. pszClientName = NULL;
  896. }
  897. //
  898. // MAJOR HACK -- Set the State to WTSIdle so we skip it
  899. // when we check for new sessions.
  900. //
  901. pSession[j].State = WTSIdle;
  902. break;
  903. default:
  904. //
  905. // It's no longer in a state we care about, delete it.
  906. //
  907. bDelete = TRUE;
  908. break;
  909. }
  910. }
  911. else
  912. {
  913. //
  914. // The list item doesn't have any matching information, so this means
  915. // that the user probably has logged off. Delete it.
  916. //
  917. bDelete = TRUE;
  918. }
  919. if (bDelete)
  920. {
  921. //
  922. // This item needs to be deleted from the list.
  923. //
  924. delete pUserInfo;
  925. m_pUserArray->RemoveAt(i, 1);
  926. //
  927. // Loop back without incrementing i.
  928. //
  929. continue;
  930. }
  931. i++;
  932. }
  933. //
  934. // Now that we updated all entries in the m_pUserArray, we need double
  935. // check the session data to see if we have any new sessions. See the
  936. // MAJOR HACK comment above. We change the state of all updated
  937. // sessions to WTSIdle so we skip them in the loop below.
  938. //
  939. for (j = 0; j < nSessions; j++)
  940. {
  941. switch( pSession[j].State )
  942. {
  943. case WTSActive:
  944. case WTSDisconnected:
  945. case WTSShadow:
  946. //
  947. // OK, we've discovered a NEW session that's in a
  948. // state that we care about. Add it to the list.
  949. //
  950. pNewUser = new CUserInfo;
  951. if (pNewUser == NULL)
  952. {
  953. // Not much we can do here.
  954. break;
  955. }
  956. pNewUser->m_dwSessionId = pSession[j].SessionId;
  957. //
  958. // TODO: BRADG
  959. // See about writing this as a loop
  960. //
  961. //
  962. // Query all the cool info about the session.
  963. //
  964. b = WTSQuerySessionInformation(
  965. WTS_CURRENT_SERVER_HANDLE,
  966. pSession[j].SessionId,
  967. WTSClientName,
  968. &pszClientName,
  969. &dwSize
  970. );
  971. if (!b)
  972. {
  973. delete pNewUser;
  974. break;
  975. }
  976. hr = pNewUser->SetData(
  977. (pszClientName == NULL) ? TEXT("") : pszClientName,
  978. pSession[j].pWinStationName,
  979. pSession[j].State,
  980. g_Options.m_fShowDomainNames,
  981. uPassCount,
  982. FALSE
  983. );
  984. if (pszClientName != NULL)
  985. {
  986. WTSFreeMemory(pszClientName);
  987. pszClientName = NULL;
  988. }
  989. if (FAILED(hr))
  990. {
  991. delete pNewUser;
  992. break;
  993. }
  994. b = WTSQuerySessionInformation(
  995. WTS_CURRENT_SERVER_HANDLE,
  996. pSession[j].SessionId,
  997. WTSUserName,
  998. &pszUserName,
  999. &dwSize
  1000. );
  1001. if (!b || pszUserName == NULL)
  1002. {
  1003. delete pNewUser;
  1004. break;
  1005. }
  1006. //
  1007. //This is case of disconnected session 0
  1008. //when nobody is logged on.
  1009. //
  1010. if(pszUserName[0] == 0)
  1011. {
  1012. WTSFreeMemory(pszUserName);
  1013. pszUserName = NULL;
  1014. delete pNewUser;
  1015. break;
  1016. }
  1017. StringCchCopy( pNewUser->m_szUserName, ARRAYSIZE(pNewUser->m_szUserName), pszUserName );
  1018. WTSFreeMemory(pszUserName);
  1019. pszUserName = NULL;
  1020. b = WTSQuerySessionInformation(
  1021. WTS_CURRENT_SERVER_HANDLE,
  1022. pSession[j].SessionId,
  1023. WTSDomainName,
  1024. &pszDomainName,
  1025. &dwSize
  1026. );
  1027. if (!b || pszDomainName == NULL)
  1028. {
  1029. delete pNewUser;
  1030. break;
  1031. }
  1032. StringCchCopy( pNewUser->m_szDomainName, ARRAYSIZE(pNewUser->m_szDomainName), pszDomainName );
  1033. WTSFreeMemory(pszDomainName);
  1034. pszDomainName = NULL;
  1035. pNewUser->m_fDirty = 1;
  1036. // All went well, so add it to the array
  1037. if (!(m_pUserArray->Add( (LPVOID) pNewUser)))
  1038. {
  1039. delete pNewUser;
  1040. }
  1041. break;
  1042. default:
  1043. // Don't care about this one.
  1044. break;
  1045. }
  1046. }
  1047. //
  1048. // Free up the memory allocated on the WTSEnumerateSessions call
  1049. //
  1050. WTSFreeMemory( pSession );
  1051. pSession = NULL;
  1052. }
  1053. UpdateUserListview();
  1054. uPassCount.QuadPart++;
  1055. }
  1056. /*++ class CUserInfo::SetData
  1057. Class Description:
  1058. Updates (or initializes) the info about a running task
  1059. Arguments:
  1060. uPassCount- Current passcount, used to timestamp the last update of
  1061. this object
  1062. fUpdate - only worry about information that can change during a
  1063. task's lifetime
  1064. Return Value:
  1065. HRESULT
  1066. Revision History:
  1067. Nov-16-95 BradG Created
  1068. --*/
  1069. HRESULT CUserInfo::SetData(
  1070. LPTSTR lpszClientName,
  1071. LPTSTR lpszWinStaName,
  1072. WTS_CONNECTSTATE_CLASS wtsState,
  1073. BOOL fShowDomainName,
  1074. LARGE_INTEGER uPassCount,
  1075. BOOL fUpdateOnly)
  1076. {
  1077. HRESULT hr = S_OK;
  1078. m_uPassCount.QuadPart = uPassCount.QuadPart;
  1079. //
  1080. // For each of the fields, we check to see if anything has changed, and if
  1081. // so, we mark that particular column as having changed, and update the value.
  1082. // This allows me to opimize which fields of the listview to repaint, since
  1083. // repainting an entire listview column causes flicker and looks bad in
  1084. // general
  1085. //
  1086. //
  1087. // WinStation
  1088. //
  1089. if (!fUpdateOnly || lstrcmp(m_pszWinStaName, lpszWinStaName))
  1090. {
  1091. LPTSTR pszOld = m_pszWinStaName;
  1092. DWORD cchLen = lstrlen(lpszWinStaName) + 1;
  1093. m_pszWinStaName = (LPTSTR) LocalAlloc( 0, cchLen * sizeof(*m_pszWinStaName) );
  1094. if (NULL == m_pszWinStaName)
  1095. {
  1096. m_pszWinStaName = pszOld;
  1097. hr = E_OUTOFMEMORY;
  1098. }
  1099. else
  1100. {
  1101. // UI only - don't care if it gets truncated
  1102. StringCchCopy( m_pszWinStaName, cchLen, lpszWinStaName );
  1103. m_fDirty_COL_WINSTANAME = TRUE;
  1104. if ( NULL != pszOld )
  1105. {
  1106. LocalFree(pszOld);
  1107. }
  1108. }
  1109. }
  1110. //
  1111. // Client Name
  1112. //
  1113. if ( NULL != lpszClientName && lstrcmp(m_szClientName, lpszClientName) )
  1114. {
  1115. // UI only - don't care if it gets truncated
  1116. StringCchCopy(m_szClientName, ARRAYSIZE(m_szClientName), lpszClientName);
  1117. m_fDirty_COL_CLIENTNAME = TRUE;
  1118. }
  1119. //
  1120. // Session Status
  1121. //
  1122. if (wtsState != m_wtsState) {
  1123. m_wtsState = wtsState;
  1124. m_fDirty_COL_SESSIONSTATUS = TRUE;
  1125. }
  1126. //
  1127. // Domain Name Status
  1128. //
  1129. if (fShowDomainName != m_fShowDomainName)
  1130. {
  1131. m_fShowDomainName = fShowDomainName;
  1132. m_fDirty_COL_USERNAME = TRUE;
  1133. }
  1134. return hr;
  1135. }
  1136. /*++ CUserPage::SizeUserPage
  1137. Routine Description:
  1138. Sizes its children based on the size of the
  1139. tab control on which it appears.
  1140. Arguments:
  1141. Return Value:
  1142. Revision History:
  1143. Nov-29-95 BradG Created
  1144. --*/
  1145. static const INT aUserControls[] =
  1146. {
  1147. IDM_DISCONNECT,
  1148. IDM_LOGOFF,
  1149. IDM_SENDMESSAGE
  1150. };
  1151. void CUserPage::SizeUserPage()
  1152. {
  1153. //
  1154. // Get the coords of the outer dialog
  1155. //
  1156. RECT rcParent;
  1157. GetClientRect(m_hPage, &rcParent);
  1158. HDWP hdwp = BeginDeferWindowPos( 1 + ARRAYSIZE(aUserControls) );
  1159. //
  1160. // Calc the deltas in the x and y positions that we need to
  1161. // move each of the child controls
  1162. //
  1163. RECT rcMaster;
  1164. HWND hwndMaster = GetDlgItem(m_hPage, IDM_SENDMESSAGE);
  1165. GetWindowRect(hwndMaster, &rcMaster);
  1166. MapWindowPoints(HWND_DESKTOP, m_hPage, (LPPOINT) &rcMaster, 2);
  1167. INT dx = ((rcParent.right - g_DefSpacing * 2) - rcMaster.right);
  1168. INT dy = ((rcParent.bottom - g_DefSpacing * 2) - rcMaster.bottom);
  1169. //
  1170. // Size the listbox
  1171. //
  1172. HWND hwndListbox = GetDlgItem(m_hPage, IDC_USERLIST);
  1173. RECT rcListbox;
  1174. GetWindowRect(hwndListbox, &rcListbox);
  1175. MapWindowPoints(HWND_DESKTOP, m_hPage, (LPPOINT) &rcListbox, 2);
  1176. INT lbX = rcMaster.right - rcListbox.left + dx;
  1177. INT lbY = rcMaster.top - rcListbox.top + dy - g_DefSpacing;
  1178. DeferWindowPos(hdwp, hwndListbox, NULL,
  1179. 0, 0,
  1180. lbX,
  1181. lbY,
  1182. SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  1183. //
  1184. // Move each of the child controls by the above delta
  1185. //
  1186. for (int i = 0; i < ARRAYSIZE(aUserControls); i++)
  1187. {
  1188. HWND hwndCtrl = GetDlgItem(m_hPage, aUserControls[i]);
  1189. RECT rcCtrl;
  1190. GetWindowRect(hwndCtrl, &rcCtrl);
  1191. MapWindowPoints(HWND_DESKTOP, m_hPage, (LPPOINT) &rcCtrl, 2);
  1192. DeferWindowPos(hdwp, hwndCtrl, NULL,
  1193. rcCtrl.left + dx,
  1194. rcCtrl.top + dy,
  1195. 0, 0,
  1196. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  1197. }
  1198. EndDeferWindowPos(hdwp);
  1199. }
  1200. /*++ CUserPage::HandleWMCOMMAND
  1201. Routine Description:
  1202. Handles WM_COMMANDS received at the main page dialog
  1203. Arguments:
  1204. id - Command id of command received
  1205. Return Value:
  1206. Revision History:
  1207. Dec-01-95 BradG Created
  1208. --*/
  1209. void CUserPage::HandleWMCOMMAND(INT id)
  1210. {
  1211. int iResult;
  1212. INT i;
  1213. INT iFinalOne = -1;
  1214. BOOL bFinalOne = FALSE;
  1215. BOOL bNeedToRefresh = FALSE;
  1216. DWORD dwSize;
  1217. LPTSTR psz;
  1218. WCHAR szCaption[ MAX_PATH ];
  1219. WCHAR szText1[ MAX_PATH * 2 ];
  1220. WCHAR szText2[ MAX_PATH * 2 ];
  1221. LoadString(g_hInstance, IDS_APPTITLE, szCaption, MAX_PATH);
  1222. CPtrArray * pArray = GetSelectedUsers();
  1223. if (!pArray)
  1224. {
  1225. //Assert( 0 && "WM_COMMAND but nothing selected" );
  1226. goto done;
  1227. }
  1228. if (id == IDM_SENDMESSAGE)
  1229. {
  1230. CSendMessageDlg SMDlg;
  1231. if(SMDlg.DoDialog(m_hwndTabs)!=IDOK)
  1232. {
  1233. goto done;
  1234. }
  1235. // UI - only - don't care if it gets truncated
  1236. StringCchCopy( szText1, ARRAYSIZE(szText1), SMDlg.GetTitle() );
  1237. StringCchCopy( szText2, ARRAYSIZE(szText2), SMDlg.GetMessage() );
  1238. }
  1239. else if (id == IDM_LOGOFF || id == IDM_DISCONNECT)
  1240. {
  1241. //
  1242. // Verify this is what the user wants done
  1243. //
  1244. LoadString(
  1245. g_hInstance,
  1246. (id == IDM_LOGOFF) ? IDS_WARN_LOGOFF : IDS_WARN_DISCONNECT,
  1247. szText1,
  1248. ARRAYSIZE(szText1)
  1249. );
  1250. iResult = MessageBox(m_hwndTabs, szText1, szCaption, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION);
  1251. if (iResult == IDNO)
  1252. goto done;
  1253. }
  1254. BOOL b;
  1255. for( i = 0; i < pArray->GetSize(); i++ )
  1256. {
  1257. finalretry:
  1258. CUserInfo * pUser = (CUserInfo *) pArray->GetAt(i);
  1259. if (pUser == NULL)
  1260. {
  1261. Assert(0);
  1262. if (bFinalOne)
  1263. {
  1264. break; // Bail out, nothing left to process
  1265. }
  1266. else
  1267. {
  1268. continue; // try the next one
  1269. }
  1270. }
  1271. retry:
  1272. b = TRUE;
  1273. switch(id)
  1274. {
  1275. case IDM_SENDMESSAGE:
  1276. b = WTSSendMessage(
  1277. WTS_CURRENT_SERVER_HANDLE,
  1278. pUser->m_dwSessionId,
  1279. szText1,
  1280. lstrlen(szText1) * sizeof(WCHAR),
  1281. szText2,
  1282. lstrlen(szText2) * sizeof(WCHAR),
  1283. MB_OK | MB_TOPMOST | MB_ICONINFORMATION,
  1284. 0, // ignored
  1285. &dwSize, // ignored, but it won't accept a NULL
  1286. FALSE
  1287. );
  1288. break;
  1289. case IDM_DISCONNECT:
  1290. if (g_dwMySessionId == pUser->m_dwSessionId && !bFinalOne)
  1291. {
  1292. // I don't want to kill of myself before everything else is complete,
  1293. // so I'll set a flag and skip myself for now.
  1294. iFinalOne = i;
  1295. continue;
  1296. }
  1297. b = WTSDisconnectSession(
  1298. WTS_CURRENT_SERVER_HANDLE,
  1299. pUser->m_dwSessionId,
  1300. FALSE
  1301. );
  1302. if (b)
  1303. {
  1304. bNeedToRefresh = TRUE;
  1305. }
  1306. break;
  1307. case IDM_LOGOFF:
  1308. if (g_dwMySessionId == pUser->m_dwSessionId && !bFinalOne)
  1309. {
  1310. // I don't want to kill of myself before everything else is complete,
  1311. // so I'll set a flag and skip myself for now.
  1312. iFinalOne = i;
  1313. continue;
  1314. }
  1315. b = WTSLogoffSession(
  1316. WTS_CURRENT_SERVER_HANDLE,
  1317. pUser->m_dwSessionId,
  1318. FALSE
  1319. );
  1320. if (b)
  1321. {
  1322. bNeedToRefresh = TRUE;
  1323. }
  1324. break;
  1325. case IDM_CONNECT:
  1326. {
  1327. WCHAR szPassword[ PASSWORD_LENGTH + 1 ];
  1328. BOOL bFirstTime = TRUE;
  1329. HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
  1330. // Start the connect loop with null password to try first.
  1331. szPassword[0] = '\0';
  1332. for( ;; )
  1333. {
  1334. DWORD Error;
  1335. BOOL fRet;
  1336. DWORD cch;
  1337. LPWSTR pszErrString;
  1338. fRet = WinStationConnect(hServer, pUser->m_dwSessionId, LOGONID_CURRENT, szPassword, TRUE);
  1339. if( fRet )
  1340. break; // success - break out of loop
  1341. Error = GetLastError();
  1342. // If a 'logon failure' brought us here, issue password dialog.
  1343. if(Error == ERROR_LOGON_FAILURE)
  1344. {
  1345. UINT ids = ( bFirstTime ? IDS_PWDDLG_USER : IDS_PWDDLG_USER2 );
  1346. CConnectPasswordDlg CPDlg( ids );
  1347. bFirstTime = FALSE;
  1348. if (CPDlg.DoDialog(m_hwndTabs) != IDOK)
  1349. {
  1350. break; // user CANCEL: break connect loop
  1351. }
  1352. else
  1353. {
  1354. StringCchCopy( szPassword, ARRAYSIZE(szPassword), CPDlg.GetPassword( ) );
  1355. continue; // try again with new password.
  1356. }
  1357. }
  1358. //
  1359. // Unhandled error occured. Pop up a message box and then bail the loop.
  1360. //
  1361. LoadString(g_hInstance, IDS_ERR_CONNECT, szText2, MAX_PATH);
  1362. // Retrieve system string for error.
  1363. cch = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
  1364. | FORMAT_MESSAGE_FROM_SYSTEM
  1365. | FORMAT_MESSAGE_IGNORE_INSERTS,
  1366. NULL,
  1367. Error,
  1368. 0,
  1369. (LPWSTR) &pszErrString,
  1370. 0,
  1371. NULL
  1372. );
  1373. if ( cch == 0 )
  1374. {
  1375. pszErrString = L'\0';
  1376. }
  1377. StringCchPrintf( szText1, ARRAYSIZE(szText1), szText2, Error, pszErrString );
  1378. MessageBox( m_hwndTabs, szText1, szCaption, MB_OK | MB_ICONEXCLAMATION );
  1379. if ( cch != 0 )
  1380. {
  1381. LocalFree( pszErrString );
  1382. }
  1383. break; // exit
  1384. }
  1385. //
  1386. // Destroy the password.
  1387. //
  1388. ZeroMemory( szPassword, sizeof(szPassword) );
  1389. }
  1390. break;
  1391. case IDM_REMOTECONTROL:
  1392. Shadow(m_hwndTabs, pUser);
  1393. break;
  1394. }
  1395. if (!b)
  1396. {
  1397. DWORD dwLastError = GetLastError();
  1398. UINT uiStr = 0;
  1399. //
  1400. // An error happened while processing the command
  1401. //
  1402. switch (id)
  1403. {
  1404. case IDM_DISCONNECT:
  1405. uiStr = IDS_ERR_DISCONNECT;
  1406. break;
  1407. case IDM_LOGOFF:
  1408. uiStr = IDS_ERR_LOGOFF;
  1409. break;
  1410. case IDM_SENDMESSAGE:
  1411. uiStr = IDS_ERR_SENDMESSAGE;
  1412. break;
  1413. }
  1414. if (uiStr)
  1415. {
  1416. LoadString(g_hInstance, uiStr, szText1, ARRAYSIZE(szText1));
  1417. // UI - only - don't care if it gets truncated
  1418. StringCchPrintf( szText2, ARRAYSIZE(szText2), szText1, pUser->m_szUserName, pUser->m_dwSessionId);
  1419. }
  1420. else
  1421. {
  1422. szText1[0] = L'\0';
  1423. }
  1424. psz = szText2 + lstrlen(szText2);
  1425. dwSize = ARRAYSIZE(szText2) - lstrlen(szText2) - 1;
  1426. FormatMessage(
  1427. FORMAT_MESSAGE_FROM_SYSTEM,
  1428. NULL,
  1429. dwLastError,
  1430. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1431. psz,
  1432. dwSize,
  1433. NULL
  1434. );
  1435. szText2[ ARRAYSIZE(szText2) - 1 ] = L'\0'; // make sure it is terminated.
  1436. iResult = MessageBox(
  1437. m_hwndTabs,
  1438. szText2,
  1439. szCaption,
  1440. MB_ICONSTOP | MB_ABORTRETRYIGNORE
  1441. );
  1442. if (iResult == IDCANCEL || iResult == IDABORT)
  1443. {
  1444. goto done;
  1445. }
  1446. else if (iResult == IDRETRY)
  1447. {
  1448. goto retry;
  1449. }
  1450. }
  1451. //
  1452. // Break out of the loop if we just went back to handle
  1453. // the special case of performing a disconnect or logoff
  1454. // on our own session.
  1455. //
  1456. if (bFinalOne)
  1457. break;
  1458. } // next i;
  1459. //
  1460. // Check here to see if we skiped our own session
  1461. //
  1462. if (iFinalOne != -1 && !bFinalOne)
  1463. {
  1464. // Yep, we skipped ourself. Lets put i back to the
  1465. // right location and try again.
  1466. bFinalOne = TRUE;
  1467. i = iFinalOne;
  1468. goto finalretry;
  1469. }
  1470. done:
  1471. if (pArray)
  1472. {
  1473. delete pArray;
  1474. }
  1475. Unpause();
  1476. //
  1477. // If we disconnected or logged off a user, go ahead and
  1478. // refresh the list. It should be up to date by now.
  1479. //
  1480. if (bNeedToRefresh)
  1481. {
  1482. TimerEvent();
  1483. }
  1484. }
  1485. /*++ UserPageProc
  1486. Routine Description:
  1487. Dialogproc for the task manager page.
  1488. Arguments:
  1489. hwnd - handle to dialog box
  1490. uMsg - message
  1491. wParam - first message parameter
  1492. lParam - second message parameter
  1493. Return Value:
  1494. For WM_INITDIALOG, TRUE == user32 sets focus, FALSE == we set focus
  1495. For others, TRUE == this proc handles the message
  1496. Revision History:
  1497. Nov-28-95 BradG Created
  1498. --*/
  1499. INT_PTR CALLBACK UserPageProc(
  1500. HWND hwnd, // handle to dialog box
  1501. UINT uMsg, // message
  1502. WPARAM wParam, // first message parameter
  1503. LPARAM lParam // second message parameter
  1504. )
  1505. {
  1506. CUserPage * thispage = (CUserPage *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  1507. //
  1508. // See if the parent wants this message
  1509. //
  1510. if (TRUE == CheckParentDeferrals(uMsg, wParam, lParam))
  1511. {
  1512. return TRUE;
  1513. }
  1514. switch(uMsg)
  1515. {
  1516. case WM_INITDIALOG:
  1517. {
  1518. SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
  1519. CUserPage * thispage = (CUserPage *) lParam;
  1520. thispage->OnInitDialog(hwnd);
  1521. }
  1522. // We handle focus during Activate(). Return FALSE here so the
  1523. // dialog manager doesn't try to set focus.
  1524. return FALSE;
  1525. case WM_LBUTTONUP:
  1526. case WM_LBUTTONDOWN:
  1527. // We need to fake client mouse clicks in this child to appear as nonclient
  1528. // (caption) clicks in the parent so that the user can drag the entire app
  1529. // when the title bar is hidden by dragging the client area of this child
  1530. if (g_Options.m_fNoTitle)
  1531. {
  1532. SendMessage(g_hMainWnd,
  1533. uMsg == WM_LBUTTONUP ? WM_NCLBUTTONUP : WM_NCLBUTTONDOWN,
  1534. HTCAPTION,
  1535. lParam);
  1536. }
  1537. break;
  1538. case WM_COMMAND:
  1539. if(LOWORD(wParam) == IDM_USERCOLS)
  1540. {
  1541. if(ColSelectDlg.DoDialog(hwnd) == IDOK)
  1542. {
  1543. // Set up the columns in the listview
  1544. if (SUCCEEDED(thispage->SetupColumns()))
  1545. {
  1546. thispage->TimerEvent();
  1547. }
  1548. }
  1549. }
  1550. else
  1551. {
  1552. thispage->HandleWMCOMMAND(LOWORD(wParam));
  1553. }
  1554. break;
  1555. case WM_NOTIFY:
  1556. return thispage->HandleUserPageNotify((LPNMHDR) lParam);
  1557. case WM_MENUSELECT:
  1558. if ((UINT) HIWORD(wParam) == 0xFFFF)
  1559. {
  1560. // Menu dismissed, resume display
  1561. thispage->Unpause();
  1562. }
  1563. break;
  1564. case WM_CONTEXTMENU:
  1565. if ((HWND) wParam == GetDlgItem(hwnd, IDC_USERLIST))
  1566. {
  1567. thispage->HandleUserListContextMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  1568. return TRUE;
  1569. }
  1570. break;
  1571. case WM_SIZE:
  1572. //
  1573. // Size our kids
  1574. //
  1575. thispage->SizeUserPage();
  1576. return TRUE;
  1577. case WM_SETTINGCHANGE:
  1578. thispage->OnSettingsChange();
  1579. // fall through
  1580. case WM_SYSCOLORCHANGE:
  1581. SendMessage(GetDlgItem(hwnd, IDC_USERLIST), uMsg, wParam, lParam);
  1582. return TRUE;
  1583. }
  1584. return FALSE;
  1585. }
  1586. //
  1587. //
  1588. //
  1589. void CUserPage::OnInitDialog(HWND hPage)
  1590. {
  1591. m_hPage = hPage;
  1592. HWND hUserList = GetDlgItem(m_hPage, IDC_USERLIST);
  1593. ListView_SetImageList(hUserList, m_himlUsers, LVSIL_SMALL);
  1594. //
  1595. // Turn on SHOWSELALWAYS so that the selection is still highlighted even
  1596. // when focus is lost to one of the buttons (for example)
  1597. //
  1598. SetWindowLong(hUserList, GWL_STYLE, GetWindowLong(hUserList, GWL_STYLE) | LVS_SHOWSELALWAYS);
  1599. ListView_SetExtendedListViewStyle(hUserList, LVS_EX_DOUBLEBUFFER);
  1600. }
  1601. //
  1602. //
  1603. //
  1604. void CUserPage::OnSettingsChange()
  1605. {
  1606. // in going between large-font settings and normal settings, the size of small
  1607. // icons changes; so throw away all our icons and change the size of images in
  1608. // our lists
  1609. BOOL fPaused = m_fPaused; // pause the page so we can get through
  1610. m_fPaused = TRUE; // the below without being updated
  1611. RemoveAllUsers();
  1612. m_pUserArray->RemoveAll();
  1613. m_fPaused = fPaused; // restore the paused state
  1614. TimerEvent(); // even if we're paused, we'll want to redraw
  1615. }
  1616. /*++ CUserPage::GetTitle
  1617. Routine Description:
  1618. Copies the title of this page to the caller-supplied buffer
  1619. Arguments:
  1620. pszText - the buffer to copy to
  1621. bufsize - size of buffer, in characters
  1622. Return Value:
  1623. Revision History:
  1624. Nov-28-95 BradG Created
  1625. --*/
  1626. void CUserPage::GetTitle(LPTSTR pszText, size_t bufsize)
  1627. {
  1628. LoadString(g_hInstance, IDS_USERPAGETITLE, pszText, static_cast<int>(bufsize));
  1629. }
  1630. /*++ CUserPage::Activate
  1631. Routine Description:
  1632. Brings this page to the front, sets its initial position,
  1633. and shows it
  1634. Arguments:
  1635. Return Value:
  1636. HRESULT (S_OK on success)
  1637. Revision History:
  1638. Nov-28-95 BradG Created
  1639. --*/
  1640. HRESULT CUserPage::Activate()
  1641. {
  1642. //
  1643. // Make this page visible
  1644. //
  1645. ShowWindow(m_hPage, SW_SHOW);
  1646. SetWindowPos(
  1647. m_hPage,
  1648. HWND_TOP,
  1649. 0, 0, 0, 0,
  1650. SWP_NOMOVE | SWP_NOSIZE
  1651. );
  1652. //
  1653. // Change the menu bar to be the menu for this page
  1654. //
  1655. HMENU hMenuOld = GetMenu(g_hMainWnd);
  1656. HMENU hMenuNew = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MAINMENU_USER));
  1657. AdjustMenuBar(hMenuNew);
  1658. g_hMenu = hMenuNew;
  1659. if (g_Options.m_fNoTitle == FALSE)
  1660. {
  1661. SetMenu(g_hMainWnd, hMenuNew);
  1662. }
  1663. if (hMenuOld)
  1664. {
  1665. DestroyMenu(hMenuOld);
  1666. }
  1667. UpdateUIState();
  1668. //
  1669. // If the tab control has focus, leave it there. Otherwise, set focus
  1670. // to the listview. If we don't set focus, it may stay on the previous
  1671. // page, now hidden, which can confuse the dialog manager and may cause
  1672. // us to hang.
  1673. //
  1674. if (GetFocus() != m_hwndTabs)
  1675. {
  1676. SetFocus(GetDlgItem(m_hPage, IDC_USERLIST));
  1677. }
  1678. return S_OK;
  1679. }
  1680. /*++ class CUserPage::SetupColumns
  1681. Class Description:
  1682. Removes any existing columns from the taskmanager listview and
  1683. adds all of the columns listed in the g_ActiveUserCol array.
  1684. Arguments:
  1685. Return Value:
  1686. HRESULT
  1687. Revision History:
  1688. Nov-29-95 BradG Created
  1689. --*/
  1690. HRESULT CUserPage::SetupColumns()
  1691. {
  1692. HWND hwndList = GetDlgItem(m_hPage, IDC_USERLIST);
  1693. if (NULL == hwndList)
  1694. {
  1695. return E_UNEXPECTED;
  1696. }
  1697. ListView_DeleteAllItems(hwndList);
  1698. //
  1699. // Remove all existing columns
  1700. // save column widths.
  1701. //
  1702. LV_COLUMN lvcolumn;
  1703. lvcolumn.mask = LVCF_SUBITEM | LVCF_WIDTH;
  1704. UserColumn *pCol = ColSelectDlg.GetColumns();
  1705. do
  1706. {
  1707. if(ListView_GetColumn(hwndList, 0, &lvcolumn))
  1708. {
  1709. if(lvcolumn.iSubItem >= USR_COL_USERSNAME &&
  1710. lvcolumn.iSubItem < USR_MAX_COLUMN)
  1711. {
  1712. pCol[lvcolumn.iSubItem].Width=lvcolumn.cx;
  1713. }
  1714. }
  1715. } while( ListView_DeleteColumn(hwndList, 0) );
  1716. //
  1717. // Add all of the new columns
  1718. //
  1719. INT iColumn = 0;
  1720. for (int i=0; i<USR_MAX_COLUMN; i++)
  1721. {
  1722. if(pCol[i].bActive)
  1723. {
  1724. WCHAR szTitle[MAX_PATH];
  1725. LoadString( g_hInstance, pCol[i].dwNameID, szTitle, ARRAYSIZE(szTitle) );
  1726. lvcolumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  1727. lvcolumn.fmt = pCol[i].Align;
  1728. lvcolumn.cx = pCol[i].Width;
  1729. lvcolumn.pszText = szTitle;
  1730. lvcolumn.iSubItem = i;
  1731. if (-1 == ListView_InsertColumn(hwndList, iColumn, &lvcolumn))
  1732. {
  1733. return E_FAIL;
  1734. }
  1735. iColumn++;
  1736. }
  1737. }
  1738. return S_OK;
  1739. }
  1740. /*++ CUserPage::Initialize
  1741. Routine Description:
  1742. Initializes the task manager page
  1743. Arguments:
  1744. hwndParent - Parent on which to base sizing on: not used for creation,
  1745. since the main app window is always used as the parent in
  1746. order to keep tab order correct
  1747. Return Value:
  1748. Revision History:
  1749. Nov-28-95 BradG Created
  1750. --*/
  1751. HRESULT CUserPage::Initialize(HWND hwndParent)
  1752. {
  1753. HRESULT hr = S_OK;
  1754. //
  1755. // Create the ptr array used to hold the info on running tasks
  1756. //
  1757. m_pUserArray = new CPtrArray(GetProcessHeap());
  1758. if (NULL == m_pUserArray)
  1759. {
  1760. hr = E_OUTOFMEMORY;
  1761. }
  1762. else
  1763. {
  1764. // Our pseudo-parent is the tab contrl, and is what we base our
  1765. // sizing on. However, in order to keep tab order right among
  1766. // the controls, we actually create ourselves with the main
  1767. // window as the parent
  1768. m_hwndTabs = hwndParent;
  1769. //
  1770. // Create the image lists
  1771. //
  1772. UINT flags = ILC_MASK;
  1773. if(IS_WINDOW_RTL_MIRRORED(hwndParent))
  1774. {
  1775. flags |= ILC_MIRROR;
  1776. }
  1777. m_himlUsers = ImageList_Create(
  1778. GetSystemMetrics(SM_CXSMICON),
  1779. GetSystemMetrics(SM_CYSMICON),
  1780. flags,
  1781. 2,
  1782. 1
  1783. );
  1784. if (NULL == m_himlUsers)
  1785. {
  1786. hr = E_FAIL;
  1787. }
  1788. }
  1789. if (SUCCEEDED(hr))
  1790. {
  1791. // Load the default icons
  1792. hr = LoadDefaultIcons();
  1793. }
  1794. if (SUCCEEDED(hr))
  1795. {
  1796. //
  1797. // Load the status strings
  1798. //
  1799. WCHAR szText[ MAX_PATH ];
  1800. for( int i = 0; i < MAX_STAT_STRINGS; i++ )
  1801. {
  1802. if ( LoadString(g_hInstance, FIRST_STAT_STRING + i, szText, ARRAYSIZE(szText) ) )
  1803. {
  1804. DWORD cchLen = lstrlen(szText) + 1;
  1805. g_pszStatString[i] = (LPWSTR) LocalAlloc( 0, cchLen * sizeof(*g_pszStatString[i]) );
  1806. if (g_pszStatString[i])
  1807. {
  1808. // shouldn't be truncated as we dynamically allocated the array above
  1809. HRESULT hr = StringCchCopy( g_pszStatString[i], cchLen, szText);
  1810. ASSERT( S_OK == hr );
  1811. hr; // unreferenced on FRE builds.
  1812. }
  1813. else
  1814. {
  1815. hr = E_OUTOFMEMORY;
  1816. }
  1817. }
  1818. else
  1819. {
  1820. hr = E_FAIL;
  1821. break;
  1822. }
  1823. }
  1824. }
  1825. if (SUCCEEDED(hr))
  1826. {
  1827. //
  1828. // Create the dialog which represents the body of this page
  1829. //
  1830. m_hPage = CreateDialogParam(
  1831. g_hInstance, // handle to application instance
  1832. MAKEINTRESOURCE(IDD_USERSPAGE), // identifies dialog box template name
  1833. g_hMainWnd, // handle to owner window
  1834. UserPageProc, // pointer to dialog box procedure
  1835. (LPARAM) this // User data (our this pointer)
  1836. );
  1837. if (NULL == m_hPage)
  1838. {
  1839. hr = GetLastHRESULT();
  1840. }
  1841. }
  1842. if (SUCCEEDED(hr))
  1843. {
  1844. // Set up the columns in the listview
  1845. hr = SetupColumns();
  1846. }
  1847. if (SUCCEEDED(hr))
  1848. {
  1849. TimerEvent();
  1850. }
  1851. //
  1852. // If any failure along the way, clean up what got allocated
  1853. // up to that point
  1854. //
  1855. if (FAILED(hr))
  1856. {
  1857. if (m_hPage)
  1858. {
  1859. DestroyWindow(m_hPage);
  1860. }
  1861. m_hwndTabs = NULL;
  1862. }
  1863. return hr;
  1864. }
  1865. //
  1866. //
  1867. //
  1868. HRESULT CUserPage::LoadDefaultIcons()
  1869. {
  1870. HICON hIcon;
  1871. hIcon = (HICON) LoadImage(
  1872. g_hInstance,
  1873. MAKEINTRESOURCE(IDI_USER),
  1874. IMAGE_ICON,
  1875. GetSystemMetrics(SM_CXSMICON),
  1876. GetSystemMetrics(SM_CYSMICON),
  1877. 0
  1878. );
  1879. if (!hIcon)
  1880. {
  1881. return GetLastHRESULT();
  1882. }
  1883. m_iUserIcon = ImageList_AddIcon(m_himlUsers, hIcon);
  1884. DestroyIcon(hIcon);
  1885. if (-1 == m_iUserIcon)
  1886. {
  1887. return E_FAIL;
  1888. }
  1889. hIcon = (HICON) LoadImage(
  1890. g_hInstance,
  1891. MAKEINTRESOURCE(IDI_CURRENTUSER),
  1892. IMAGE_ICON,
  1893. GetSystemMetrics(SM_CXSMICON),
  1894. GetSystemMetrics(SM_CYSMICON),
  1895. 0
  1896. );
  1897. if (!hIcon)
  1898. {
  1899. return GetLastHRESULT();
  1900. }
  1901. m_iCurrentUserIcon = ImageList_AddIcon(m_himlUsers, hIcon);
  1902. DestroyIcon(hIcon);
  1903. if (-1 == m_iUserIcon)
  1904. {
  1905. return E_FAIL;
  1906. }
  1907. return S_OK;
  1908. }
  1909. /*++ CUserPage::Destroy
  1910. Routine Description:
  1911. Frees whatever has been allocated by the Initialize call and saves last
  1912. column settings.
  1913. Arguments:
  1914. Return Value:
  1915. Revision History:
  1916. Nov-28-95 BradG Created
  1917. --*/
  1918. HRESULT CUserPage::Destroy()
  1919. {
  1920. //
  1921. // Get current column widths
  1922. //
  1923. HWND hwndList = GetDlgItem(m_hPage, IDC_USERLIST);
  1924. if (hwndList)
  1925. {
  1926. LV_COLUMN lvcolumn;
  1927. lvcolumn.mask = LVCF_SUBITEM | LVCF_WIDTH;
  1928. UserColumn *pCol = ColSelectDlg.GetColumns();
  1929. for(int i=0; ListView_GetColumn(hwndList, i, &lvcolumn); i++)
  1930. {
  1931. if(lvcolumn.iSubItem >= USR_COL_USERSNAME &&
  1932. lvcolumn.iSubItem < USR_MAX_COLUMN)
  1933. {
  1934. pCol[lvcolumn.iSubItem].Width=lvcolumn.cx;
  1935. }
  1936. }
  1937. }
  1938. //
  1939. // Save settings
  1940. //
  1941. ColSelectDlg.Save();
  1942. //
  1943. // Page cleanup
  1944. //
  1945. if (m_hPage)
  1946. {
  1947. DestroyWindow(m_hPage);
  1948. m_hPage = NULL;
  1949. }
  1950. return S_OK;
  1951. }
  1952. /*++ CUserPage::Deactivate
  1953. Routine Description:
  1954. Called when this page is losing its place up front
  1955. Arguments:
  1956. Return Value:
  1957. Revision History:
  1958. Nov-28-95 BradG Created
  1959. --*/
  1960. void CUserPage::Deactivate()
  1961. {
  1962. if (m_hPage)
  1963. {
  1964. ShowWindow(m_hPage, SW_HIDE);
  1965. }
  1966. }
  1967. DWORD Shadow_WarningProc(HWND *phwnd);
  1968. INT_PTR CALLBACK ShadowWarn_WndProc( HWND hwnd , UINT msg , WPARAM wp , LPARAM lp );
  1969. void CenterDlg(HWND hwndToCenterOn , HWND hDlg );
  1970. /*++ Shadow
  1971. Routine Description:
  1972. remote control session
  1973. Arguments:
  1974. Return Value:
  1975. Revision History:
  1976. Feb-8-2000 a-skuzin Created
  1977. --*/
  1978. void Shadow(HWND hwnd, CUserInfo * pUser)
  1979. {
  1980. WINSTATIONCONFIG WSConfig;
  1981. SHADOWCLASS Shadow;
  1982. ULONG ReturnLength;
  1983. HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
  1984. //Error handling
  1985. WCHAR szMsgText[MAX_PATH+1];
  1986. WCHAR szCaption[MAX_PATH+1];
  1987. WCHAR szTemplate[MAX_PATH+1];
  1988. LoadString(g_hInstance, IDS_APPTITLE, szCaption, MAX_PATH);
  1989. //
  1990. // Determine the WinStation's shadow state.
  1991. //
  1992. if(!WinStationQueryInformation(hServer,
  1993. pUser->m_dwSessionId,
  1994. WinStationConfiguration,
  1995. &WSConfig, sizeof(WINSTATIONCONFIG),
  1996. &ReturnLength ) )
  1997. {
  1998. // Can't query WinStation configuration; complain and return
  1999. return;
  2000. }
  2001. Shadow = WSConfig.User.Shadow;
  2002. //
  2003. // If shadowing is disabled, let the user know and return
  2004. //
  2005. if(Shadow == Shadow_Disable )
  2006. {
  2007. LoadString(g_hInstance,IDS_ERR_SHADOW_DISABLED,szTemplate,MAX_PATH);
  2008. // UI only - don't care it if gets truncated.
  2009. StringCchPrintf(szMsgText, ARRAYSIZE(szMsgText), szTemplate, pUser->m_dwSessionId );
  2010. MessageBox(hwnd, szMsgText, szCaption, MB_OK | MB_ICONEXCLAMATION);
  2011. return;
  2012. }
  2013. //
  2014. // If the WinStation is disconnected and shadow notify is 'on',
  2015. // let the user know and break out.
  2016. //
  2017. if((pUser->m_wtsState == WTSDisconnected) &&
  2018. ((Shadow == Shadow_EnableInputNotify) ||
  2019. (Shadow == Shadow_EnableNoInputNotify)) )
  2020. {
  2021. LoadString(g_hInstance,IDS_ERR_SHADOW_DISCONNECTED_NOTIFY_ON,szTemplate,MAX_PATH);
  2022. // UI only - don't care it if gets truncated.
  2023. StringCchPrintf( szMsgText, ARRAYSIZE(szMsgText), szTemplate, pUser->m_dwSessionId );
  2024. MessageBox(hwnd, szMsgText, szCaption, MB_OK | MB_ICONEXCLAMATION);
  2025. return;
  2026. }
  2027. //
  2028. // Display the 'start shadow' dialog for hotkey reminder and
  2029. // final 'ok' prior to shadowing.
  2030. //
  2031. CShadowStartDlg SSDlg;
  2032. if (SSDlg.DoDialog(hwnd) != IDOK)
  2033. {
  2034. return;
  2035. }
  2036. //
  2037. // launch UI thread (???)
  2038. //
  2039. HWND hwndShadowWarn = NULL;
  2040. hwndShadowWarn = CreateDialogParam( g_hInstance
  2041. , MAKEINTRESOURCE( IDD_DIALOG_SHADOWWARN )
  2042. , hwnd
  2043. , ShadowWarn_WndProc
  2044. , (LPARAM) 0
  2045. );
  2046. if(hwndShadowWarn)
  2047. {
  2048. ShowWindow( hwndShadowWarn, SW_SHOW );
  2049. UpdateWindow( hwndShadowWarn );
  2050. }
  2051. //
  2052. // Invoke the shadow DLL.
  2053. //
  2054. HCURSOR hOldCursor = SetCursor(LoadCursor(NULL,IDC_WAIT));
  2055. //
  2056. // allow UI thread to init window (??? but this is the same thread)
  2057. //
  2058. Sleep( 900 );
  2059. //
  2060. // Shadow API always connects to local server,
  2061. // passing target servername as a parameter.
  2062. //
  2063. WCHAR szCompName[MAX_COMPUTERNAME_LENGTH + 1];
  2064. DWORD dwSize=MAX_COMPUTERNAME_LENGTH;
  2065. GetComputerName(szCompName,&dwSize);
  2066. BOOL bOK = WinStationShadow(WTS_CURRENT_SERVER_HANDLE, szCompName, pUser->m_dwSessionId,
  2067. (BYTE)(SSDlg.GetShadowHotkeyKey()),(WORD)(SSDlg.GetShadowHotkeyShift()));
  2068. if( hwndShadowWarn != NULL )
  2069. {
  2070. DestroyWindow(hwndShadowWarn);
  2071. }
  2072. if( !bOK )
  2073. {
  2074. LoadString(g_hInstance,IDS_ERR_SHADOW,szTemplate,MAX_PATH);
  2075. // UI only - don't care it if gets truncated.
  2076. StringCchPrintf( szMsgText, ARRAYSIZE(szMsgText), szTemplate, pUser->m_dwSessionId );
  2077. MessageBox(hwnd, szMsgText, szCaption, MB_OK | MB_ICONEXCLAMATION);
  2078. }
  2079. SetCursor(hOldCursor);
  2080. }
  2081. //
  2082. //
  2083. //
  2084. DWORD Shadow_WarningProc(HWND *phwnd)
  2085. {
  2086. DialogBoxParam( g_hInstance , MAKEINTRESOURCE( IDD_DIALOG_SHADOWWARN ) , NULL , ShadowWarn_WndProc,
  2087. (LPARAM)phwnd);
  2088. return( 0 );
  2089. }
  2090. //
  2091. //
  2092. //
  2093. INT_PTR CALLBACK ShadowWarn_WndProc( HWND hwnd , UINT msg , WPARAM , LPARAM )
  2094. {
  2095. switch( msg )
  2096. {
  2097. case WM_INITDIALOG:
  2098. CenterDlg( GetDesktopWindow( ) , hwnd );
  2099. break;
  2100. }
  2101. return FALSE;
  2102. }
  2103. //
  2104. //
  2105. //
  2106. void CenterDlg(HWND hwndToCenterOn , HWND hDlg )
  2107. {
  2108. RECT rc, rcwk, rcToCenterOn;
  2109. SetRect( &rcToCenterOn , 0 , 0 , GetSystemMetrics(SM_CXSCREEN) , GetSystemMetrics( SM_CYSCREEN ) );
  2110. if (hwndToCenterOn != NULL)
  2111. {
  2112. GetWindowRect(hwndToCenterOn, &rcToCenterOn);
  2113. }
  2114. GetWindowRect( hDlg , &rc);
  2115. UINT uiWidth = rc.right - rc.left;
  2116. UINT uiHeight = rc.bottom - rc.top;
  2117. rc.left = (rcToCenterOn.left + rcToCenterOn.right) / 2 - ( rc.right - rc.left ) / 2;
  2118. rc.top = (rcToCenterOn.top + rcToCenterOn.bottom) / 2 - ( rc.bottom - rc.top ) / 2;
  2119. //
  2120. // Ensure the dialog always with the work area
  2121. //
  2122. if(SystemParametersInfo(SPI_GETWORKAREA, 0, &rcwk, 0))
  2123. {
  2124. UINT wkWidth = rcwk.right - rcwk.left;
  2125. UINT wkHeight = rcwk.bottom - rcwk.top;
  2126. if(rc.left + uiWidth > wkWidth) //right cut
  2127. rc.left = wkWidth - uiWidth;
  2128. if(rc.top + uiHeight > wkHeight) //bottom cut
  2129. rc.top = wkHeight - uiHeight;
  2130. if(rc.left < rcwk.left) //left cut
  2131. rc.left += rcwk.left - rc.left;
  2132. if(rc.top < rcwk.top) //top cut
  2133. rc.top += rcwk.top - rc.top;
  2134. }
  2135. SetWindowPos( hDlg, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER |
  2136. SWP_NOCOPYBITS | SWP_DRAWFRAME);
  2137. }