//+---------------------------------------------------------------------------- // // File: Monitor.cpp // // Module: CMMON32.EXE // // Synopsis: Implement class CMonitor // // Copyright (c) 1998-1999 Microsoft Corporation // // Author: fengsun Created 01/22/98 // //+---------------------------------------------------------------------------- #include "cmmaster.h" #include "Monitor.h" #include "Connection.h" // The following blocks are copied from winuser.h and wtsapi32.h (we compile with // _WIN32_WINNT set to less than 5.01, so we can't get these values via a #include) // #include "winuser.h" #define WM_WTSSESSION_CHANGE 0x02B1 // #include "WtsApi32.h" #define WTS_CONSOLE_CONNECT 0x1 #define WTS_CONSOLE_DISCONNECT 0x2 #define WTS_REMOTE_CONNECT 0x3 #define WTS_REMOTE_DISCONNECT 0x4 #define WTS_SESSION_LOGON 0x5 #define WTS_SESSION_LOGOFF 0x6 #define WTS_SESSION_LOCK 0x7 #define WTS_SESSION_UNLOCK 0x8 #include "shelldll.cpp" // for common source // // The monitor invisible window class name // static const TCHAR* const c_pszCmMonWndClass = TEXT("CM Monitor Window"); // // static class data members // HINSTANCE CMonitor::m_hInst = NULL; CMonitor* CMonitor::m_pThis = NULL; inline CMonitor::CMonitor() { MYDBGASSERT(m_pThis == NULL); m_pThis = this; m_hProcess = NULL; } inline CMonitor::~CMonitor() { MYDBGASSERT(m_InternalConnArray.GetSize() == 0); MYDBGASSERT(m_ReconnectConnArray.GetSize() == 0); MYDBGASSERT(m_hProcess == NULL); }; //+---------------------------------------------------------------------------- // // Function: WinMain // // Synopsis: WinMain of the exe // // // History: Created Header 1/22/98 // //+---------------------------------------------------------------------------- int WINAPI WinMain(HINSTANCE , HINSTANCE hPrevInst, LPSTR pszCmdLine, int iCmdShow) { // // First Things First, lets initialize the U Api's // if (!InitUnicodeAPI()) { // // Without our U api's we are going no where. Bail. Don't show the message if // we are running in the system account since we might be running without a user // present. // if (!IsLogonAsSystem()) { MessageBox(NULL, TEXT("Cmmon32.exe Initialization Error: Unable to initialize Unicode to ANSI conversion layer, exiting."), TEXT("Connection Manager"), MB_OK | MB_ICONERROR); } return FALSE; } #ifdef DEBUG DWORD cb = 0; HWINSTA hWSta = GetProcessWindowStation(); HDESK hDesk = GetThreadDesktop(GetCurrentThreadId()); TCHAR szWinStation[MAX_PATH] = {0}; TCHAR szDesktopName[MAX_PATH] = {0}; GetUserObjectInformation(hDesk, UOI_NAME, szDesktopName, sizeof(szDesktopName), &cb); GetUserObjectInformation(hWSta, UOI_NAME, szWinStation, sizeof(szWinStation), &cb); CMTRACE(TEXT("=====================================================")); CMTRACE1(TEXT(" CMMON32.EXE - LOADING - Process ID is 0x%x "), GetCurrentProcessId()); CMTRACE1(TEXT(" WindowStation Name = %s"), szWinStation); CMTRACE1(TEXT(" Desktop Name = %s"), szDesktopName); CMTRACE(TEXT("=====================================================")); #endif int iRet = CMonitor::WinMain(GetModuleHandleA(NULL), hPrevInst, pszCmdLine, iCmdShow); #ifdef DEBUG CMTRACE(TEXT("=====================================================")); CMTRACE1(TEXT(" CMMON32.EXE - UNLOADING - Process ID is 0x%x "), GetCurrentProcessId()); CMTRACE(TEXT("=====================================================")); #endif if (!UnInitUnicodeAPI()) { CMASSERTMSG(FALSE, TEXT("cmmon32.exe WinMain, UnInitUnicodeAPI failed - we are probably leaking a handle")); } // // that's what C runtime does to exit. // ExitProcess(iRet); return iRet; } //+---------------------------------------------------------------------------- // // Function: CMonitor::WinMain // // Synopsis: Called by ::WinMain // // Arguments: Same as WinMain // // // Returns: int - return value of the process // // History: Created Header 1/22/98 // //+---------------------------------------------------------------------------- int CMonitor::WinMain(HINSTANCE hInst, HINSTANCE /*hPrevInst*/, LPSTR /*pszCmdLine*/, int /*iCmdShow*/) { m_hInst = hInst; // // The only Monitor object exist during the life time of WinMain // CMonitor theMonitor; if (!theMonitor.Initialize()) { CMTRACE(TEXT("theMonitor.Initialize failed")); return 0; } MSG msg; // // Loop until PostQuitMessage is called, // This happens when both connected and reconnecting array are down to 0 // while(GetMessageU(&msg, NULL,0,0)) { TranslateMessage(&msg); DispatchMessageU(&msg); } theMonitor.Terminate(); CMTRACE(TEXT("The Monitor is terminated")); return 0; } //+---------------------------------------------------------------------------- // // Function: CMonitor::Initialize // // Synopsis: Initialize before the monitor start the message loop // // Arguments: None // // Returns: BOOL - Whether successfully initialized // // History: fengsun Created Header 2/17/98 // //+---------------------------------------------------------------------------- BOOL CMonitor::Initialize() { DWORD dwProcessId = GetCurrentProcessId(); m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); MYDBGASSERT(m_hProcess); BOOL fStandAlone = FALSE; // whether cmmon is lauched directly instead of through cmdial if (FAILED(m_SharedTable.Open())) { #ifdef DEBUG if ( MessageBox(NULL, TEXT("CMMON32.exe has to be launched by CMDIAL. \nContinue testing?"), TEXT("CmMon32 ERROR"), MB_YESNO|MB_ICONQUESTION|MB_SYSTEMMODAL) == IDNO) { return FALSE; } fStandAlone = TRUE; if (FAILED(m_SharedTable.Create())) #endif return FALSE; } #ifdef DEBUG // // No other CMMON running // HWND hwndMonitor; m_SharedTable.GetMonitorWnd(&hwndMonitor); MYDBGASSERT(hwndMonitor == NULL); #endif if ((m_hwndMonitor = CreateMonitorWindow()) == NULL) { CMTRACE(TEXT("CreateMonitorWindow failed")); return FALSE; } MYVERIFY(SUCCEEDED(m_SharedTable.SetMonitorWnd(m_hwndMonitor))); // // Register for user changes (XP onwards only) // if (OS_NT51) { HINSTANCE hInstLib = LoadLibrary(TEXT("WTSAPI32.DLL")); if (hInstLib) { BOOL (WINAPI *pfnWTSRegisterSessionNotification)(HWND, DWORD); pfnWTSRegisterSessionNotification = (BOOL(WINAPI *)(HWND, DWORD)) GetProcAddress(hInstLib, "WTSRegisterSessionNotification") ; if (pfnWTSRegisterSessionNotification) { pfnWTSRegisterSessionNotification(m_hwndMonitor, NOTIFY_FOR_ALL_SESSIONS); } FreeLibrary(hInstLib); } else { MYDBGASSERT(0); } } // // Tell CmDial32.dll, CmMon is ready to receive message // HANDLE hEvent = OpenEventU(EVENT_ALL_ACCESS, FALSE, c_pszCmMonReadyEvent); if (hEvent) { SetEvent(hEvent); CloseHandle(hEvent); } else if (!fStandAlone) // if Cmmon was launched stand alone for debugging purposes, cmdial32.dll won't have created the event beforehand so ignore the error. { DWORD dw = GetLastError(); CMTRACE1(TEXT("CreateMonitorWindow -- OpenEvent failed, GLE=%d"), dw); CMASSERTMSG(FALSE, TEXT("CreateMonitorWindow -- OpenEvent failed. Please check cmtrace for the specific error code.")); } return TRUE; } //+---------------------------------------------------------------------------- // // Function: CMonitor::Terminate // // Synopsis: Cleanup, before exit // // Arguments: None // // Returns: Nothing // // History: Created Header 2/17/98 // //+---------------------------------------------------------------------------- void CMonitor::Terminate() { // // All the thread should exited at this point // if (m_ReconnectConnArray.GetSize() != 0) { MYDBGASSERT(FALSE); } if (m_InternalConnArray.GetSize() != 0) { MYDBGASSERT(FALSE); } // // Unregister for user changes (XP onwards only) // if (OS_NT51) { HINSTANCE hInstLib = LoadLibrary(TEXT("WTSAPI32.DLL")); if (hInstLib) { BOOL (WINAPI *pfnWTSUnRegisterSessionNotification)(HWND); pfnWTSUnRegisterSessionNotification = (BOOL(WINAPI *)(HWND)) GetProcAddress(hInstLib, "WTSUnRegisterSessionNotification") ; if (pfnWTSUnRegisterSessionNotification) { pfnWTSUnRegisterSessionNotification(m_hwndMonitor); } FreeLibrary(hInstLib); } else { MYDBGASSERT(0); } } #ifdef DEBUG HWND hwndMonitor; m_SharedTable.GetMonitorWnd(&hwndMonitor); MYDBGASSERT(hwndMonitor == m_hwndMonitor); #endif MYVERIFY(SUCCEEDED(m_SharedTable.SetMonitorWnd(NULL))); m_SharedTable.Close(); CloseHandle(m_hProcess); m_hProcess = NULL; } //+---------------------------------------------------------------------------- // // Function: CMonitor::CreateMonitorWindow // // Synopsis: Register and create the invisible monitor window // // Arguments: None // // Returns: HWND - The monitor window handle // // History: Created Header 2/17/98 // //+---------------------------------------------------------------------------- HWND CMonitor::CreateMonitorWindow() { // // Register a window class and create the window // WNDCLASSEX wc; ZeroMemory(&wc, sizeof(wc)); wc.lpszClassName = c_pszCmMonWndClass; wc.lpfnWndProc = MonitorWindowProc; wc.cbSize = sizeof(wc); if (!RegisterClassExU( &wc )) { CMTRACE(TEXT("RegisterClassEx failed")); return NULL; } return CreateWindowExU(0, c_pszCmMonWndClass, TEXT(""), 0, 0, 0, 0, 0, 0, 0, m_hInst, 0); } //+---------------------------------------------------------------------------- // // Function: CMonitor::HandleFastUserSwitch // // Synopsis: Does any disconnects required when XP does a fast user switch // // Arguments: dwAction - a WTS_ value indicating how the user's state has changed // // Returns: BOOL - success or failure // // History: 10-Jul-2001 SumitC Created // //+---------------------------------------------------------------------------- BOOL CMonitor::HandleFastUserSwitch(IN DWORD dwAction) { BOOL bRet = TRUE; BOOL fDisconnecting = FALSE; MYDBGASSERT(OS_NT51); if (!OS_NT51) { goto Cleanup; } CMTRACE(TEXT("CMonitor::HandleFastUserSwitch - Start")); if ((WTS_SESSION_LOCK == dwAction) || (WTS_SESSION_UNLOCK == dwAction)) { CMTRACE(TEXT("CMonitor::HandleFastUserSwitch - Ignore, either WTS_SESSION_LOCK or WTS_SESSION_UNLOCK")); // don't do anything for lock and unlock goto Cleanup; } // // See if we are disconnecting // if ((WTS_CONSOLE_DISCONNECT == dwAction) || (WTS_REMOTE_DISCONNECT == dwAction) || (WTS_SESSION_LOGOFF == dwAction)) { CMTRACE(TEXT("CMonitor::HandleFastUserSwitch - Disconnecting: WTS_CONSOLE_DISCONNECT, WTS_REMOTE_DISCONNECT, WTS_SESSION_LOGOFF")); fDisconnecting = TRUE; } else { CMTRACE1(TEXT("CMonitor::HandleFastUserSwitch - Stay connected. dwAction = 0x%x"), dwAction); } // // If a session is being disconnected, find out if any of the connected // connectoids are single-user, and disconnect them if so. // if (fDisconnecting) { CMTRACE(TEXT("CMonitor::HandleFastUserSwitch -- see if theres anything to disconnect")); for (INT i = 0; i < m_InternalConnArray.GetSize(); ++i) { CCmConnection* pConnection = (CCmConnection*)m_InternalConnArray[i]; ASSERT_VALID(pConnection); if (pConnection && (FALSE == pConnection->m_fGlobalGlobal)) { CMTRACE1(TEXT("CMonitor::HandleFastUserSwitch -- found one, disconnecting %s"), pConnection->GetServiceName()); MYVERIFY(TRUE == pConnection->OnEndSession(TRUE, FALSE)); } } } Cleanup: return bRet; } //+---------------------------------------------------------------------------- // // Function: CMonitor::MonitorWindowProc // // Synopsis: The window procedure of the invisible monitor window // // Arguments: HWND hWnd - Window Proc parameters // UINT uMsg - // WPARAM wParam - // LPARAM lParam - // // Returns: LRESULT - // // History: Created Header 2/3/98 // //+---------------------------------------------------------------------------- LRESULT CALLBACK CMonitor::MonitorWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COPYDATA: { ASSERT_VALID(m_pThis); COPYDATASTRUCT* pCopyData = (COPYDATASTRUCT*) lParam; MYDBGASSERT(pCopyData); switch(pCopyData->dwData) { case CMMON_CONNECTED_INFO: MYDBGASSERT(pCopyData->cbData >= sizeof(CM_CONNECTED_INFO)); m_pThis->OnConnected((CM_CONNECTED_INFO*)pCopyData->lpData); return TRUE; case CMMON_HANGUP_INFO: MYDBGASSERT(pCopyData->cbData == sizeof(CM_HANGUP_INFO)); m_pThis->OnHangup((CM_HANGUP_INFO*)pCopyData->lpData); return TRUE; default: MYDBGASSERT(FALSE); return FALSE; } } break; case WM_REMOVE_CONNECTION: ASSERT_VALID(m_pThis); m_pThis->OnRemoveConnection((DWORD)wParam, (CCmConnection*)lParam); return TRUE; break; case WM_QUERYENDSESSION: CMTRACE(TEXT("CMonitor::MonitorWindowProc -- Got WM_QUERYENDSESSION message")); return m_pThis->OnQueryEndSession((BOOL)lParam); break; case WM_ENDSESSION: CMTRACE(TEXT("CMonitor::MonitorWindowProc -- Got WM_ENDSESSION message")); break; default: break; } return DefWindowProcU(hWnd, uMsg, wParam, lParam); } //+---------------------------------------------------------------------------- // // Function: CMonitor::OnConnected // // Synopsis: Called upon CMMON_CONNECTED_INFO received from cmdial // // Arguments: const CONNECTED_INFO* pConnectedInfo - Info from CmDial // // Returns: Nothing // // History: fengsun Created Header 2/3/98 // //+---------------------------------------------------------------------------- void CMonitor::OnConnected(const CM_CONNECTED_INFO* pConnectedInfo) { ASSERT_VALID(this); CMTRACE(TEXT("CMonitor::OnConnected")); RestoreWorkingSet(); MYDBGASSERT(pConnectedInfo); // // Not in the connected table // MYDBGASSERT(!LookupConnection(m_InternalConnArray, pConnectedInfo->szEntryName)); // ASSERT in the shared table CM_CONNECTION ConnectionEntry; if (FAILED(m_SharedTable.GetEntry(pConnectedInfo->szEntryName, &ConnectionEntry))) { MYDBGASSERT(!"CMonitor::OnConnected: Can not find the connection"); return; } CCmConnection* pConnection = new CCmConnection(pConnectedInfo, &ConnectionEntry); MYDBGASSERT(pConnection); if (pConnection) { m_InternalConnArray.Add(pConnection); pConnection->StartConnectionThread(); } } //+---------------------------------------------------------------------------- // // Function: CMonitor::OnHangup // // Synopsis: Upon CMMON_HANGUP_INFO request from CMDIAL // Post the request to the thread // // Arguments: const CM_HANGUP_INFO* pHangupInfo - Info from CmDial // // Returns: Nothing // // History: fengsun Created Header 2/12/98 // //+---------------------------------------------------------------------------- void CMonitor::OnHangup(const CM_HANGUP_INFO* pHangupInfo) { ASSERT_VALID(this); RestoreWorkingSet(); MYDBGASSERT(pHangupInfo); MYDBGASSERT(pHangupInfo->szEntryName[0]); // // Upon hangup request from CMDIAL.DLL // Look up the InternalConnArray for the connection // CCmConnection* pConnection = LookupConnection(m_InternalConnArray,pHangupInfo->szEntryName); // // CMDIAL post this message regardless whether there is a connection // if (!pConnection) { return; } pConnection->PostHangupMsg(); // // The connection thread will post a REMOVE_CONNECTION message back when finished // } //+---------------------------------------------------------------------------- // // Function: CMonitor::LookupConnection // // Synopsis: Look up a connection from connection array by service name // // Arguments: const CPtrArray& ConnArray - The array to lookup // const TCHAR* pServiceName - The servicename of the connection // // Returns: CCmConnection* - the connection found or NULL // // History: fengsun Created Header 2/17/98 // //+---------------------------------------------------------------------------- CCmConnection* CMonitor::LookupConnection(const CPtrArray& ConnArray, const TCHAR* pServiceName) const { for (int i =0; iGetServiceName()) == 0) { return pConnection; } } return NULL; } //+---------------------------------------------------------------------------- // // Function: CMonitor::LookupConnection // // Synopsis: Look up a connection from connection array by connection pointer // // Arguments: const CPtrArray& ConnArray - The array to lookup // const CCmConnection* pConnection - The connection pointer // // Returns: int - the index to the array, or -1 if not found // // History: Created Header 2/17/98 // //+---------------------------------------------------------------------------- int CMonitor::LookupConnection(const CPtrArray& ConnArray, const CCmConnection* pConnection) const { ASSERT_VALID(pConnection); for (int i =0; im_SharedTable.ClearEntry(pConnection->GetServiceName()); } // // The internal connection list is not safe to be accessed by multiple thread // Message will be processed in monitor thread OnRemoveConnection // PostMessageU(GetMonitorWindow(), WM_REMOVE_CONNECTION, REMOVE_CONNECTION, (LPARAM)pConnection); } //+---------------------------------------------------------------------------- // // Function: CMonitor::MoveToReconnectingConn // // Synopsis: Called by connection thread. Move a connection from connected // array to reconnecting array // // Arguments: CCmConnection* pConnection - The connectio to move // // Returns: Nothing // // History: fengsun Created Header 2/23/98 // //+---------------------------------------------------------------------------- void CMonitor::MoveToReconnectingConn(CCmConnection* pConnection) { // // Message will be processed in OnRemoveConnection // Note: SendMessage to another thread can cause deadlock, if the reveiving // thread also SendMessage back to this thread. // Use SendMessageTimeout if that is the case // PostMessageU(GetMonitorWindow(), WM_REMOVE_CONNECTION, MOVE_TO_RECONNECTING, (LPARAM)pConnection); } //+---------------------------------------------------------------------------- // // Function: CMonitor::OnRemoveConnection // // Synopsis: Called whether a remove connection request is received from // connection thread. // Remove the connection from connected array or reconnecting array // Delete it from the shared connectio table // If both array are down to 0, exit cmmon // // Arguments: DWORD dwRequestType - // REMOVE_CONNECTION remove the connection from either array // MOVE_TO_RECONNECTING move the connection from connected array // to reconnecting array // // CCmConnection* pConnection - The connetion to remove or move // // Returns: Nothing // // History: fengsun Created Header 2/3/98 // //+---------------------------------------------------------------------------- void CMonitor::OnRemoveConnection(DWORD dwRequestType, CCmConnection* pConnection) { ASSERT_VALID(this); ASSERT_VALID(pConnection); switch(dwRequestType) { case REMOVE_CONNECTION: { int nIndex = LookupConnection(m_InternalConnArray, pConnection); if (nIndex != -1) { // // Remove the entry from connected array // m_InternalConnArray.RemoveAt(nIndex); } else { // // Remove the entry from reconnecting array // nIndex = LookupConnection(m_ReconnectConnArray, pConnection); MYDBGASSERT(nIndex != -1); if (nIndex == -1) { break; } m_ReconnectConnArray.RemoveAt(nIndex); } delete pConnection; } break; case MOVE_TO_RECONNECTING: { // // Move from connected array to reconnecting array // int nIndex = LookupConnection(m_InternalConnArray, pConnection); MYDBGASSERT(nIndex != -1); if (nIndex == -1) { break; } m_InternalConnArray.RemoveAt(nIndex); m_ReconnectConnArray.Add(pConnection); } break; default: MYDBGASSERT(FALSE); break; } // // If there are no connections, quit CmMon // if (m_ReconnectConnArray.GetSize() == 0 && m_InternalConnArray.GetSize() == 0) { PostQuitMessage(0); } } //+---------------------------------------------------------------------------- // // Function: CMonitor::OnQueryEndSession // // Synopsis: This message processes the WM_QUERYENDSESSION message by passing // it to all the connection threads. // // Arguments: Nothing // // Returns: TRUE if successful, FALSE otherwise // // History: quintinb Created 3/18/99 // //+---------------------------------------------------------------------------- BOOL CMonitor::OnQueryEndSession(BOOL fLogOff) const { // // This is the code that was here before fixing bug .NET Server 442193 for fast user switching. // This method is called on WM_QUERYENDSESSION. The issue is that at logoff, // the threads weren't getting enough time to finish and thus weren't disconnecting. // We need to disconnect for each thread now. // BOOL bOkayToEndSession = TRUE; BOOL bReturn = FALSE; for (int i = 0; i < m_InternalConnArray.GetSize(); i++) { CCmConnection* pCmConnection = (CCmConnection*)m_InternalConnArray[i]; if (pCmConnection) { ASSERT_VALID(pCmConnection); bReturn = pCmConnection->OnEndSession(TRUE, fLogOff); // fEndSession == TRUE bOkayToEndSession = bOkayToEndSession && bReturn; } } return bOkayToEndSession; } #ifdef DEBUG //+---------------------------------------------------------------------------- // // Function: CMonitor::AssertValid // // Synopsis: Helper function for debug. Assert the object is in a valid state // // Arguments: None // // Returns: Nothing // // History: Created Header 2/17/98 // //+---------------------------------------------------------------------------- void CMonitor::AssertValid() const { MYDBGASSERT(IsWindow(m_hwndMonitor)); MYDBGASSERT(m_pThis == this); ASSERT_VALID(&m_InternalConnArray); ASSERT_VALID(&m_ReconnectConnArray); } #endif