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.

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