// File: conf.cpp #include "precomp.h" #include #include #include "NmLdap.h" #include "conf.h" #include "confwnd.h" #include "taskbar.h" #include #include #include "iapplet.h" #include "inodecnt.h" #include "ConfCpl.h" #include "confroom.h" #include "rtoolbar.h" #include "GenWindow.h" #include "cmd.h" #include "confman.h" #include "splash.h" #include "calllog.h" #include "call.h" // for FreeCallList #include "popupmsg.h" #include "floatbar.h" #include "confman.h" #include #include #include "wininet.h" #include "setupapi.h" #include "autoconf.h" #include "ConfNmSysInfoNotify.h" #include "ConfPolicies.h" #include "DShowDlg.h" #include "Callto.h" #include "passdlg.h" // SDK includes #include "NetMeeting.h" #include "NmApp.h" #include "NmManager.h" #include "NmCall.h" #include "NmConference.h" #include "SDKWindow.h" #include "confapi.h" #include "FtHook.h" #include "t120app.h" #include "certui.h" #include "dlgcall2.h" #include "ConfMsgFilter.h" BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_NetMeeting, CNetMeetingObj) OBJECT_ENTRY(CLSID_NmManager, CNmManagerObj) OBJECT_ENTRY(CLSID_NmApplet, CNmAppletObj) END_OBJECT_MAP() extern VOID SaveDefaultCodecSettings(UINT uBandWidth); extern int WabReadMe(void); HRESULT InitSDK(); void CleanupSDK(); /////////////////////////////////////////////////////////////////////////// // Global Variables LPTSTR g_lpCmdLine = NULL; CCallLog * g_pInCallLog = NULL; // The incoming call log object CSimpleArray *g_pDialogList = NULL; // Global list of modeless dialogs CRITICAL_SECTION dialogListCriticalSection; // This is to avoid multiple access to the dialogList INmSysInfo2 * g_pNmSysInfo = NULL; // Interface to SysInfo INmManager2* g_pInternalNmManager = NULL; DWORD g_dwSysInfoNotifyCookie = 0; bool g_bNeedCleanup = false; bool g_bEmbedding = FALSE; // Started with the embedding flag UINT g_uEndSessionMsg; // The "NetMeeting EndSession" message BOOL g_fHiColor = FALSE; // TRUE if we have more than 256 colors HWND g_hwndDropDown = NULL; // BOOL g_WSAStarted = FALSE; // WSAStartup CCallto * g_pCCallto = NULL; // The flag to indicate if the NetMeeting's NT display driver is enabled. BOOL g_fNTDisplayDriverEnabled = FALSE; OSVERSIONINFO g_osvi; // The os version info structure global /////////////////////////////////////////////////////////////////////////// // IPC-related globals: HANDLE g_hInitialized = NULL; HANDLE g_hShutdown = NULL; /////////////////////////////////////////////////////////////////////////// // Hidden window-related globals: CHiddenWindow * g_pHiddenWnd = NULL; HWND g_hwndESHidden = NULL; const TCHAR g_cszESHiddenWndClassName[] = _TEXT("ConfESHiddenWindow"); LRESULT CALLBACK ESHiddenWndProc(HWND, UINT, WPARAM, LPARAM); /////////////////////////////////////////////////////////////////////////// // Remote control service related declarations INT_PTR CALLBACK ServiceRunningDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam); VOID RestartRemoteControlService(); const int MAX_REMOTE_TRIES = 30; // number of seconds to wait for service to shut down const int SERVICE_IN_CALL = 1001; /////////////////////////////////////////////////////////////////////////// // Media Caps ULONG g_uMediaCaps = 0; BOOL FIsAudioAllowed() { return g_uMediaCaps & CAPFLAGS_AUDIO; } BOOL FIsReceiveVideoAllowed() { return g_uMediaCaps & CAPFLAG_RECV_VIDEO; } BOOL FIsSendVideoAllowed() { return g_uMediaCaps & CAPFLAG_SEND_VIDEO; } BOOL FIsAVCapable() { return (FIsAudioAllowed() || FIsReceiveVideoAllowed() || FIsSendVideoAllowed()); } extern BOOL SetProcessDefaultLayout(int iLayout); typedef BOOL (WINAPI* PFNSPDL)(int); #define LAYOUT_LTR 0 int g_iLayout = LAYOUT_LTR; DWORD g_wsLayout = 0; VOID CheckLanguageLayout(void) { TCHAR szLayout[CCHMAXUINT]; if (!FLoadString(IDS_DEFAULT_LAYOUT, szLayout, CCHMAX(szLayout))) return; g_iLayout = (int) DecimalStringToUINT(szLayout); if (0 == g_iLayout) { #ifdef DEBUG RegEntry re(DEBUG_KEY, HKEY_LOCAL_MACHINE); g_iLayout = re.GetNumber(REGVAL_DBG_RTL, DEFAULT_DBG_RTL); if (0 == g_iLayout) #endif /* DEBUG */ return; } HMODULE hmod = GetModuleHandle(TEXT("USER32")); if (NULL == hmod) return; PFNSPDL pfn = (PFNSPDL) GetProcAddress(hmod, "SetProcessDefaultLayout"); if (NULL == pfn) return; BOOL fResult = pfn(g_iLayout); if (fResult) { g_wsLayout = WS_EX_NOINHERIT_LAYOUT; } else { ERROR_OUT(("Problem with SetProcessDefaultLayout")); } } /////////////////////////////////////////////////////////////////////////// // External Function Prototypes // from dbgmenu.cpp BOOL InitDebugMemoryOptions(void); /////////////////////////////////////////////////////////////////////////// // Local Function Prototypes BOOL HandleDialogMessage(LPMSG pMsg); // This is for command line parsing... LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2) { while (p1 != NULL && *p1 != NULL) { LPCTSTR p = p2; while (p != NULL && *p != NULL) { if (*p1 == *p) return CharNext(p1); p = CharNext(p); } p1 = CharNext(p1); } return NULL; } // This launches a rundll32.exe which loads msconf.dll which will then wait for // us to terminate and make sure that the mnmdd display driver was properly deactivated. BOOL CreateWatcherProcess() { BOOL bRet = FALSE; HANDLE hProcess; // open a handle to ourselves that the watcher process can inherit hProcess = OpenProcess(SYNCHRONIZE, TRUE, GetCurrentProcessId()); if (hProcess) { TCHAR szWindir[MAX_PATH]; if (GetSystemDirectory(szWindir, sizeof(szWindir)/sizeof(szWindir[0]))) { TCHAR szCmdLine[MAX_PATH * 2]; PROCESS_INFORMATION pi = {0}; STARTUPINFO si = {0}; si.cb = sizeof(si); wsprintf(szCmdLine, "\"%s\\rundll32.exe\" msconf.dll,CleanupNetMeetingDispDriver %ld", szWindir, hProcess); if (CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, // we want the watcher to inherit hProcess, so we must set bInheritHandles = TRUE 0, NULL, NULL, &si, &pi)) { bRet = TRUE; CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } } CloseHandle(hProcess); } return bRet; } /////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPTSTR lpCmdLine, int nCmdShow) { // if there is another instance of NetMeeting shutting down // get out of here. Ideally we should display a message and/or wait for shutdown. HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, _TEXT("CONF:ShuttingDown")); if (NULL != hEvent) { DWORD dwResult = WaitForSingleObject(hEvent, INFINITE); CloseHandle(hEvent); if (WAIT_TIMEOUT == dwResult) { return TRUE; } } // Init debug output as soon as possible ASSERT(::InitDebugMemoryOptions()); ASSERT(::InitDebugModule(TEXT("CONF"))); ASSERT(::InitDebugZones()); g_lpCmdLine = lpCmdLine; int nRet = TRUE; BOOL fRestartService = FALSE; HRESULT hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if( SUCCEEDED( hr ) ) { // Init CComModule _Module.Init(ObjectMap, hInstance, &LIBID_NetMeetingLib); _Module.m_dwThreadID = GetCurrentThreadId(); _Module.m_hResourceModule = hInstance; TCHAR szCommandLineSeps[] = _T("-/"); // Check to see if this is a reg/unreg request or background... BOOL fShowUI = TRUE; BOOL bRun = TRUE; LPCTSTR lpszToken = FindOneOf(lpCmdLine, szCommandLineSeps); while (lpszToken != NULL) { if (lstrcmpi(lpszToken, _T("Embedding"))==0) { TRACE_OUT(("We are started with the -Embedding flag")); g_bEmbedding = TRUE; } if (lstrcmpi(lpszToken, _T("UnregServer"))==0) { _Module.UpdateRegistryFromResource(IDR_NETMEETING, FALSE); nRet = _Module.UnregisterServer(TRUE); // These will fail without complaints DeleteShortcut(CSIDL_DESKTOP, g_szEmpty); DeleteShortcut(CSIDL_APPDATA, QUICK_LAUNCH_SUBDIR); bRun = FALSE; break; } if (lstrcmpi(lpszToken, _T("RegServer"))==0) { _Module.UpdateRegistryFromResource(IDR_NETMEETING, TRUE); nRet = _Module.RegisterServer(TRUE); bRun = FALSE; break; } if (lstrcmpi(lpszToken, g_cszBackgroundSwitch)==0) { fShowUI = FALSE; } lpszToken = FindOneOf(lpszToken, szCommandLineSeps); } if (bRun) { // Setup and RDS rely on the following event to determine whether NetMeeting is Running // this event creation should not be removed and the name should not be changed g_hInitialized = ::CreateEvent(NULL, TRUE, FALSE, _TEXT("CONF:Init")); if (NULL != g_hInitialized) { if (ERROR_ALREADY_EXISTS == ::GetLastError()) { // CreateEvent returned a valid handle, but we don't want initialization to // succeed if we are running another copy of this exe, so we cleanup and exit WARNING_OUT(("Detected another conf.exe - sending a message")); IInternalConfExe * pInternalConfExe; hr = CoCreateInstance( CLSID_NmManager, NULL, CLSCTX_ALL, IID_IInternalConfExe, (LPVOID *) &pInternalConfExe ); if (SUCCEEDED(hr)) { if(FAILED(pInternalConfExe->Launch())) { // If we are in INIT_CONTROL mode, then we can't launch NetMeeting or applets ::ConfMsgBox(NULL, (LPCTSTR) IDS_CANT_START_NM_BECAUSE_SDK_APP_OWNS_NM); } pInternalConfExe->Release(); } } else if(SUCCEEDED(InitHtmlHelpMarshaler(_Module.GetModuleInstance()))) { // We create a seperate watcher process that will cleanup the mnmdd display driver // if we terminate unexpectedly. This is necessary since if we do not disable the // mirrored driver, all DX games will fail to run CreateWatcherProcess(); //initialize ATL control contaiment code AtlAxWinInit(); hr = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE); if( SUCCEEDED( hr ) ) { BOOL fContinue = TRUE; if( FAILED(InitSDK()) ) { fContinue = FALSE; } if(!g_bEmbedding) { // Before doing anything else, take care of the remote control service. fContinue = CheckRemoteControlService(); fRestartService = fContinue; if(fContinue) { fContinue = SUCCEEDED(InitConfExe(fShowUI)); } } if(fContinue) { TRACE_OUT(("Entering event loop...")); MSG msg; while (::GetMessage(&msg, NULL, 0, 0)) { BOOL bHandled = FALSE; if(g_pPing) // This is TRUE if InitConfExe has been called... { bHandled = ::HandleDialogMessage(&msg); } if(!bHandled) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } TRACE_OUT(("Conf received WM_QUIT")); } if(g_bNeedCleanup) { CleanUp(); } CleanupSDK(); _Module.RevokeClassObjects(); } } ::CloseHandle(g_hInitialized); if (g_hShutdown) { SetEvent(g_hShutdown); ::CloseHandle(g_hShutdown); } } else { ERROR_OUT(("CreateEvent (init) failed!")); hr = E_FAIL; } _Module.Term(); } ::CoUninitialize(); // // Restart the remote control service if we need to. // if (fRestartService) RestartRemoteControlService(); } #ifdef DEBUG ::ExitDebugModule(); TRACE_OUT(("returned from ExitDebugModule")); ::DeinitDebugZones(); TRACE_OUT(("returned from DeinitDebugZones")); #endif //DEBUG return nRet; } VOID CheckMachineNameForExtendedChars ( VOID ) { DBGENTRY(CheckMachineNameForExtendedChars); // First we have to get the computer name TCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 1]; DWORD cchMachineName = CCHMAX(szMachineName); // Next we check to see if the computer nami is invalid if (GetComputerName(szMachineName, &cchMachineName)) { for ( LPTSTR p = szMachineName; *p != _T('\0'); p++ ) { if ( (WORD)(*p) & 0xFF80 ) { // The machine name is invalid because it contains an invalid character CDontShowDlg MachineNameWarningDlg( IDS_MACHINENAMEWARNING, REGVAL_DS_MACHINE_NAME_WARNING, DSD_ALWAYSONTOP | MB_SETFOREGROUND | MB_OK ); MachineNameWarningDlg.DoModal(NULL); goto end; } } } else { ERROR_OUT(("GetComputerName() failed, err=%lu", GetLastError())); *szMachineName = TEXT('\0'); } end: DBGEXIT(CheckMachineNameForExtendedChars); } VOID HandleConfSettingsChange(DWORD dwSettings) { DebugEntry(HandleConfSettingsChange); USES_CONVERSION; TRACE_OUT(("HandleConfSettingsChange, dwSettings=0x%x", dwSettings)); // Tell the user if she changed something that won't take // effect right away if (CSETTING_L_REQUIRESRESTARTMASK & dwSettings) { ::ConfMsgBox(NULL, (LPCTSTR) IDS_NEED_RESTART); } if (CSETTING_L_REQUIRESNEXTCALLMASK & dwSettings) { if (::FIsConferenceActive()) { ::ConfMsgBox(NULL, (LPCTSTR) IDS_NEED_NEXTCALL); } } if (CSETTING_L_BANDWIDTH & dwSettings) { if (NULL != g_pNmSysInfo) { int nMegahertz=300, nProcFamily=6; RegEntry re(AUDIO_KEY, HKEY_CURRENT_USER); UINT uSysPolBandwidth; UINT uBandwidth; uBandwidth = re.GetNumber(REGVAL_TYPICALBANDWIDTH, BW_DEFAULT); #ifdef _M_IX86 GetNormalizedCPUSpeed(&nMegahertz, &nProcFamily); TRACE_OUT(("Normalized Processor Speed = %d, Processor type = %d\n", nMegahertz, nProcFamily)); #endif // convert bandwidth ID (1-4) to bits/sec uBandwidth = GetBandwidthBits(uBandwidth, nMegahertz); // the existance of a QOS - maximum bandwidth key implies that // the user's bandwidth is being over-rided (ONLY if his setting is LAN) uSysPolBandwidth = SysPol::GetMaximumBandwidth(); if ((uSysPolBandwidth > 0) && (uBandwidth >= BW_SLOWLAN_BITS)) { uBandwidth = max(uSysPolBandwidth, BW_144KBS_BITS); } g_pNmSysInfo->SetOption(NM_SYSOPT_BANDWIDTH, uBandwidth); } } if (CSETTING_L_SHOWTASKBAR & dwSettings) { // This will remove one if one is already there: ::RemoveTaskbarIcon(::GetHiddenWindow()); // This will add the icon if the registry switch is on: ::AddTaskbarIcon(::GetHiddenWindow()); } if (CSETTING_L_AUDIODEVICE & dwSettings) { CConfRoom* pcr = ::GetConfRoom(); if (NULL != pcr) { pcr->OnAudioDeviceChanged(); } } if (CSETTING_L_AGC & dwSettings) { CConfRoom* pcr = ::GetConfRoom(); if (NULL != pcr) { pcr->OnAGC_Changed(); } } if ((CSETTING_L_AUTOMIC|CSETTING_L_MICSENSITIVITY) & dwSettings) { CConfRoom* pcr = ::GetConfRoom(); if (NULL != pcr) { pcr->OnSilenceLevelChanged(); } } if( CSETTING_L_ULSSETTINGS & dwSettings ) { if(g_pLDAP) { g_pLDAP->OnSettingsChanged(); } if (NULL != g_pNmSysInfo) { RegEntry re(ISAPI_CLIENT_KEY, HKEY_CURRENT_USER); LPCTSTR pcszName = re.GetString(REGVAL_ULS_NAME); g_pNmSysInfo->SetProperty(NM_SYSPROP_USER_NAME, CComBSTR(pcszName)); } } if (CSETTING_L_FULLDUPLEX & dwSettings) { RegEntry re(AUDIO_KEY, HKEY_CURRENT_USER); BOOL bFullDuplex = FALSE; UINT uSoundCardCaps = re.GetNumber(REGVAL_SOUNDCARDCAPS,SOUNDCARD_NONE); if (ISSOUNDCARDFULLDUPLEX(uSoundCardCaps)) { bFullDuplex = re.GetNumber(REGVAL_FULLDUPLEX,FULLDUPLEX_DISABLED); } ASSERT(g_pNmSysInfo); if (NULL != g_pNmSysInfo) { g_pNmSysInfo->SetOption(NM_SYSOPT_FULLDUPLEX, bFullDuplex); } } if (CSETTING_L_CAPTUREDEVICE & dwSettings) { if (NULL != g_pNmSysInfo) { RegEntry re(VIDEO_KEY, HKEY_CURRENT_USER); DWORD dwCaptureID = re.GetNumber(REGVAL_CAPTUREDEVICEID, 0); g_pNmSysInfo->SetOption(NM_SYSOPT_CAPTURE_DEVICE, dwCaptureID); } } if (CSETTING_L_DIRECTSOUND & dwSettings) { ASSERT(g_pNmSysInfo); if (NULL != g_pNmSysInfo) { RegEntry re(AUDIO_KEY, HKEY_CURRENT_USER); DWORD dwDS = re.GetNumber(REGVAL_DIRECTSOUND, DSOUND_USER_DISABLED); g_pNmSysInfo->SetOption(NM_SYSOPT_DIRECTSOUND, dwDS); } } DebugExitVOID(HandleConfSettingsChange); } // DeleteOldRegSettings is called the first time // this build of NetMeeting is run by the user // We don't touch UI\Directory as it is populated by the INF file VOID DeleteOldRegSettings() { // "%KEY_CONFERENCING%\UI" HKEY hKey; long lRet = ::RegOpenKey(HKEY_CURRENT_USER, UI_KEY, &hKey); if (NO_ERROR == lRet) { ::RegDeleteValue(hKey, REGVAL_MP_WINDOW_X); ::RegDeleteValue(hKey, REGVAL_MP_WINDOW_Y); ::RegDeleteValue(hKey, REGVAL_MP_WINDOW_WIDTH); ::RegDeleteValue(hKey, REGVAL_MP_WINDOW_HEIGHT); ::RegCloseKey(hKey); } } static HRESULT _ValidatePolicySettings() { HRESULT hr = S_OK; if( g_pNmSysInfo ) { // // LAURABU BUGBUG BOGUS: // // If security required, and not available, warning // If security incoming required/outgoing preferred, warning // } else { ERROR_OUT(("g_pNmySysInfo should not me NULL")); hr = E_UNEXPECTED; } return hr; } HRESULT InitSDK() { DBGENTRY(InitSDK); HRESULT hr = S_OK; if(FAILED(hr = CSDKWindow::InitSDK())) goto end; if(FAILED(hr = CNmCallObj::InitSDK())) goto end; if(FAILED(hr = CNmManagerObj::InitSDK())) goto end; if(FAILED(hr = CNmConferenceObj::InitSDK())) goto end; if(FAILED(hr = CNetMeetingObj::InitSDK())) goto end; if(FAILED(hr = CFt::InitFt())) goto end; g_pCCallto = new CCallto; ASSERT( g_pCCallto != NULL ); if( g_pCCallto == NULL ) { hr = E_FAIL; } end: DBGEXIT_HR(InitSDK,hr); return hr; } void CleanupSDK() { DBGENTRY(CleanupSDK); // Revoke the old filter object CoRegisterMessageFilter(NULL, NULL); CNmCallObj::CleanupSDK(); CNmManagerObj::CleanupSDK(); CNmConferenceObj::CleanupSDK(); CSDKWindow::CleanupSDK(); CNetMeetingObj::CleanupSDK(); CFt::CloseFtApplet(); DBGEXIT(CleanupSDK); } /* I N I T C O N F E X E */ /*------------------------------------------------------------------------- %%Function: InitConfExe -------------------------------------------------------------------------*/ HRESULT InitConfExe(BOOL fShowUI) { // Create a message filter object CComPtr spMsgFilter; CComPtr spOldMsgFilter; HRESULT hr = CConfMsgFilter::_CreatorClass::CreateInstance(NULL, IID_IMessageFilter, reinterpret_cast(&spMsgFilter)); if(FAILED(hr)) return hr; // Register the message filter object hr = CoRegisterMessageFilter(spMsgFilter, &spOldMsgFilter); if(FAILED(hr)) return hr; // Wipe out default find directory entry... we no longer wish to persist this... // in some future overhaul / cleanup we should stop using the registry for this... RegEntry re( DLGCALL_MRU_KEY, HKEY_CURRENT_USER ); re.SetValue( REGVAL_DLGCALL_DEFDIR, TEXT( "" ) ); LPCTSTR lpCmdLine = g_lpCmdLine; TRACE_OUT(("InitConfExe")); // Init UI objects (NOTE: we continue if this fails) CPopupMsg::Init(); CPasswordDlg::Init(); // Allocate dialog list object: g_pDialogList = new CSimpleArray; if (NULL == g_pDialogList) { ERROR_OUT(("Could not allocate g_pDialogList!")); return E_FAIL; } // // Initialize the critical section to protect the dialogList // InitializeCriticalSection(&dialogListCriticalSection); // Determine if we have MORE THAN 256 colors { HDC hdc = GetDC(NULL); if (NULL != hdc) { g_fHiColor = 8 < (::GetDeviceCaps(hdc, BITSPIXEL) * ::GetDeviceCaps(hdc, PLANES)); ReleaseDC(NULL, hdc); } } // Get the default dialog (GUI) font for international // REVIEW: should we check the registry for a localized font? g_hfontDlg = (HFONT) ::GetStockObject(DEFAULT_GUI_FONT); if (NULL == g_hfontDlg) { return E_FAIL; } LoadIconImages(); // On Windows NT, determine if the NetMeeting display driver is // enabled. Note that this depends on being initialized. // // Since NT 5.0 will support dynamic loading of the display driver, // we assume that the driver is enabled if the OS major version // number is greater than 4. if (::IsWindowsNT()) { RegEntry re(NM_NT_DISPLAY_DRIVER_KEY, HKEY_LOCAL_MACHINE, FALSE); g_fNTDisplayDriverEnabled = 4 < g_osvi.dwMajorVersion || NT_DRIVER_START_DISABLED != re.GetNumber( REGVAL_NM_NT_DISPLAY_DRIVER_ENABLED, NT_DRIVER_START_DISABLED); } else { ASSERT(FALSE == g_fNTDisplayDriverEnabled); } // Check the language layout (UI can be displayed after this point) CheckLanguageLayout(); // AutoConfiguration CAutoConf::DoIt(); TRACE_OUT(("Command Line is \"%s\"", lpCmdLine)); // Register hidden window class: WNDCLASS wcESHidden = { 0L, ESHiddenWndProc, 0, 0, _Module.GetModuleInstance(), NULL, NULL, NULL, NULL, g_cszESHiddenWndClassName }; if (!RegisterClass(&wcESHidden)) { ERROR_OUT(("Could not register hidden wnd classes")); return E_FAIL; } // Register the "NetMeeting EndSession" message: g_uEndSessionMsg = ::RegisterWindowMessage(NM_ENDSESSION_MSG_NAME); // Create a hidden window for event processing: g_pHiddenWnd = new CHiddenWindow(); if (NULL == g_pHiddenWnd) { return(E_FAIL); } g_pHiddenWnd->Create(); g_hwndESHidden = ::CreateWindow( g_cszESHiddenWndClassName, _TEXT(""), WS_POPUP, // not visible! 0, 0, 0, 0, NULL, NULL, _Module.GetModuleInstance(), NULL); HWND hwndHidden = g_pHiddenWnd->GetWindow(); if ((NULL == hwndHidden) || (NULL == g_hwndESHidden)) { ERROR_OUT(("Could not create hidden windows")); return E_FAIL; } LONG lSoundCaps = SOUNDCARD_NONE; // Start the run-once wizard (if needed): RegEntry reConf(CONFERENCING_KEY, HKEY_CURRENT_USER); // check to see if the wizard has been run in UI mode for this build DWORD dwVersion = reConf.GetNumber(REGVAL_WIZARD_VERSION_UI, 0); BOOL fRanWizardUI = ((VER_PRODUCTVERSION_W & HIWORD(dwVersion)) == VER_PRODUCTVERSION_W); BOOL fForceWizard = FALSE; if (!fRanWizardUI) { dwVersion = reConf.GetNumber(REGVAL_WIZARD_VERSION_NOUI, 0); BOOL fRanWizardNoUI = (VER_PRODUCTVERSION_DW == dwVersion); // wizard has not been run in UI mode if (!fRanWizardNoUI) { // wizard has not been run before, delete old registry settings DeleteOldRegSettings(); fForceWizard = TRUE; } else { // wizard has been run in NoUI mode, we only need to run it if we are in UI mode if(fShowUI) { fForceWizard = TRUE; } } if (fForceWizard) { WabReadMe(); } } hr = ::StartRunOnceWizard(&lSoundCaps, fForceWizard, fShowUI); if (FAILED(hr)) { WARNING_OUT(("Did not retrieve necessary info from wizard")); ConfMsgBox(NULL, MAKEINTRESOURCE(IDS_ERROR_BAD_ADMIN_SETTINGS)); return E_FAIL; } else if( S_FALSE == hr ) { return NM_E_USER_CANCELED_SETUP; } if (fForceWizard) { reConf.SetValue(fShowUI ? REGVAL_WIZARD_VERSION_UI : REGVAL_WIZARD_VERSION_NOUI, VER_PRODUCTVERSION_DW); } // Start NetMeeting At Page Once if( fShowUI && fForceWizard ) { if( ConfPolicies::IsShowFirstTimeUrlEnabled() ) { CmdLaunchWebPage(ID_HELP_WEB_SUPPORT); } } // The following hack is to fix the don't run wizard twice bug // the side effect is that the codec ordering is blown away. // this code restores the key in the event that this wizard is not run. HKEY hKey; long lRet = ::RegOpenKey(HKEY_LOCAL_MACHINE, INTERNET_AUDIO_KEY TEXT("\\") REGVAL_ACMH323ENCODINGS , &hKey); if (NO_ERROR == lRet) { ::RegCloseKey(hKey); } else { RegEntry reAudio(AUDIO_KEY, HKEY_CURRENT_USER); UINT uBandwidth = reAudio.GetNumber ( REGVAL_TYPICALBANDWIDTH, BW_DEFAULT ); SaveDefaultCodecSettings(uBandwidth); } // Start the Splash screen only after the wizard is complete if (fShowUI) { ::StartSplashScreen(NULL); } // Init incoming call log: g_pInCallLog = new CCallLog(LOG_INCOMING_KEY, TEXT("CallLog")); // Init capabilities: g_uMediaCaps = CAPFLAG_DATA; // // NOTE: THIS IS WHERE TO CHANGE TO DISABLE H323 CALLS FOR INTEL ET AL. // if(!_Module.DidSDKDisableH323()) { g_uMediaCaps |= CAPFLAG_H323_CC; if (SOUNDCARD_NONE != lSoundCaps) { if (!SysPol::NoAudio()) { g_uMediaCaps |= CAPFLAGS_AUDIO; } } if (!SysPol::NoVideoReceive()) { g_uMediaCaps |= CAPFLAG_RECV_VIDEO; } if (!SysPol::NoVideoSend()) { g_uMediaCaps |= CAPFLAG_SEND_VIDEO; } } // Create Manager hr = CoCreateInstance(CLSID_NmManager2, NULL, CLSCTX_INPROC, IID_INmManager2, (void**)&g_pInternalNmManager); if (FAILED(hr)) { ERROR_OUT(("Could not create INmManager")); return E_FAIL; } // Get the INmSysInfo3 CComPtr spSysInfo; if (SUCCEEDED(g_pInternalNmManager->GetSysInfo(&spSysInfo))) { if (FAILED(spSysInfo->QueryInterface(IID_INmSysInfo2, (void **)&g_pNmSysInfo))) { ERROR_OUT(("Could not get INmSysInfo2")); } else { ASSERT( g_pNmSysInfo ); CComPtr spNmSysInfoNotify; if( SUCCEEDED ( CConfNmSysInfoNotifySink::_CreatorClass::CreateInstance( NULL, IID_INmSysInfoNotify, reinterpret_cast(&spNmSysInfoNotify)))) { ASSERT(spNmSysInfoNotify); ASSERT(0 == g_dwSysInfoNotifyCookie); NmAdvise(g_pNmSysInfo, spNmSysInfoNotify, IID_INmSysInfoNotify, &g_dwSysInfoNotifyCookie); } } } _ValidatePolicySettings(); hr = g_pInternalNmManager->Initialize(NULL, &g_uMediaCaps); if (FAILED(hr)) { UINT_PTR uErrorID; switch (hr) { case UI_RC_NO_NODE_NAME: { // No error in this case - the user probably cancelled from // the intro wizard. uErrorID = 0; break; } case UI_RC_BACKLEVEL_LOADED: { uErrorID = IDS_BACKLEVEL_LOADED; break; } case UI_RC_T120_ALREADY_INITIALIZED: { uErrorID = IDS_T120_ALREADY_INITIALIZED; break; } case UI_RC_T120_FAILURE: { WARNING_OUT(("T.120 failed to initialize (winsock problem?)")); uErrorID = IDS_CANT_START; break; } default: { uErrorID = IDS_CANT_START; break; } } if (0 != uErrorID) { ::ConfMsgBox(NULL, (LPCTSTR) uErrorID); } return E_FAIL; } // force the update of dll settings HandleConfSettingsChange(CSETTING_L_BANDWIDTH | CSETTING_L_CAPTUREDEVICE | CSETTING_L_ULSSETTINGS | CSETTING_L_DIRECTSOUND| CSETTING_L_FULLDUPLEX); if (FALSE == ::ConfRoomInit(_Module.GetModuleInstance())) { ::ConfMsgBox(NULL, (LPCTSTR) IDS_CANT_START); return E_FAIL; } // Now perform the check on the machine name and warn if // it is problematic. ::CheckMachineNameForExtendedChars(); // Create the main conference manager to make sure // we can handle incoming calls, even in background mode if (!CConfMan::FCreate(g_pInternalNmManager)) { ERROR_OUT(("Unable to create Conference Manager")); return E_FAIL; } // Initialize winsock (for name/address resolution) { WSADATA wsaData; int iErr = WSAStartup(0x0101, &wsaData); if (0 != iErr) { ERROR_OUT(("WSAStartup() failed: %i", iErr)); return E_FAIL; } g_WSAStarted = TRUE; } // Initialize T.120 Security settings ::InitT120SecurityFromRegistry(); StopSplashScreen(); CreateConfRoomWindow(fShowUI); g_pPing = new CPing; if( ConfPolicies::GetCallingMode() == ConfPolicies::CallingMode_Direct ) { // Initialize gatewayContext... RegEntry reConf( CONFERENCING_KEY, HKEY_CURRENT_USER ); if( reConf.GetNumber( REGVAL_USE_H323_GATEWAY ) != 0 ) { g_pCCallto->SetGatewayName( reConf.GetString( REGVAL_H323_GATEWAY ) ); g_pCCallto->SetGatewayEnabled( true ); } if(ConfPolicies::LogOntoIlsWhenNetMeetingStartsIfInDirectCallingMode() && !_Module.DidSDKDisableInitialILSLogon()) { InitNmLdapAndLogon(); } } else { GkLogon(); } if(!_Module.InitControlMode()) { ::AddTaskbarIcon(::GetHiddenWindow()); } g_bNeedCleanup = true; CNmManagerObj::NetMeetingLaunched(); return S_OK; } VOID CleanUpUi(void) { SysPol::CloseKey(); if( 0 != g_dwSysInfoNotifyCookie ) { NmUnadvise(g_pNmSysInfo, IID_INmSysInfoNotify, g_dwSysInfoNotifyCookie); g_dwSysInfoNotifyCookie = 0; } if (NULL != g_pNmSysInfo) { if( IsGatekeeperLoggedOn() ) { g_pNmSysInfo->GkLogoff(); } g_pNmSysInfo->Release(); g_pNmSysInfo = NULL; } FreeIconImages(); CGenWindow::DeleteStandardPalette(); CGenWindow::DeleteStandardBrush(); CMainUI::CleanUpVideoWindow(); CFindSomeone::Destroy(); } VOID CleanUp(BOOL fLogoffWindows) { FreeCallList(); // Kill the taskbar icon: if (NULL != g_pHiddenWnd) { HWND hwndHidden = g_pHiddenWnd->GetWindow(); TRACE_OUT(("Removing taskbar icon...")); ::RemoveTaskbarIcon(hwndHidden); DestroyWindow(hwndHidden); g_pHiddenWnd->Release(); g_pHiddenWnd = NULL; } // NOTE: during WM_ENDSESSION, we want // to log off after doing all other clean-up, in case it gets stuck // waiting for the logon thread to complete. if (FALSE == fLogoffWindows) { if(g_pLDAP) { g_pLDAP->Logoff(); delete g_pLDAP; g_pLDAP = NULL; } } delete g_pCCallto; g_pCCallto = NULL; delete g_pPing; g_pPing = NULL; CleanUpUi(); // These must happen AFTER all the UI is cleaned up if(g_pInternalNmManager) { g_pInternalNmManager->Release(); } CConfMan::Destroy(); // destroy incoming call log: delete g_pInCallLog; g_pInCallLog = NULL; CPopupMsg::Cleanup(); CPasswordDlg::Cleanup(); // Code to clean up gracefully if (FALSE == fLogoffWindows) { // NOTE: we intentionally leak this list object when shutting down // due to logging off windows, because we don't want to put a NULL // check in HandleDialogMessage() and there is no WM_QUIT to guarantee that // we've stopped receiving messages when shutting down in that code path EnterCriticalSection(&dialogListCriticalSection); for( int i = 0; i < g_pDialogList->GetSize(); ++i ) { ASSERT( NULL != (*g_pDialogList)[i] ); RemoveTranslateAccelerator( (*g_pDialogList)[i] ); } LeaveCriticalSection(&dialogListCriticalSection); // Delete the dialog list: delete g_pDialogList; // // Delete the critical section // DeleteCriticalSection(&dialogListCriticalSection); g_pDialogList = NULL; } // Auto-disconnect from MSN: ::SendDialmonMessage(WM_APP_EXITING); if (g_WSAStarted) { WSACleanup(); g_WSAStarted = FALSE; } delete g_pConfRoom; g_pConfRoom = NULL; g_bNeedCleanup = false; } /* S E N D D I A L M O N M E S S A G E */ /*------------------------------------------------------------------------- %%Function: SendDialmonMessage Send a message to the dialing monitor. Either WINSOCK_ACTIVITY_TIMER or WM_APP_EXITING. (The code comes from Internet Explorer) -------------------------------------------------------------------------*/ VOID SendDialmonMessage(UINT uMsg) { HWND hwndAutodisconnectMonitor = ::FindWindow(_TEXT("MS_AutodialMonitor"), NULL); if (NULL != hwndAutodisconnectMonitor) { ::SendMessage(hwndAutodisconnectMonitor, uMsg, 0, 0); } } // This window procedure exists for the sole purpose of receiving // WM_ENDSESSION. Because of bug 2287, we cannot have the regular // hidden window handle WM_ENDSESSION. DCL has subclassed our hidden // window, and if we unload them inside one of it's messages, then we // will fault. It's too bad that we can't find a better fix (such as // removing the subclass), but we are under time pressure to fix this // bug for v1.0 LRESULT CALLBACK ESHiddenWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if ((WM_ENDSESSION == uMsg) && (TRUE == (BOOL) wParam)) { TRACE_OUT(("Conf received WM_ENDSESSION, fLogoff=%s", GetBOOLString((BOOL)lParam))); TRACE_OUT(("Conf calling UIEndSession()")); CConfRoom::UIEndSession((BOOL) lParam); TRACE_OUT(("Conf testing lParam=%d", lParam)); if ((BOOL) lParam) { // Logging off: TRACE_OUT(("Conf calling CleanUp()")); // NOTE: Passing TRUE into CleanUp() because we don't // want to logoff ULS / de-init name services until after insuring that DCL // has cleaned up properly, because it can take enough time that // our task might get killed. ::CleanUp(TRUE); // // Restart the remote control service if we need to. // RestartRemoteControlService(); } else { TRACE_OUT(("Conf not cleaning up - Windows shutting down")); } #if 0 // LONCHANC: it faults 100% on my main dev machine. if( g_pLDAP != NULL ) { g_pLDAP->Logoff(); } #endif return 0; } else { return DefWindowProc(hwnd, uMsg, wParam, lParam); } } /* C M D S H U T D O W N */ /*------------------------------------------------------------------------- %%Function: CmdShutdown -------------------------------------------------------------------------*/ VOID CmdShutdown(void) { HWND hwndMain = ::GetMainWindow(); if (NULL != hwndMain) { // We have UI up, so post a WM_CLOSE with lParam = 1, // which indicates a forced "Exit and Stop" ::PostMessage(hwndMain, WM_CLOSE, 0, 1); } else { ::PostThreadMessage(_Module.m_dwThreadID, WM_QUIT, 0, 0); } } void SignalShutdownStarting(void) { if (NULL == g_hShutdown) { g_hShutdown = ::CreateEvent(NULL, TRUE, FALSE, _TEXT("CONF:ShuttingDown")); _Module.RevokeClassObjects(); } } /* H A N D L E D I A L O G M E S S A G E */ /*------------------------------------------------------------------------- %%Function: HandleDialogMessage Global modeless dialog handler -------------------------------------------------------------------------*/ BOOL HandleDialogMessage(LPMSG pMsg) { if (g_hwndDropDown != NULL) { switch (pMsg->message) { case WM_KEYDOWN: { if ((VK_ESCAPE != pMsg->wParam) && (VK_TAB != pMsg->wParam)) break; if (0 != SendMessage(g_hwndDropDown, WM_CONF_DROP_KEY, pMsg->wParam, (LPARAM) pMsg->hwnd)) { return TRUE; // message was handled } break; } case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_NCLBUTTONDOWN: case WM_NCRBUTTONDOWN: { if (g_hwndDropDown == pMsg->hwnd) break; // message is for the window, pass along as normal if (0 != SendMessage(g_hwndDropDown, WM_CONF_DROP_CLICK, 0, (LPARAM) pMsg->hwnd)) { return TRUE; // message was handled } break; } default: break; } /* switch (pMsg->message) */ } ASSERT(NULL != g_pDialogList); EnterCriticalSection(&dialogListCriticalSection); for( int i = 0; i < g_pDialogList->GetSize(); ++i ) { ITranslateAccelerator *pTrans = (*g_pDialogList)[i]; ASSERT( NULL != pTrans ); if( S_OK == pTrans->TranslateAccelerator(pMsg, 0) ) { LeaveCriticalSection(&dialogListCriticalSection); return TRUE; } } LeaveCriticalSection(&dialogListCriticalSection); return FALSE; } ////////////////////////////////////////////////////////////////////////// /* R E M O T E P A S S W O R D D L G P R O C */ // Handles the dialog box asking if the user wants to start conf.exe even though the remote control // service is in a call. INT_PTR CALLBACK ServiceRunningDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) { switch (iMsg) { case WM_INITDIALOG: return TRUE; break; case WM_COMMAND: switch (LOWORD(wParam)) { case ID_START_CONF: EndDialog(hDlg,1); break; case ID_EXIT: EndDialog(hDlg,0); break; default: break; } return TRUE; break; } return FALSE; } BOOL CheckRemoteControlService() { BOOL fContinue = TRUE; // Store OS version info g_osvi.dwOSVersionInfoSize = sizeof(g_osvi); if (FALSE == ::GetVersionEx(&g_osvi)) { ERROR_OUT(("GetVersionEx() failed!")); return FALSE; } if (::IsWindowsNT()) { SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); SC_HANDLE hRemoteControl = NULL; SERVICE_STATUS serviceStatus; if (hSCManager != NULL) { hRemoteControl = OpenService(hSCManager,REMOTE_CONTROL_NAME,SERVICE_ALL_ACCESS); DWORD dwError = GetLastError(); if (hRemoteControl != NULL) { // If service is running... BOOL fSuccess = QueryServiceStatus(hRemoteControl,&serviceStatus); if (fSuccess && serviceStatus.dwCurrentState != SERVICE_STOPPED && serviceStatus.dwCurrentState != SERVICE_PAUSED) { if (serviceStatus.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) // Service is in a call { fContinue = (BOOL)DialogBox(::GetInstanceHandle(),MAKEINTRESOURCE(IDD_SERVICE_RUNNING),GetDesktopWindow(),ServiceRunningDlgProc); } if (fContinue) { ControlService(hRemoteControl,SERVICE_CONTROL_PAUSE,&serviceStatus); for (int i = 0; i < MAX_REMOTE_TRIES; i++) { fSuccess = QueryServiceStatus(hRemoteControl,&serviceStatus); if (serviceStatus.dwCurrentState == SERVICE_PAUSED) break; TRACE_OUT(("Waiting for srvc - status is %d...", serviceStatus.dwCurrentState)); Sleep(1000); } if ( MAX_REMOTE_TRIES == i ) { // If we don't manage to shut down the service // we shouldn't try to start - it will only fail. WARNING_OUT(("TIMED OUT WAITING FOR SRVC!!")); fContinue = FALSE; } } } CloseServiceHandle(hRemoteControl); } CloseServiceHandle(hSCManager); } return fContinue; } else { // Windows 95 HANDLE hServiceEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, SERVICE_PAUSE_EVENT); HANDLE hActiveEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, SERVICE_ACTIVE_EVENT); DWORD dwError = GetLastError(); if (hServiceEvent != NULL && hActiveEvent != NULL) { // Service is running and is active CloseHandle(hActiveEvent); HANDLE hCallEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, SERVICE_CALL_EVENT); if (hCallEvent != NULL) { // Service is in a call fContinue = (BOOL)DialogBox(::GetInstanceHandle(),MAKEINTRESOURCE(IDD_SERVICE_RUNNING),GetDesktopWindow(),ServiceRunningDlgProc); CloseHandle(hCallEvent); } if (fContinue) { SetEvent(hServiceEvent); CloseHandle(hServiceEvent); for (int i = 0; i < MAX_REMOTE_TRIES; i++) { hActiveEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, SERVICE_ACTIVE_EVENT); if (NULL == hActiveEvent) break; TRACE_OUT(("Waiting for srvc")); CloseHandle(hActiveEvent); Sleep(1000); } if ( MAX_REMOTE_TRIES == i ) { // If we don't manage to shut down the service // we shouldn't try to start - it will only fail. WARNING_OUT(("TIMED OUT WAITING FOR SRVC!!")); fContinue = FALSE; } } } return fContinue; } } VOID RestartRemoteControlService() { RegEntry reLM = RegEntry(REMOTECONTROL_KEY, HKEY_LOCAL_MACHINE); if (!reLM.GetNumber(REMOTE_REG_RUNSERVICE,0)) return; if (ConfPolicies::IsRDSDisabled()) { WARNING_OUT(("RDS launch disallowed by policy")); return; } BOOL fActivate = reLM.GetNumber(REMOTE_REG_ACTIVATESERVICE, DEFAULT_REMOTE_ACTIVATESERVICE); if (::IsWindowsNT()) { SERVICE_STATUS serviceStatus; SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); SC_HANDLE hRemoteControl = OpenService(hSCManager,REMOTE_CONTROL_NAME,SERVICE_ALL_ACCESS); if (hRemoteControl != NULL) { BOOL fSuccess = QueryServiceStatus(hRemoteControl,&serviceStatus); if (SERVICE_STOPPED == serviceStatus.dwCurrentState) { StartService(hRemoteControl,0,NULL); } else { if (fActivate) { ControlService(hRemoteControl, SERVICE_CONTROL_CONTINUE, &serviceStatus); } } } else { WARNING_OUT(("Error starting RDS")); } } else { if (ConfPolicies::IsRDSDisabledOnWin9x()) { WARNING_OUT(("RDS launch disallowed by policy on Win9x")); } else { // Windows 95 HANDLE hServiceEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, SERVICE_CONTINUE_EVENT); if (hServiceEvent) // Service is running { if (fActivate) { SetEvent(hServiceEvent); } CloseHandle(hServiceEvent); } else { WinExec(WIN95_SERVICE_APP_NAME,SW_SHOWNORMAL); } } } }