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

2887 lines
86 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. BOOL fLostConnection = FALSE;
  559. CMTRACE(TEXT("Enter StateConnected"));
  560. StateConnectedInit();
  561. HANDLE hThreadRnaReconnect = NULL;
  562. if (OS_W95)
  563. {
  564. hThreadRnaReconnect = ZapRNAReconnectStart(NULL);
  565. }
  566. //
  567. // Ignore return value
  568. //
  569. BOOL fRV = CheckRasConnection(fLostConnection);
  570. //
  571. // If we lost the connection, we need to hangup so that rasman has the correct
  572. // ref count.
  573. //
  574. if (fLostConnection)
  575. {
  576. CMTRACE(TEXT("StateConnected - Actually not connected. Need to hangup to notify rasman."));
  577. m_dwState = StateConnectedProcessEvent(EVENT_CMDIAL_HANGUP);
  578. }
  579. else
  580. {
  581. while (m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN)
  582. {
  583. CONN_EVENT dwEvent = StateConnectedGetEvent();
  584. MYDBGASSERT(dwEvent <= EVENT_NONE);
  585. if (dwEvent < EVENT_NONE )
  586. {
  587. //
  588. // Call the event handler to process the event
  589. //
  590. m_dwState = StateConnectedProcessEvent(dwEvent);
  591. }
  592. }
  593. }
  594. if (hThreadRnaReconnect)
  595. {
  596. ZapRNAReconnectStop(hThreadRnaReconnect);
  597. }
  598. }
  599. //+----------------------------------------------------------------------------
  600. //
  601. // Function: CCmConnection::StateConnectedGetEvent
  602. //
  603. // Synopsis: In the state of CONNECTED/COUNTDOWN. Wait until some event happens.
  604. // Also runs the message loop
  605. //
  606. // Arguments: None
  607. //
  608. // Returns: CCmConnection::CONN_EVENT - The event that can cause the
  609. // connection change the state other than CONNECTED/COUNTDOWN
  610. //
  611. // History: Created Header 2/18/98
  612. //
  613. //+----------------------------------------------------------------------------
  614. CCmConnection::CONN_EVENT CCmConnection::StateConnectedGetEvent()
  615. {
  616. ASSERT_VALID(this);
  617. //
  618. // The last time SateConnectedOnTimer got called
  619. //
  620. DWORD dwLastTimerCalled = 0;
  621. //
  622. // Loop until we got some event
  623. //
  624. while (TRUE)
  625. {
  626. MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
  627. //
  628. // Process all the messages in the message queue
  629. //
  630. MSG msg;
  631. while(PeekMessageU(&msg, NULL,0,0,PM_REMOVE))
  632. {
  633. if (msg.hwnd == NULL)
  634. {
  635. //
  636. // it is a thread message
  637. //
  638. MYDBGASSERT((msg.message >= WM_APP) || (msg.message == WM_CONN_EVENT));
  639. if (msg.message == WM_CONN_EVENT)
  640. {
  641. MYDBGASSERT(msg.wParam < EVENT_NONE);
  642. return (CONN_EVENT)msg.wParam;
  643. }
  644. }
  645. else
  646. {
  647. //
  648. // Also dispatch message for Modeless dialog box
  649. //
  650. if (!IsDialogMessageU(m_StatusDlg.GetHwnd(), &msg))
  651. {
  652. TranslateMessage(&msg);
  653. DispatchMessageU(&msg);
  654. }
  655. }
  656. }
  657. //
  658. // whether cmmon needs timer.
  659. // The timer is needed, if we have check the ras connection on timer,
  660. // or we have to check idle disconnect on timer,
  661. // or the state is disconnect-count-down,
  662. // or status dialog is visible
  663. //
  664. BOOL fNeedTimer = !m_hEventRasNotify
  665. || m_IdleStatistics.IsStarted()
  666. || m_dwState == STATE_COUNTDOWN
  667. || IsWindowVisible(m_StatusDlg.GetHwnd());
  668. //
  669. // If more than 1 seconds elapsed, call the timer
  670. //
  671. if (fNeedTimer && GetTickCount() - dwLastTimerCalled >= TIMER_INTERVAL)
  672. {
  673. dwLastTimerCalled = GetTickCount();
  674. CONN_EVENT dwEvent = StateConnectedOnTimer();
  675. if (dwEvent != EVENT_NONE)
  676. {
  677. return dwEvent;
  678. }
  679. }
  680. //
  681. // Setup the opbject array for MsgWaitForMultipleObjects
  682. //
  683. HANDLE ahObjectsToWait[3];
  684. int nObjects = 0;
  685. if (m_hEventRasNotify)
  686. {
  687. ahObjectsToWait[nObjects] = m_hEventRasNotify;
  688. nObjects++;
  689. }
  690. if (m_WatchProcess.GetSize())
  691. {
  692. //
  693. // If we have any process to watch, just add the first hProcess
  694. // Since we want to know whether all processes exit
  695. //
  696. ahObjectsToWait[nObjects] = m_WatchProcess.GetProcess(0);
  697. MYDBGASSERT(ahObjectsToWait[nObjects]);
  698. nObjects++;
  699. }
  700. //
  701. // From MSDN:
  702. // The documentation for MsgWaitForMultipleObjects() says that the API returns
  703. // successfully when either the objects are signalled or the input is available.
  704. // However, the API behaves as if it requires that the objects are signalled
  705. // and the input is available.
  706. //
  707. // Put an extra event seems to fix it for NT
  708. //
  709. if (OS_NT && nObjects)
  710. {
  711. ahObjectsToWait[nObjects] = ahObjectsToWait[nObjects-1];
  712. nObjects++;
  713. }
  714. if (m_fToMinimizeWorkingSet)
  715. {
  716. //
  717. // If we do not need a timer here, minimize the working set.
  718. // before calling MsgWaitForMultipleObjects.
  719. //
  720. CMonitor::MinimizeWorkingSet();
  721. m_fToMinimizeWorkingSet = FALSE;
  722. }
  723. DWORD dwRes = MsgWaitForMultipleObjects(nObjects, ahObjectsToWait, FALSE,
  724. fNeedTimer ? 1000 : INFINITE,
  725. QS_ALLINPUT);
  726. //
  727. // Timeout
  728. //
  729. if (dwRes == WAIT_TIMEOUT)
  730. {
  731. //
  732. // We always checks timer on the beginning of the loop
  733. //
  734. continue;
  735. }
  736. //
  737. // An event
  738. //
  739. #pragma warning(push)
  740. #pragma warning(disable:4296)
  741. if (dwRes >= WAIT_OBJECT_0 && dwRes < WAIT_OBJECT_0 + nObjects)
  742. #pragma warning(pop)
  743. {
  744. BOOL fLostConnection;
  745. //
  746. // Ras Event
  747. //
  748. if (m_hEventRasNotify && ahObjectsToWait[dwRes - WAIT_OBJECT_0] == m_hEventRasNotify &&
  749. !CheckRasConnection(fLostConnection))
  750. {
  751. //
  752. // Got a notification that the RAS connection is losted
  753. //
  754. CMTRACE(TEXT("CCmConnection::StateConnectedGetEvent() - m_hEventRasNotify && ahObjectsToWait[dwRes - WAIT_OBJECT_0] == m_hEventRasNotify"));
  755. return EVENT_LOST_CONNECTION;
  756. }
  757. else
  758. {
  759. //
  760. // A watch process exits
  761. // IsIdle() remove the process from the list
  762. //
  763. if (m_WatchProcess.IsIdle())
  764. {
  765. //
  766. // If all the watch process are terminated, change to disconnect countdown
  767. //
  768. CMTRACE(TEXT("CCmConnection::StateConnectedGetEvent() - m_WatchProcess.IsIdle()"));
  769. return EVENT_IDLE;
  770. }
  771. continue;
  772. }
  773. }
  774. //
  775. // A message
  776. //
  777. if (dwRes == WAIT_OBJECT_0 + nObjects)
  778. {
  779. continue;
  780. }
  781. if (-1 == dwRes)
  782. {
  783. CMTRACE1(TEXT("MsgWaitForMultipleObjects failed, LastError:%d"), GetLastError());
  784. //
  785. // Something does wrong
  786. //
  787. continue;
  788. }
  789. //
  790. // what is this return value
  791. //
  792. CMTRACE1(TEXT("MsgWaitForMultipleObjects returns %d"), dwRes);
  793. continue;
  794. }
  795. //
  796. // should never get here
  797. //
  798. MYDBGASSERT(FALSE);
  799. return EVENT_USER_DISCONNECT;
  800. }
  801. //+----------------------------------------------------------------------------
  802. //
  803. // Function: CCmConnection::StateConnectedOnTimer
  804. //
  805. // Synopsis: Process the timer in the state of CONNECTED/COUNTDOWN
  806. //
  807. // Arguments: None
  808. //
  809. // Returns: CCmConnection::CONN_EVENT - The event that can cause the
  810. // connection change the state other than CONNECTED/COUNTDOWN or EVENT_NONE
  811. //
  812. // History: fengsun Created Header 2/18/98
  813. //
  814. //+----------------------------------------------------------------------------
  815. CCmConnection::CONN_EVENT CCmConnection::StateConnectedOnTimer()
  816. {
  817. ASSERT_VALID(this);
  818. MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
  819. if (m_dwState != STATE_CONNECTED && m_dwState != STATE_COUNTDOWN)
  820. {
  821. return EVENT_NONE;
  822. }
  823. //
  824. // Check whether CM is still connected, only if the Ras notify event is not available
  825. //
  826. if (m_hEventRasNotify == NULL)
  827. {
  828. BOOL fLostConnection;
  829. BOOL fConnect = CheckRasConnection(fLostConnection);
  830. CMTRACE2(TEXT("CCmConnection::StateConnectedOnTimer - CheckRasConnection returns %d - fLostConnection is %d"), fConnect, fLostConnection);
  831. if (!fConnect)
  832. {
  833. return (fLostConnection ? EVENT_LOST_CONNECTION : EVENT_USER_DISCONNECT);
  834. }
  835. }
  836. //
  837. // If we don't have stats, something went wrong accessing the
  838. // registry stats earlier, so try to initialize again here.
  839. //
  840. if (OS_W98 && !m_ConnStatistics.IsAvailable())
  841. {
  842. //
  843. // Try to initialize the perf stats from the registry again
  844. //
  845. CMASSERTMSG(FALSE, TEXT("StateConnectedOnTimer() - Statistics unavailable, re-initializing stats now."));
  846. m_ConnStatistics.Open(CMonitor::GetInstance(),
  847. (DWORD)-1,
  848. (DWORD)-1,
  849. m_hRasDial,
  850. m_hRasTunnel);
  851. }
  852. //
  853. // Get statistics for Win9x
  854. //
  855. if (m_ConnStatistics.IsAvailable())
  856. {
  857. m_ConnStatistics.Update();
  858. //
  859. // collecting data points for monitoring idle-disconnect
  860. // check whether ICM has received more data points than the IdleThreshold value
  861. // during the past IDLE_SPREAD time
  862. // Start() is not called for NT
  863. //
  864. if (m_IdleStatistics.IsStarted())
  865. {
  866. m_IdleStatistics.UpdateEveryInterval(m_ConnStatistics.GetBytesRead());
  867. }
  868. }
  869. if (m_dwState == STATE_CONNECTED)
  870. {
  871. //
  872. // Check idle time out
  873. //
  874. if (m_IdleStatistics.IsIdleTimeout())
  875. {
  876. //
  877. // Disconnect count down
  878. //
  879. CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - m_IdleStatistics.IsIdleTimeout()"));
  880. return EVENT_IDLE;
  881. }
  882. //
  883. // Check process watch list
  884. //
  885. if (m_WatchProcess.IsIdle())
  886. {
  887. //
  888. // Disconnect count down
  889. //
  890. CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - m_WatchProcess.IsIdle())"));
  891. return EVENT_IDLE;
  892. }
  893. //
  894. // Update the status window, only if the window is visible
  895. //
  896. if (IsWindowVisible(m_StatusDlg.GetHwnd()))
  897. {
  898. if (m_ConnStatistics.IsAvailable())
  899. {
  900. m_StatusDlg.UpdateStats(m_ConnStatistics.GetBaudRate(),
  901. m_ConnStatistics.GetBytesRead(),
  902. m_ConnStatistics.GetBytesWrite(),
  903. m_ConnStatistics.GetBytesPerSecRead(),
  904. m_ConnStatistics.GetBytesPerSecWrite());
  905. }
  906. //
  907. // We have exact duration numbers from RAS on NT5, so use them.
  908. //
  909. if (m_ConnStatistics.GetDuration())
  910. {
  911. m_StatusDlg.UpdateDuration(m_ConnStatistics.GetDuration() / 1000);
  912. }
  913. else
  914. {
  915. m_StatusDlg.UpdateDuration((GetTickCount() - m_dwConnectStartTime) / 1000);
  916. }
  917. }
  918. return EVENT_NONE;
  919. }
  920. else // m_dwState == STATE_COUNTDOWN
  921. {
  922. //
  923. // Note: NetBEUI seems to insist on sending unsolicited
  924. // stuff over the dial-up adapter. So we will define
  925. // "idle" as "not having received anything".
  926. //
  927. //
  928. // check whether we have new traffic that exceeds the threshold.
  929. // But we don't care about network traffic if the countdown is caused
  930. // by 0 watched process
  931. //
  932. if (!m_WatchProcess.IsIdle() && !m_IdleStatistics.IsIdle() )
  933. {
  934. //
  935. // We were in our idle wait, but we just picked up some
  936. // activity. Stay on line.
  937. // If this is NT5 we don't use our own status dialog so just hide
  938. // it again. If this is downlevel then just change the dialog to
  939. // a status dialog
  940. //
  941. if (OS_NT5)
  942. {
  943. m_StatusDlg.DismissStatusDlg();
  944. }
  945. else
  946. {
  947. m_StatusDlg.ChangeToStatus();
  948. }
  949. m_dwState = STATE_CONNECTED;
  950. return EVENT_NONE;
  951. }
  952. //
  953. // If the time elapsed is more than 30 second, timeout
  954. //
  955. DWORD dwElapsed = GetTickCount() - m_dwCountDownStartTime;
  956. if (dwElapsed > IDLE_DLG_WAIT_TIMEOUT)
  957. {
  958. //
  959. // Connection has been idle for timeout period and the
  960. // grace period is up with no user intervention, so we
  961. // quit/disconnect, and ask for reconnect
  962. //
  963. CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - dwElapsed > IDLE_DLG_WAIT_TIMEOUT"));
  964. return EVENT_COUNTDOWN_ZERO;
  965. }
  966. else
  967. {
  968. //
  969. // Connection has been idle for timeout period but we
  970. // are still in the grace period, show countdown.
  971. //
  972. int nTimeLeft = (int) ((IDLE_DLG_WAIT_TIMEOUT - dwElapsed) / 1000);
  973. //
  974. // Update duration and countdown seconds left.
  975. //
  976. if (m_ConnStatistics.GetDuration()) // NT5 only
  977. {
  978. m_StatusDlg.UpdateCountDown(m_ConnStatistics.GetDuration() / 1000,
  979. nTimeLeft);
  980. }
  981. else
  982. {
  983. m_StatusDlg.UpdateCountDown((GetTickCount() - m_dwConnectStartTime) / 1000,
  984. nTimeLeft);
  985. }
  986. }
  987. return EVENT_NONE;
  988. }
  989. }
  990. //+----------------------------------------------------------------------------
  991. //
  992. // Function: CCmConnection::StateConnectedProcessEvent
  993. //
  994. // Synopsis: Process the connection event while in CONNECTED/COUNTDOWN state
  995. //
  996. // Arguments: CONN_EVENT dwEvent - the event to process
  997. //
  998. // Returns: CCmConnection::CONN_STATE - The new state of the connection
  999. //
  1000. // History: fengsun Created Header 2/19/98
  1001. //
  1002. //+----------------------------------------------------------------------------
  1003. CCmConnection::CONN_STATE CCmConnection::StateConnectedProcessEvent(CONN_EVENT dwEvent)
  1004. {
  1005. ASSERT_VALID(this);
  1006. switch (dwEvent)
  1007. {
  1008. case EVENT_IDLE:
  1009. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_IDLE"));
  1010. if (m_dwState != STATE_COUNTDOWN)
  1011. {
  1012. //
  1013. // No-traffic/ No-watch-process idle event
  1014. // change to Disconnect count down
  1015. //
  1016. m_dwCountDownStartTime = GetTickCount();
  1017. m_StatusDlg.ChangeToCountDown();
  1018. //
  1019. // Update duration and countdown seconds left
  1020. //
  1021. int nTimeLeft = IDLE_DLG_WAIT_TIMEOUT / 1000;
  1022. if (m_ConnStatistics.GetDuration()) // NT5 only
  1023. {
  1024. m_StatusDlg.UpdateCountDown(m_ConnStatistics.GetDuration() / 1000,
  1025. nTimeLeft);
  1026. }
  1027. else
  1028. {
  1029. m_StatusDlg.UpdateCountDown((GetTickCount() - m_dwConnectStartTime) / 1000,
  1030. nTimeLeft);
  1031. }
  1032. //
  1033. // Don't show the UI if we are at Winlogon unless we are on NT4
  1034. //
  1035. if (!IsLogonAsSystem() || OS_NT4)
  1036. {
  1037. m_StatusDlg.BringToTop();
  1038. }
  1039. }
  1040. return STATE_COUNTDOWN;
  1041. case EVENT_CMDIAL_HANGUP:
  1042. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_CMDIAL_HANGUP"));
  1043. m_Log.Log(DISCONNECT_EXT);
  1044. //
  1045. // Cmdial posted cmmon a message to clean up the connection.
  1046. // Do not need to call hangup here
  1047. //
  1048. StateConnectedCleanup();
  1049. return STATE_TERMINATED;
  1050. case EVENT_LOST_CONNECTION:
  1051. case EVENT_COUNTDOWN_ZERO:
  1052. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_LOST_CONNECTION/EVENT_COUNTDOWN_ZERO"));
  1053. //
  1054. // lost-ras-connection event or the count down counter is down to 0
  1055. //
  1056. if (IsPromptReconnectEnabled() && !m_WatchProcess.IsIdle() ||
  1057. ( dwEvent == EVENT_LOST_CONNECTION && IsAutoReconnectEnabled() ) )
  1058. {
  1059. CmCustomHangup(TRUE); // fPromptReconnect = TRUE, do not remove from Conn Table
  1060. //
  1061. // It is possible
  1062. // Someone else called cmdial to disconnect, while we are disconnecting.
  1063. // If the ref count is down to 0, cmdial will remove the entry
  1064. //
  1065. CM_CONNECTION CmEntry;
  1066. if (CMonitor::ConnTableGetEntry(m_szServiceName, &CmEntry))
  1067. {
  1068. //
  1069. // Cmdial should change the state to CM_RECONNECTPROMPT
  1070. //
  1071. CMTRACE2(TEXT("CmEntry.CmState is %d, event is %d"), CmEntry.CmState, dwEvent);
  1072. MYDBGASSERT(CmEntry.CmState == CM_RECONNECTPROMPT);
  1073. if (EVENT_LOST_CONNECTION == dwEvent)
  1074. {
  1075. m_Log.Log(DISCONNECT_EXT_LOST_CONN);
  1076. }
  1077. else if (EVENT_COUNTDOWN_ZERO == dwEvent)
  1078. {
  1079. m_Log.Log(DISCONNECT_INT_AUTO);
  1080. }
  1081. //
  1082. // is auto reconnect enabled(don't show reconnect prompt)?
  1083. //
  1084. if (dwEvent == EVENT_LOST_CONNECTION && IsAutoReconnectEnabled())
  1085. {
  1086. //
  1087. // On win98 Gold, we have a timing issue with Auto Reconnect because
  1088. // notification that the line was dropped is sent before cleanup for
  1089. // the connection takes place. Thus, we need to poll the connection
  1090. // status using RasGetConnectionStatus until the line is available
  1091. // before trying to reconnect. NTRAID 273033.
  1092. //
  1093. if (OS_W98 && (NULL != m_hRasDial))
  1094. {
  1095. BOOL bConnectionActive;
  1096. BOOL bLostConnection; //ignored
  1097. int iCount = 0;
  1098. do
  1099. {
  1100. bConnectionActive = CheckRasConnection(bLostConnection);
  1101. if (bConnectionActive)
  1102. {
  1103. Sleep(50);
  1104. }
  1105. iCount++;
  1106. // 50 Milliseconds * 200 = 10 seconds
  1107. // If waiting 10 seconds hasn't fixed it, not sure that it will
  1108. // get fixed by this method.
  1109. } while ((200 >= iCount) && (bConnectionActive));
  1110. }
  1111. return STATE_RECONNECTING;
  1112. }
  1113. return STATE_PROMPT_RECONNECT;
  1114. }
  1115. else
  1116. {
  1117. return STATE_TERMINATED;
  1118. }
  1119. }
  1120. //
  1121. // Else fall through
  1122. //
  1123. case EVENT_USER_DISCONNECT:
  1124. CMTRACE(TEXT("StateConnectedProcessEvent EVENT_USER_DISCONNECT"));
  1125. m_Log.Log(DISCONNECT_INT_MANUAL);
  1126. //
  1127. // For EVENT_USER_DISCONNECT
  1128. // User can disconnect from tray icon, status dialog, or countdown dialog.
  1129. // Or prompt reconnect is not enabled
  1130. //
  1131. CmCustomHangup(FALSE); // fPromptReconnect = FALSE
  1132. return STATE_TERMINATED;
  1133. default:
  1134. //
  1135. // Unexpected event, do the same thing as EVENT_USER_DISCONNECT
  1136. //
  1137. MYDBGASSERT(FALSE);
  1138. CmCustomHangup(FALSE); // fPromptReconnect = FALSE
  1139. return STATE_TERMINATED;
  1140. }
  1141. }
  1142. //+----------------------------------------------------------------------------
  1143. //
  1144. // Function: CCmConnection::IsAutoReconnectEnabled
  1145. //
  1146. // Synopsis: See if auto-reconnect is enabled, but only if we aren't logged-in
  1147. // as system.
  1148. //
  1149. // Arguments: None
  1150. //
  1151. // Returns: Nothing
  1152. //
  1153. // History: fengsun Created Header 2/18/98
  1154. // tomkel moved from connection.h 06/06/2001
  1155. //
  1156. //+----------------------------------------------------------------------------
  1157. BOOL CCmConnection::IsAutoReconnectEnabled() const
  1158. {
  1159. //
  1160. // Load the AutoReconnect flag from profile
  1161. // Default is FALSE
  1162. //
  1163. BOOL fReturn = FALSE;
  1164. if (!IsLogonAsSystem())
  1165. {
  1166. fReturn = m_IniService.GPPB(c_pszCmSection, c_pszCmEntryAutoReconnect, FALSE);
  1167. }
  1168. return fReturn;
  1169. }
  1170. //+----------------------------------------------------------------------------
  1171. //
  1172. // Function: CCmConnection::StateConnectedCleanup
  1173. //
  1174. // Synopsis: Cleanup before exiting the connected state
  1175. //
  1176. // Arguments: BOOL fEndSession, whether windows is going to logoff/shutdown
  1177. //
  1178. // Returns: Nothing
  1179. //
  1180. // History: fengsun Created Header 2/18/98
  1181. //
  1182. //+----------------------------------------------------------------------------
  1183. void CCmConnection::StateConnectedCleanup(BOOL fEndSession)
  1184. {
  1185. ASSERT_VALID(this);
  1186. //
  1187. // Remove the trayicon, destroy the status dialog
  1188. //
  1189. m_TrayIcon.RemoveIcon();
  1190. m_ConnStatistics.Close();
  1191. //
  1192. // Do not close window on WM_ENDSESSION. Otherwise, cmmon would be terminated right here
  1193. //
  1194. if (!fEndSession)
  1195. {
  1196. m_StatusDlg.KillRasMonitorWindow();
  1197. DestroyWindow(m_StatusDlg.GetHwnd());
  1198. }
  1199. }
  1200. //+----------------------------------------------------------------------------
  1201. //
  1202. // Function: CCmConnection::IsPromptReconnectEnabled
  1203. //
  1204. // Synopsis: When prompt-reconnect is enabled by the profile
  1205. //
  1206. // Arguments: None
  1207. //
  1208. // Returns: Nothing
  1209. //
  1210. // History: fengsun Created Header 2/18/98
  1211. //
  1212. //+----------------------------------------------------------------------------
  1213. BOOL CCmConnection::IsPromptReconnectEnabled() const
  1214. {
  1215. //
  1216. // Load the NoPromptReconnect flag from profile. Also check the Unattended flag and
  1217. // if we are running in the system account on win2k or Whistler. If any of the above
  1218. // are true then we don't prompt, otherwise we do.
  1219. //
  1220. BOOL fPromptReconnect = !(m_IniService.GPPB(c_pszCmSection,
  1221. c_pszCmEntryNoPromptReconnect, FALSE));
  1222. if (!fPromptReconnect || (m_ReconnectInfo.dwCmFlags & FL_UNATTENDED) || (IsLogonAsSystem() && OS_NT5))
  1223. {
  1224. //
  1225. // Do not prompt reconnect
  1226. //
  1227. return FALSE;
  1228. }
  1229. return TRUE;
  1230. }
  1231. //+----------------------------------------------------------------------------
  1232. //
  1233. // Function: CCmConnection::CmCustomHangup
  1234. //
  1235. // Synopsis: Call cmdial to hangup the connection
  1236. //
  1237. // Arguments: BOOL fPromptReconnect, whether cmmon are going to prompt the
  1238. // reconnect dialog
  1239. // BOOL fEndSession, whther windows is going to logoff/shutdown
  1240. // Default value is FALSE
  1241. //
  1242. // Returns: Nothing
  1243. //
  1244. // History: fegnsun Created Header 2/11/98
  1245. //
  1246. //+----------------------------------------------------------------------------
  1247. BOOL CCmConnection::CmCustomHangup(BOOL fPromptReconnect, BOOL fEndSession)
  1248. {
  1249. ASSERT_VALID(this);
  1250. CMTRACE2(TEXT("CCmConnection::CmCustomHangup - fPromptReconnect is %d and fEndSession is %d"), fPromptReconnect, fEndSession);
  1251. //
  1252. // Remove the trayicon close status dlg, before hangup
  1253. //
  1254. StateConnectedCleanup(fEndSession);
  1255. //
  1256. // It is possible the connection is disconnected or disconnecting
  1257. //
  1258. CM_CONNECTION CmEntry;
  1259. if (!CMonitor::ConnTableGetEntry(m_szServiceName, &CmEntry) ||
  1260. CmEntry.CmState != CM_CONNECTED)
  1261. {
  1262. //
  1263. // The connection is disconnected by someone else, do not hangup.
  1264. //
  1265. CMTRACE(TEXT("CCmConnection::CmCustomHangup - Entry is not connected, canceling hangup"));
  1266. return TRUE;
  1267. }
  1268. //
  1269. // Call cmdial32.dll CmCustomHangup
  1270. //
  1271. //
  1272. // The destructor of CDynamicLibrary calls FreeLibrary
  1273. //
  1274. CDynamicLibrary LibCmdial;
  1275. if (!LibCmdial.Load(TEXT("cmdial32.dll")))
  1276. {
  1277. MYDBGASSERT(FALSE);
  1278. return FALSE;
  1279. }
  1280. CmCustomHangUpFUNC pfnCmCustomHangUp;
  1281. pfnCmCustomHangUp = (CmCustomHangUpFUNC)LibCmdial.GetProcAddress(c_pszCmHangup);
  1282. MYDBGASSERT(pfnCmCustomHangUp);
  1283. if (pfnCmCustomHangUp)
  1284. {
  1285. //
  1286. // hRasConn = NULL,
  1287. // fIgnoreRefCount = TRUE, always except for InetDialHandler
  1288. // fPersist = fPromptReconnect, do not remove the entry if
  1289. // we are going to prompt for reconnect
  1290. //
  1291. DWORD dwRet;
  1292. dwRet = pfnCmCustomHangUp(NULL, m_szServiceName, TRUE, fPromptReconnect);
  1293. CMTRACE1(TEXT("CCmConnection::CmCustomHangup -- Return Value from CmCustomHangup is %u"), dwRet);
  1294. return (ERROR_SUCCESS == dwRet);
  1295. }
  1296. return FALSE;
  1297. }
  1298. //+----------------------------------------------------------------------------
  1299. //
  1300. // Function: CCmConnection::CallRasConnectionNotification
  1301. //
  1302. // Synopsis: call RasConnectionNotify. Ras will set the event when connection
  1303. // is lost
  1304. //
  1305. // Arguments: HRASCONN hRasDial - The dial ras handle
  1306. // HRASCONN hRasTunnel - The tunnel ras handle
  1307. //
  1308. // Returns: HANDLE - the event handle, or NULL if failed
  1309. //
  1310. // History: Created Header 2/17/98
  1311. //
  1312. //+----------------------------------------------------------------------------
  1313. HANDLE CCmConnection::CallRasConnectionNotification(HRASCONN hRasDial, HRASCONN hRasTunnel)
  1314. {
  1315. DWORD dwRes;
  1316. MYDBGASSERT(hRasDial || hRasTunnel);
  1317. //
  1318. // Call RasConnectionNotification. Ras will call us back when connection is losted.
  1319. // However, this fuction is not avaliable for Win95 with DUN 1.0
  1320. //
  1321. if (!m_RasApiDll.HasRasConnectionNotification())
  1322. {
  1323. return NULL;
  1324. }
  1325. HANDLE hEvent = NULL;
  1326. if (OS_W9X)
  1327. {
  1328. //
  1329. // Create an manual-reset non-signaled event on Win95, Win98 & WinME
  1330. //
  1331. hEvent = CreateEventU(NULL, TRUE, FALSE, NULL);
  1332. }
  1333. else
  1334. {
  1335. //
  1336. // Create an auto-reset non-signaled event
  1337. //
  1338. hEvent = CreateEventU(NULL, FALSE, FALSE, NULL);
  1339. }
  1340. //
  1341. // v-vijayb: Changed to use INVALID_HANDLE_VALUE(notify for all disconnects), as we where
  1342. // not getting notified after a reconnect or after connecting thru winlogon.
  1343. // StateConnectedGetEvent() will check if this connection is lost to determine if it was
  1344. // a disconnection of this connection or some other.
  1345. //
  1346. if (hRasDial)
  1347. {
  1348. //
  1349. // Copied from RAS.h
  1350. // #if (WINVER >= 0x401)
  1351. //
  1352. #define RASCN_Disconnection 0x00000002
  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(hRasDial, 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. if (hRasTunnel)
  1369. {
  1370. if (OS_NT)
  1371. {
  1372. dwRes = m_RasApiDll.RasConnectionNotification((HRASCONN) INVALID_HANDLE_VALUE, hEvent, RASCN_Disconnection);
  1373. }
  1374. else
  1375. {
  1376. dwRes = m_RasApiDll.RasConnectionNotification(hRasTunnel, hEvent, RASCN_Disconnection);
  1377. }
  1378. if (dwRes != ERROR_SUCCESS)
  1379. {
  1380. CMASSERTMSG(FALSE, TEXT("RasConnectionNotification Failed"));
  1381. CloseHandle(hEvent);
  1382. return NULL;
  1383. }
  1384. }
  1385. return hEvent;
  1386. }
  1387. //+---------------------------------------------------------------------------
  1388. //
  1389. // struct RASMON_THREAD_INFO
  1390. //
  1391. // Description: Information passed to RasMonitorDlgThread by OnStatusDetails
  1392. //
  1393. // History: fengsun Created 2/11/98
  1394. //
  1395. //----------------------------------------------------------------------------
  1396. struct RASMON_THREAD_INFO
  1397. {
  1398. HRASCONN hRasConn; // the ras handle to display status
  1399. HWND hwndParent; // the parent window for the RasMonitorDlg
  1400. };
  1401. //+----------------------------------------------------------------------------
  1402. //
  1403. // Function: CCmConnection::OnStatusDetails
  1404. //
  1405. // Synopsis: Called upon pressing detailed button on NT status dlg
  1406. // Call RasMonitorDlg to display the dial-up monitor
  1407. //
  1408. // Arguments: None
  1409. //
  1410. // Returns: Nothing
  1411. //
  1412. // History: fengsun Created Header 2/11/98
  1413. //
  1414. //+----------------------------------------------------------------------------
  1415. void CCmConnection::OnStatusDetails()
  1416. {
  1417. ASSERT_VALID(this);
  1418. //
  1419. // RasDlg.dll is not available for Win9X
  1420. //
  1421. MYDBGASSERT(OS_NT4);
  1422. if (!OS_NT4)
  1423. {
  1424. return;
  1425. }
  1426. //
  1427. // RasMonitorDlg pops up a modal dialog box, which will block the thread message loop.
  1428. // No thread message or event can be processed
  1429. // Create another thread to call RasMonitorDlg
  1430. //
  1431. //
  1432. // Alloc the parametor from heap. It is not safe to use stack here.
  1433. // CreateThread can return before the thread routine is called
  1434. // The thread is responsible to free the pointer
  1435. //
  1436. RASMON_THREAD_INFO* pInfo = (RASMON_THREAD_INFO*)CmMalloc(sizeof(RASMON_THREAD_INFO));
  1437. if (NULL == pInfo)
  1438. {
  1439. CMTRACE(TEXT("CCmConnection::OnStatusDetails alloc for pInfo failed"));
  1440. return;
  1441. }
  1442. pInfo->hRasConn = m_hRasTunnel?m_hRasTunnel : m_hRasDial;
  1443. pInfo->hwndParent = m_StatusDlg.GetHwnd();
  1444. DWORD dwThreadId;
  1445. HANDLE hThread;
  1446. if ((hThread = CreateThread(NULL, 0, RasMonitorDlgThread ,pInfo,0,&dwThreadId)) == NULL)
  1447. {
  1448. MYDBGASSERT(FALSE);
  1449. CMTRACE(TEXT("CCmConnection::OnStatusDetails CreateThread failed"));
  1450. CmFree(pInfo);
  1451. return ;
  1452. }
  1453. CloseHandle(hThread);
  1454. }
  1455. //+----------------------------------------------------------------------------
  1456. //
  1457. // Function: static CCmConnection::RasMonitorDlgThread
  1458. //
  1459. // Synopsis: The thread to call RasMonitorDlg to avoid blocking the thread
  1460. // message loop. RasMonitorDlg is a modal dialogbox. The thead exits
  1461. // when the dialog is closed. That happens when user close the dialog box,
  1462. // or m_StatusDlg.KillRasMonitorWindow() is called
  1463. //
  1464. // Arguments: LPVOID lParam - RASMON_THREAD_INFO* the information passed to the thread
  1465. //
  1466. // Returns: DWORD WINAPI - The thread return value
  1467. //
  1468. // History: fengsun Created Header 2/19/98
  1469. //
  1470. //+----------------------------------------------------------------------------
  1471. DWORD WINAPI CCmConnection::RasMonitorDlgThread(LPVOID lParam)
  1472. {
  1473. MYDBGASSERT(lParam);
  1474. RASMON_THREAD_INFO* pInfo = (RASMON_THREAD_INFO*)lParam;
  1475. MYDBGASSERT(pInfo->hRasConn);
  1476. //
  1477. // Get the device name first, if tunnel is available, use tunnel device
  1478. //
  1479. RASCONNSTATUS rcsStatus;
  1480. memset(&rcsStatus,0,sizeof(rcsStatus));
  1481. rcsStatus.dwSize = sizeof(rcsStatus);
  1482. //
  1483. // Load Rasapi32.dll here. This dll is not loaded on NT
  1484. // Destructor will unload the DLL
  1485. //
  1486. CRasApiDll rasApiDll;
  1487. if (!rasApiDll.Load())
  1488. {
  1489. MYDBGASSERT(FALSE);
  1490. CmFree(pInfo);
  1491. return 1;
  1492. }
  1493. DWORD dwRes = rasApiDll.RasGetConnectStatus(pInfo->hRasConn, &rcsStatus);
  1494. CMASSERTMSG(dwRes == ERROR_SUCCESS, TEXT("RasGetConnectStatus failed"));
  1495. //
  1496. // The connection is lost. Still call RasMonitorDlg with empty name
  1497. //
  1498. RASMONITORDLG RasInfo;
  1499. WORD (WINAPI *pfnFunc)(LPTSTR,LPRASMONITORDLG) = NULL;
  1500. ZeroMemory(&RasInfo,sizeof(RasInfo));
  1501. RasInfo.dwSize = sizeof(RasInfo);
  1502. RasInfo.hwndOwner = pInfo->hwndParent;
  1503. RasInfo.dwStartPage = RASMDPAGE_Status;
  1504. CmFree(pInfo);
  1505. //
  1506. // Call rasdlg.dll -> RasMonitorDlg
  1507. //
  1508. //
  1509. // The destructor of CDynamicLibrary calls FreeLibrary
  1510. //
  1511. CDynamicLibrary LibRasdlg;
  1512. if (!LibRasdlg.Load(TEXT("RASDLG.DLL")))
  1513. {
  1514. CMTRACE1(TEXT("Rasdlg.dll LoadLibrary() failed, GLE=%u."), GetLastError());
  1515. return 1;
  1516. }
  1517. pfnFunc = (WORD (WINAPI *)(LPTSTR,LPRASMONITORDLG))LibRasdlg.GetProcAddress("RasMonitorDlg"A_W);
  1518. if (pfnFunc)
  1519. {
  1520. pfnFunc(rcsStatus.szDeviceName, &RasInfo);
  1521. }
  1522. LibRasdlg.Unload();
  1523. rasApiDll.Unload();
  1524. //
  1525. // Minimize the working set before exit the thread.
  1526. //
  1527. CMonitor::MinimizeWorkingSet();
  1528. return 0;
  1529. }
  1530. //+----------------------------------------------------------------------------
  1531. //
  1532. // Function: CCmConnection::StatePrompt
  1533. //
  1534. // Synopsis: The connection is in the prompt-reconnect stste
  1535. // Run the message loop until state is changed
  1536. //
  1537. // Arguments: None
  1538. //
  1539. // Returns: CONN_STATE - The new state, either STATE_TERMINATED or
  1540. // STATE_RECONNECTING
  1541. //
  1542. // History: fengsun Created Header 2/4/98
  1543. //
  1544. //+----------------------------------------------------------------------------
  1545. CCmConnection::CONN_STATE CCmConnection::StatePrompt()
  1546. {
  1547. ASSERT_VALID(this);
  1548. MYDBGASSERT(m_dwState == STATE_PROMPT_RECONNECT);
  1549. // MYDBGASSERT(!IsWindow(m_StatusDlg.GetHwnd()));
  1550. CMTRACE(TEXT("Enter StatePrompt"));
  1551. LPTSTR pszReconnectMsg = CmFmtMsg(CMonitor::GetInstance(),IDMSG_RECONNECT,m_szServiceName);
  1552. m_ReconnectDlg.Create(CMonitor::GetInstance(), NULL,
  1553. pszReconnectMsg,m_hBigIcon);
  1554. CmFree(pszReconnectMsg);
  1555. //
  1556. // Change the window position, so multiple status window will not be at
  1557. // the same position
  1558. //
  1559. PositionWindow(m_ReconnectDlg.GetHwnd(), m_dwPositionId);
  1560. //
  1561. // Change the dialog titlebar icon. This does not work,
  1562. // Reconnect dialog does not have system menu icon
  1563. //
  1564. SendMessageU(m_ReconnectDlg.GetHwnd(),WM_SETICON,ICON_BIG,(LPARAM) m_hBigIcon);
  1565. SendMessageU(m_ReconnectDlg.GetHwnd(),WM_SETICON,ICON_SMALL,(LPARAM) m_hSmallIcon);
  1566. //
  1567. // Minimize the working set
  1568. //
  1569. CMonitor::MinimizeWorkingSet();
  1570. MSG msg;
  1571. while(GetMessageU(&msg, NULL,0,0))
  1572. {
  1573. if (msg.hwnd == NULL)
  1574. {
  1575. //
  1576. // it is a thread message
  1577. //
  1578. MYDBGASSERT((msg.message >= WM_APP) || (msg.message == WM_CONN_EVENT));
  1579. if (msg.message == WM_CONN_EVENT)
  1580. {
  1581. MYDBGASSERT(msg.wParam == EVENT_USER_DISCONNECT
  1582. || msg.wParam == EVENT_RECONNECT
  1583. || msg.wParam == EVENT_CMDIAL_HANGUP);
  1584. break;
  1585. }
  1586. }
  1587. else
  1588. {
  1589. //
  1590. // Also dispatch message for Modeless dialog box
  1591. //
  1592. if (!IsDialogMessageU(m_ReconnectDlg.GetHwnd(), &msg))
  1593. {
  1594. TranslateMessage(&msg);
  1595. DispatchMessageU(&msg);
  1596. }
  1597. }
  1598. }
  1599. //
  1600. // If the status window is not destroyed yet, destroy it now
  1601. //
  1602. if (IsWindow(m_ReconnectDlg.GetHwnd()))
  1603. {
  1604. DestroyWindow(m_ReconnectDlg.GetHwnd());
  1605. }
  1606. if (msg.wParam == EVENT_RECONNECT)
  1607. {
  1608. return STATE_RECONNECTING;
  1609. }
  1610. else
  1611. {
  1612. return STATE_TERMINATED;
  1613. }
  1614. }
  1615. //+----------------------------------------------------------------------------
  1616. //
  1617. // Function: CCmConnection::OnTrayIcon
  1618. //
  1619. // Synopsis: called Upon tray icon message
  1620. //
  1621. // Arguments: WPARAM wParam - wParam of the message
  1622. // LPARAM lParam - lParam of the message
  1623. //
  1624. // Returns: DWORD - return value of the message
  1625. //
  1626. // History: fengsun Created Header 2/4/98
  1627. //
  1628. //+----------------------------------------------------------------------------
  1629. DWORD CCmConnection::OnTrayIcon(WPARAM, LPARAM lParam)
  1630. {
  1631. ASSERT_VALID(this);
  1632. MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
  1633. switch (lParam)
  1634. {
  1635. case WM_LBUTTONDBLCLK:
  1636. //
  1637. // Don't show the UI if we are at Winlogon unless we are on NT4
  1638. //
  1639. if (!IsLogonAsSystem() || OS_NT4)
  1640. {
  1641. m_StatusDlg.BringToTop();
  1642. }
  1643. break;
  1644. case WM_RBUTTONUP:
  1645. {
  1646. //
  1647. // Popup the tray icon menu at the mouse location
  1648. //
  1649. POINT PosMouse;
  1650. if (!GetCursorPos(&PosMouse))
  1651. {
  1652. MYDBGASSERT(FALSE);
  1653. break;
  1654. }
  1655. //
  1656. // From Microsoft Knowledge Base
  1657. // PRB: Menus for Notification Icons Don't Work Correctly
  1658. // To correct the first behavior, you need to make the current window
  1659. // the foreground window before calling TrackPopupMenu
  1660. //
  1661. // see also fixes for Whistler bugs 41696 and 90576
  1662. //
  1663. if (FALSE == SetForegroundWindow(m_StatusDlg.GetHwnd()))
  1664. {
  1665. CMTRACE(TEXT("SetForegroundWindow before TrackPopupMenu failed"));
  1666. }
  1667. m_TrayIcon.PopupMenu(PosMouse.x, PosMouse.y, m_StatusDlg.GetHwnd());
  1668. PostMessageU(m_StatusDlg.GetHwnd(), WM_NULL, 0, 0);
  1669. }
  1670. break;
  1671. default:
  1672. break;
  1673. }
  1674. return TRUE;
  1675. }
  1676. //+----------------------------------------------------------------------------
  1677. //
  1678. // Function: CCmConnection::OnStayOnLine
  1679. //
  1680. // Synopsis: Called when "Stay Online" button is pressed in the disconnect
  1681. // count down dialog box
  1682. //
  1683. // Arguments: None
  1684. //
  1685. // Returns: Nothing
  1686. //
  1687. // History: fengsun Created Header 2/11/98
  1688. //
  1689. //+----------------------------------------------------------------------------
  1690. void CCmConnection::OnStayOnLine()
  1691. {
  1692. ASSERT_VALID(this);
  1693. //
  1694. // Change the dialog to display status
  1695. //
  1696. m_StatusDlg.ChangeToStatus();
  1697. m_dwState = STATE_CONNECTED;
  1698. if (m_WatchProcess.IsIdle())
  1699. {
  1700. //
  1701. // If idle bacause of no watching process
  1702. // User clickes stay online, 0 watch process is not idle any more
  1703. //
  1704. m_WatchProcess.SetNotIdle();
  1705. }
  1706. if (m_IdleStatistics.IsIdleTimeout())
  1707. {
  1708. //
  1709. // If idle bacause of no traffic
  1710. // User clickes stay online, restart the idle counter
  1711. //
  1712. m_IdleStatistics.Reset();
  1713. }
  1714. }
  1715. //+----------------------------------------------------------------------------
  1716. //
  1717. // Function: CCmConnection::PostHangupMsg
  1718. //
  1719. // Synopsis: Called by monitor to clean up the connection. The request was
  1720. // come from cmdial to remove tray icon and status dialog
  1721. // cmdial is responsible to actually hangup the connection
  1722. //
  1723. // Arguments: None
  1724. //
  1725. // Returns: Nothing
  1726. //
  1727. // History: fengsun Created Header 2/11/98
  1728. //
  1729. //+----------------------------------------------------------------------------
  1730. void CCmConnection::PostHangupMsg() const
  1731. {
  1732. //
  1733. // NOTE: This function is called within the Monitor thread
  1734. // Do not referrence any volatile data
  1735. // The CMMON_HANGUP_INFO request can come in at any state
  1736. //
  1737. //
  1738. // Post a message, so this will be handled in the connection thread
  1739. //
  1740. BOOL fRet = PostThreadMessageU(m_dwThreadId,WM_CONN_EVENT, EVENT_CMDIAL_HANGUP, 0);
  1741. #if DBG
  1742. if (FALSE == fRet)
  1743. {
  1744. CMTRACE1(TEXT("CCmConnection::PostHangupMsg -- PostThreadMessage failed (GLE = %d)"), GetLastError());
  1745. }
  1746. #endif
  1747. }
  1748. //+----------------------------------------------------------------------------
  1749. //
  1750. // Function: CCmConnection::Reconnect
  1751. //
  1752. // Synopsis: The connection is in reconnecting state
  1753. // Simply load cmdial32.dll and call CmCustomDialDlg
  1754. //
  1755. // Arguments: None
  1756. //
  1757. // Returns: BOOL whether reconnect successfully
  1758. //
  1759. // History: fengsun Created Header 2/11/98
  1760. //
  1761. //+----------------------------------------------------------------------------
  1762. BOOL CCmConnection::Reconnect()
  1763. {
  1764. ASSERT_VALID(this);
  1765. MYDBGASSERT(m_dwState == STATE_RECONNECTING);
  1766. CMTRACE(TEXT("Enter Reconnect"));
  1767. //
  1768. // Load cmdial32.dll and call CmReConnect();
  1769. //
  1770. //
  1771. // The destructor of CDynamicLibrary calls FreeLibrary
  1772. //
  1773. CDynamicLibrary LibCmdial;
  1774. if (!LibCmdial.Load(TEXT("cmdial32.dll")))
  1775. {
  1776. MYDBGASSERT(FALSE);
  1777. return FALSE;
  1778. }
  1779. CmReConnectFUNC pfnCmReConnect;
  1780. pfnCmReConnect = (CmReConnectFUNC)LibCmdial.GetProcAddress(c_pszCmReconnect);
  1781. MYDBGASSERT(pfnCmReConnect);
  1782. if (!pfnCmReConnect)
  1783. {
  1784. return FALSE;
  1785. }
  1786. //
  1787. // Log that we're reconnecting
  1788. //
  1789. m_Log.Log(RECONNECT_EVENT);
  1790. //
  1791. // If we have a RAS phonebook name pass it to reconnect, else NULL for system
  1792. //
  1793. if (m_szRasPhoneBook[0])
  1794. {
  1795. return (pfnCmReConnect(m_szRasPhoneBook, m_szServiceName, &m_ReconnectInfo));
  1796. }
  1797. else
  1798. {
  1799. return (pfnCmReConnect(NULL, m_szServiceName, &m_ReconnectInfo));
  1800. }
  1801. }
  1802. //+----------------------------------------------------------------------------
  1803. //
  1804. // Function: CCmConnection::CheckRasConnection
  1805. //
  1806. // Synopsis: Check whether the RAS connection is still connected
  1807. //
  1808. // Arguments: OUT BOOL& fLostConnection -
  1809. // If the no longer connected, TRUE means lost connection
  1810. // FALSE means user disconnect
  1811. //
  1812. // Returns: BOOL - Whether still connected
  1813. //
  1814. // History: fengsun Created Header 2/8/98
  1815. //
  1816. //+----------------------------------------------------------------------------
  1817. BOOL CCmConnection::CheckRasConnection(OUT BOOL& fLostConnection)
  1818. {
  1819. ASSERT_VALID(this);
  1820. MYDBGASSERT(m_hRasDial != NULL || m_hRasTunnel != NULL);
  1821. // Whether we are still connected
  1822. BOOL fConnected = TRUE;
  1823. RASCONNSTATUS rcsStatus;
  1824. DWORD dwRes = ERROR_SUCCESS;
  1825. if (NULL == m_hRasDial && NULL == m_hRasTunnel)
  1826. {
  1827. //
  1828. // If both m_hRasTunnel and m_hRasDial are NULL, we're definitely not
  1829. // connected. Yes, we have lost the connection, and return value is FALSE.
  1830. //
  1831. fLostConnection = TRUE;
  1832. return FALSE;
  1833. }
  1834. // check tunnel status first
  1835. if (m_hRasTunnel != NULL)
  1836. {
  1837. memset(&rcsStatus,0,sizeof(rcsStatus));
  1838. rcsStatus.dwSize = sizeof(rcsStatus);
  1839. //
  1840. // This function will load RASAPI32.dll, if not loaded yet
  1841. // Will not unload RASAPI32.dll, since this function is called on timer
  1842. //
  1843. dwRes = m_RasApiDll.RasGetConnectStatus(m_hRasTunnel, &rcsStatus);
  1844. if (dwRes != ERROR_SUCCESS || rcsStatus.rasconnstate == RASCS_Disconnected)
  1845. {
  1846. //
  1847. // The connection is lost
  1848. //
  1849. fConnected = FALSE;
  1850. }
  1851. }
  1852. // check dialup connection status
  1853. if (fConnected && m_hRasDial != NULL)
  1854. {
  1855. memset(&rcsStatus,0,sizeof(rcsStatus));
  1856. rcsStatus.dwSize = sizeof(rcsStatus);
  1857. dwRes = m_RasApiDll.RasGetConnectStatus(m_hRasDial, &rcsStatus);
  1858. }
  1859. if ((dwRes == ERROR_SUCCESS)
  1860. && ((rcsStatus.rasconnstate != RASCS_Disconnected)
  1861. || (rcsStatus.dwError == PENDING)))
  1862. {
  1863. // CMTRACE(TEXT("CCmConnection::CheckRasConnection - rcsStatus.rasconnstate != RASCS_Disconnected || rcsStatus.dwError == PENDING"));
  1864. return TRUE;
  1865. }
  1866. //
  1867. // CM is no longer connected
  1868. //
  1869. CMTRACE3(TEXT("OnTimer() RasGetConnectStatus() returns %u, rasconnstate=%u, dwError=%u."), dwRes,
  1870. rcsStatus.rasconnstate, rcsStatus.dwError);
  1871. if (rcsStatus.dwError == ERROR_USER_DISCONNECTION && OS_W9X)
  1872. {
  1873. fLostConnection = FALSE;
  1874. //
  1875. // On NT, ERROR_USER_DISCONNECTION is received in
  1876. // the event of idle disconnect which we consider
  1877. // a lost connection
  1878. //
  1879. }
  1880. else
  1881. {
  1882. fLostConnection = TRUE;
  1883. }
  1884. return FALSE;
  1885. }
  1886. //+----------------------------------------------------------------------------
  1887. //
  1888. // Function: CCmConnection::OnEndSession
  1889. //
  1890. // Synopsis: Called upon WM_ENDSESSION message
  1891. //
  1892. // Arguments: BOOL fEndSession - whether the session is being ended, wParam
  1893. // BOOL fLogOff - whether the user is logging off or shutting down,
  1894. // lParam
  1895. //
  1896. // Returns: Nothing
  1897. //
  1898. // History: fengsun Created Header 5/4/98
  1899. //
  1900. //+----------------------------------------------------------------------------
  1901. BOOL CCmConnection::OnEndSession(BOOL fEndSession, BOOL)
  1902. {
  1903. CMTRACE(TEXT("CCmConnection::OnEndSession"));
  1904. if (fEndSession)
  1905. {
  1906. //
  1907. // The session can end any time after this function returns
  1908. // If we are connected, hangup the connection
  1909. //
  1910. if(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN)
  1911. {
  1912. return CmCustomHangup(FALSE, TRUE); // fPromptReconnect = FALSE, fEndSession = TRUE
  1913. }
  1914. }
  1915. return TRUE;
  1916. }
  1917. //+----------------------------------------------------------------------------
  1918. //
  1919. // Function: CCmConnection::OnAdditionalTrayMenu
  1920. //
  1921. // Synopsis: Called upon additional menuitem is selected from tray icon menu
  1922. //
  1923. // Arguments: WORD nCmd - LOWORD(wParam) of WM_COMMAND, the menu id
  1924. //
  1925. // Returns: Nothing
  1926. //
  1927. // History: fengsun Created Header 2/12/98
  1928. //
  1929. //+----------------------------------------------------------------------------
  1930. void CCmConnection::OnAdditionalTrayMenu(WORD nCmd)
  1931. {
  1932. ASSERT_VALID(this);
  1933. MYDBGASSERT( (nCmd >= IDM_TRAYMENU)
  1934. && nCmd <(IDM_TRAYMENU + m_TrayIcon.GetAdditionalMenuNum()));
  1935. nCmd -= IDM_TRAYMENU; // get the index for the command
  1936. if (nCmd >= m_TrayIcon.GetAdditionalMenuNum())
  1937. {
  1938. return;
  1939. }
  1940. //
  1941. // Run the command line
  1942. //
  1943. ExecCmdLine(m_TrayIcon.GetMenuCommand(nCmd), m_IniService.GetFile());
  1944. }
  1945. //+----------------------------------------------------------------------------
  1946. //
  1947. // Function: CCmConnection::GetProcessId
  1948. //
  1949. // Synopsis: Find the process specified by pszModule & returns its PID
  1950. //
  1951. // Arguments: WCHAR *pszModule
  1952. //
  1953. // Returns: PID of process or 0 if not found
  1954. //
  1955. // History: v-vijayb Created 7/20/99
  1956. //
  1957. //+----------------------------------------------------------------------------
  1958. DWORD CCmConnection::GetProcessId(WCHAR *pszModule)
  1959. {
  1960. DWORD dwPID = 0;
  1961. HINSTANCE hInstLib;
  1962. DWORD cbPIDs, cbNeeded, iPID, cPIDs;
  1963. DWORD *pdwPIDs = NULL;
  1964. HANDLE hProcess;
  1965. HMODULE hMod;
  1966. WCHAR szFileName[MAX_PATH + 1];
  1967. //
  1968. // PSAPI Function Pointers.
  1969. //
  1970. BOOL (WINAPI *lpfEnumProcesses)(DWORD *, DWORD cb, DWORD *);
  1971. BOOL (WINAPI *lpfEnumProcessModules)(HANDLE, HMODULE *, DWORD, LPDWORD);
  1972. DWORD (WINAPI *lpfGetModuleBaseName)(HANDLE, HMODULE, WCHAR *, DWORD);
  1973. hInstLib = LoadLibrary(TEXT("PSAPI.DLL"));
  1974. if (hInstLib == NULL)
  1975. {
  1976. return (0);
  1977. }
  1978. //
  1979. // Get procedure addresses.
  1980. //
  1981. lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*)) GetProcAddress(hInstLib, "EnumProcesses") ;
  1982. lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress(hInstLib, "EnumProcessModules") ;
  1983. lpfGetModuleBaseName =(DWORD (WINAPI *)(HANDLE, HMODULE, WCHAR *, DWORD)) GetProcAddress(hInstLib, "GetModuleBaseNameW") ;
  1984. if (lpfEnumProcesses == NULL || lpfEnumProcessModules == NULL || lpfGetModuleBaseName == NULL)
  1985. {
  1986. goto OnError;
  1987. }
  1988. cbPIDs = 256 * sizeof(DWORD);
  1989. cbNeeded = 0;
  1990. do
  1991. {
  1992. if (pdwPIDs != NULL)
  1993. {
  1994. HeapFree(GetProcessHeap(), 0, pdwPIDs);
  1995. cbPIDs = cbNeeded;
  1996. }
  1997. pdwPIDs = (DWORD *) CmMalloc(cbPIDs);
  1998. if (pdwPIDs == NULL)
  1999. {
  2000. goto OnError;
  2001. }
  2002. if (!lpfEnumProcesses(pdwPIDs, cbPIDs, &cbNeeded))
  2003. {
  2004. goto OnError;
  2005. }
  2006. } while (cbNeeded > cbPIDs);
  2007. cPIDs = cbNeeded / sizeof(DWORD);
  2008. for (iPID = 0; iPID < cPIDs; iPID ++)
  2009. {
  2010. szFileName[0] = 0;
  2011. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pdwPIDs[iPID]);
  2012. if (hProcess != NULL)
  2013. {
  2014. if (lpfEnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
  2015. {
  2016. if (lpfGetModuleBaseName(hProcess, hMod, szFileName, sizeof(szFileName)))
  2017. {
  2018. if (lstrcmpiW(pszModule, szFileName) == 0)
  2019. {
  2020. dwPID = pdwPIDs[iPID];
  2021. }
  2022. }
  2023. }
  2024. CloseHandle(hProcess);
  2025. if (dwPID != 0)
  2026. {
  2027. break;
  2028. }
  2029. }
  2030. }
  2031. OnError:
  2032. if (pdwPIDs != NULL)
  2033. {
  2034. CmFree(pdwPIDs);
  2035. }
  2036. FreeLibrary(hInstLib);
  2037. return (dwPID);
  2038. }
  2039. typedef BOOL (WINAPI *lpfDuplicateTokenEx)(
  2040. HANDLE,
  2041. DWORD,
  2042. LPSECURITY_ATTRIBUTES,
  2043. SECURITY_IMPERSONATION_LEVEL,
  2044. TOKEN_TYPE,
  2045. PHANDLE
  2046. );
  2047. //+----------------------------------------------------------------------------
  2048. //
  2049. // Function: CCmConnection::RunAsUser
  2050. //
  2051. // Synopsis: Run the action as an exe or other shell object on the choosen desktop
  2052. //
  2053. // Arguments: WCHAR *pszProgram - name of module to be launched
  2054. // WCHAR *pszParams - parameters to be passed to module
  2055. // WCHAR *pszDesktop - desktop on which to launch module
  2056. //
  2057. // Returns: HANDLE - The action Process handle, for Win32 only
  2058. //
  2059. // History: 07/19/99 v-vijayb Created
  2060. // 07/27/99 nickball Return codes and explicit path.
  2061. //
  2062. //+----------------------------------------------------------------------------
  2063. HANDLE CCmConnection::RunAsUser(WCHAR *pszProgram, WCHAR *pszParams, WCHAR *pszDesktop)
  2064. {
  2065. STARTUPINFOW StartupInfo = {0};
  2066. PROCESS_INFORMATION ProcessInfo = {0};
  2067. WCHAR szShell[MAX_PATH + 1];
  2068. DWORD dwPID;
  2069. HANDLE hProcess = NULL;
  2070. HANDLE hUserToken = NULL;
  2071. HANDLE hProcessToken = NULL;
  2072. MYDBGASSERT(pszProgram);
  2073. CMTRACE(TEXT("RunAsUser"));
  2074. //
  2075. // NOTE: Normally we only have one icon in the systray, being run by Explorer,
  2076. // thus any menuitem execution is done in the user's account. On NT4,
  2077. // we can't rely on the Connections Folder to handle this for us, so we
  2078. // create and manage the systray icon ourselves, but we have to make
  2079. // sure than any items executed, are executed using the User's account.
  2080. // Or, on NT5 and later, we can have the strange case where HideTrayIcon
  2081. // is set to 0, thus requiring us to create/manage a systray icon (so
  2082. // there are 2 connectoids in the systray), hence the code below checks
  2083. // for all flavors of NT (rather than just NT4).
  2084. //
  2085. if (!OS_NT)
  2086. {
  2087. MYDBGASSERT(FALSE);
  2088. return NULL;
  2089. }
  2090. //
  2091. // Get the PID for the shell. We expect explorer.exe, but could be others
  2092. //
  2093. GetProfileString(TEXT("windows"), TEXT("shell"), TEXT("explorer.exe"), szShell, MAX_PATH);
  2094. dwPID = GetProcessId(szShell);
  2095. //
  2096. // Now extract the token from the shell process
  2097. //
  2098. if (dwPID)
  2099. {
  2100. //
  2101. // Get the Process handle from the PID
  2102. //
  2103. hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID);
  2104. CMTRACE1(TEXT("RunAsUser/OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID) returns 0x%x"), hProcess);
  2105. if (hProcess)
  2106. {
  2107. //
  2108. // Get the token
  2109. //
  2110. if (OpenProcessToken(hProcess, TOKEN_ASSIGN_PRIMARY | TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_DUPLICATE, &hProcessToken))
  2111. {
  2112. HINSTANCE hInstLibrary = LoadLibrary(TEXT("ADVAPI32.DLL"));
  2113. CMTRACE1(TEXT("RunAsUser/LoadLibrary(ADVAPI32.DLL) returns 0x%x"), hInstLibrary);
  2114. //
  2115. // Get user token via DuplicateTokenEx and impersonate the user
  2116. //
  2117. if (hInstLibrary)
  2118. {
  2119. lpfDuplicateTokenEx lpfuncDuplicateTokenEx = (lpfDuplicateTokenEx) GetProcAddress(hInstLibrary, "DuplicateTokenEx");
  2120. CMTRACE1(TEXT("RunAsUser/GetProcAddress(hInstLibrary, DuplicateTokenEx) returns 0x%x"), lpfuncDuplicateTokenEx);
  2121. if (lpfuncDuplicateTokenEx)
  2122. {
  2123. if (lpfuncDuplicateTokenEx(hProcessToken,
  2124. TOKEN_ASSIGN_PRIMARY | TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_DUPLICATE,
  2125. NULL,
  2126. SecurityImpersonation,
  2127. TokenPrimary,
  2128. &hUserToken))
  2129. {
  2130. BOOL bRes = ImpersonateLoggedOnUser(hUserToken);
  2131. if (FALSE == bRes)
  2132. {
  2133. hUserToken = NULL;
  2134. CMTRACE1(TEXT("RunAsUser/ImpersonateLoggedOnUser failed, GLE=&u"), GetLastError());
  2135. }
  2136. }
  2137. }
  2138. FreeLibrary(hInstLibrary);
  2139. }
  2140. CloseHandle(hProcessToken);
  2141. }
  2142. CloseHandle(hProcess);
  2143. }
  2144. }
  2145. CMTRACE1(TEXT("RunAsUser - hUserToken is 0x%x"), hUserToken);
  2146. //
  2147. // Can't impersonate user, don't run because we're in the system account
  2148. //
  2149. if (NULL == hUserToken)
  2150. {
  2151. MYDBGASSERT(FALSE);
  2152. return NULL;
  2153. }
  2154. //
  2155. // Now prep CreateProcess
  2156. //
  2157. StartupInfo.cb = sizeof(StartupInfo);
  2158. if (pszDesktop)
  2159. {
  2160. StartupInfo.lpDesktop = pszDesktop;
  2161. StartupInfo.wShowWindow = SW_SHOW;
  2162. }
  2163. //
  2164. // Build the path and params
  2165. //
  2166. LPWSTR pszwCommandLine = (LPWSTR) CmMalloc((2 + lstrlen(pszProgram) + 1 + lstrlen(pszParams) + 1) * sizeof(TCHAR));
  2167. if (NULL == pszwCommandLine)
  2168. {
  2169. MYDBGASSERT(FALSE);
  2170. return NULL;
  2171. }
  2172. //
  2173. // Copy path, but surround with double quotes for security
  2174. //
  2175. lstrcpyU(pszwCommandLine, TEXT("\""));
  2176. lstrcatU(pszwCommandLine, pszProgram);
  2177. lstrcatU(pszwCommandLine, TEXT("\""));
  2178. if (pszParams[0] != L'\0')
  2179. {
  2180. lstrcatU(pszwCommandLine, TEXT(" "));
  2181. lstrcatU(pszwCommandLine, pszParams);
  2182. }
  2183. CMTRACE1(TEXT("RunAsUser/CreateProcessAsUser() - Launching %s"), pszwCommandLine);
  2184. if (NULL == CreateProcessAsUser(hUserToken, NULL, pszwCommandLine,
  2185. NULL, NULL, FALSE, CREATE_SEPARATE_WOW_VDM,
  2186. NULL, NULL,
  2187. &StartupInfo, &ProcessInfo))
  2188. {
  2189. CMTRACE1(TEXT("RunAsUser/CreateProcessAsUser() failed, GLE=%u."), GetLastError());
  2190. ProcessInfo.hProcess = NULL;
  2191. ProcessInfo.hThread = NULL;
  2192. }
  2193. if (ProcessInfo.hThread)
  2194. {
  2195. CloseHandle(ProcessInfo.hThread);
  2196. }
  2197. CloseHandle(hUserToken);
  2198. RevertToSelf();
  2199. CmFree(pszwCommandLine);
  2200. return (ProcessInfo.hProcess);
  2201. }
  2202. //+----------------------------------------------------------------------------
  2203. //
  2204. // Function: CCmConnection::ExecCmdLine
  2205. //
  2206. // Synopsis: ShellExecute the command line
  2207. // The code is copied from CActionList::RunAsExe
  2208. //
  2209. // Arguments: const TCHAR* pszCmdLine - the command line to run, including
  2210. // arguments
  2211. // const TCHAR* pszCmsFile - Full path of CMS file
  2212. //
  2213. // Returns: BOOL - Whether ShellExecute succeed
  2214. //
  2215. // Notes: Menu actions consists of a command string and an optional argument string.
  2216. // The first delimited string is considered the command portion and anything
  2217. // thereafter is treated as the argument portion, which formatless and passed
  2218. // indiscriminately to ShellExecuteEx. Long filename command paths are
  2219. // enclosed in "+" signs by CMAK. Thus the following permutations are allowed:
  2220. //
  2221. // "C:\\Progra~1\\Custom.Exe"
  2222. // "C:\\Progra~1\\Custom.Exe Args"
  2223. // "+C:\\Program Files\\Custom.Exe+"
  2224. // "+C:\\Program Files\\Custom.Exe+ Args"
  2225. //
  2226. // History: 02/10/98 fengsun Created Header
  2227. // 02/09/99 nickball Fixed long filename bug one year later.
  2228. //
  2229. //+----------------------------------------------------------------------------
  2230. BOOL CCmConnection::ExecCmdLine(const TCHAR* pszCmdLine, const TCHAR* pszCmsFile)
  2231. {
  2232. LPTSTR pszArgs = NULL;
  2233. LPTSTR pszCmd = NULL;
  2234. BOOL bRes = FALSE;
  2235. MYDBGASSERT(pszCmdLine);
  2236. MYDBGASSERT(pszCmsFile);
  2237. if (NULL == pszCmdLine || NULL == pszCmsFile)
  2238. {
  2239. return FALSE;
  2240. }
  2241. CMTRACE1(TEXT("ExecCmdLine() pszCmdLine is %s"), pszCmdLine);
  2242. if (CmParsePath(pszCmdLine, pszCmsFile, &pszCmd, &pszArgs))
  2243. {
  2244. CMTRACE1(TEXT("ExecCmdLine() pszCmd is %s"), pszCmd);
  2245. CMTRACE1(TEXT("ExecCmdLine() pszArgs is %s"), pszArgs);
  2246. //
  2247. // Now we have the exe name and args separated, execute it
  2248. //
  2249. if (IsLogonAsSystem())
  2250. {
  2251. HANDLE hProcess = RunAsUser(pszCmd, pszArgs, TEXT("Winsta0\\default"));
  2252. if (hProcess)
  2253. {
  2254. CloseHandle(hProcess);
  2255. bRes = TRUE;
  2256. }
  2257. else
  2258. {
  2259. bRes = FALSE;
  2260. }
  2261. }
  2262. else
  2263. {
  2264. SHELLEXECUTEINFO seiInfo;
  2265. ZeroMemory(&seiInfo,sizeof(seiInfo));
  2266. seiInfo.cbSize = sizeof(seiInfo);
  2267. seiInfo.fMask |= SEE_MASK_FLAG_NO_UI;
  2268. seiInfo.lpFile = pszCmd;
  2269. seiInfo.lpParameters = pszArgs;
  2270. seiInfo.nShow = SW_SHOW;
  2271. //
  2272. // Load Shell32.dll and call Shell_ExecuteEx
  2273. //
  2274. CShellDll ShellDll(TRUE); // TRUE == don't unload shell32.dll because of bug 289463
  2275. bRes = ShellDll.ExecuteEx(&seiInfo);
  2276. CMTRACE2(TEXT("ExecCmdLine/ShellExecuteEx() returns %u - GLE=%u"), bRes, GetLastError());
  2277. }
  2278. }
  2279. CmFree(pszCmd);
  2280. CmFree(pszArgs);
  2281. CMTRACE1(TEXT("ExecCmdLine() - Returns %d"), bRes);
  2282. return bRes;
  2283. }
  2284. //+----------------------------------------------------------------------------
  2285. //
  2286. // Function: CCmConnection::LoadHelpFileName
  2287. //
  2288. // Synopsis: Get the connection help file name
  2289. //
  2290. // Arguments: None
  2291. //
  2292. // Returns: LPTSTR - The help file name, can be a empty string
  2293. // caller is responsible to free the pointer
  2294. //
  2295. // History: Created Header 2/13/98
  2296. //
  2297. //+----------------------------------------------------------------------------
  2298. LPTSTR CCmConnection::LoadHelpFileName()
  2299. {
  2300. //
  2301. // Read the filename from profile first
  2302. //
  2303. LPTSTR pszFullPath = NULL;
  2304. LPTSTR pszFileName = m_IniService.GPPS(c_pszCmSection,c_pszCmEntryHelpFile);
  2305. if (pszFileName != NULL && pszFileName[0])
  2306. {
  2307. //
  2308. // The help file name is relative to the .cmp file, convert it into full name
  2309. //
  2310. pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszFileName);
  2311. }
  2312. CmFree(pszFileName);
  2313. return pszFullPath;
  2314. }
  2315. //+----------------------------------------------------------------------------
  2316. //
  2317. // Function: CCmConnection::LoadConnectionIcons
  2318. //
  2319. // Synopsis: Load big and small icon of the connection
  2320. //
  2321. // Arguments: None
  2322. //
  2323. // Returns: Nothing
  2324. //
  2325. // History: Created Header 2/13/98
  2326. //
  2327. //+----------------------------------------------------------------------------
  2328. void CCmConnection::LoadConnectionIcons()
  2329. {
  2330. // Load large icon name
  2331. LPTSTR pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntryBigIcon);
  2332. if (*pszTmp)
  2333. {
  2334. //
  2335. // The icon name is relative to the .cmp file, convert it into full name
  2336. //
  2337. LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp);
  2338. m_hBigIcon = CmLoadIcon(CMonitor::GetInstance(), pszFullPath);
  2339. CmFree(pszFullPath);
  2340. }
  2341. CmFree(pszTmp);
  2342. // Use default (EXE) large icon if no user icon found
  2343. if (!m_hBigIcon)
  2344. {
  2345. m_hBigIcon = CmLoadIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP));
  2346. }
  2347. // Load the small Icon
  2348. // Load small icon name
  2349. pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntrySmallIcon);
  2350. if (*pszTmp)
  2351. {
  2352. //
  2353. // The icon name is relative to the .cmp file, convert it into full name
  2354. //
  2355. LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp);
  2356. m_hSmallIcon = CmLoadSmallIcon(CMonitor::GetInstance(), pszFullPath);
  2357. CmFree(pszFullPath);
  2358. }
  2359. CmFree(pszTmp);
  2360. // Use default (EXE) small icon if no user icon found
  2361. if (!m_hSmallIcon)
  2362. {
  2363. m_hSmallIcon = CmLoadSmallIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP));
  2364. }
  2365. }
  2366. //+----------------------------------------------------------------------------
  2367. //
  2368. // Function: CCmConnection::PositionWindow
  2369. //
  2370. // Synopsis: Position the window according to dwPositionId, so multiple
  2371. // connection window would be positioned differently
  2372. //
  2373. // Arguments: HWND hWnd - The window to position
  2374. // dwPositionId - the id of the window
  2375. //
  2376. // Returns: Nothing
  2377. //
  2378. // History: fengsun Created Header 3/25/98
  2379. //
  2380. //+----------------------------------------------------------------------------
  2381. void CCmConnection::PositionWindow(HWND hWnd, DWORD dwPositionId)
  2382. {
  2383. MYDBGASSERT(IsWindow(hWnd));
  2384. //
  2385. // Get the rect of this window
  2386. //
  2387. RECT rcDlg;
  2388. GetWindowRect(hWnd, &rcDlg);
  2389. //
  2390. // center within work area
  2391. //
  2392. RECT rcArea;
  2393. SystemParametersInfoA(SPI_GETWORKAREA, NULL, &rcArea, NULL);
  2394. //
  2395. // Position the window on the desktop work area according to the PositionId
  2396. // x = Desktop.midX - width/2
  2397. // y = Desktop.midY - hight/2 + hight * (PostitionId%3 -1)
  2398. // X position is always the same
  2399. // Y position repeat for every three time
  2400. //
  2401. int xLeft = (rcArea.left + rcArea.right) / 2 - (rcDlg.right - rcDlg.left) / 2;
  2402. int yTop = (rcArea.top + rcArea.bottom) / 2 - (rcDlg.bottom - rcDlg.top) / 2
  2403. + (rcDlg.bottom - rcDlg.top) * ((int)dwPositionId%3 -1);
  2404. //
  2405. // if the dialog is outside the screen, move it inside
  2406. //
  2407. if (xLeft < rcArea.left)
  2408. {
  2409. xLeft = rcArea.left;
  2410. }
  2411. else if (xLeft + (rcDlg.right - rcDlg.left) > rcArea.right)
  2412. {
  2413. xLeft = rcArea.right - (rcDlg.right - rcDlg.left);
  2414. }
  2415. if (yTop < rcArea.top)
  2416. {
  2417. yTop = rcArea.top;
  2418. }
  2419. else if (yTop + (rcDlg.bottom - rcDlg.top) > rcArea.bottom)
  2420. {
  2421. yTop = rcArea.bottom - (rcDlg.bottom - rcDlg.top);
  2422. }
  2423. SetWindowPos(hWnd, NULL, xLeft, yTop, -1, -1,
  2424. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  2425. }
  2426. #ifdef DEBUG
  2427. //+----------------------------------------------------------------------------
  2428. //
  2429. // Function: CCmConnection::AssertValid
  2430. //
  2431. // Synopsis: For debug purpose only, assert the connection object is valid
  2432. //
  2433. // Arguments: None
  2434. //
  2435. // Returns: Nothing
  2436. //
  2437. // History: Created Header 2/12/98
  2438. //
  2439. //+----------------------------------------------------------------------------
  2440. void CCmConnection::AssertValid() const
  2441. {
  2442. MYDBGASSERT(m_dwState >= 0 && m_dwState <= STATE_TERMINATED);
  2443. MYDBGASSERT(m_hRasDial != NULL || m_hRasTunnel != NULL);
  2444. ASSERT_VALID(&m_ConnStatistics);
  2445. ASSERT_VALID(&m_IdleStatistics);
  2446. ASSERT_VALID(&m_StatusDlg);
  2447. // ASSERT_VALID(m_TrayIcon);
  2448. // ASSERT_VALID(m_WatchProcess);
  2449. }
  2450. #endif