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.

2865 lines
82 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // File: connection.cpp
  4. //
  5. // Module: CMMON32.EXE
  6. //
  7. // Synopsis:
  8. // Implement class CCmConnection
  9. // CCmConnection manages a single connection.
  10. //
  11. // The m_StatusDlg lives through out the CONNECTED/DISCONNECT_COUNTDOWN
  12. // state. The appearance changes when it comes to COUNTDOWN state.
  13. //
  14. // The m_ReconnectDlg is the reconnect prompt dialog. It exist during
  15. // STATE_PROMPT_RECONNECT state.
  16. //
  17. // Both dialogs are modeless. (We need a initially invisible status dialog
  18. // to receive timer and trayicon message. I did not find any way to create
  19. // a invisible modal dialog without flashing.)
  20. //
  21. // CreateDialog will simply return, unlike DialogBox, which returns only after
  22. // Dialog is ended. To simplify the implementation, we handle end-dialog event
  23. // in the thread routine instead of in the dialog procedure.
  24. //
  25. // When we need to end the status or reconnect dialog, we simply post a thread
  26. // message to end the dialog and continue to the next state. The connection
  27. // thread runs a message loop and processes thread message.
  28. //
  29. // The RasMonitorDlg on NT is running in another thread. Otherwise the connection
  30. // thread message can not be processed
  31. //
  32. // The connection is event driven module. ConnectionThread() is the entry
  33. // point of the connection thread.
  34. //
  35. //
  36. // Copyright (c) 1998-1999 Microsoft Corporation
  37. //
  38. // Author: fengsun Created 2/11/98
  39. //
  40. //+----------------------------------------------------------------------------
  41. #include "cmmaster.h"
  42. #include "connection.h"
  43. #include "Monitor.h"
  44. #include "TrayIcon.h"
  45. #include "ShellDll.h"
  46. #include <tchar.h>
  47. #include <rasdlg.h>
  48. #include "cmdial.h"
  49. #include <wininet.h> // for INTERNET_DIALSTATE_DISCONNECTED
  50. #include "DynamicLib.h"
  51. #include "log_str.h"
  52. #include "userinfo_str.h"
  53. HINSTANCE g_hInst = NULL;
  54. //
  55. // Functions in cmdial32.dll, the prototype is in cmdial.h
  56. //
  57. static const CHAR* const c_pszCmReconnect = "CmReConnect";
  58. static const CHAR* const c_pszCmHangup = "CmCustomHangUp";
  59. //
  60. // CMS flags used exclusively by connection.cpp
  61. //
  62. static const TCHAR* const c_pszCmEntryIdleThreshold = TEXT("IdleThreshold");
  63. static const TCHAR* const c_pszCmEntryNoPromptReconnect = TEXT("NoPromptReconnect");
  64. static const TCHAR* const c_pszCmEntryHideTrayIcon = TEXT("HideTrayIcon");
  65. typedef BOOL (WINAPI * CmReConnectFUNC)(LPTSTR lpszPhonebook,
  66. LPTSTR lpszEntry,
  67. LPCMDIALINFO lpCmInfo);
  68. typedef DWORD (WINAPI * CmCustomHangUpFUNC)(HRASCONN hRasConn,
  69. LPCTSTR pszEntry,
  70. BOOL fIgnoreRefCount,
  71. BOOL fPersist);
  72. //
  73. // The timer interval for StateConnectedOnTimer();
  74. //
  75. const DWORD TIMER_INTERVAL = 1000;
  76. DWORD CCmConnection::m_dwCurPositionId = 0;
  77. //+----------------------------------------------------------------------------
  78. //
  79. // Function: CCmConnection::CCmConnection
  80. //
  81. // Synopsis: Constructor, called in the monitor thread
  82. //
  83. // Arguments: const CONNECTED_INFO* pConnectedInfo - Information passed from
  84. // cmdial upon conected
  85. // const CM_CONNECTION* pConnectionEntry - Information in the
  86. // Connection table
  87. //
  88. // Returns: Nothing
  89. //
  90. // History: fengsun Created Header 2/3/98
  91. //
  92. //+----------------------------------------------------------------------------
  93. CCmConnection::CCmConnection(const CM_CONNECTED_INFO* pConnectedInfo,
  94. const CM_CONNECTION* pConnectionEntry) :
  95. #pragma warning(disable:4355) //'this' : used in base member initializer list
  96. m_StatusDlg(this),
  97. #pragma warning(default:4355)
  98. m_TrayIcon()
  99. {
  100. MYDBGASSERT(pConnectedInfo);
  101. MYDBGASSERT(pConnectionEntry);
  102. m_dwState = STATE_CONNECTED;
  103. m_hBigIcon = m_hSmallIcon = NULL;
  104. m_dwConnectStartTime = GetTickCount() - 500; // .5 second for round off
  105. m_dwCountDownStartTime = 0;
  106. m_dwThreadId = 0;
  107. //
  108. // set this to TRUE, so the WorkingSet will be minimized before MsgWait
  109. // while there is no more message
  110. //
  111. m_fToMinimizeWorkingSet = TRUE;
  112. m_fHideTrayIcon = FALSE;
  113. //
  114. // Save the data from pConnectedInfo
  115. //
  116. lstrcpynU(m_ReconnectInfo.szPassword, pConnectedInfo->szPassword,
  117. sizeof(m_ReconnectInfo.szPassword)/sizeof(m_ReconnectInfo.szPassword[0]));
  118. lstrcpynU(m_ReconnectInfo.szInetPassword, pConnectedInfo->szInetPassword,
  119. sizeof(m_ReconnectInfo.szPassword)/sizeof(m_ReconnectInfo.szPassword[0]));
  120. m_ReconnectInfo.dwCmFlags = pConnectedInfo->dwCmFlags | FL_RECONNECT; // Cm specific flags
  121. lstrcpynU(m_szServiceName, pConnectedInfo->szEntryName, sizeof(m_szServiceName)/sizeof(m_szServiceName[0]));
  122. //
  123. // NOTE: Fast User Switching is only available on WinXP and beyond, and this
  124. // member variable should only be accessed/used for WinXP and beyond.
  125. //
  126. m_fGlobalGlobal = (pConnectionEntry->fAllUser && (pConnectedInfo->dwCmFlags & FL_GLOBALCREDS));
  127. CMTRACE1(TEXT("CCmConnection::CCmConnection set m_fGlobalGlobal to %d"), m_fGlobalGlobal);
  128. //
  129. // Get the RAS phonebook
  130. //
  131. lstrcpynU(m_szRasPhoneBook, pConnectedInfo->szRasPhoneBook, sizeof(m_szRasPhoneBook)/sizeof(m_szRasPhoneBook[0]));
  132. //
  133. // Init m_IniProfile, m_IniService and m_IniBoth
  134. //
  135. InitIniFiles(pConnectedInfo->szProfilePath);
  136. //
  137. // Because the IdleTimeout and EnableLogging values are not saved
  138. // per access point as all the other profile settings are, we must change the PrimaryRegPath
  139. // value of m_IniBoth so that it points to the non-access point registry location.
  140. //
  141. LPCTSTR c_pszUserInfoRegPath = (pConnectionEntry->fAllUser) ? c_pszRegCmUserInfo : c_pszRegCmSingleUserInfo;
  142. LPTSTR pszSavedPrimaryRegPath = CmStrCpyAlloc(m_IniBoth.GetPrimaryRegPath());
  143. LPTSTR pszPrimaryRegPath = (LPTSTR)CmMalloc(sizeof(TCHAR)*(lstrlenU(c_pszUserInfoRegPath) + lstrlenU(m_szServiceName) + 1));
  144. if (pszPrimaryRegPath && pszSavedPrimaryRegPath)
  145. {
  146. wsprintfU(pszPrimaryRegPath, TEXT("%s%s"), c_pszUserInfoRegPath, m_szServiceName);
  147. m_IniBoth.SetPrimaryRegPath(pszPrimaryRegPath);
  148. CmFree(pszPrimaryRegPath);
  149. }
  150. //
  151. // Initialize Logging
  152. //
  153. m_Log.Init(g_hInst, pConnectionEntry->fAllUser, GetServiceName());
  154. BOOL fEnabled = FALSE;
  155. DWORD dwMaxSize = 0;
  156. LPTSTR pszFileDir = NULL;
  157. fEnabled = m_IniBoth.GPPB(c_pszCmSection, c_pszCmEntryEnableLogging, c_fEnableLogging);
  158. dwMaxSize = m_IniService.GPPI(c_pszCmSectionLogging, c_pszCmEntryMaxLogFileSize, c_dwMaxFileSize);
  159. pszFileDir = m_IniService.GPPS(c_pszCmSectionLogging, c_pszCmEntryLogFileDirectory, c_szLogFileDirectory);
  160. m_Log.SetParams(fEnabled, dwMaxSize, pszFileDir);
  161. if (m_Log.IsEnabled())
  162. {
  163. m_Log.Start(FALSE); // FALSE => no banner
  164. }
  165. else
  166. {
  167. m_Log.Stop();
  168. }
  169. CmFree(pszFileDir);
  170. //
  171. // Whether to enable auto disconnect for no-traffic and no-watch-process
  172. // 0 of dwIdleTime means never timeout
  173. //
  174. const DWORD DEFAULT_IDLETIMEOUT = 10; // default idle time out is 10 minute
  175. DWORD dwIdleTime = (DWORD) m_IniBoth.GPPI(c_pszCmSection,
  176. c_pszCmEntryIdleTimeout,
  177. DEFAULT_IDLETIMEOUT);
  178. //
  179. // Set the m_IniBoth object back to its previous Primary Reg path
  180. //
  181. if (pszSavedPrimaryRegPath)
  182. {
  183. m_IniBoth.SetPrimaryRegPath(pszSavedPrimaryRegPath);
  184. CmFree(pszSavedPrimaryRegPath);
  185. }
  186. //
  187. // No watch-process time-out if IdleTime is "never"
  188. //
  189. if (dwIdleTime)
  190. {
  191. for (int i=0; pConnectedInfo->ahWatchHandles[i] != 0; i++)
  192. {
  193. m_WatchProcess.Add(pConnectedInfo->ahWatchHandles[i]);
  194. }
  195. }
  196. if (!OS_NT4)
  197. {
  198. m_ConnStatistics.SetDialupTwo(pConnectedInfo->fDialup2);
  199. m_ConnStatistics.Open(CMonitor::GetInstance(),
  200. pConnectedInfo->dwInitBytesRecv,
  201. pConnectedInfo->dwInitBytesSend,
  202. pConnectionEntry->hDial,
  203. pConnectionEntry->hTunnel);
  204. if (dwIdleTime)
  205. {
  206. //
  207. // Adjust minutes value to milliseconds
  208. //
  209. dwIdleTime = dwIdleTime * 1000 * 60;
  210. DWORD dwIdleThreshold = m_IniService.GPPI(c_pszCmSection,
  211. c_pszCmEntryIdleThreshold,
  212. 0L); // default threshold is always 0 bytes
  213. //
  214. // Start idle statistic counter anyway
  215. // IsIdle will return FALSE, if it is never updated
  216. //
  217. m_IdleStatistics.Start(dwIdleThreshold, dwIdleTime);
  218. }
  219. }
  220. //
  221. // Save data from pConnectionEntry
  222. //
  223. MYDBGASSERT(pConnectionEntry->hDial || pConnectionEntry->hTunnel);
  224. MYDBGASSERT(pConnectionEntry->CmState == CM_CONNECTED);
  225. m_hRasDial = pConnectionEntry->hDial;
  226. m_hRasTunnel = pConnectionEntry->hTunnel;
  227. m_szHelpFile[0] = 0;
  228. //
  229. // the position id increased by 1 for each connection
  230. //
  231. m_dwPositionId = m_dwCurPositionId;
  232. m_dwCurPositionId++;
  233. }
  234. //+----------------------------------------------------------------------------
  235. //
  236. // Function: CCmConnection::~CCmConnection
  237. //
  238. // Synopsis:
  239. //
  240. // Arguments: None
  241. //
  242. // Returns: Nothing
  243. //
  244. // History: Created Header 2/18/98
  245. //
  246. //+----------------------------------------------------------------------------
  247. CCmConnection::~CCmConnection()
  248. {
  249. ASSERT_VALID(this);
  250. if (m_hBigIcon)
  251. {
  252. DeleteObject(m_hBigIcon);
  253. }
  254. if (m_hSmallIcon)
  255. {
  256. DeleteObject(m_hSmallIcon);
  257. }
  258. if (m_hEventRasNotify)
  259. {
  260. CloseHandle(m_hEventRasNotify);
  261. }
  262. //
  263. // UnInitialize Logging
  264. //
  265. m_Log.DeInit();
  266. }
  267. //+----------------------------------------------------------------------------
  268. //
  269. // Function: CCmConnection::InitIniFiles
  270. //
  271. // Synopsis: Initialize data member m_IniProfile, m_IniService and m_IniBoth
  272. //
  273. // Arguments: const TCHAR* pszProfileName - the full path of the .cmp file
  274. //
  275. // Returns: Nothing
  276. //
  277. // History: fengsun Created Header 2/10/98
  278. //
  279. //+----------------------------------------------------------------------------
  280. void CCmConnection::InitIniFiles(const TCHAR* pszProfileName)
  281. {
  282. if (NULL == pszProfileName)
  283. {
  284. return;
  285. }
  286. g_hInst = CMonitor::GetInstance();
  287. //
  288. // .cmp file
  289. //
  290. m_IniProfile.Clear();
  291. m_IniProfile.SetHInst(CMonitor::GetInstance());
  292. m_IniProfile.SetFile(pszProfileName);
  293. //
  294. // .cms file
  295. //
  296. m_IniService.Clear();
  297. m_IniService.SetHInst(CMonitor::GetInstance());
  298. LPTSTR pszService = m_IniProfile.GPPS(c_pszCmSection,c_pszCmEntryCmsFile);
  299. MYDBGASSERT(pszService);
  300. //
  301. // the .cms file is relative to .cmp path, convert it to absolute path
  302. //
  303. LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszService);
  304. MYDBGASSERT(pszFullPath);
  305. if (pszFullPath)
  306. {
  307. m_IniService.SetFile(pszFullPath);
  308. }
  309. CmFree(pszFullPath);
  310. CmFree(pszService);
  311. // both: .CMP file takes precedence over .CMS file
  312. // use .CMP file as primary file
  313. //
  314. m_IniBoth.Clear();
  315. m_IniBoth.SetHInst(CMonitor::GetInstance());
  316. m_IniBoth.SetFile(m_IniService.GetFile());
  317. m_IniBoth.SetPrimaryFile(m_IniProfile.GetFile());
  318. }
  319. //+----------------------------------------------------------------------------
  320. //
  321. // Function: CCmConnection::StartConnectionThread
  322. //
  323. // Synopsis: Start the connection thread. Called by monitor on CONNECTED
  324. // message from cmdial
  325. //
  326. // Arguments: None
  327. //
  328. // Returns: BOOL - Whether the thread is created successfully
  329. //
  330. // History: fengsun Created Header 2/3/98
  331. //
  332. //+----------------------------------------------------------------------------
  333. BOOL CCmConnection::StartConnectionThread()
  334. {
  335. DWORD dwThreadId;
  336. HANDLE hThread;
  337. if ((hThread = CreateThread(NULL, 0, ConnectionThread ,this,0,&dwThreadId)) == NULL)
  338. {
  339. MYDBGASSERT(FALSE);
  340. CMTRACE(TEXT("CCmConnection::StartConnectionThread CreateThread failed"));
  341. return FALSE;
  342. }
  343. CloseHandle(hThread);
  344. return TRUE;
  345. }
  346. //+----------------------------------------------------------------------------
  347. //
  348. // Function: static CCmConnection::ConnectionThread
  349. //
  350. // Synopsis: The connection thread. Call back function for CreateThread
  351. //
  352. // Arguments: LPVOID lParam - pConnection
  353. //
  354. // Returns: DWORD WINAPI - thread exit code
  355. //
  356. // History: fengsun Created Header 2/12/98
  357. //
  358. //+----------------------------------------------------------------------------
  359. DWORD WINAPI CCmConnection::ConnectionThread(LPVOID lParam)
  360. {
  361. MYDBGASSERT(lParam);
  362. //
  363. // Call the non-static function
  364. //
  365. return ((CCmConnection*)lParam)->ConnectionThread();
  366. }
  367. //+----------------------------------------------------------------------------
  368. //
  369. // Function: CCmConnection::ConnectionThread
  370. //
  371. // Synopsis: The non-static connection thread, so we can referrence
  372. // data/fuction directly
  373. //
  374. // Arguments: None
  375. //
  376. // Returns: DWORD - thread exit code
  377. //
  378. // History: Created Header 2/12/98
  379. //
  380. //+----------------------------------------------------------------------------
  381. DWORD CCmConnection::ConnectionThread()
  382. {
  383. m_dwThreadId = GetCurrentThreadId();
  384. m_dwState = STATE_CONNECTED;
  385. //
  386. // Run the connected/disconnect-count-down state
  387. // StateConnected() will change m_dwState to the new state
  388. //
  389. StateConnected();
  390. //
  391. // Whether to remove the connection from the shared connection table
  392. // This is TRUE, only if user clicks No for the prompt reconnect dialog
  393. //
  394. BOOL fRemoveFromSharedTable = FALSE;
  395. if (m_dwState != STATE_TERMINATED)
  396. {
  397. //
  398. // if auto reconnect is not enabled, then show the reconnect prompt
  399. //
  400. if (m_dwState != STATE_RECONNECTING)
  401. {
  402. //
  403. // Run the prompt reconnect state
  404. //
  405. m_dwState = StatePrompt();
  406. }
  407. if (m_dwState != STATE_RECONNECTING)
  408. {
  409. //
  410. // User clicks No for the reconnect-prompt dialog
  411. // Clear the entry from connection table
  412. //
  413. fRemoveFromSharedTable = TRUE;
  414. }
  415. else
  416. {
  417. //
  418. // User clicks Yes for the reconnect-prompt dialog
  419. // Move from Connected array to reconnecting array
  420. //
  421. CMonitor::MoveToReconnectingConn(this);
  422. //
  423. // Run the reconnect state
  424. //
  425. Reconnect();
  426. }
  427. }
  428. CMTRACE(TEXT("The connection thread is terminated"));
  429. //
  430. // The connection is terminated without need to ask for reconnect
  431. // Remove the connection from monitor conntected connection array
  432. // If fRemoveFromSharedTable is FALSE, do not clear the entry from shared table.
  433. // CmCustomHangup clears the table
  434. // Monitor will delete the connection object. Must exit the thread after this.
  435. //
  436. CMonitor::RemoveConnection(this, fRemoveFromSharedTable);
  437. CMonitor::MinimizeWorkingSet();
  438. return 0;
  439. }
  440. //+----------------------------------------------------------------------------
  441. //
  442. // Function: CCmConnection::StateConnectedInit
  443. //
  444. // Synopsis: Initialization for the connected state, unlike the connstructor
  445. // This is called with in the connection thread
  446. //
  447. // Arguments: None
  448. //
  449. // Returns: Nothing
  450. //
  451. // History: fengsun Created Header 2/12/98
  452. //
  453. //+----------------------------------------------------------------------------
  454. void CCmConnection::StateConnectedInit()
  455. {
  456. m_dwConnectStartTime = GetTickCount();
  457. //
  458. // Load big and small connection icon: m_hBigIcon and m_hSmallIcon
  459. //
  460. LoadConnectionIcons();
  461. m_StatusDlg.Create(CMonitor::GetInstance(), CMonitor::GetMonitorWindow(), m_szServiceName, m_hBigIcon);
  462. m_StatusDlg.ChangeToStatus();
  463. //
  464. // Change the window position, so multiple status window will not be at
  465. // the same position
  466. //
  467. PositionWindow(m_StatusDlg.GetHwnd(), m_dwPositionId);
  468. //
  469. // Change the dialog titlebar icon
  470. //
  471. SendMessageU(m_StatusDlg.GetHwnd(),WM_SETICON,ICON_BIG,(LPARAM) m_hBigIcon);
  472. SendMessageU(m_StatusDlg.GetHwnd(),WM_SETICON,ICON_SMALL,(LPARAM) m_hSmallIcon);
  473. //
  474. // Set the help file name
  475. //
  476. LPTSTR lpHelpFile = LoadHelpFileName();
  477. if (lpHelpFile)
  478. {
  479. m_StatusDlg.SetHelpFileName(lpHelpFile);
  480. }
  481. else
  482. {
  483. m_StatusDlg.SetHelpFileName(c_pszDefaultHelpFile);
  484. }
  485. CmFree(lpHelpFile);
  486. //
  487. // Determine whether or not, we're hiding the icon. The default is TRUE
  488. // for NT5 because we already have full support from the folder.
  489. //
  490. m_fHideTrayIcon= m_IniService.GPPI(c_pszCmSection, c_pszCmEntryHideTrayIcon, OS_NT5);
  491. if (!m_fHideTrayIcon && !(OS_NT5 && IsLogonAsSystem()))
  492. {
  493. HICON hIcon = NULL;
  494. LPTSTR pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntryTrayIcon);
  495. if (*pszTmp)
  496. {
  497. //
  498. // The icon name is relative to the .cmp file, convert it into full name
  499. //
  500. LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp);
  501. hIcon = CmLoadSmallIcon(CMonitor::GetInstance(), pszFullPath);
  502. CmFree(pszFullPath);
  503. }
  504. CmFree(pszTmp);
  505. //
  506. // Use the default tray icon
  507. //
  508. if (!hIcon)
  509. {
  510. hIcon = CmLoadSmallIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP));
  511. }
  512. //
  513. // m_TrayIcon is responsible to delete the hIcon object
  514. //
  515. m_TrayIcon.SetIcon(hIcon, m_StatusDlg.GetHwnd(), WM_TRAYICON, 0,m_szServiceName);
  516. // Question: , shall we also load the tray icon cmd from iniProfile?
  517. m_TrayIcon.CreateMenu(&m_IniService, IDM_TRAYMENU);
  518. }
  519. //
  520. // Try to call RasConnectionNotification. When connection is losted
  521. // a event will be signaled
  522. //
  523. m_RasApiDll.Load();
  524. m_hEventRasNotify = CallRasConnectionNotification(m_hRasDial, m_hRasTunnel);
  525. if (m_hEventRasNotify)
  526. {
  527. //
  528. // If we got the event, unload RAS, otherwise, need to check connection on timer
  529. //
  530. m_RasApiDll.Unload();
  531. }
  532. }
  533. //+----------------------------------------------------------------------------
  534. //
  535. // Function: CCmConnection::StateConnected
  536. //
  537. // Synopsis: The connection is in the connected or disconnect-count-down state
  538. // Run the message loop until state is changed
  539. //
  540. // Arguments: None
  541. //
  542. // Returns: CONN_STATE - The new state, either STATE_TERMINATED or
  543. // STATE_PROMPT_RECONNECT
  544. //
  545. // History: fengsun Created Header 2/4/98
  546. //
  547. //+----------------------------------------------------------------------------
  548. //
  549. // The reconnect dialog only shows up on Win95 Gold
  550. // And we need to use service name to find the dialog.
  551. //
  552. void ZapRNAReconnectStop(HANDLE hThread);
  553. HANDLE ZapRNAReconnectStart(BOOL *pbConnLost);
  554. void CCmConnection::StateConnected()
  555. {
  556. ASSERT_VALID(this);
  557. MYDBGASSERT(m_dwState == STATE_CONNECTED);
  558. CMTRACE(TEXT("Enter StateConnected"));
  559. StateConnectedInit();
  560. HANDLE hThreadRnaReconnect = NULL;
  561. if (OS_W95)
  562. {
  563. hThreadRnaReconnect = ZapRNAReconnectStart(NULL);
  564. }
  565. while (m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN)
  566. {
  567. CONN_EVENT dwEvent = StateConnectedGetEvent();
  568. MYDBGASSERT(dwEvent <= EVENT_NONE);
  569. if (dwEvent < EVENT_NONE )
  570. {
  571. //
  572. // Call the event handler to process the event
  573. //
  574. m_dwState = StateConnectedProcessEvent(dwEvent);
  575. }
  576. }
  577. if (hThreadRnaReconnect)
  578. {
  579. ZapRNAReconnectStop(hThreadRnaReconnect);
  580. }
  581. }
  582. //+----------------------------------------------------------------------------
  583. //
  584. // Function: CCmConnection::StateConnectedGetEvent
  585. //
  586. // Synopsis: In the state of CONNECTED/COUNTDOWN. Wait until some event happens.
  587. // Also runs the message loop
  588. //
  589. // Arguments: None
  590. //
  591. // Returns: CCmConnection::CONN_EVENT - The event that can cause the
  592. // connection change the state other than CONNECTED/COUNTDOWN
  593. //
  594. // History: Created Header 2/18/98
  595. //
  596. //+----------------------------------------------------------------------------
  597. CCmConnection::CONN_EVENT CCmConnection::StateConnectedGetEvent()
  598. {
  599. ASSERT_VALID(this);
  600. //
  601. // The last time SateConnectedOnTimer got called
  602. //
  603. DWORD dwLastTimerCalled = 0;
  604. //
  605. // Loop until we got some event
  606. //
  607. while (TRUE)
  608. {
  609. MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
  610. //
  611. // Process all the messages in the message queue
  612. //
  613. MSG msg;
  614. while(PeekMessageU(&msg, NULL,0,0,PM_REMOVE))
  615. {
  616. if (msg.hwnd == NULL)
  617. {
  618. //
  619. // it is a thread message
  620. //
  621. MYDBGASSERT((msg.message >= WM_APP) || (msg.message == WM_CONN_EVENT));
  622. if (msg.message == WM_CONN_EVENT)
  623. {
  624. MYDBGASSERT(msg.wParam < EVENT_NONE);
  625. return (CONN_EVENT)msg.wParam;
  626. }
  627. }
  628. else
  629. {
  630. //
  631. // Also dispatch message for Modeless dialog box
  632. //
  633. if (!IsDialogMessageU(m_StatusDlg.GetHwnd(), &msg))
  634. {
  635. TranslateMessage(&msg);
  636. DispatchMessageU(&msg);
  637. }
  638. }
  639. }
  640. //
  641. // whether cmmon needs timer.
  642. // The timer is needed, if we have check the ras connection on timer,
  643. // or we have to check idle disconnect on timer,
  644. // or the state is disconnect-count-down,
  645. // or status dialog is visible
  646. //
  647. BOOL fNeedTimer = !m_hEventRasNotify
  648. || m_IdleStatistics.IsStarted()
  649. || m_dwState == STATE_COUNTDOWN
  650. || IsWindowVisible(m_StatusDlg.GetHwnd());
  651. //
  652. // If more than 1 seconds elapsed, call the timer
  653. //
  654. if (fNeedTimer && GetTickCount() - dwLastTimerCalled >= TIMER_INTERVAL)
  655. {
  656. dwLastTimerCalled = GetTickCount();
  657. CONN_EVENT dwEvent = StateConnectedOnTimer();
  658. if (dwEvent != EVENT_NONE)
  659. {
  660. return dwEvent;
  661. }
  662. }
  663. //
  664. // Setup the opbject array for MsgWaitForMultipleObjects
  665. //
  666. HANDLE ahObjectsToWait[3];
  667. int nObjects = 0;
  668. if (m_hEventRasNotify)
  669. {
  670. ahObjectsToWait[nObjects] = m_hEventRasNotify;
  671. nObjects++;
  672. }
  673. if (m_WatchProcess.GetSize())
  674. {
  675. //
  676. // If we have any process to watch, just add the first hProcess
  677. // Since we want to know whether all processes exit
  678. //
  679. ahObjectsToWait[nObjects] = m_WatchProcess.GetProcess(0);
  680. MYDBGASSERT(ahObjectsToWait[nObjects]);
  681. nObjects++;
  682. }
  683. //
  684. // From MSDN:
  685. // The documentation for MsgWaitForMultipleObjects() says that the API returns
  686. // successfully when either the objects are signalled or the input is available.
  687. // However, the API behaves as if it requires that the objects are signalled
  688. // and the input is available.
  689. //
  690. // Put an extra event seems to fix it for NT
  691. //
  692. if (OS_NT && nObjects)
  693. {
  694. ahObjectsToWait[nObjects] = ahObjectsToWait[nObjects-1];
  695. nObjects++;
  696. }
  697. if (m_fToMinimizeWorkingSet)
  698. {
  699. //
  700. // If we do not need a timer here, minimize the working set.
  701. // before calling MsgWaitForMultipleObjects.
  702. //
  703. CMonitor::MinimizeWorkingSet();
  704. m_fToMinimizeWorkingSet = FALSE;
  705. }
  706. DWORD dwRes = MsgWaitForMultipleObjects(nObjects, ahObjectsToWait, FALSE,
  707. fNeedTimer ? 1000 : INFINITE,
  708. QS_ALLINPUT);
  709. //
  710. // Timeout
  711. //
  712. if (dwRes == WAIT_TIMEOUT)
  713. {
  714. //
  715. // We always checks timer on the beginning of the loop
  716. //
  717. continue;
  718. }
  719. //
  720. // An event
  721. //
  722. #pragma warning(push)
  723. #pragma warning(disable:4296)
  724. if (dwRes >= WAIT_OBJECT_0 && dwRes < WAIT_OBJECT_0 + nObjects)
  725. #pragma warning(pop)
  726. {
  727. BOOL fLostConnection;
  728. //
  729. // Ras Event
  730. //
  731. if (m_hEventRasNotify && ahObjectsToWait[dwRes - WAIT_OBJECT_0] == m_hEventRasNotify &&
  732. !CheckRasConnection(fLostConnection))
  733. {
  734. //
  735. // Got a notification that the RAS connection is losted
  736. //
  737. CMTRACE(TEXT("CCmConnection::StateConnectedGetEvent() - m_hEventRasNotify && ahObjectsToWait[dwRes - WAIT_OBJECT_0] == m_hEventRasNotify"));
  738. return EVENT_LOST_CONNECTION;
  739. }
  740. else
  741. {
  742. //
  743. // A watch process exits
  744. // IsIdle() remove the process from the list
  745. //
  746. if (m_WatchProcess.IsIdle())
  747. {
  748. //
  749. // If all the watch process are terminated, change to disconnect countdown
  750. //
  751. CMTRACE(TEXT("CCmConnection::StateConnectedGetEvent() - m_WatchProcess.IsIdle()"));
  752. return EVENT_IDLE;
  753. }
  754. continue;
  755. }
  756. }
  757. //
  758. // A message
  759. //
  760. if (dwRes == WAIT_OBJECT_0 + nObjects)
  761. {
  762. continue;
  763. }
  764. if (-1 == dwRes)
  765. {
  766. CMTRACE1(TEXT("MsgWaitForMultipleObjects failed, LastError:%d"), GetLastError());
  767. //
  768. // Something does wrong
  769. //
  770. continue;
  771. }
  772. //
  773. // what is this return value
  774. //
  775. CMTRACE1(TEXT("MsgWaitForMultipleObjects returns %d"), dwRes);
  776. continue;
  777. }
  778. //
  779. // should never get here
  780. //
  781. MYDBGASSERT(FALSE);
  782. return EVENT_USER_DISCONNECT;
  783. }
  784. //+----------------------------------------------------------------------------
  785. //
  786. // Function: CCmConnection::StateConnectedOnTimer
  787. //
  788. // Synopsis: Process the timer in the state of CONNECTED/COUNTDOWN
  789. //
  790. // Arguments: None
  791. //
  792. // Returns: CCmConnection::CONN_EVENT - The event that can cause the
  793. // connection change the state other than CONNECTED/COUNTDOWN or EVENT_NONE
  794. //
  795. // History: fengsun Created Header 2/18/98
  796. //
  797. //+----------------------------------------------------------------------------
  798. CCmConnection::CONN_EVENT CCmConnection::StateConnectedOnTimer()
  799. {
  800. ASSERT_VALID(this);
  801. MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
  802. if (m_dwState != STATE_CONNECTED && m_dwState != STATE_COUNTDOWN)
  803. {
  804. return EVENT_NONE;
  805. }
  806. //
  807. // Check whether CM is still connected, only if the Ras notify event is not available
  808. //
  809. if (m_hEventRasNotify == NULL)
  810. {
  811. BOOL fLostConnection;
  812. BOOL fConnect = CheckRasConnection(fLostConnection);
  813. CMTRACE2(TEXT("CCmConnection::StateConnectedOnTimer - CheckRasConnection returns %d - fLostConnection is %d"), fConnect, fLostConnection);
  814. if (!fConnect)
  815. {
  816. return (fLostConnection ? EVENT_LOST_CONNECTION : EVENT_USER_DISCONNECT);
  817. }
  818. }
  819. //
  820. // If we don't have stats, something went wrong accessing the
  821. // registry stats earlier, so try to initialize again here.
  822. //
  823. if (OS_W98 && !m_ConnStatistics.IsAvailable())
  824. {
  825. //
  826. // Try to initialize the perf stats from the registry again
  827. //
  828. CMASSERTMSG(FALSE, TEXT("StateConnectedOnTimer() - Statistics unavailable, re-initializing stats now."));
  829. m_ConnStatistics.Open(CMonitor::GetInstance(),
  830. (DWORD)-1,
  831. (DWORD)-1,
  832. m_hRasDial,
  833. m_hRasTunnel);
  834. }
  835. //
  836. // Get statistics for Win9x
  837. //
  838. if (m_ConnStatistics.IsAvailable())
  839. {
  840. m_ConnStatistics.Update();
  841. //
  842. // collecting data points for monitoring idle-disconnect
  843. // check whether ICM has received more data points than the IdleThreshold value
  844. // during the past IDLE_SPREAD time
  845. // Start() is not called for NT
  846. //
  847. if (m_IdleStatistics.IsStarted())
  848. {
  849. m_IdleStatistics.UpdateEveryInterval(m_ConnStatistics.GetBytesRead());
  850. }
  851. }
  852. if (m_dwState == STATE_CONNECTED)
  853. {
  854. //
  855. // Check idle time out
  856. //
  857. if (m_IdleStatistics.IsIdleTimeout())
  858. {
  859. //
  860. // Disconnect count down
  861. //
  862. CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - m_IdleStatistics.IsIdleTimeout()"));
  863. return EVENT_IDLE;
  864. }
  865. //
  866. // Check process watch list
  867. //
  868. if (m_WatchProcess.IsIdle())
  869. {
  870. //
  871. // Disconnect count down
  872. //
  873. CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - m_WatchProcess.IsIdle())"));
  874. return EVENT_IDLE;
  875. }
  876. //
  877. // Update the status window, only if the window is visible
  878. //
  879. if (IsWindowVisible(m_StatusDlg.GetHwnd()))
  880. {
  881. if (m_ConnStatistics.IsAvailable())
  882. {
  883. m_StatusDlg.UpdateStats(m_ConnStatistics.GetBaudRate(),
  884. m_ConnStatistics.GetBytesRead(),
  885. m_ConnStatistics.GetBytesWrite(),
  886. m_ConnStatistics.GetBytesPerSecRead(),
  887. m_ConnStatistics.GetBytesPerSecWrite());
  888. }
  889. //
  890. // We have exact duration numbers from RAS on NT5, so use them.
  891. //
  892. if (m_ConnStatistics.GetDuration())
  893. {
  894. m_StatusDlg.UpdateDuration(m_ConnStatistics.GetDuration() / 1000);
  895. }
  896. else
  897. {
  898. m_StatusDlg.UpdateDuration((GetTickCount() - m_dwConnectStartTime) / 1000);
  899. }
  900. }
  901. return EVENT_NONE;
  902. }
  903. else // m_dwState == STATE_COUNTDOWN
  904. {
  905. //
  906. // Note: NetBEUI seems to insist on sending unsolicited
  907. // stuff over the dial-up adapter. So we will define
  908. // "idle" as "not having received anything".
  909. //
  910. //
  911. // check whether we have new traffic that exceeds the threshold.
  912. // But we don't care about network traffic if the countdown is caused
  913. // by 0 watched process
  914. //
  915. if (!m_WatchProcess.IsIdle() && !m_IdleStatistics.IsIdle() )
  916. {
  917. //
  918. // We were in our idle wait, but we just picked up some
  919. // activity. Stay on line.
  920. // If this is NT5 we don't use our own status dialog so just hide
  921. // it again. If this is downlevel then just change the dialog to
  922. // a status dialog
  923. //
  924. if (OS_NT5)
  925. {
  926. m_StatusDlg.DismissStatusDlg();
  927. }
  928. else
  929. {
  930. m_StatusDlg.ChangeToStatus();
  931. }
  932. m_dwState = STATE_CONNECTED;
  933. return EVENT_NONE;
  934. }
  935. //
  936. // If the time elapsed is more than 30 second, timeout
  937. //
  938. DWORD dwElapsed = GetTickCount() - m_dwCountDownStartTime;
  939. if (dwElapsed > IDLE_DLG_WAIT_TIMEOUT)
  940. {
  941. //
  942. // Connection has been idle for timeout period and the
  943. // grace period is up with no user intervention, so we
  944. // quit/disconnect, and ask for reconnect
  945. //
  946. CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - dwElapsed > IDLE_DLG_WAIT_TIMEOUT"));
  947. return EVENT_COUNTDOWN_ZERO;
  948. }
  949. else
  950. {
  951. //
  952. // Connection has been idle for timeout period but we
  953. // are still in the grace period, show countdown.
  954. //
  955. int nTimeLeft = (int) ((IDLE_DLG_WAIT_TIMEOUT - dwElapsed) / 1000);
  956. //
  957. // Update duration and countdown seconds left.
  958. //
  959. if (m_ConnStatistics.GetDuration()) // NT5 only
  960. {
  961. m_StatusDlg.UpdateCountDown(m_ConnStatistics.GetDuration() / 1000,
  962. nTimeLeft);
  963. }
  964. else
  965. {
  966. m_StatusDlg.UpdateCountDown((GetTickCount() - m_dwConnectStartTime) / 1000,
  967. nTimeLeft);
  968. }
  969. }
  970. return EVENT_NONE;
  971. }
  972. }
  973. //+----------------------------------------------------------------------------
  974. //
  975. // Function: CCmConnection::StateConnectedProcessEvent
  976. //
  977. // Synopsis: Process the connection event while in CONNECTED/COUNTDOWN state
  978. //
  979. // Arguments: CONN_EVENT dwEvent - the event to process
  980. //
  981. // Returns: CCmConnection::CONN_STATE - The new state of the connection
  982. //
  983. // History: fengsun Created Header 2/19/98
  984. //
  985. //+----------------------------------------------------------------------------
  986. CCmConnection::CONN_STATE CCmConnection::StateConnectedProcessEvent(CONN_EVENT dwEvent)
  987. {
  988. ASSERT_VALID(this);
  989. switch (dwEvent)
  990. {
  991. case EVENT_IDLE:
  992. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_IDLE"));
  993. if (m_dwState != STATE_COUNTDOWN)
  994. {
  995. //
  996. // No-traffic/ No-watch-process idle event
  997. // change to Disconnect count down
  998. //
  999. m_dwCountDownStartTime = GetTickCount();
  1000. m_StatusDlg.ChangeToCountDown();
  1001. //
  1002. // Update duration and countdown seconds left
  1003. //
  1004. int nTimeLeft = IDLE_DLG_WAIT_TIMEOUT / 1000;
  1005. if (m_ConnStatistics.GetDuration()) // NT5 only
  1006. {
  1007. m_StatusDlg.UpdateCountDown(m_ConnStatistics.GetDuration() / 1000,
  1008. nTimeLeft);
  1009. }
  1010. else
  1011. {
  1012. m_StatusDlg.UpdateCountDown((GetTickCount() - m_dwConnectStartTime) / 1000,
  1013. nTimeLeft);
  1014. }
  1015. //
  1016. // Don't show the UI if we are at Winlogon unless we are on NT4
  1017. //
  1018. if (!IsLogonAsSystem() || OS_NT4)
  1019. {
  1020. m_StatusDlg.BringToTop();
  1021. }
  1022. }
  1023. return STATE_COUNTDOWN;
  1024. case EVENT_CMDIAL_HANGUP:
  1025. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_CMDIAL_HANGUP"));
  1026. m_Log.Log(DISCONNECT_EXT);
  1027. //
  1028. // Cmdial posted cmmon a message to clean up the connection.
  1029. // Do not need to call hangup here
  1030. //
  1031. StateConnectedCleanup();
  1032. return STATE_TERMINATED;
  1033. case EVENT_LOST_CONNECTION:
  1034. case EVENT_COUNTDOWN_ZERO:
  1035. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_LOST_CONNECTION/EVENT_COUNTDOWN_ZERO"));
  1036. //
  1037. // lost-ras-connection event or the count down counter is down to 0
  1038. //
  1039. if (IsPromptReconnectEnabled() && !m_WatchProcess.IsIdle() ||
  1040. ( dwEvent == EVENT_LOST_CONNECTION && IsAutoReconnectEnabled() ) )
  1041. {
  1042. CmCustomHangup(TRUE); // fPromptReconnect = TRUE, do not remove from Conn Table
  1043. //
  1044. // It is possible
  1045. // Someone else called cmdial to disconnect, while we are disconnecting.
  1046. // If the ref count is down to 0, cmdial will remove the entry
  1047. //
  1048. CM_CONNECTION CmEntry;
  1049. if (CMonitor::ConnTableGetEntry(m_szServiceName, &CmEntry))
  1050. {
  1051. //
  1052. // Cmdial should change the state to CM_RECONNECTPROMPT
  1053. //
  1054. CMTRACE2(TEXT("CmEntry.CmState is %d, event is %d"), CmEntry.CmState, dwEvent);
  1055. MYDBGASSERT(CmEntry.CmState == CM_RECONNECTPROMPT);
  1056. if (EVENT_LOST_CONNECTION == dwEvent)
  1057. {
  1058. m_Log.Log(DISCONNECT_EXT_LOST_CONN);
  1059. }
  1060. else if (EVENT_COUNTDOWN_ZERO == dwEvent)
  1061. {
  1062. m_Log.Log(DISCONNECT_INT_AUTO);
  1063. }
  1064. //
  1065. // is auto reconnect enabled(don't show reconnect prompt)?
  1066. //
  1067. if (dwEvent == EVENT_LOST_CONNECTION && IsAutoReconnectEnabled())
  1068. {
  1069. //
  1070. // On win98 Gold, we have a timing issue with Auto Reconnect because
  1071. // notification that the line was dropped is sent before cleanup for
  1072. // the connection takes place. Thus, we need to poll the connection
  1073. // status using RasGetConnectionStatus until the line is available
  1074. // before trying to reconnect. NTRAID 273033.
  1075. //
  1076. if (OS_W98 && (NULL != m_hRasDial))
  1077. {
  1078. BOOL bConnectionActive;
  1079. BOOL bLostConnection; //ignored
  1080. int iCount = 0;
  1081. do
  1082. {
  1083. bConnectionActive = CheckRasConnection(bLostConnection);
  1084. if (bConnectionActive)
  1085. {
  1086. Sleep(50);
  1087. }
  1088. iCount++;
  1089. // 50 Milliseconds * 200 = 10 seconds
  1090. // If waiting 10 seconds hasn't fixed it, not sure that it will
  1091. // get fixed by this method.
  1092. } while ((200 >= iCount) && (bConnectionActive));
  1093. }
  1094. return STATE_RECONNECTING;
  1095. }
  1096. return STATE_PROMPT_RECONNECT;
  1097. }
  1098. else
  1099. {
  1100. return STATE_TERMINATED;
  1101. }
  1102. }
  1103. //
  1104. // Else fall through
  1105. //
  1106. case EVENT_USER_DISCONNECT:
  1107. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_USER_DISCONNECT"));
  1108. m_Log.Log(DISCONNECT_INT_MANUAL);
  1109. //
  1110. // For EVENT_USER_DISCONNECT
  1111. // User can disconnect from tray icon, status dialog, or countdown dialog.
  1112. // Or prompt reconnect is not enabled
  1113. //
  1114. CmCustomHangup(FALSE); // fPromptReconnect = FALSE
  1115. return STATE_TERMINATED;
  1116. default:
  1117. //
  1118. // Unexpected event, do the same thing as EVENT_USER_DISCONNECT
  1119. //
  1120. MYDBGASSERT(FALSE);
  1121. CmCustomHangup(FALSE); // fPromptReconnect = FALSE
  1122. return STATE_TERMINATED;
  1123. }
  1124. }
  1125. //+----------------------------------------------------------------------------
  1126. //
  1127. // Function: CCmConnection::IsAutoReconnectEnabled
  1128. //
  1129. // Synopsis: See if auto-reconnect is enabled, but only if we aren't logged-in
  1130. // as system.
  1131. //
  1132. // Arguments: None
  1133. //
  1134. // Returns: Nothing
  1135. //
  1136. // History: fengsun Created Header 2/18/98
  1137. // tomkel moved from connection.h 06/06/2001
  1138. //
  1139. //+----------------------------------------------------------------------------
  1140. BOOL CCmConnection::IsAutoReconnectEnabled() const
  1141. {
  1142. //
  1143. // Load the AutoReconnect flag from profile
  1144. // Default is FALSE
  1145. //
  1146. BOOL fReturn = FALSE;
  1147. if (!IsLogonAsSystem())
  1148. {
  1149. fReturn = m_IniService.GPPB(c_pszCmSection, c_pszCmEntryAutoReconnect, FALSE);
  1150. }
  1151. return fReturn;
  1152. }
  1153. //+----------------------------------------------------------------------------
  1154. //
  1155. // Function: CCmConnection::StateConnectedCleanup
  1156. //
  1157. // Synopsis: Cleanup before exiting the connected state
  1158. //
  1159. // Arguments: BOOL fEndSession, whether windows is going to logoff/shutdown
  1160. //
  1161. // Returns: Nothing
  1162. //
  1163. // History: fengsun Created Header 2/18/98
  1164. //
  1165. //+----------------------------------------------------------------------------
  1166. void CCmConnection::StateConnectedCleanup(BOOL fEndSession)
  1167. {
  1168. ASSERT_VALID(this);
  1169. //
  1170. // Remove the trayicon, destroy the status dialog
  1171. //
  1172. m_TrayIcon.RemoveIcon();
  1173. m_ConnStatistics.Close();
  1174. //
  1175. // Do not close window on WM_ENDSESSION. Otherwise, cmmon would be terminated right here
  1176. //
  1177. if (!fEndSession)
  1178. {
  1179. m_StatusDlg.KillRasMonitorWindow();
  1180. DestroyWindow(m_StatusDlg.GetHwnd());
  1181. }
  1182. }
  1183. //+----------------------------------------------------------------------------
  1184. //
  1185. // Function: CCmConnection::IsPromptReconnectEnabled
  1186. //
  1187. // Synopsis: When prompt-reconnect is enabled by the profile
  1188. //
  1189. // Arguments: None
  1190. //
  1191. // Returns: Nothing
  1192. //
  1193. // History: fengsun Created Header 2/18/98
  1194. //
  1195. //+----------------------------------------------------------------------------
  1196. BOOL CCmConnection::IsPromptReconnectEnabled() const
  1197. {
  1198. //
  1199. // Load the NoPromptReconnect flag from profile. Also check the Unattended flag and
  1200. // if we are running in the system account on win2k or Whistler. If any of the above
  1201. // are true then we don't prompt, otherwise we do.
  1202. //
  1203. BOOL fPromptReconnect = !(m_IniService.GPPB(c_pszCmSection,
  1204. c_pszCmEntryNoPromptReconnect, FALSE));
  1205. if (!fPromptReconnect || (m_ReconnectInfo.dwCmFlags & FL_UNATTENDED) || (IsLogonAsSystem() && OS_NT5))
  1206. {
  1207. //
  1208. // Do not prompt reconnect
  1209. //
  1210. return FALSE;
  1211. }
  1212. return TRUE;
  1213. }
  1214. //+----------------------------------------------------------------------------
  1215. //
  1216. // Function: CCmConnection::CmCustomHangup
  1217. //
  1218. // Synopsis: Call cmdial to hangup the connection
  1219. //
  1220. // Arguments: BOOL fPromptReconnect, whether cmmon are going to prompt the
  1221. // reconnect dialog
  1222. // BOOL fEndSession, whther windows is going to logoff/shutdown
  1223. // Default value is FALSE
  1224. //
  1225. // Returns: Nothing
  1226. //
  1227. // History: fegnsun Created Header 2/11/98
  1228. //
  1229. //+----------------------------------------------------------------------------
  1230. BOOL CCmConnection::CmCustomHangup(BOOL fPromptReconnect, BOOL fEndSession)
  1231. {
  1232. ASSERT_VALID(this);
  1233. CMTRACE2(TEXT("CCmConnection::CmCustomHangup - fPromptReconnect is %d and fEndSession is %d"), fPromptReconnect, fEndSession);
  1234. //
  1235. // Remove the trayicon close status dlg, before hangup
  1236. //
  1237. StateConnectedCleanup(fEndSession);
  1238. //
  1239. // It is possible the connection is disconnected or disconnecting
  1240. //
  1241. CM_CONNECTION CmEntry;
  1242. if (!CMonitor::ConnTableGetEntry(m_szServiceName, &CmEntry) ||
  1243. CmEntry.CmState != CM_CONNECTED)
  1244. {
  1245. //
  1246. // The connection is disconnected by someone else, do not hangup.
  1247. //
  1248. CMTRACE(TEXT("CCmConnection::CmCustomHangup - Entry is not connected, canceling hangup"));
  1249. return TRUE;
  1250. }
  1251. //
  1252. // Call cmdial32.dll CmCustomHangup
  1253. //
  1254. //
  1255. // The destructor of CDynamicLibrary calls FreeLibrary
  1256. //
  1257. CDynamicLibrary LibCmdial;
  1258. if (!LibCmdial.Load(TEXT("cmdial32.dll")))
  1259. {
  1260. MYDBGASSERT(FALSE);
  1261. return FALSE;
  1262. }
  1263. CmCustomHangUpFUNC pfnCmCustomHangUp;
  1264. pfnCmCustomHangUp = (CmCustomHangUpFUNC)LibCmdial.GetProcAddress(c_pszCmHangup);
  1265. MYDBGASSERT(pfnCmCustomHangUp);
  1266. if (pfnCmCustomHangUp)
  1267. {
  1268. //
  1269. // hRasConn = NULL,
  1270. // fIgnoreRefCount = TRUE, always except for InetDialHandler
  1271. // fPersist = fPromptReconnect, do not remove the entry if
  1272. // we are going to prompt for reconnect
  1273. //
  1274. DWORD dwRet;
  1275. dwRet = pfnCmCustomHangUp(NULL, m_szServiceName, TRUE, fPromptReconnect);
  1276. CMTRACE1(TEXT("CCmConnection::CmCustomHangup -- Return Value from CmCustomHangup is %u"), dwRet);
  1277. return (ERROR_SUCCESS == dwRet);
  1278. }
  1279. return FALSE;
  1280. }
  1281. //+----------------------------------------------------------------------------
  1282. //
  1283. // Function: CCmConnection::CallRasConnectionNotification
  1284. //
  1285. // Synopsis: call RasConnectionNotify. Ras will set the event when connection
  1286. // is lost
  1287. //
  1288. // Arguments: HRASCONN hRasDial - The dial ras handle
  1289. // HRASCONN hRasTunnel - The tunnel ras handle
  1290. //
  1291. // Returns: HANDLE - the event handle, or NULL if failed
  1292. //
  1293. // History: Created Header 2/17/98
  1294. //
  1295. //+----------------------------------------------------------------------------
  1296. HANDLE CCmConnection::CallRasConnectionNotification(HRASCONN hRasDial, HRASCONN hRasTunnel)
  1297. {
  1298. DWORD dwRes;
  1299. MYDBGASSERT(hRasDial || hRasTunnel);
  1300. //
  1301. // Call RasConnectionNotification. Ras will call us back when connection is losted.
  1302. // However, this fuction is not avaliable for Win95 with DUN 1.0
  1303. //
  1304. if (!m_RasApiDll.HasRasConnectionNotification())
  1305. {
  1306. return NULL;
  1307. }
  1308. HANDLE hEvent = NULL;
  1309. if (OS_W9X)
  1310. {
  1311. //
  1312. // Create an manual-reset non-signaled event on Win95, Win98 & WinME
  1313. //
  1314. hEvent = CreateEventU(NULL, TRUE, FALSE, NULL);
  1315. }
  1316. else
  1317. {
  1318. //
  1319. // Create an auto-reset non-signaled event
  1320. //
  1321. hEvent = CreateEventU(NULL, FALSE, FALSE, NULL);
  1322. }
  1323. //
  1324. // v-vijayb: Changed to use INVALID_HANDLE_VALUE(notify for all disconnects), as we where
  1325. // not getting notified after a reconnect or after connecting thru winlogon.
  1326. // StateConnectedGetEvent() will check if this connection is lost to determine if it was
  1327. // a disconnection of this connection or some other.
  1328. //
  1329. if (hRasDial)
  1330. {
  1331. //
  1332. // Copied from RAS.h
  1333. // #if (WINVER >= 0x401)
  1334. //
  1335. #define RASCN_Disconnection 0x00000002
  1336. if (OS_NT)
  1337. {
  1338. dwRes = m_RasApiDll.RasConnectionNotification((HRASCONN) INVALID_HANDLE_VALUE, hEvent, RASCN_Disconnection);
  1339. }
  1340. else
  1341. {
  1342. dwRes = m_RasApiDll.RasConnectionNotification(hRasDial, hEvent, RASCN_Disconnection);
  1343. }
  1344. if (dwRes != ERROR_SUCCESS)
  1345. {
  1346. CMASSERTMSG(FALSE, TEXT("RasConnectionNotification Failed"));
  1347. CloseHandle(hEvent);
  1348. return NULL;
  1349. }
  1350. }
  1351. if (hRasTunnel)
  1352. {
  1353. if (OS_NT)
  1354. {
  1355. dwRes = m_RasApiDll.RasConnectionNotification((HRASCONN) INVALID_HANDLE_VALUE, hEvent, RASCN_Disconnection);
  1356. }
  1357. else
  1358. {
  1359. dwRes = m_RasApiDll.RasConnectionNotification(hRasTunnel, hEvent, RASCN_Disconnection);
  1360. }
  1361. if (dwRes != ERROR_SUCCESS)
  1362. {
  1363. CMASSERTMSG(FALSE, TEXT("RasConnectionNotification Failed"));
  1364. CloseHandle(hEvent);
  1365. return NULL;
  1366. }
  1367. }
  1368. return hEvent;
  1369. }
  1370. //+---------------------------------------------------------------------------
  1371. //
  1372. // struct RASMON_THREAD_INFO
  1373. //
  1374. // Description: Information passed to RasMonitorDlgThread by OnStatusDetails
  1375. //
  1376. // History: fengsun Created 2/11/98
  1377. //
  1378. //----------------------------------------------------------------------------
  1379. struct RASMON_THREAD_INFO
  1380. {
  1381. HRASCONN hRasConn; // the ras handle to display status
  1382. HWND hwndParent; // the parent window for the RasMonitorDlg
  1383. };
  1384. //+----------------------------------------------------------------------------
  1385. //
  1386. // Function: CCmConnection::OnStatusDetails
  1387. //
  1388. // Synopsis: Called upon pressing detailed button on NT status dlg
  1389. // Call RasMonitorDlg to display the dial-up monitor
  1390. //
  1391. // Arguments: None
  1392. //
  1393. // Returns: Nothing
  1394. //
  1395. // History: fengsun Created Header 2/11/98
  1396. //
  1397. //+----------------------------------------------------------------------------
  1398. void CCmConnection::OnStatusDetails()
  1399. {
  1400. ASSERT_VALID(this);
  1401. //
  1402. // RasDlg.dll is not available for Win9X
  1403. //
  1404. MYDBGASSERT(OS_NT4);
  1405. if (!OS_NT4)
  1406. {
  1407. return;
  1408. }
  1409. //
  1410. // RasMonitorDlg pops up a modal dialog box, which will block the thread message loop.
  1411. // No thread message or event can be processed
  1412. // Create another thread to call RasMonitorDlg
  1413. //
  1414. //
  1415. // Alloc the parametor from heap. It is not safe to use stack here.
  1416. // CreateThread can return before the thread routine is called
  1417. // The thread is responsible to free the pointer
  1418. //
  1419. RASMON_THREAD_INFO* pInfo = (RASMON_THREAD_INFO*)CmMalloc(sizeof(RASMON_THREAD_INFO));
  1420. if (NULL == pInfo)
  1421. {
  1422. CMTRACE(TEXT("CCmConnection::OnStatusDetails alloc for pInfo failed"));
  1423. return;
  1424. }
  1425. pInfo->hRasConn = m_hRasTunnel?m_hRasTunnel : m_hRasDial;
  1426. pInfo->hwndParent = m_StatusDlg.GetHwnd();
  1427. DWORD dwThreadId;
  1428. HANDLE hThread;
  1429. if ((hThread = CreateThread(NULL, 0, RasMonitorDlgThread ,pInfo,0,&dwThreadId)) == NULL)
  1430. {
  1431. MYDBGASSERT(FALSE);
  1432. CMTRACE(TEXT("CCmConnection::OnStatusDetails CreateThread failed"));
  1433. CmFree(pInfo);
  1434. return ;
  1435. }
  1436. CloseHandle(hThread);
  1437. }
  1438. //+----------------------------------------------------------------------------
  1439. //
  1440. // Function: static CCmConnection::RasMonitorDlgThread
  1441. //
  1442. // Synopsis: The thread to call RasMonitorDlg to avoid blocking the thread
  1443. // message loop. RasMonitorDlg is a modal dialogbox. The thead exits
  1444. // when the dialog is closed. That happens when user close the dialog box,
  1445. // or m_StatusDlg.KillRasMonitorWindow() is called
  1446. //
  1447. // Arguments: LPVOID lParam - RASMON_THREAD_INFO* the information passed to the thread
  1448. //
  1449. // Returns: DWORD WINAPI - The thread return value
  1450. //
  1451. // History: fengsun Created Header 2/19/98
  1452. //
  1453. //+----------------------------------------------------------------------------
  1454. DWORD WINAPI CCmConnection::RasMonitorDlgThread(LPVOID lParam)
  1455. {
  1456. MYDBGASSERT(lParam);
  1457. RASMON_THREAD_INFO* pInfo = (RASMON_THREAD_INFO*)lParam;
  1458. MYDBGASSERT(pInfo->hRasConn);
  1459. //
  1460. // Get the device name first, if tunnel is available, use tunnel device
  1461. //
  1462. RASCONNSTATUS rcsStatus;
  1463. memset(&rcsStatus,0,sizeof(rcsStatus));
  1464. rcsStatus.dwSize = sizeof(rcsStatus);
  1465. //
  1466. // Load Rasapi32.dll here. This dll is not loaded on NT
  1467. // Destructor will unload the DLL
  1468. //
  1469. CRasApiDll rasApiDll;
  1470. if (!rasApiDll.Load())
  1471. {
  1472. MYDBGASSERT(FALSE);
  1473. CmFree(pInfo);
  1474. return 1;
  1475. }
  1476. DWORD dwRes = rasApiDll.RasGetConnectStatus(pInfo->hRasConn, &rcsStatus);
  1477. CMASSERTMSG(dwRes == ERROR_SUCCESS, TEXT("RasGetConnectStatus failed"));
  1478. //
  1479. // The connection is lost. Still call RasMonitorDlg with empty name
  1480. //
  1481. RASMONITORDLG RasInfo;
  1482. WORD (WINAPI *pfnFunc)(LPTSTR,LPRASMONITORDLG) = NULL;
  1483. ZeroMemory(&RasInfo,sizeof(RasInfo));
  1484. RasInfo.dwSize = sizeof(RasInfo);
  1485. RasInfo.hwndOwner = pInfo->hwndParent;
  1486. RasInfo.dwStartPage = RASMDPAGE_Status;
  1487. CmFree(pInfo);
  1488. //
  1489. // Call rasdlg.dll -> RasMonitorDlg
  1490. //
  1491. //
  1492. // The destructor of CDynamicLibrary calls FreeLibrary
  1493. //
  1494. CDynamicLibrary LibRasdlg;
  1495. if (!LibRasdlg.Load(TEXT("RASDLG.DLL")))
  1496. {
  1497. CMTRACE1(TEXT("Rasdlg.dll LoadLibrary() failed, GLE=%u."), GetLastError());
  1498. return 1;
  1499. }
  1500. pfnFunc = (WORD (WINAPI *)(LPTSTR,LPRASMONITORDLG))LibRasdlg.GetProcAddress("RasMonitorDlg"A_W);
  1501. if (pfnFunc)
  1502. {
  1503. pfnFunc(rcsStatus.szDeviceName, &RasInfo);
  1504. }
  1505. LibRasdlg.Unload();
  1506. rasApiDll.Unload();
  1507. //
  1508. // Minimize the working set before exit the thread.
  1509. //
  1510. CMonitor::MinimizeWorkingSet();
  1511. return 0;
  1512. }
  1513. //+----------------------------------------------------------------------------
  1514. //
  1515. // Function: CCmConnection::StatePrompt
  1516. //
  1517. // Synopsis: The connection is in the prompt-reconnect stste
  1518. // Run the message loop until state is changed
  1519. //
  1520. // Arguments: None
  1521. //
  1522. // Returns: CONN_STATE - The new state, either STATE_TERMINATED or
  1523. // STATE_RECONNECTING
  1524. //
  1525. // History: fengsun Created Header 2/4/98
  1526. //
  1527. //+----------------------------------------------------------------------------
  1528. CCmConnection::CONN_STATE CCmConnection::StatePrompt()
  1529. {
  1530. ASSERT_VALID(this);
  1531. MYDBGASSERT(m_dwState == STATE_PROMPT_RECONNECT);
  1532. // MYDBGASSERT(!IsWindow(m_StatusDlg.GetHwnd()));
  1533. CMTRACE(TEXT("Enter StatePrompt"));
  1534. LPTSTR pszReconnectMsg = CmFmtMsg(CMonitor::GetInstance(),IDMSG_RECONNECT,m_szServiceName);
  1535. m_ReconnectDlg.Create(CMonitor::GetInstance(), NULL,
  1536. pszReconnectMsg,m_hBigIcon);
  1537. CmFree(pszReconnectMsg);
  1538. //
  1539. // Change the window position, so multiple status window will not be at
  1540. // the same position
  1541. //
  1542. PositionWindow(m_ReconnectDlg.GetHwnd(), m_dwPositionId);
  1543. //
  1544. // Change the dialog titlebar icon. This does not work,
  1545. // Reconnect dialog does not have system menu icon
  1546. //
  1547. SendMessageU(m_ReconnectDlg.GetHwnd(),WM_SETICON,ICON_BIG,(LPARAM) m_hBigIcon);
  1548. SendMessageU(m_ReconnectDlg.GetHwnd(),WM_SETICON,ICON_SMALL,(LPARAM) m_hSmallIcon);
  1549. //
  1550. // Minimize the working set
  1551. //
  1552. CMonitor::MinimizeWorkingSet();
  1553. MSG msg;
  1554. while(GetMessageU(&msg, NULL,0,0))
  1555. {
  1556. if (msg.hwnd == NULL)
  1557. {
  1558. //
  1559. // it is a thread message
  1560. //
  1561. MYDBGASSERT((msg.message >= WM_APP) || (msg.message == WM_CONN_EVENT));
  1562. if (msg.message == WM_CONN_EVENT)
  1563. {
  1564. MYDBGASSERT(msg.wParam == EVENT_USER_DISCONNECT
  1565. || msg.wParam == EVENT_RECONNECT
  1566. || msg.wParam == EVENT_CMDIAL_HANGUP);
  1567. break;
  1568. }
  1569. }
  1570. else
  1571. {
  1572. //
  1573. // Also dispatch message for Modeless dialog box
  1574. //
  1575. if (!IsDialogMessageU(m_ReconnectDlg.GetHwnd(), &msg))
  1576. {
  1577. TranslateMessage(&msg);
  1578. DispatchMessageU(&msg);
  1579. }
  1580. }
  1581. }
  1582. //
  1583. // If the status window is not destroyed yet, destroy it now
  1584. //
  1585. if (IsWindow(m_ReconnectDlg.GetHwnd()))
  1586. {
  1587. DestroyWindow(m_ReconnectDlg.GetHwnd());
  1588. }
  1589. if (msg.wParam == EVENT_RECONNECT)
  1590. {
  1591. return STATE_RECONNECTING;
  1592. }
  1593. else
  1594. {
  1595. return STATE_TERMINATED;
  1596. }
  1597. }
  1598. //+----------------------------------------------------------------------------
  1599. //
  1600. // Function: CCmConnection::OnTrayIcon
  1601. //
  1602. // Synopsis: called Upon tray icon message
  1603. //
  1604. // Arguments: WPARAM wParam - wParam of the message
  1605. // LPARAM lParam - lParam of the message
  1606. //
  1607. // Returns: DWORD - return value of the message
  1608. //
  1609. // History: fengsun Created Header 2/4/98
  1610. //
  1611. //+----------------------------------------------------------------------------
  1612. DWORD CCmConnection::OnTrayIcon(WPARAM, LPARAM lParam)
  1613. {
  1614. ASSERT_VALID(this);
  1615. MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
  1616. switch (lParam)
  1617. {
  1618. case WM_LBUTTONDBLCLK:
  1619. //
  1620. // Don't show the UI if we are at Winlogon unless we are on NT4
  1621. //
  1622. if (!IsLogonAsSystem() || OS_NT4)
  1623. {
  1624. m_StatusDlg.BringToTop();
  1625. }
  1626. break;
  1627. case WM_RBUTTONUP:
  1628. {
  1629. //
  1630. // Popup the tray icon menu at the mouse location
  1631. //
  1632. POINT PosMouse;
  1633. if (!GetCursorPos(&PosMouse))
  1634. {
  1635. MYDBGASSERT(FALSE);
  1636. break;
  1637. }
  1638. //
  1639. // From Microsoft Knowledge Base
  1640. // PRB: Menus for Notification Icons Don't Work Correctly
  1641. // To correct the first behavior, you need to make the current window
  1642. // the foreground window before calling TrackPopupMenu
  1643. //
  1644. // see also fixes for Whistler bugs 41696 and 90576
  1645. //
  1646. if (FALSE == SetForegroundWindow(m_StatusDlg.GetHwnd()))
  1647. {
  1648. CMTRACE(TEXT("SetForegroundWindow before TrackPopupMenu failed"));
  1649. }
  1650. m_TrayIcon.PopupMenu(PosMouse.x, PosMouse.y, m_StatusDlg.GetHwnd());
  1651. PostMessageU(m_StatusDlg.GetHwnd(), WM_NULL, 0, 0);
  1652. }
  1653. break;
  1654. default:
  1655. break;
  1656. }
  1657. return TRUE;
  1658. }
  1659. //+----------------------------------------------------------------------------
  1660. //
  1661. // Function: CCmConnection::OnStayOnLine
  1662. //
  1663. // Synopsis: Called when "Stay Online" button is pressed in the disconnect
  1664. // count down dialog box
  1665. //
  1666. // Arguments: None
  1667. //
  1668. // Returns: Nothing
  1669. //
  1670. // History: fengsun Created Header 2/11/98
  1671. //
  1672. //+----------------------------------------------------------------------------
  1673. void CCmConnection::OnStayOnLine()
  1674. {
  1675. ASSERT_VALID(this);
  1676. //
  1677. // Change the dialog to display status
  1678. //
  1679. m_StatusDlg.ChangeToStatus();
  1680. m_dwState = STATE_CONNECTED;
  1681. if (m_WatchProcess.IsIdle())
  1682. {
  1683. //
  1684. // If idle bacause of no watching process
  1685. // User clickes stay online, 0 watch process is not idle any more
  1686. //
  1687. m_WatchProcess.SetNotIdle();
  1688. }
  1689. if (m_IdleStatistics.IsIdleTimeout())
  1690. {
  1691. //
  1692. // If idle bacause of no traffic
  1693. // User clickes stay online, restart the idle counter
  1694. //
  1695. m_IdleStatistics.Reset();
  1696. }
  1697. }
  1698. //+----------------------------------------------------------------------------
  1699. //
  1700. // Function: CCmConnection::PostHangupMsg
  1701. //
  1702. // Synopsis: Called by monitor to clean up the connection. The request was
  1703. // come from cmdial to remove tray icon and status dialog
  1704. // cmdial is responsible to actually hangup the connection
  1705. //
  1706. // Arguments: None
  1707. //
  1708. // Returns: Nothing
  1709. //
  1710. // History: fengsun Created Header 2/11/98
  1711. //
  1712. //+----------------------------------------------------------------------------
  1713. void CCmConnection::PostHangupMsg() const
  1714. {
  1715. //
  1716. // NOTE: This function is called within the Monitor thread
  1717. // Do not referrence any volatile data
  1718. // The CMMON_HANGUP_INFO request can come in at any state
  1719. //
  1720. //
  1721. // Post a message, so this will be handled in the connection thread
  1722. //
  1723. BOOL fRet = PostThreadMessageU(m_dwThreadId,WM_CONN_EVENT, EVENT_CMDIAL_HANGUP, 0);
  1724. #if DBG
  1725. if (FALSE == fRet)
  1726. {
  1727. CMTRACE1(TEXT("CCmConnection::PostHangupMsg -- PostThreadMessage failed (GLE = %d)"), GetLastError());
  1728. }
  1729. #endif
  1730. }
  1731. //+----------------------------------------------------------------------------
  1732. //
  1733. // Function: CCmConnection::Reconnect
  1734. //
  1735. // Synopsis: The connection is in reconnecting state
  1736. // Simply load cmdial32.dll and call CmCustomDialDlg
  1737. //
  1738. // Arguments: None
  1739. //
  1740. // Returns: BOOL whether reconnect successfully
  1741. //
  1742. // History: fengsun Created Header 2/11/98
  1743. //
  1744. //+----------------------------------------------------------------------------
  1745. BOOL CCmConnection::Reconnect()
  1746. {
  1747. ASSERT_VALID(this);
  1748. MYDBGASSERT(m_dwState == STATE_RECONNECTING);
  1749. CMTRACE(TEXT("Enter Reconnect"));
  1750. //
  1751. // Load cmdial32.dll and call CmReConnect();
  1752. //
  1753. //
  1754. // The destructor of CDynamicLibrary calls FreeLibrary
  1755. //
  1756. CDynamicLibrary LibCmdial;
  1757. if (!LibCmdial.Load(TEXT("cmdial32.dll")))
  1758. {
  1759. MYDBGASSERT(FALSE);
  1760. return FALSE;
  1761. }
  1762. CmReConnectFUNC pfnCmReConnect;
  1763. pfnCmReConnect = (CmReConnectFUNC)LibCmdial.GetProcAddress(c_pszCmReconnect);
  1764. MYDBGASSERT(pfnCmReConnect);
  1765. if (!pfnCmReConnect)
  1766. {
  1767. return FALSE;
  1768. }
  1769. //
  1770. // Log that we're reconnecting
  1771. //
  1772. m_Log.Log(RECONNECT_EVENT);
  1773. //
  1774. // If we have a RAS phonebook name pass it to reconnect, else NULL for system
  1775. //
  1776. if (m_szRasPhoneBook[0])
  1777. {
  1778. return (pfnCmReConnect(m_szRasPhoneBook, m_szServiceName, &m_ReconnectInfo));
  1779. }
  1780. else
  1781. {
  1782. return (pfnCmReConnect(NULL, m_szServiceName, &m_ReconnectInfo));
  1783. }
  1784. }
  1785. //+----------------------------------------------------------------------------
  1786. //
  1787. // Function: CCmConnection::CheckRasConnection
  1788. //
  1789. // Synopsis: Check whether the RAS connection is still connected
  1790. //
  1791. // Arguments: OUT BOOL& fLostConnection -
  1792. // If the no longer connected, TRUE means lost connection
  1793. // FALSE means user disconnect
  1794. //
  1795. // Returns: BOOL - Whether still connected
  1796. //
  1797. // History: fengsun Created Header 2/8/98
  1798. //
  1799. //+----------------------------------------------------------------------------
  1800. BOOL CCmConnection::CheckRasConnection(OUT BOOL& fLostConnection)
  1801. {
  1802. ASSERT_VALID(this);
  1803. MYDBGASSERT(m_hRasDial != NULL || m_hRasTunnel != NULL);
  1804. // Whether we are still connected
  1805. BOOL fConnected = TRUE;
  1806. RASCONNSTATUS rcsStatus;
  1807. DWORD dwRes = ERROR_SUCCESS;
  1808. if (NULL == m_hRasDial && NULL == m_hRasTunnel)
  1809. {
  1810. //
  1811. // If both m_hRasTunnel and m_hRasDial are NULL, we're definitely not
  1812. // connected. Yes, we have lost the connection, and return value is FALSE.
  1813. //
  1814. fLostConnection = TRUE;
  1815. return FALSE;
  1816. }
  1817. // check tunnel status first
  1818. if (m_hRasTunnel != NULL)
  1819. {
  1820. memset(&rcsStatus,0,sizeof(rcsStatus));
  1821. rcsStatus.dwSize = sizeof(rcsStatus);
  1822. //
  1823. // This function will load RASAPI32.dll, if not loaded yet
  1824. // Will not unload RASAPI32.dll, since this function is called on timer
  1825. //
  1826. dwRes = m_RasApiDll.RasGetConnectStatus(m_hRasTunnel, &rcsStatus);
  1827. if (dwRes != ERROR_SUCCESS || rcsStatus.rasconnstate == RASCS_Disconnected)
  1828. {
  1829. //
  1830. // The connection is lost
  1831. //
  1832. fConnected = FALSE;
  1833. }
  1834. }
  1835. // check dialup connection status
  1836. if (fConnected && m_hRasDial != NULL)
  1837. {
  1838. memset(&rcsStatus,0,sizeof(rcsStatus));
  1839. rcsStatus.dwSize = sizeof(rcsStatus);
  1840. dwRes = m_RasApiDll.RasGetConnectStatus(m_hRasDial, &rcsStatus);
  1841. }
  1842. if ((dwRes == ERROR_SUCCESS)
  1843. && ((rcsStatus.rasconnstate != RASCS_Disconnected)
  1844. || (rcsStatus.dwError == PENDING)))
  1845. {
  1846. // CMTRACE(TEXT("CCmConnection::CheckRasConnection - rcsStatus.rasconnstate != RASCS_Disconnected || rcsStatus.dwError == PENDING"));
  1847. return TRUE;
  1848. }
  1849. //
  1850. // CM is no longer connected
  1851. //
  1852. CMTRACE3(TEXT("OnTimer() RasGetConnectStatus() returns %u, rasconnstate=%u, dwError=%u."), dwRes,
  1853. rcsStatus.rasconnstate, rcsStatus.dwError);
  1854. if (rcsStatus.dwError == ERROR_USER_DISCONNECTION && OS_W9X)
  1855. {
  1856. fLostConnection = FALSE;
  1857. //
  1858. // On NT, ERROR_USER_DISCONNECTION is received in
  1859. // the event of idle disconnect which we consider
  1860. // a lost connection
  1861. //
  1862. }
  1863. else
  1864. {
  1865. fLostConnection = TRUE;
  1866. }
  1867. return FALSE;
  1868. }
  1869. //+----------------------------------------------------------------------------
  1870. //
  1871. // Function: CCmConnection::OnEndSession
  1872. //
  1873. // Synopsis: Called upon WM_ENDSESSION message
  1874. //
  1875. // Arguments: BOOL fEndSession - whether the session is being ended, wParam
  1876. // BOOL fLogOff - whether the user is logging off or shutting down,
  1877. // lParam
  1878. //
  1879. // Returns: Nothing
  1880. //
  1881. // History: fengsun Created Header 5/4/98
  1882. //
  1883. //+----------------------------------------------------------------------------
  1884. BOOL CCmConnection::OnEndSession(BOOL fEndSession, BOOL)
  1885. {
  1886. CMTRACE(TEXT("CCmConnection::OnEndSession"));
  1887. if (fEndSession)
  1888. {
  1889. //
  1890. // The session can end any time after this function returns
  1891. // If we are connected, hangup the connection
  1892. //
  1893. if(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN)
  1894. {
  1895. return CmCustomHangup(FALSE, TRUE); // fPromptReconnect = FALSE, fEndSession = TRUE
  1896. }
  1897. }
  1898. return TRUE;
  1899. }
  1900. //+----------------------------------------------------------------------------
  1901. //
  1902. // Function: CCmConnection::OnAdditionalTrayMenu
  1903. //
  1904. // Synopsis: Called upon additional menuitem is selected from tray icon menu
  1905. //
  1906. // Arguments: WORD nCmd - LOWORD(wParam) of WM_COMMAND, the menu id
  1907. //
  1908. // Returns: Nothing
  1909. //
  1910. // History: fengsun Created Header 2/12/98
  1911. //
  1912. //+----------------------------------------------------------------------------
  1913. void CCmConnection::OnAdditionalTrayMenu(WORD nCmd)
  1914. {
  1915. ASSERT_VALID(this);
  1916. MYDBGASSERT( (nCmd >= IDM_TRAYMENU)
  1917. && nCmd <(IDM_TRAYMENU + m_TrayIcon.GetAdditionalMenuNum()));
  1918. nCmd -= IDM_TRAYMENU; // get the index for the command
  1919. if (nCmd >= m_TrayIcon.GetAdditionalMenuNum())
  1920. {
  1921. return;
  1922. }
  1923. //
  1924. // Run the command line
  1925. //
  1926. ExecCmdLine(m_TrayIcon.GetMenuCommand(nCmd), m_IniService.GetFile());
  1927. }
  1928. //+----------------------------------------------------------------------------
  1929. //
  1930. // Function: CCmConnection::GetProcessId
  1931. //
  1932. // Synopsis: Find the process specified by pszModule & returns its PID
  1933. //
  1934. // Arguments: WCHAR *pszModule
  1935. //
  1936. // Returns: PID of process or 0 if not found
  1937. //
  1938. // History: v-vijayb Created 7/20/99
  1939. //
  1940. //+----------------------------------------------------------------------------
  1941. DWORD CCmConnection::GetProcessId(WCHAR *pszModule)
  1942. {
  1943. DWORD dwPID = 0;
  1944. HINSTANCE hInstLib;
  1945. DWORD cbPIDs, cbNeeded, iPID, cPIDs;
  1946. DWORD *pdwPIDs = NULL;
  1947. HANDLE hProcess;
  1948. HMODULE hMod;
  1949. WCHAR szFileName[MAX_PATH + 1];
  1950. //
  1951. // PSAPI Function Pointers.
  1952. //
  1953. BOOL (WINAPI *lpfEnumProcesses)(DWORD *, DWORD cb, DWORD *);
  1954. BOOL (WINAPI *lpfEnumProcessModules)(HANDLE, HMODULE *, DWORD, LPDWORD);
  1955. DWORD (WINAPI *lpfGetModuleBaseName)(HANDLE, HMODULE, WCHAR *, DWORD);
  1956. hInstLib = LoadLibrary(TEXT("PSAPI.DLL"));
  1957. if (hInstLib == NULL)
  1958. {
  1959. return (0);
  1960. }
  1961. //
  1962. // Get procedure addresses.
  1963. //
  1964. lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*)) GetProcAddress(hInstLib, "EnumProcesses") ;
  1965. lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress(hInstLib, "EnumProcessModules") ;
  1966. lpfGetModuleBaseName =(DWORD (WINAPI *)(HANDLE, HMODULE, WCHAR *, DWORD)) GetProcAddress(hInstLib, "GetModuleBaseNameW") ;
  1967. if (lpfEnumProcesses == NULL || lpfEnumProcessModules == NULL || lpfGetModuleBaseName == NULL)
  1968. {
  1969. goto OnError;
  1970. }
  1971. cbPIDs = 256 * sizeof(DWORD);
  1972. cbNeeded = 0;
  1973. do
  1974. {
  1975. if (pdwPIDs != NULL)
  1976. {
  1977. HeapFree(GetProcessHeap(), 0, pdwPIDs);
  1978. cbPIDs = cbNeeded;
  1979. }
  1980. pdwPIDs = (DWORD *) CmMalloc(cbPIDs);
  1981. if (pdwPIDs == NULL)
  1982. {
  1983. goto OnError;
  1984. }
  1985. if (!lpfEnumProcesses(pdwPIDs, cbPIDs, &cbNeeded))
  1986. {
  1987. goto OnError;
  1988. }
  1989. } while (cbNeeded > cbPIDs);
  1990. cPIDs = cbNeeded / sizeof(DWORD);
  1991. for (iPID = 0; iPID < cPIDs; iPID ++)
  1992. {
  1993. szFileName[0] = 0;
  1994. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pdwPIDs[iPID]);
  1995. if (hProcess != NULL)
  1996. {
  1997. if (lpfEnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
  1998. {
  1999. if (lpfGetModuleBaseName(hProcess, hMod, szFileName, sizeof(szFileName)))
  2000. {
  2001. if (lstrcmpiW(pszModule, szFileName) == 0)
  2002. {
  2003. dwPID = pdwPIDs[iPID];
  2004. }
  2005. }
  2006. }
  2007. CloseHandle(hProcess);
  2008. if (dwPID != 0)
  2009. {
  2010. break;
  2011. }
  2012. }
  2013. }
  2014. OnError:
  2015. if (pdwPIDs != NULL)
  2016. {
  2017. CmFree(pdwPIDs);
  2018. }
  2019. FreeLibrary(hInstLib);
  2020. return (dwPID);
  2021. }
  2022. typedef BOOL (WINAPI *lpfDuplicateTokenEx)(
  2023. HANDLE,
  2024. DWORD,
  2025. LPSECURITY_ATTRIBUTES,
  2026. SECURITY_IMPERSONATION_LEVEL,
  2027. TOKEN_TYPE,
  2028. PHANDLE
  2029. );
  2030. //+----------------------------------------------------------------------------
  2031. //
  2032. // Function: CCmConnection::RunAsUser
  2033. //
  2034. // Synopsis: Run the action as an exe or other shell object on the choosen desktop
  2035. //
  2036. // Arguments: WCHAR *pszProgram - name of module to be launched
  2037. // WCHAR *pszParams - parameters to be passed to module
  2038. // WCHAR *pszDesktop - desktop on which to launch module
  2039. //
  2040. // Returns: HANDLE - The action Process handle, for Win32 only
  2041. //
  2042. // History: 07/19/99 v-vijayb Created
  2043. // 07/27/99 nickball Return codes and explicit path.
  2044. //
  2045. //+----------------------------------------------------------------------------
  2046. HANDLE CCmConnection::RunAsUser(WCHAR *pszProgram, WCHAR *pszParams, WCHAR *pszDesktop)
  2047. {
  2048. STARTUPINFOW StartupInfo = {0};
  2049. PROCESS_INFORMATION ProcessInfo = {0};
  2050. WCHAR szShell[MAX_PATH + 1];
  2051. DWORD dwPID;
  2052. HANDLE hProcess = NULL;
  2053. HANDLE hUserToken = NULL;
  2054. HANDLE hProcessToken = NULL;
  2055. MYDBGASSERT(pszProgram);
  2056. CMTRACE(TEXT("RunAsUser"));
  2057. //
  2058. // NOTE: Normally we only have one icon in the systray, being run by Explorer,
  2059. // thus any menuitem execution is done in the user's account. On NT4,
  2060. // we can't rely on the Connections Folder to handle this for us, so we
  2061. // create and manage the systray icon ourselves, but we have to make
  2062. // sure than any items executed, are executed using the User's account.
  2063. // Or, on NT5 and later, we can have the strange case where HideTrayIcon
  2064. // is set to 0, thus requiring us to create/manage a systray icon (so
  2065. // there are 2 connectoids in the systray), hence the code below checks
  2066. // for all flavors of NT (rather than just NT4).
  2067. //
  2068. if (!OS_NT)
  2069. {
  2070. MYDBGASSERT(FALSE);
  2071. return NULL;
  2072. }
  2073. //
  2074. // Get the PID for the shell. We expect explorer.exe, but could be others
  2075. //
  2076. GetProfileString(TEXT("windows"), TEXT("shell"), TEXT("explorer.exe"), szShell, MAX_PATH);
  2077. dwPID = GetProcessId(szShell);
  2078. //
  2079. // Now extract the token from the shell process
  2080. //
  2081. if (dwPID)
  2082. {
  2083. //
  2084. // Get the Process handle from the PID
  2085. //
  2086. hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID);
  2087. CMTRACE1(TEXT("RunAsUser/OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID) returns 0x%x"), hProcess);
  2088. if (hProcess)
  2089. {
  2090. //
  2091. // Get the token
  2092. //
  2093. if (OpenProcessToken(hProcess, TOKEN_ASSIGN_PRIMARY | TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_DUPLICATE, &hProcessToken))
  2094. {
  2095. HINSTANCE hInstLibrary = LoadLibrary(TEXT("ADVAPI32.DLL"));
  2096. CMTRACE1(TEXT("RunAsUser/LoadLibrary(ADVAPI32.DLL) returns 0x%x"), hInstLibrary);
  2097. //
  2098. // Get user token via DuplicateTokenEx and impersonate the user
  2099. //
  2100. if (hInstLibrary)
  2101. {
  2102. lpfDuplicateTokenEx lpfuncDuplicateTokenEx = (lpfDuplicateTokenEx) GetProcAddress(hInstLibrary, "DuplicateTokenEx");
  2103. CMTRACE1(TEXT("RunAsUser/GetProcAddress(hInstLibrary, DuplicateTokenEx) returns 0x%x"), lpfuncDuplicateTokenEx);
  2104. if (lpfuncDuplicateTokenEx)
  2105. {
  2106. if (lpfuncDuplicateTokenEx(hProcessToken,
  2107. TOKEN_ASSIGN_PRIMARY | TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_DUPLICATE,
  2108. NULL,
  2109. SecurityImpersonation,
  2110. TokenPrimary,
  2111. &hUserToken))
  2112. {
  2113. BOOL bRes = ImpersonateLoggedOnUser(hUserToken);
  2114. if (FALSE == bRes)
  2115. {
  2116. hUserToken = NULL;
  2117. CMTRACE1(TEXT("RunAsUser/ImpersonateLoggedOnUser failed, GLE=&u"), GetLastError());
  2118. }
  2119. }
  2120. }
  2121. FreeLibrary(hInstLibrary);
  2122. }
  2123. CloseHandle(hProcessToken);
  2124. }
  2125. CloseHandle(hProcess);
  2126. }
  2127. }
  2128. CMTRACE1(TEXT("RunAsUser - hUserToken is 0x%x"), hUserToken);
  2129. //
  2130. // Can't impersonate user, don't run because we're in the system account
  2131. //
  2132. if (NULL == hUserToken)
  2133. {
  2134. MYDBGASSERT(FALSE);
  2135. return NULL;
  2136. }
  2137. //
  2138. // Now prep CreateProcess
  2139. //
  2140. StartupInfo.cb = sizeof(StartupInfo);
  2141. if (pszDesktop)
  2142. {
  2143. StartupInfo.lpDesktop = pszDesktop;
  2144. StartupInfo.wShowWindow = SW_SHOW;
  2145. }
  2146. //
  2147. // Build the path and params
  2148. //
  2149. LPWSTR pszwCommandLine = (LPWSTR) CmMalloc((2 + lstrlen(pszProgram) + 1 + lstrlen(pszParams) + 1) * sizeof(TCHAR));
  2150. if (NULL == pszwCommandLine)
  2151. {
  2152. MYDBGASSERT(FALSE);
  2153. return NULL;
  2154. }
  2155. //
  2156. // Copy path, but surround with double quotes for security
  2157. //
  2158. lstrcpyU(pszwCommandLine, TEXT("\""));
  2159. lstrcatU(pszwCommandLine, pszProgram);
  2160. lstrcatU(pszwCommandLine, TEXT("\""));
  2161. if (pszParams[0] != L'\0')
  2162. {
  2163. lstrcatU(pszwCommandLine, TEXT(" "));
  2164. lstrcatU(pszwCommandLine, pszParams);
  2165. }
  2166. CMTRACE1(TEXT("RunAsUser/CreateProcessAsUser() - Launching %s"), pszwCommandLine);
  2167. if (NULL == CreateProcessAsUser(hUserToken, NULL, pszwCommandLine,
  2168. NULL, NULL, FALSE, CREATE_SEPARATE_WOW_VDM,
  2169. NULL, NULL,
  2170. &StartupInfo, &ProcessInfo))
  2171. {
  2172. CMTRACE1(TEXT("RunAsUser/CreateProcessAsUser() failed, GLE=%u."), GetLastError());
  2173. ProcessInfo.hProcess = NULL;
  2174. }
  2175. CloseHandle(hUserToken);
  2176. RevertToSelf();
  2177. CmFree(pszwCommandLine);
  2178. return (ProcessInfo.hProcess);
  2179. }
  2180. //+----------------------------------------------------------------------------
  2181. //
  2182. // Function: CCmConnection::ExecCmdLine
  2183. //
  2184. // Synopsis: ShellExecute the command line
  2185. // The code is copied from CActionList::RunAsExe
  2186. //
  2187. // Arguments: const TCHAR* pszCmdLine - the command line to run, including
  2188. // arguments
  2189. // const TCHAR* pszCmsFile - Full path of CMS file
  2190. //
  2191. // Returns: BOOL - Whether ShellExecute succeed
  2192. //
  2193. // Notes: Menu actions consists of a command string and an optional argument string.
  2194. // The first delimited string is considered the command portion and anything
  2195. // thereafter is treated as the argument portion, which formatless and passed
  2196. // indiscriminately to ShellExecuteEx. Long filename command paths are
  2197. // enclosed in "+" signs by CMAK. Thus the following permutations are allowed:
  2198. //
  2199. // "C:\\Progra~1\\Custom.Exe"
  2200. // "C:\\Progra~1\\Custom.Exe Args"
  2201. // "+C:\\Program Files\\Custom.Exe+"
  2202. // "+C:\\Program Files\\Custom.Exe+ Args"
  2203. //
  2204. // History: 02/10/98 fengsun Created Header
  2205. // 02/09/99 nickball Fixed long filename bug one year later.
  2206. //
  2207. //+----------------------------------------------------------------------------
  2208. BOOL CCmConnection::ExecCmdLine(const TCHAR* pszCmdLine, const TCHAR* pszCmsFile)
  2209. {
  2210. LPTSTR pszArgs = NULL;
  2211. LPTSTR pszCmd = NULL;
  2212. BOOL bRes = FALSE;
  2213. MYDBGASSERT(pszCmdLine);
  2214. MYDBGASSERT(pszCmsFile);
  2215. if (NULL == pszCmdLine || NULL == pszCmsFile)
  2216. {
  2217. return FALSE;
  2218. }
  2219. CMTRACE1(TEXT("ExecCmdLine() pszCmdLine is %s"), pszCmdLine);
  2220. if (CmParsePath(pszCmdLine, pszCmsFile, &pszCmd, &pszArgs))
  2221. {
  2222. CMTRACE1(TEXT("ExecCmdLine() pszCmd is %s"), pszCmd);
  2223. CMTRACE1(TEXT("ExecCmdLine() pszArgs is %s"), pszArgs);
  2224. //
  2225. // Now we have the exe name and args separated, execute it
  2226. //
  2227. if (IsLogonAsSystem())
  2228. {
  2229. HANDLE hProcess = RunAsUser(pszCmd, pszArgs, TEXT("Winsta0\\default"));
  2230. if (hProcess)
  2231. {
  2232. CloseHandle(hProcess);
  2233. bRes = TRUE;
  2234. }
  2235. else
  2236. {
  2237. bRes = FALSE;
  2238. }
  2239. }
  2240. else
  2241. {
  2242. SHELLEXECUTEINFO seiInfo;
  2243. ZeroMemory(&seiInfo,sizeof(seiInfo));
  2244. seiInfo.cbSize = sizeof(seiInfo);
  2245. seiInfo.fMask |= SEE_MASK_FLAG_NO_UI;
  2246. seiInfo.lpFile = pszCmd;
  2247. seiInfo.lpParameters = pszArgs;
  2248. seiInfo.nShow = SW_SHOW;
  2249. //
  2250. // Load Shell32.dll and call Shell_ExecuteEx
  2251. //
  2252. CShellDll ShellDll(TRUE); // TRUE == don't unload shell32.dll because of bug 289463
  2253. bRes = ShellDll.ExecuteEx(&seiInfo);
  2254. CMTRACE2(TEXT("ExecCmdLine/ShellExecuteEx() returns %u - GLE=%u"), bRes, GetLastError());
  2255. }
  2256. }
  2257. CmFree(pszCmd);
  2258. CmFree(pszArgs);
  2259. CMTRACE1(TEXT("ExecCmdLine() - Returns %d"), bRes);
  2260. return bRes;
  2261. }
  2262. //+----------------------------------------------------------------------------
  2263. //
  2264. // Function: CCmConnection::LoadHelpFileName
  2265. //
  2266. // Synopsis: Get the connection help file name
  2267. //
  2268. // Arguments: None
  2269. //
  2270. // Returns: LPTSTR - The help file name, can be a empty string
  2271. // caller is responsible to free the pointer
  2272. //
  2273. // History: Created Header 2/13/98
  2274. //
  2275. //+----------------------------------------------------------------------------
  2276. LPTSTR CCmConnection::LoadHelpFileName()
  2277. {
  2278. //
  2279. // Read the filename from profile first
  2280. //
  2281. LPTSTR pszFullPath = NULL;
  2282. LPTSTR pszFileName = m_IniService.GPPS(c_pszCmSection,c_pszCmEntryHelpFile);
  2283. if (pszFileName != NULL && pszFileName[0])
  2284. {
  2285. //
  2286. // The help file name is relative to the .cmp file, convert it into full name
  2287. //
  2288. pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszFileName);
  2289. }
  2290. CmFree(pszFileName);
  2291. return pszFullPath;
  2292. }
  2293. //+----------------------------------------------------------------------------
  2294. //
  2295. // Function: CCmConnection::LoadConnectionIcons
  2296. //
  2297. // Synopsis: Load big and small icon of the connection
  2298. //
  2299. // Arguments: None
  2300. //
  2301. // Returns: Nothing
  2302. //
  2303. // History: Created Header 2/13/98
  2304. //
  2305. //+----------------------------------------------------------------------------
  2306. void CCmConnection::LoadConnectionIcons()
  2307. {
  2308. // Load large icon name
  2309. LPTSTR pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntryBigIcon);
  2310. if (*pszTmp)
  2311. {
  2312. //
  2313. // The icon name is relative to the .cmp file, convert it into full name
  2314. //
  2315. LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp);
  2316. m_hBigIcon = CmLoadIcon(CMonitor::GetInstance(), pszFullPath);
  2317. CmFree(pszFullPath);
  2318. }
  2319. CmFree(pszTmp);
  2320. // Use default (EXE) large icon if no user icon found
  2321. if (!m_hBigIcon)
  2322. {
  2323. m_hBigIcon = CmLoadIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP));
  2324. }
  2325. // Load the small Icon
  2326. // Load small icon name
  2327. pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntrySmallIcon);
  2328. if (*pszTmp)
  2329. {
  2330. //
  2331. // The icon name is relative to the .cmp file, convert it into full name
  2332. //
  2333. LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp);
  2334. m_hSmallIcon = CmLoadSmallIcon(CMonitor::GetInstance(), pszFullPath);
  2335. CmFree(pszFullPath);
  2336. }
  2337. CmFree(pszTmp);
  2338. // Use default (EXE) small icon if no user icon found
  2339. if (!m_hSmallIcon)
  2340. {
  2341. m_hSmallIcon = CmLoadSmallIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP));
  2342. }
  2343. }
  2344. //+----------------------------------------------------------------------------
  2345. //
  2346. // Function: CCmConnection::PositionWindow
  2347. //
  2348. // Synopsis: Position the window according to dwPositionId, so multiple
  2349. // connection window would be positioned differently
  2350. //
  2351. // Arguments: HWND hWnd - The window to position
  2352. // dwPositionId - the id of the window
  2353. //
  2354. // Returns: Nothing
  2355. //
  2356. // History: fengsun Created Header 3/25/98
  2357. //
  2358. //+----------------------------------------------------------------------------
  2359. void CCmConnection::PositionWindow(HWND hWnd, DWORD dwPositionId)
  2360. {
  2361. MYDBGASSERT(IsWindow(hWnd));
  2362. //
  2363. // Get the rect of this window
  2364. //
  2365. RECT rcDlg;
  2366. GetWindowRect(hWnd, &rcDlg);
  2367. //
  2368. // center within work area
  2369. //
  2370. RECT rcArea;
  2371. SystemParametersInfoA(SPI_GETWORKAREA, NULL, &rcArea, NULL);
  2372. //
  2373. // Position the window on the desktop work area according to the PositionId
  2374. // x = Desktop.midX - width/2
  2375. // y = Desktop.midY - hight/2 + hight * (PostitionId%3 -1)
  2376. // X position is always the same
  2377. // Y position repeat for every three time
  2378. //
  2379. int xLeft = (rcArea.left + rcArea.right) / 2 - (rcDlg.right - rcDlg.left) / 2;
  2380. int yTop = (rcArea.top + rcArea.bottom) / 2 - (rcDlg.bottom - rcDlg.top) / 2
  2381. + (rcDlg.bottom - rcDlg.top) * ((int)dwPositionId%3 -1);
  2382. //
  2383. // if the dialog is outside the screen, move it inside
  2384. //
  2385. if (xLeft < rcArea.left)
  2386. {
  2387. xLeft = rcArea.left;
  2388. }
  2389. else if (xLeft + (rcDlg.right - rcDlg.left) > rcArea.right)
  2390. {
  2391. xLeft = rcArea.right - (rcDlg.right - rcDlg.left);
  2392. }
  2393. if (yTop < rcArea.top)
  2394. {
  2395. yTop = rcArea.top;
  2396. }
  2397. else if (yTop + (rcDlg.bottom - rcDlg.top) > rcArea.bottom)
  2398. {
  2399. yTop = rcArea.bottom - (rcDlg.bottom - rcDlg.top);
  2400. }
  2401. SetWindowPos(hWnd, NULL, xLeft, yTop, -1, -1,
  2402. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  2403. }
  2404. #ifdef DEBUG
  2405. //+----------------------------------------------------------------------------
  2406. //
  2407. // Function: CCmConnection::AssertValid
  2408. //
  2409. // Synopsis: For debug purpose only, assert the connection object is valid
  2410. //
  2411. // Arguments: None
  2412. //
  2413. // Returns: Nothing
  2414. //
  2415. // History: Created Header 2/12/98
  2416. //
  2417. //+----------------------------------------------------------------------------
  2418. void CCmConnection::AssertValid() const
  2419. {
  2420. MYDBGASSERT(m_dwState >= 0 && m_dwState <= STATE_TERMINATED);
  2421. MYDBGASSERT(m_hRasDial != NULL || m_hRasTunnel != NULL);
  2422. ASSERT_VALID(&m_ConnStatistics);
  2423. ASSERT_VALID(&m_IdleStatistics);
  2424. ASSERT_VALID(&m_StatusDlg);
  2425. // ASSERT_VALID(m_TrayIcon);
  2426. // ASSERT_VALID(m_WatchProcess);
  2427. }
  2428. #endif