// -------------------------------------------------------------------------------- // INSTANCE.CPP // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved // -------------------------------------------------------------------------------- #include "pch.hxx" #include "instance.h" #include "acctutil.h" #include "inetcfg.h" #include #include "zmouse.h" #include "migrate.h" #include #include "conman.h" #include "browser.h" #include "note.h" #include "reutil.h" #include "spengine.h" #include "addrobj.h" #include "statnery.h" #include "thumb.h" #include "imagelst.h" #include "url.h" #include "secutil.h" #include "shlwapip.h" #include "ruleutil.h" #include "newfldr.h" #include "envfact.h" #include "storutil.h" #include "multiusr.h" #include "newsstor.h" #include "storutil.h" #include #include "cleanup.h" #include #include #include #include "menures.h" #include "shared.h" #include "acctcach.h" #include #include #include "useragnt.h" #include "demand.h" #include static DWORD g_dwAcctAdvise = 0xffffffff; DWORD g_dwHideMessenger = BL_DEFAULT; extern BOOL g_fMigrationDone; extern UINT GetCurColorRes(void); // -------------------------------------------------------------------------------- // Forward Decls // -------------------------------------------------------------------------------- void SimpleMAPICleanup(void); // smapi.cpp BOOL DemandLoadMSOEACCT(void); BOOL DemandLoadMSOERT2(void); BOOL DemandLoadINETCOMM(void); // -------------------------------------------------------------------------------- // Init Common Control Flags // -------------------------------------------------------------------------------- #define ICC_FLAGS (ICC_WIN95_CLASSES|ICC_DATE_CLASSES|ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES|ICC_COOL_CLASSES|ICC_NATIVEFNTCTL_CLASS) #ifdef DEBUG // -------------------------------------------------------------------------------- // INITSOURCEINFO // -------------------------------------------------------------------------------- typedef struct tagINITSOURCEINFO *LPINITSOURCEINFO; typedef struct tagINITSOURCEINFO { LPSTR pszSource; DWORD cRefs; LPINITSOURCEINFO pNext; } INITSOURCEINFO; static LPINITSOURCEINFO g_InitSourceHead=NULL; #endif // DEBUG // -------------------------------------------------------------------------------- // MAKEERROR // -------------------------------------------------------------------------------- #define MAKEERROR(_pInfo, _nPrefixIds, _nErrorIds, _nReasonIds, _pszExtra1) \ { \ (_pInfo)->nTitleIds = idsAthena; \ (_pInfo)->nPrefixIds = _nPrefixIds; \ (_pInfo)->nErrorIds = _nErrorIds; \ (_pInfo)->nReasonIds = _nReasonIds; \ (_pInfo)->nHelpIds = IDS_ERROR_START_HELP; \ (_pInfo)->pszExtra1 = _pszExtra1; \ (_pInfo)->ulLastError = GetLastError(); \ } // -------------------------------------------------------------------------------- // CoStartOutlookExpress // -------------------------------------------------------------------------------- MSOEAPI CoStartOutlookExpress(DWORD dwFlags, LPCWSTR pwszCmdLine, INT nCmdShow) { // Tracing TraceCall("CoStartOutlookExpress"); // Verify that we have an outlook express object Assert(g_pInstance); // E_OUTOFMEMORY if (NULL == g_pInstance) { // We should show an error, but the liklyhood of this happening is almost zero return TraceResult(E_OUTOFMEMORY); } // Run... return g_pInstance->Start(dwFlags, pwszCmdLine, nCmdShow); } // -------------------------------------------------------------------------------- // CoCreateOutlookExpress // -------------------------------------------------------------------------------- MSOEAPI CoCreateOutlookExpress(IUnknown *pUnkOuter, IUnknown **ppUnknown) { // Locals HRESULT hr=S_OK; // Trace TraceCall("CoCreateOutlookExpress"); // Invalid Arg Assert(NULL != ppUnknown && NULL == pUnkOuter); // No global object yet ? AssertSz(g_pInstance, "This gets created in dllmain.cpp DllProcessAttach."); // Lets not crash if (NULL == g_pInstance) { hr = TraceResult(E_OUTOFMEMORY); goto exit; } // AddRef that badboy g_pInstance->AddRef(); // Return the Innter *ppUnknown = SAFECAST(g_pInstance, IOutlookExpress *); exit: // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::COutlookExpress // -------------------------------------------------------------------------------- COutlookExpress::COutlookExpress(void) { // Trace TraceCall("COutlookExpress::COutlookExpress"); // Init Members m_cRef = 1; m_hInstMutex = NULL; m_fPumpingMsgs = FALSE; m_cDllRef = 0; m_cDllLock = 0; m_cDllInit = 0; m_dwThreadId = GetCurrentThreadId(); m_fSwitchingUsers = FALSE; m_szSwitchToUsername = NULL; m_hwndSplash = NULL; m_pSplash = NULL; m_fIncremented = FALSE; m_hTrayIcon = 0; // Init Thread Safety InitializeCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // COutlookExpress::~COutlookExpress // -------------------------------------------------------------------------------- COutlookExpress::~COutlookExpress(void) { // Trace TraceCall("COutlookExpress::~COutlookExpress"); // We should have been un-inited Assert(0 == m_cDllInit && 0 == m_cDllRef && 0 == m_cDllLock); // Free the mutex SafeCloseHandle(m_hInstMutex); // Kill CritSect DeleteCriticalSection(&m_cs); if(m_hTrayIcon) { DestroyIcon(m_hTrayIcon); } // Free the switch to if necessary if (m_szSwitchToUsername) MemFree(m_szSwitchToUsername); } // -------------------------------------------------------------------------------- // COutlookExpress::QueryInterface // -------------------------------------------------------------------------------- STDMETHODIMP COutlookExpress::QueryInterface(REFIID riid, LPVOID *ppv) { // Locals HRESULT hr=S_OK; // Stack TraceCall("COutlookExpress::QueryInterface"); // check params if (ppv == NULL) return TrapError(E_INVALIDARG); // Find IID if (IID_IUnknown == riid) *ppv = (IUnknown *)this; else if (IID_IOutlookExpress == riid) *ppv = (IOutlookExpress *)this; else { *ppv = NULL; hr = TraceResult(E_NOINTERFACE); goto exit; } // AddRef It ((IUnknown *)*ppv)->AddRef(); exit: // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::AddRef // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) COutlookExpress::AddRef(void) { TraceCall("COutlookExpress::AddRef"); return InterlockedIncrement(&m_cRef); } // -------------------------------------------------------------------------------- // COutlookExpress::Release // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) COutlookExpress::Release(void) { TraceCall("COutlookExpress::Release"); LONG cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) delete this; return (ULONG)cRef; } // -------------------------------------------------------------------------------- // COutlookExpress::LockServer // -------------------------------------------------------------------------------- HRESULT COutlookExpress::LockServer(BOOL fLock) { // Locals HRESULT hr=S_OK; // Stack TraceCall("COutlookExpress::LockServer"); if (TRUE == fLock) { InterlockedIncrement(&m_cDllLock); } else { InterlockedDecrement(&m_cDllLock); } // Trace //TraceInfo(_MSG("Lock: %d, CoIncrementInit Count = %d, Reference Count = %d, Lock Count = %d", fLock, m_cDllInit, m_cDllRef, m_cDllLock)); // Done return S_OK; } // -------------------------------------------------------------------------------- // COutlookExpress::DllAddRef // -------------------------------------------------------------------------------- HRESULT COutlookExpress::DllAddRef(void) { // Locals HRESULT hr=S_OK; // Stack TraceCall("COutlookExpress::DllAddRef"); // Thread Safety if (InterlockedIncrement(&m_cDllRef) <= 0) { // refcount already below zero hr = S_FALSE; } // Trace //TraceInfo(_MSG("CoIncrementInit Count = %d, Reference Count = %d, Lock Count = %d", m_cDllInit, m_cDllRef, m_cDllLock)); // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::DllRelease // -------------------------------------------------------------------------------- HRESULT COutlookExpress::DllRelease(void) { // Locals HRESULT hr=S_OK; // Stack TraceCall("COutlookExpress::DllRelease"); // Thread Safety if (InterlockedDecrement(&m_cDllRef) < 0) { // refcount already below zero hr = S_FALSE; } // Trace //TraceInfo(_MSG("CoIncrementInit Count = %d, Reference Count = %d, Lock Count = %d", m_cDllInit, m_cDllRef, m_cDllLock)); // Done return hr; } // -------------------------------------------------------------------------------- // _CheckForJunkMail // -------------------------------------------------------------------------------- void _CheckForJunkMail() { HKEY hkey; DWORD dw=0, cb; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegRoot, 0, KEY_QUERY_VALUE, &hkey)) { cb = sizeof(dw); RegQueryValueEx(hkey, c_szRegJunkMailOn, 0, NULL, (LPBYTE)&dw, &cb); if (dw) g_dwAthenaMode |= MODE_JUNKMAIL; RegCloseKey(hkey); } } // -------------------------------------------------------------------------------- // COutlookExpress::Start // -------------------------------------------------------------------------------- STDMETHODIMP COutlookExpress::Start(DWORD dwFlags, LPCWSTR pwszCmdLine, INT nCmdShow) { // Locals HRESULT hr=S_OK; MSG msg; HWND hwndTimeout; HINITREF hInitRef=NULL; BOOL fErrorDisplayed=FALSE; LPWSTR pwszFree=NULL; LPWSTR pwszCmdLineDup = NULL; // Stack TraceCall("COutlookExpress::Start"); // Make sure OLE is initialized on the thread OleInitialize(NULL); // Duplicate It IF_NULLEXIT(pwszCmdLineDup = PszDupW(pwszCmdLine)); // pwszCmdLineDup will change, remember the allocated block pwszFree = pwszCmdLineDup; //We want to process the switches set the mode flags before we call CoIncrementInit _ProcessCommandLineFlags(&pwszCmdLineDup, dwFlags); // AddRef the Dll.. hr = CoIncrementInit("COutlookExpress", dwFlags, pwszCmdLine, &hInitRef); if (FAILED(hr)) { fErrorDisplayed = TRUE; TraceResult(hr); goto exit; } // Process the Command Line.. IF_FAILEXIT(hr = ProcessCommandLine(nCmdShow, pwszCmdLineDup, &fErrorDisplayed)); // No splash screen CloseSplashScreen(); // Do a CoDecrementInit IF_FAILEXIT(hr = CoDecrementInit("COutlookExpress", &hInitRef)); // No need for a Message Pump ? if (S_OK == DllCanUnloadNow() || FALSE == ISFLAGSET(dwFlags, MSOEAPI_START_MESSAGEPUMP)) goto exit; // Start the message Pump EnterCriticalSection(&m_cs); // Do we already have a pump running ? if (TRUE == m_fPumpingMsgs) { LeaveCriticalSection(&m_cs); goto exit; } // We are going to pump m_fPumpingMsgs = TRUE; // Thread Safety LeaveCriticalSection(&m_cs); SetSwitchingUsers(FALSE); // Message Loop while (GetMessageWrapW(&msg, NULL, 0, 0) && ((m_cDllInit > 0) || !SwitchingUsers())) { CNote *pNote = GetTlsGlobalActiveNote(); // Ask it to translate an accelerator if (g_pBrowser && g_pBrowser->TranslateAccelerator(&msg) == S_OK) continue; // Hand message off to the active note, but ignore init window msgs and ignore per-task msgs where hwnd=0 if (msg.hwnd != g_hwndInit && IsWindow(msg.hwnd)) { pNote = GetTlsGlobalActiveNote(); // Give it to the active note if a note has focus, call it's XLateAccelerator... if (pNote && pNote->TranslateAccelerator(&msg) == S_OK) continue; } // Get Timeout Window for this thread hwndTimeout = (HWND)TlsGetValue(g_dwTlsTimeout); // Check for Is modeless timeout dialog window message if (hwndTimeout && TRUE == IsDialogMessageWrapW(hwndTimeout, &msg)) continue; // If Still not processed TranslateMessage(&msg); DispatchMessageWrapW(&msg); } // We are no longer pumping messages EnterCriticalSection(&m_cs); m_fPumpingMsgs = FALSE; LeaveCriticalSection(&m_cs); if(SwitchingUsers()) { HrCloseWabWindow(); while (PeekMessageWrapW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageWrapW(&msg); } MU_ResetRegRoot(); } exit: // Free command line copy SafeMemFree(pwszFree); // Do a CoDecrementInit CoDecrementInit("COutlookExpress", &hInitRef); // No splash screen CloseSplashScreen(); // Is there an error ? if (FALSE == fErrorDisplayed && FAILED(hr) && hrUserCancel != hr && MAPI_E_USER_CANCEL != hr) { REPORTERRORINFO rError={0}; MAKEERROR(&rError, 0, IDS_ERROR_UNKNOWN, 0, NULL); _ReportError(g_hLocRes, hr, 0, &rError); } //Bug #101360 - (erici) OleInitialize created a window, this destroys it. OleUninitialize(); // Done return (SwitchingUsers() ? S_RESTART_OE : hr); } // -------------------------------------------------------------------------------- // COutlookExpress::_ReportError // -------------------------------------------------------------------------------- BOOL COutlookExpress::_ReportError( HINSTANCE hInstance, // Dll Instance HRESULT hrResult, // HRESULT of the error LONG lResult, // LRESULT from like a registry function LPREPORTERRORINFO pInfo) // Report Error Information { // Locals TCHAR szRes[255], szMessage[1024], szTitle[128]; // INit *szMessage = '\0'; // Is there a prefix if (pInfo->nPrefixIds) { // Load the string LoadString(hInstance, pInfo->nPrefixIds, szMessage, ARRAYSIZE(szMessage)); } // Error ? if (pInfo->nErrorIds) { // Are there extras in this error string if (NULL != pInfo->pszExtra1) { // Locals TCHAR szTemp[255]; // Load and format LoadString(hInstance, pInfo->nErrorIds, szTemp, ARRAYSIZE(szTemp)); // Format the string wnsprintf(szRes, ARRAYSIZE(szRes), szTemp, pInfo->pszExtra1); } // Load the string else { // Load the error string LoadString(hInstance, pInfo->nErrorIds, szRes, ARRAYSIZE(szRes)); } // Add to szMessage StrCatBuff(szMessage, g_szSpace, ARRAYSIZE(szMessage)); StrCatBuff(szMessage, szRes, ARRAYSIZE(szMessage)); } // Reason ? if (pInfo->nReasonIds) { // Load the string LoadString(hInstance, pInfo->nReasonIds, szRes, ARRAYSIZE(szRes)); // Add to szMessage StrCatBuff(szMessage, g_szSpace, ARRAYSIZE(szMessage)); StrCatBuff(szMessage, szRes, ARRAYSIZE(szMessage)); } // Load the string LoadString(hInstance, pInfo->nHelpIds, szRes, ARRAYSIZE(szRes)); // Add to szMessage StrCatBuff(szMessage, g_szSpace, ARRAYSIZE(szMessage)); StrCatBuff(szMessage, szRes, ARRAYSIZE(szMessage)); // Append Error Results if (lResult != 0 && E_FAIL == hrResult && pInfo->ulLastError) wnsprintf(szRes, ARRAYSIZE(szRes), "(%d, %d)", lResult, pInfo->ulLastError); else if (lResult != 0 && E_FAIL == hrResult && 0 == pInfo->ulLastError) wnsprintf(szRes, ARRAYSIZE(szRes), "(%d)", lResult); else if (pInfo->ulLastError) wnsprintf(szRes, ARRAYSIZE(szRes), "(0x%08X, %d)", hrResult, pInfo->ulLastError); else wnsprintf(szRes, ARRAYSIZE(szRes), "(0x%08X)", hrResult); // Add to szMessage StrCatBuff(szMessage, g_szSpace, ARRAYSIZE(szMessage)); StrCatBuff(szMessage, szRes, ARRAYSIZE(szMessage)); // Get the title LoadString(hInstance, pInfo->nTitleIds, szTitle, ARRAYSIZE(szTitle)); // Show the error message MessageBox(NULL, szMessage, szTitle, MB_OK | MB_SETFOREGROUND | MB_ICONEXCLAMATION); // Done return TRUE; } #ifdef DEAD // -------------------------------------------------------------------------------- // COutlookExpress::_ValidateDll // -------------------------------------------------------------------------------- HRESULT COutlookExpress::_ValidateDll(LPCSTR pszDll, BOOL fDemandResult, HMODULE hModule, HRESULT hrLoadError, HRESULT hrVersionError, LPREPORTERRORINFO pError) { // Locals HRESULT hr=S_OK; PFNGETDLLMAJORVERSION pfnGetVersion; // Tracing TraceCall("COutlookExpress::_ValidateDll"); // We must load these here in order to show errors and not crash - Load MSOERT2.DLL if (FALSE == fDemandResult) { MAKEERROR(pError, IDS_ERROR_PREFIX1, IDS_ERROR_MISSING_DLL, IDS_ERROR_REASON2, pszDll); hr = TraceResult(hrLoadError); goto exit; } // Try to get the current verion else { // Get Version Proc Address pfnGetVersion = (PFNGETDLLMAJORVERSION)GetProcAddress(hModule, STR_GETDLLMAJORVERSION); // Not the Correct Version if (NULL == pfnGetVersion || OEDLL_VERSION_CURRENT != (*pfnGetVersion)()) { MAKEERROR(pError, IDS_ERROR_PREFIX1, IDS_ERROR_BADVER_DLL, IDS_ERROR_REASON2, pszDll); hr = TraceResult(hrVersionError); goto exit; } } exit: // Done return hr; } #endif // DEAD // -------------------------------------------------------------------------------- // COutlookExpress::CoIncrementInitDebug // -------------------------------------------------------------------------------- #ifdef DEBUG HRESULT COutlookExpress::CoIncrementInitDebug(LPCSTR pszSource, DWORD dwFlags, LPCWSTR pwszCmdLine, LPHINITREF phInitRef) { // Locals BOOL fFound=FALSE; LPINITSOURCEINFO pCurrent; // Trace TraceCall("COutlookExpress::CoIncrementInitDebug"); // Invalid Args Assert(pszSource); // Thread Safety EnterCriticalSection(&m_cs); // Find Source for (pCurrent = g_InitSourceHead; pCurrent != NULL; pCurrent = pCurrent->pNext) { // Is this It ? if (lstrcmpi(pszSource, pCurrent->pszSource) == 0) { // Increment Reference Count pCurrent->cRefs++; // Found fFound = TRUE; // Done break; } } // Not Found, lets add one if (FALSE == fFound) { // Set pCurrent pCurrent = (LPINITSOURCEINFO)ZeroAllocate(sizeof(INITSOURCEINFO)); Assert(pCurrent); // Set pszSource pCurrent->pszSource = PszDupA(pszSource); Assert(pCurrent->pszSource); // Set cRefs pCurrent->cRefs = 1; // Set Next pCurrent->pNext = g_InitSourceHead; // Set Head g_InitSourceHead = pCurrent; } // Thread Safety LeaveCriticalSection(&m_cs); // Call the Actual CoIncrementInit return(CoIncrementInitImpl(dwFlags, pwszCmdLine, phInitRef)); } #endif // DEBUG // -------------------------------------------------------------------------------- // COutlookExpress::CoIncrementInitImpl // -------------------------------------------------------------------------------- HRESULT COutlookExpress::CoIncrementInitImpl(DWORD dwFlags, LPCWSTR pwszCmdLine, LPHINITREF phInitRef) { // Locals HRESULT hr=S_OK; DWORD dw; RECT rc={0}; HWND hwndDesk; DWORD dwSize; INITCOMMONCONTROLSEX icex = { sizeof(icex), ICC_FLAGS }; CImnAdviseAccount *pImnAdviseAccount=NULL; WNDCLASSW wcW; WNDCLASS wc; DWORD dwType, dwVal, cb; REPORTERRORINFO rError={0}; LONG lResult=0; LPCWSTR pwszInitWndClass; BOOL fReleaseMutex=FALSE; BOOL fResult; CHAR szFolder[MAX_PATH]; IF_DEBUG(DWORD dwTickStart=GetTickCount()); // Tracing TraceCall("COutlookExpress::CoIncrementInitImpl"); // Make sure OLE is initialized on the thread OleInitialize(NULL); // Thread Safety EnterCriticalSection(&m_cs); if (!SwitchingUsers() && !m_fIncremented) { SetQueryNetSessionCount(SESSION_INCREMENT_NODEFAULTBROWSERCHECK); m_fIncremented = TRUE; } // Increment Reference Count m_cDllInit++; // Set phInitRef if (phInitRef) *phInitRef = (HINITREF)((ULONG_PTR)m_cDllInit); // First-Time Reference if (m_cDllInit > 1) { LeaveCriticalSection(&m_cs); return S_OK; } // Leave CS (This code always runs on the same primary thread LeaveCriticalSection(&m_cs); if (FAILED(hr = MU_Init(ISFLAGSET(dwFlags, MSOEAPI_START_DEFAULTIDENTITY)))) goto exit; // Is there more than one identity? g_fPluralIDs = 1 < MU_CountUsers(); if (!MU_Login(GetDesktopWindow(), FALSE, NULL)) { hr = hrUserCancel; goto exit; } // Create the instance mutex if (NULL == m_hInstMutex) { m_hInstMutex = CreateMutex(NULL, FALSE, STR_MSOEAPI_INSTANCEMUTEX); if (NULL == m_hInstMutex) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_CREATE_INSTMUTEX, IDS_ERROR_REASON1, NULL); hr = TraceResult(E_OUTOFMEMORY); goto exit; } } // Release m_hInstMutex if it is currently owned by this thread, allow new instances to start. if (FALSE == ISFLAGSET(dwFlags, MSOEAPI_START_INSTANCEMUTEX)) { // Lets grab the mutex ourselves WaitForSingleObject(m_hInstMutex, INFINITE); } // Release the mutex fReleaseMutex = TRUE; // Must init thread on primary instnance thread // If the thread id is zero, then we have uninitialized everything. // Which means we need to re-initialize everything if (0 == m_dwThreadId) { m_dwThreadId = GetCurrentThreadId(); } AssertSz(m_dwThreadId == GetCurrentThreadId(), "We are not doing first CoIncrementInit on the thread in which g_pInstance was created on."); // Set g_dwAthenaMode _CheckForJunkMail(); // Get MimeOle IMalloc Interface if (NULL == g_pMoleAlloc) { hr = MimeOleGetAllocator(&g_pMoleAlloc); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_MIMEOLE_ALLOCATOR, IDS_ERROR_REASON1, NULL); TraceResult(hr); goto exit; } } // Set OE5 mode for INetcomm. MimeOleSetCompatMode(MIMEOLE_COMPAT_MLANG2); // Create the Database Session Object if (NULL == g_pDBSession) { hr = CoCreateInstance(CLSID_DatabaseSession, NULL, CLSCTX_INPROC_SERVER, IID_IDatabaseSession, (LPVOID *)&g_pDBSession); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_OPEN_STORE, IDS_ERROR_REASON1, NULL); goto exit; } } // all migration and upgrade happens in here now. hr = MigrateAndUpgrade(); if (FAILED(hr)) { TraceResult(hr); goto exit; } // Only if there is a command line... if (pwszCmdLine) { LPSTR pszCmdLine = NULL; // If this returns S_OK, we have launched the first-run ICW exe and we need to go away. // this is consistent with IE and forces the user to deal with the ICW before partying with us. IF_NULLEXIT(pszCmdLine = PszToANSI(CP_ACP, pwszCmdLine)); hr = NeedToRunICW(pszCmdLine); MemFree(pszCmdLine); if (hr == S_OK) { hr = hrUserCancel; goto exit; } // If that failed, time to show an error message else if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_FIRST_TIME_ICW, IDS_ERROR_REASON2, NULL); hr = TraceResult(hr); goto exit; } } // Create WNDCLASS for Primary Outlook Express hidden window if (ISFLAGSET(dwFlags, MSOEAPI_START_APPWINDOW)) pwszInitWndClass = STRW_MSOEAPI_INSTANCECLASS; else pwszInitWndClass = STRW_MSOEAPI_IPSERVERCLASS; // Register the init window if (FALSE == GetClassInfoWrapW(g_hInst, pwszInitWndClass, &wcW)) { ZeroMemory(&wcW, sizeof(wcW)); wcW.lpfnWndProc = COutlookExpress::InitWndProc; wcW.hInstance = g_hInst; wcW.lpszClassName = pwszInitWndClass; if (FALSE == RegisterClassWrapW(&wcW)) { // In this case, we are in an error condition so don't care if PszToANSI fails. LPSTR pszInitWndClass = PszToANSI(CP_ACP, pwszInitWndClass); MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_REG_WNDCLASS, IDS_ERROR_REASON1, pszInitWndClass); MemFree(pszInitWndClass); hr = TraceResult(E_FAIL); goto exit; } } // Create the OutlookExpressHiddenWindow if (NULL == g_hwndInit) { g_hwndInit = CreateWindowExWrapW(WS_EX_TOPMOST, pwszInitWndClass, pwszInitWndClass, WS_POPUP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, NULL, NULL, g_hInst, NULL); if (NULL == g_hwndInit) { // In this case, we are in an error condition so don't care if PszToANSI fails. LPSTR pszInitWndClass = PszToANSI(CP_ACP, pwszInitWndClass); MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_REG_WNDCLASS, IDS_ERROR_REASON1, pszInitWndClass); MemFree(pszInitWndClass); hr = TraceResult(E_FAIL); goto exit; } } // CoIncrementInit Global Options Manager if (FALSE == InitGlobalOptions(NULL, NULL)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_INIT_GOPTIONS, IDS_ERROR_REASON1, NULL); hr = TraceResult(E_FAIL); goto exit; } // Prompt the user for a store location, if we don't have one already hr = InitializeLocalStoreDirectory(NULL, FALSE); if (hrUserCancel == hr || FAILED(hr)) { // If not user cancel, then must be another error if (hrUserCancel != hr) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_INITSTORE_DIRECTORY, IDS_ERROR_REASON1, NULL); TraceResult(hr); } // Done goto exit; } // This needs to stay in since the Intl guys want a way to work around people who aren't going to upgrade to the latest ATOK11. if (ISFLAGSET(dwFlags, MSOEAPI_START_SHOWSPLASH) && 0 == DwGetOption(OPT_NO_SPLASH) && ((g_dwAthenaMode & MODE_OUTLOOKNEWS) != MODE_OUTLOOKNEWS)) { // Create me a splash screen hr = CoCreateInstance(CLSID_IESplashScreen, NULL, CLSCTX_INPROC_SERVER, IID_ISplashScreen, (LPVOID *)&m_pSplash); // If that worked, heck, lets show it if (SUCCEEDED(hr)) { HDC hdc = GetDC(NULL); m_pSplash->Show(g_hLocRes, ((GetDeviceCaps(hdc, BITSPIXEL) > 8) ? idbSplashHiRes : idbSplash256), idbSplashLoRes, &m_hwndSplash); ReleaseDC(NULL, hdc); } // Trace else TraceResultSz(hr, "CoCreateInstance(CLSID_IESplashScreen, ...) failed, but who cares."); // Everything is good hr = S_OK; } cb = sizeof(dw); if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, c_szRegFlat, c_szHideMessenger, &dwType, (LPBYTE)&dw, &cb)) dw = 0xffffffff; cb = sizeof(dwVal); if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, c_szRegFlat, c_szHideMessenger, &dwType, (LPBYTE)&dwVal, &cb)) dwVal = 0xffffffff; if (dw != 0xffffffff && dwVal != 0xffffffff) g_dwHideMessenger = max(dw, dwVal); else if (dw != 0xffffffff) g_dwHideMessenger = dw; else if (dwVal != 0xffffffff) g_dwHideMessenger = dwVal; else g_dwHideMessenger = BL_DEFAULT; // IntelliMouse support g_msgMSWheel = RegisterWindowMessage(TEXT(MSH_MOUSEWHEEL)); AssertSz(g_msgMSWheel, "RegisterWindowMessage for the IntelliMouse failed, we can still continue."); // Create WNDCLASS for ThumbNail if (FALSE == GetClassInfo(g_hLocRes, WC_THUMBNAIL, &wc)) { ZeroMemory(&wc, sizeof(wc)); wc.lpfnWndProc = ThumbNailWndProc; wc.hInstance = g_hLocRes; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = WC_THUMBNAIL; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); if (FALSE == RegisterClass(&wc)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_REG_WNDCLASS, IDS_ERROR_REASON1, WC_THUMBNAIL); hr = TraceResult(E_FAIL); goto exit; } } // Get the desktop Window hwndDesk = GetDesktopWindow(); AssertSz(hwndDesk, "GetDesktopWindow returned NULL. We should be ok, I hope."); if (hwndDesk) { // Get the size of the desktop window GetWindowRect(hwndDesk, &rc); // sungr: following is a hack to avoid the fullscreen app detection hack that user does to modify the top-most state of the tray. rc.left += 20; rc.top += 20; rc.bottom -= 20; rc.right -= 20; } // Test to see if we should move the store cb = ARRAYSIZE(szFolder); if (ERROR_SUCCESS == AthUserGetValue(NULL, c_szNewStoreDir, &dwType, (LPBYTE)szFolder, &cb)) { DWORD dwMoveStore = 0; DWORD cb = sizeof(dwMoveStore); AthUserGetValue(NULL, c_szMoveStore, NULL, (LPBYTE)&dwMoveStore, &cb); if (SUCCEEDED(RelocateStoreDirectory(g_hwndInit, szFolder, (dwMoveStore != 0)))) { AthUserDeleteValue(NULL, c_szNewStoreDir); AthUserDeleteValue(NULL, c_szMoveStore); } } // CoIncrementInit Common Controls Library InitCommonControlsEx(&icex); // Create account manger if (NULL == g_pAcctMan) { hr = AcctUtil_CreateAccountManagerForIdentity(PGUIDCurrentOrDefault(), &g_pAcctMan); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_CREATE_ACCTMAN, IDS_ERROR_REASON1, NULL); TraceResult(hr); goto exit; } pImnAdviseAccount = new CImnAdviseAccount(); if (NULL == pImnAdviseAccount) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_ALLOC_ACCTADVISE, IDS_ERROR_REASON1, NULL); hr = TraceResult(E_OUTOFMEMORY); goto exit; } hr = pImnAdviseAccount->Initialize(); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_INIT_ACCTADVISE, IDS_ERROR_REASON1, NULL); TraceResult(hr); goto exit; } // Register Advise sink Assert(g_dwAcctAdvise == 0xffffffff); hr = g_pAcctMan->Advise(pImnAdviseAccount, &g_dwAcctAdvise); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_ADVISE_ACCTMAN, IDS_ERROR_REASON1, NULL); TraceResult(hr); goto exit; } } // Create the rules manager if (NULL == g_pRulesMan) { hr = HrCreateRulesManager(NULL, (IUnknown **)&g_pRulesMan); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, idsErrorCreateRulesMan, IDS_ERROR_REASON1, NULL); TraceResult(hr); goto exit; } // CoIncrementInit the account manager hr = g_pRulesMan->Initialize(0); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, idsErrorInitRulesMan, IDS_ERROR_REASON1, NULL); TraceResult(hr); goto exit; } } // Create the global connection manager if (NULL == g_pConMan) { g_pConMan = new CConnectionManager(); if (NULL == g_pConMan) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_ALLOC_CONMAN, IDS_ERROR_REASON1, NULL); hr = TraceResult(E_OUTOFMEMORY); goto exit; } // CoIncrementInit the Connection Manager hr = g_pConMan->HrInit(g_pAcctMan); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_INIT_CONMAN, IDS_ERROR_REASON1, NULL); TraceResult(hr); goto exit; } } // Initialize the HTTP user agent InitOEUserAgent(TRUE); // Create the Spooler Object if (NULL == g_pSpooler) { hr = CreateThreadedSpooler(NULL, &g_pSpooler, TRUE); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_CREATE_SPOOLER, IDS_ERROR_REASON1, NULL); hr = TraceResult(hr); goto exit; } } // Create the Font Cache Object if (NULL == g_lpIFontCache) { hr = CoCreateInstance(CLSID_IFontCache, NULL, CLSCTX_INPROC_SERVER, IID_IFontCache, (LPVOID *)&g_lpIFontCache); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_CREATE_FONTCACHE, IDS_ERROR_REASON1, NULL); hr = TraceResult(E_OUTOFMEMORY); goto exit; } hr = g_lpIFontCache->Init(MU_GetCurrentUserHKey(), c_szRegInternational, 0); Assert(SUCCEEDED(hr)); } // Create the Global Store Object hr = InitializeStore(dwFlags); if (FAILED(hr)) { MAKEERROR(&rError, IDS_ERROR_PREFIX1, IDS_ERROR_OPEN_STORE, IDS_ERROR_REASON1, NULL); goto exit; } DoNewsgroupSubscribe(); if (NULL == g_pSync) { g_pSync = new COfflineSync; if (NULL == g_pSync) { hr = TraceResult(E_OUTOFMEMORY); goto exit; } hr = g_pSync->Initialize(); if (FAILED(hr)) { TraceResult(hr); goto exit; } } // Start Background Compaction in XX Seconds if (DwGetOption(OPT_BACKGROUNDCOMPACT)) SideAssert(SUCCEEDED(StartBackgroundStoreCleanup(30))); // CoIncrementInit Drag Drop Information if (0 == CF_FILEDESCRIPTORA) { CF_FILEDESCRIPTORA = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA); CF_FILEDESCRIPTORW = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW); CF_FILECONTENTS = RegisterClipboardFormat(CFSTR_FILECONTENTS); CF_HTML = RegisterClipboardFormat(CFSTR_HTML); CF_INETMSG = RegisterClipboardFormat(CFSTR_INETMSG); CF_OEFOLDER = RegisterClipboardFormat(CFSTR_OEFOLDER); CF_SHELLURL = RegisterClipboardFormat(CFSTR_SHELLURL); CF_OEMESSAGES = RegisterClipboardFormat(CFSTR_OEMESSAGES); CF_OESHORTCUT = RegisterClipboardFormat(CFSTR_OESHORTCUT); } // Get the current default codepage cb = sizeof(dwVal); if (ERROR_SUCCESS == SHGetValue(MU_GetCurrentUserHKey(), c_szRegInternational, REGSTR_VAL_DEFAULT_CODEPAGE, &dwType, &dwVal, &cb)) g_uiCodePage = (UINT)dwVal; // CoIncrementInit the Wab on first run cb = sizeof(dwVal); if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, c_szNewWABKey, c_szFirstRunValue, &dwType, &dwVal, &cb)) HrInitWab(TRUE); //This call could fail if the registry gets trashed, but do we want an error box? //According to takos, no...we do not. HGetDefaultCharset(NULL); exit: // Is there an error ? if (hrUserCancel != hr && ISFLAGSET(dwFlags, MSOEAPI_START_SHOWERRORS) && (FAILED(hr) || ERROR_SUCCESS != lResult)) { // If ulError is zero, lets set it to a default if (0 == rError.nErrorIds) MAKEERROR(&rError, 0, IDS_ERROR_UNKNOWN, 0, NULL); // Report the Error _ReportError(g_hLocRes, hr, lResult, &rError); } // Release the mutex and signal the caller initialization is done if (fReleaseMutex) SideAssert(FALSE != ReleaseMutex(m_hInstMutex)); // Trace //TraceInfo(_MSG("CoIncrementInit Count = %d, Reference Count = %d, Lock Count = %d", m_cDllInit, m_cDllRef, m_cDllLock)); // Cleanup SafeRelease(pImnAdviseAccount); // If we failed, decrement the reference count if (FAILED(hr)) { CloseSplashScreen(); CoDecrementInit("COutlookExpress", phInitRef); } else Assert(g_pAcctMan); // Time To Crank TraceInfo(_MSG("Startup Time: %d", GetTickCount() - dwTickStart)); // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::CloseSplashScreen // -------------------------------------------------------------------------------- void COutlookExpress::CloseSplashScreen(void) { // Kill the splash screen if (m_pSplash) { m_pSplash->Dismiss(); m_pSplash->Release(); m_pSplash = NULL; // HACKHACKHACK // This is needed because the splash screen might still be around after we // free up OLE. if (FALSE != IsWindow(m_hwndSplash)) { SendMessage(m_hwndSplash, WM_CLOSE, 0, 0); } } } // -------------------------------------------------------------------------------- // COutlookExpress::CoDecrementInitDebug // -------------------------------------------------------------------------------- #ifdef DEBUG HRESULT COutlookExpress::CoDecrementInitDebug(LPCSTR pszSource, LPHINITREF phInitRef) { // Locals BOOL fFound=FALSE; LPINITSOURCEINFO pCurrent; LPINITSOURCEINFO pPrevious=NULL; // Trace TraceCall("COutlookExpress::CoDecrementInitDebug"); // Invalid Args Assert(pszSource); // Do I need to do this if (NULL == phInitRef || NULL != *phInitRef) { // Thread Safety EnterCriticalSection(&m_cs); // Find Source for (pCurrent = g_InitSourceHead; pCurrent != NULL; pCurrent = pCurrent->pNext) { // Is this It ? if (lstrcmpi(pszSource, pCurrent->pszSource) == 0) { // Increment Reference Count pCurrent->cRefs--; // Found fFound = TRUE; // No More Reference Counts ? if (0 == pCurrent->cRefs) { // Previous ? if (pPrevious) pPrevious->pNext = pCurrent->pNext; else g_InitSourceHead = pCurrent->pNext; // Free pszSource g_pMalloc->Free(pCurrent->pszSource); // Free pCurrent g_pMalloc->Free(pCurrent); } // Done break; } // Set Previous pPrevious = pCurrent; } // Not Found, lets add one Assert(fFound); // TraceInfoTag TraceInfoTag(TAG_INITTRACE, "********** CoDecrementInit **********"); // Find Source for (pCurrent = g_InitSourceHead; pCurrent != NULL; pCurrent = pCurrent->pNext) { // TraceInfoTag TraceInfoTag(TAG_INITTRACE, _MSG("Source: %s, Refs: %d", pCurrent->pszSource, pCurrent->cRefs)); } // Thread Safety LeaveCriticalSection(&m_cs); } // Call Actual return(CoDecrementInitImpl(phInitRef)); } #endif // DEBUG // -------------------------------------------------------------------------------- // COutlookExpress::CoDecrementInitImpl // -------------------------------------------------------------------------------- HRESULT COutlookExpress::CoDecrementInitImpl(LPHINITREF phInitRef) { // Locals HRESULT hr=S_OK; // Stack TraceCall("COutlookExpress::CoDecrementInitImpl"); // If *phInitRef = NULL, then we should no do the CoDecrementInit if (phInitRef && NULL == *phInitRef) { hr = S_OK; goto exit; } // We must de-init on the same thread that we were created on... if (m_dwThreadId != GetCurrentThreadId() && g_hwndInit && IsWindow(g_hwndInit)) { // Thunk the shutdown to the correct thread hr = (HRESULT) SendMessage(g_hwndInit, ITM_SHUTDOWNTHREAD, 0, (LPARAM)phInitRef); } else { // Forward everything off to the main function hr = _CoDecrementInitMain(phInitRef); } if (!SwitchingUsers() && m_fIncremented && (m_cDllInit == 0)) { SetQueryNetSessionCount(SESSION_DECREMENT); m_fIncremented = FALSE; } // Uninitialize Ole OleUninitialize(); exit: return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::_CoDecrementInitMain // // NOTE: We assume that we already have the critical section before this call // -------------------------------------------------------------------------------- HRESULT COutlookExpress::_CoDecrementInitMain(LPHINITREF phInitRef) { // Stack TraceCall("COutlookExpress::_CoDecrementInitMain"); // If *phInitRef = NULL, then we should no do the CoDecrementInit if (phInitRef && NULL == *phInitRef) return S_OK; // Thread Safety EnterCriticalSection(&m_cs); // This should never happen. It could only happen if g_hwndInit was NULL. AssertSz(m_dwThreadId == GetCurrentThreadId(), "We are not doing the last CoDecrementInit on the thread in which g_pInstance was created on."); // Release Assert(m_cDllInit); m_cDllInit--; // Not hit zero yet ? if (m_cDllInit > 0) { LeaveCriticalSection(&m_cs); goto exit; } // Leave Critical Section LeaveCriticalSection(&m_cs); // Validate Assert(NULL == g_InitSourceHead); // Take ownership of the mutex to block people from creating new insts while shutting down WaitForSingleObject(m_hInstMutex, INFINITE); // Cleanup the Trident data for this thread //g_hLibMAPI if (g_hlibMAPI) { FreeLibrary(g_hlibMAPI); g_hlibMAPI = 0; } // Make sure we remove our new mail notification from the tray UpdateTrayIcon(TRAYICONACTION_REMOVE); // Close Background Compaction SideAssert(SUCCEEDED(CloseBackgroundStoreCleanup())); // Kill the Spooler if (g_pSpooler) { CloseThreadedSpooler(g_pSpooler); g_pSpooler = NULL; } // de-init the http user agent InitOEUserAgent(FALSE); // A bunch of de-init things FInitRichEdit(FALSE); Note_Init(FALSE); Envelope_FreeGlobals(); // Make sure next identity can migrate g_fMigrationDone = FALSE; // De-initialize Multilanguage menu DeinitMultiLanguage(); // Deinit Stationery if (g_pStationery) { // Save the current list g_pStationery->SaveStationeryList(); // Release the object SideAssert(0 == g_pStationery->Release()); // Lets not free it again g_pStationery = NULL; } // Release the font cache SafeRelease(g_lpIFontCache); // Simple MAPI Cleanup #ifndef WIN16 SimpleMAPICleanup(); #endif // Kill the Wab HrInitWab(FALSE); /* We shouldn't have to do this anymore. This should be handled by IE when we decrement the session count #ifndef WIN16 // No RAS support in Win16 if (g_pConMan && g_pConMan->IsRasLoaded() && g_pConMan->IsConnected()) g_pConMan->Disconnect(g_hwndInit, TRUE, FALSE, TRUE); #endif */ // Image Lists FreeImageLists(); // Kill the account manager if (g_pAcctMan) { CleanupTempNewsAccounts(); if (g_dwAcctAdvise != 0xffffffff) { g_pAcctMan->Unadvise(g_dwAcctAdvise); g_dwAcctAdvise = 0xffffffff; } g_pAcctMan->Release(); g_pAcctMan = NULL; } Assert(g_dwAcctAdvise == 0xffffffff); SafeRelease(g_pSync); #ifndef WIN16 // No RAS support in Win16 SafeRelease(g_pConMan); #endif // Kill the rules manager SafeRelease(g_pRulesMan); // Take down the password cache DestroyPasswordList(); // free the account data cache FreeAccountPropCache(); // MIMEOLE Allocator SafeRelease(g_pMoleAlloc); // Kill g_hwndInit if (g_hwndInit) { SendMessage(g_hwndInit, WM_CLOSE, (WPARAM) 0, (LPARAM) 0); g_hwndInit = NULL; } // Kill the store SafeRelease(g_pStore); SafeRelease(g_pLocalStore); SafeRelease(g_pDBSession); // Global options DeInitGlobalOptions(); // Run register window classes UnregisterClass(c_szFolderWndClass, g_hInst); UnregisterClassWrapW(STRW_MSOEAPI_INSTANCECLASS, g_hInst); UnregisterClassWrapW(STRW_MSOEAPI_IPSERVERCLASS, g_hInst); UnregisterClass(c_szFolderViewClass, g_hInst); UnregisterClass(c_szBlockingPaintsClass, g_hInst); UnregisterClass(WC_THUMBNAIL, g_hInst); // Break Message Loop in RunShell if we are pumping messages and not switching identities if (m_fPumpingMsgs && !m_fSwitchingUsers) PostQuitMessage(0); else PostMessage(NULL, ITM_IDENTITYMSG, 0, 0); MU_Shutdown(); // Relase the startup/shutdown mutex ReleaseMutex(m_hInstMutex); // Make sure mark this initialization thread as dead m_dwThreadId = 0; exit: // We must have decremented succesfully if (phInitRef) *phInitRef = NULL; // Trace //TraceInfo(_MSG("_CoDecrementInitMain Count = %d, Reference Count = %d, Lock Count = %d", m_cDllInit, m_cDllRef, m_cDllLock)); // Done return S_OK; } // -------------------------------------------------------------------------------- // COutlookExpress::ActivateWindow // -------------------------------------------------------------------------------- HRESULT COutlookExpress::ActivateWindow(HWND hwnd) { // If hwnd is minimized, retstore it if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE); // If the window is not enabled, set it to the foreground if (IsWindowEnabled(hwnd)) SetForegroundWindow(hwnd); // Otherwise, I have no clue what this does else { SetForegroundWindow(GetLastActivePopup(hwnd)); MessageBeep(MB_OK); return S_FALSE; } // Done return S_OK; } // -------------------------------------------------------------------------------- // COutlookExpress::SetSwitchingUsers // -------------------------------------------------------------------------------- HRESULT COutlookExpress::SetSwitchingUsers(BOOL bSwitching) { // Set the mode to whatever was passed in m_fSwitchingUsers = bSwitching; // if we are switching, we need to enter the mutex so that // another process won't get started if (bSwitching) WaitForSingleObject(m_hInstMutex, INFINITE); return S_OK; } // -------------------------------------------------------------------------------- // COutlookExpress::SetSwitchingUsers // -------------------------------------------------------------------------------- void COutlookExpress::SetSwitchToUser(TCHAR *lpszUserName) { ULONG cchUserName = 0; if (m_szSwitchToUsername) { MemFree(m_szSwitchToUsername); m_szSwitchToUsername = NULL; } cchUserName = lstrlen(lpszUserName) + 1; MemAlloc((void **)&m_szSwitchToUsername, cchUserName); if (m_szSwitchToUsername) { StrCpyN(m_szSwitchToUsername, lpszUserName, cchUserName); } } // -------------------------------------------------------------------------------- // COutlookExpress::BrowseToObject // -------------------------------------------------------------------------------- HRESULT COutlookExpress::BrowseToObject(UINT nCmdShow, FOLDERID idFolder) { // Locals HRESULT hr=S_OK; HWND hwnd; // Trace TraceCall("COutlookExpress::BrowseToObject"); // Do we already have a global browser object ? if (g_pBrowser) { // Get its Window if (SUCCEEDED(g_pBrowser->GetWindow(&hwnd))) { // Activate that Window IF_FAILEXIT(hr = ActivateWindow(hwnd)); } // Tell the browser to browse to this object IF_FAILEXIT(hr = g_pBrowser->BrowseObject(idFolder, 0)); } // Otherwise, we need to create a new browser object else { // We should always be on the correct thread here if (m_dwThreadId == GetCurrentThreadId()) { // Create a new browser object IF_NULLEXIT(g_pBrowser = new CBrowser); // CoIncrementInit It IF_FAILEXIT(hr = g_pBrowser->HrInit(nCmdShow, idFolder)); } // Otherwise, we need to thunk across to the init thread to make this happen. // This can happen when the Finder.cpp does a BrowseToObject to open a messgae's container else { // Thunk with a message Assert(g_hwndInit && IsWindow(g_hwndInit)); IF_FAILEXIT(hr = (HRESULT)SendMessage(g_hwndInit, ITM_BROWSETOOBJECT, (WPARAM)nCmdShow, (LPARAM)idFolder)); } } exit: // Done return hr; } void COutlookExpress::_ProcessCommandLineFlags(LPWSTR *ppwszCmdLine, DWORD dwFlags) { Assert(ppwszCmdLine != NULL); DWORD Mode = 0; if (*ppwszCmdLine != NULL) { // '/mailonly' if (0 == StrCmpNIW(*ppwszCmdLine, c_wszSwitchMailOnly, lstrlenW(c_wszSwitchMailOnly))) { SetStartFolderType(FOLDER_LOCAL); Mode |= MODE_MAILONLY; *ppwszCmdLine = *ppwszCmdLine + lstrlenW(c_wszSwitchMailOnly); } // '/newsonly' else if (0 == StrCmpNIW(*ppwszCmdLine, c_wszSwitchNewsOnly, lstrlenW(c_wszSwitchNewsOnly))) { SetStartFolderType(FOLDER_NEWS); Mode |= MODE_NEWSONLY; *ppwszCmdLine = *ppwszCmdLine + lstrlenW(c_wszSwitchNewsOnly); } // '/outnews' else if (0 == StrCmpNIW(*ppwszCmdLine, c_wszSwitchOutNews, lstrlenW(c_wszSwitchOutNews))) { SetStartFolderType(FOLDER_NEWS); Mode |= MODE_OUTLOOKNEWS; *ppwszCmdLine = *ppwszCmdLine + lstrlenW(c_wszSwitchOutNews); } } if (!(dwFlags & MSOEAPI_START_ALREADY_RUNNING)) { g_dwAthenaMode |= Mode; } } // -------------------------------------------------------------------------------- // COutlookExpress::ProcessCommandLine // -------------------------------------------------------------------------------- HRESULT COutlookExpress::ProcessCommandLine(INT nCmdShow, LPWSTR pwszCmdLine, BOOL *pfErrorDisplayed) { // Locals HRESULT hr=S_OK; LPWSTR pwszArgs; FOLDERID idFolder=FOLDERID_ROOT; HWND hwnd=NULL; IF_DEBUG(DWORD dwTickStart=GetTickCount()); // Trace TraceCall("COutlookExpress::ProcessCommandLine"); // Invalid Arg Assert(pfErrorDisplayed); // Do we have a command line if (NULL == pwszCmdLine) return S_OK; // Goto Next Switch if (*pwszCmdLine == L' ') pwszCmdLine++; // '/mailurl:' if (0 == StrCmpNIW(pwszCmdLine, c_wszSwitchMailURL, lstrlenW(c_wszSwitchMailURL))) { SetStartFolderType(FOLDER_LOCAL); pwszArgs = pwszCmdLine + lstrlenW(c_wszSwitchMailURL); IF_FAILEXIT(hr = _HandleMailURL(pwszArgs, pfErrorDisplayed)); } // '/newsurl:' else if (0 == StrCmpNIW(pwszCmdLine, c_wszSwitchNewsURL, lstrlenW(c_wszSwitchNewsURL))) { SetStartFolderType(FOLDER_NEWS); pwszArgs = pwszCmdLine + lstrlenW(c_wszSwitchNewsURL); IF_FAILEXIT(hr = _HandleNewsURL(nCmdShow, pwszArgs, pfErrorDisplayed)); } // '/eml:' else if (0 == StrCmpNIW(pwszCmdLine, c_wszSwitchEml, lstrlenW(c_wszSwitchEml))) { pwszArgs = pwszCmdLine + lstrlenW(c_wszSwitchEml); IF_FAILEXIT(hr = _HandleFile(pwszArgs, pfErrorDisplayed, FALSE)); } // '/nws:' else if (0 == StrCmpNIW(pwszCmdLine, c_wszSwitchNws, lstrlenW(c_wszSwitchNws))) { pwszArgs = pwszCmdLine + lstrlenW(c_wszSwitchNws); IF_FAILEXIT(hr = _HandleFile(pwszArgs, pfErrorDisplayed, TRUE)); } // Otherwise, decide where to start a browser at... else { // Handle '/news' if (0 == StrCmpNIW(pwszCmdLine, c_wszSwitchNews, lstrlenW(c_wszSwitchNews))) { // This sets g_dwIcwFlags SetStartFolderType(FOLDER_NEWS); if (g_pBrowser) g_pBrowser->GetWindow(&hwnd); hr = ProcessICW(hwnd, FOLDER_NEWS, TRUE); if (hr != S_OK) goto exit; // Get Default News SErver GetDefaultServerId(ACCT_NEWS, &idFolder); } // Handle '/mail /defclient' else if (0 == StrCmpNIW(pwszCmdLine, c_wszSwitchMail, lstrlenW(c_wszSwitchMail)) || 0 == StrCmpNIW(pwszCmdLine, c_wszSwitchDefClient, lstrlenW(c_wszSwitchDefClient))) { // Locals FOLDERINFO Folder; FOLDERID idStore; // This sets g_dwIcwFlags SetStartFolderType(FOLDER_LOCAL); if (g_pBrowser) g_pBrowser->GetWindow(&hwnd); hr = ProcessICW(hwnd, FOLDER_LOCAL, TRUE); if (hr != S_OK) goto exit; // Get store ID of default account if (FAILED(GetDefaultServerId(ACCT_MAIL, &idStore))) idStore = FOLDERID_LOCAL_STORE; // Get Inbox Id if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idStore, FOLDER_INBOX, &Folder))) { idFolder = Folder.idFolder; g_pStore->FreeRecord(&Folder); } } // No switches else { // default launch // - if there is already a browser, just activate it // - else if the option is set, select default inbox // - else select the root (pidl = NULL) if (g_pBrowser && SUCCEEDED(g_pBrowser->GetWindow(&hwnd))) { ActivateWindow(hwnd); goto exit; } else if (DwGetOption(OPT_LAUNCH_INBOX) && (FALSE == ISFLAGSET(g_dwAthenaMode, MODE_NEWSONLY))) { // Locals FOLDERINFO Folder; FOLDERID idStore; // This sets g_dwIcwFlags SetStartFolderType(FOLDER_LOCAL); // Get store ID of default account if (FAILED(GetDefaultServerId(ACCT_MAIL, &idStore))) idStore = FOLDERID_LOCAL_STORE; // Get Inbox Id if (SUCCEEDED(g_pStore->GetSpecialFolderInfo(idStore, FOLDER_INBOX, &Folder))) { idFolder = Folder.idFolder; g_pStore->FreeRecord(&Folder); } } } // Browe to this new object, I assume if pidl=null, we browse to the root IF_FAILEXIT(hr = BrowseToObject(nCmdShow, idFolder)); } exit: /* // Cleanup SafeMemFree(pszFree); */ // Trace TraceInfo(_MSG("Process Command Line Time: %d milli-seconds", GetTickCount() - dwTickStart)); // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::_HandleFile // -------------------------------------------------------------------------------- HRESULT COutlookExpress::_HandleFile(LPWSTR pwszCmd, BOOL *pfErrorDisplayed, BOOL fNews) { // Locals HRESULT hr=S_OK; INIT_MSGSITE_STRUCT initStruct; DWORD dwCreateFlags = OENCF_SENDIMMEDIATE; if (fNews) dwCreateFlags |= OENCF_NEWSFIRST; // Stack TraceCall("COutlookExpress::_HandleFile"); // Invalid Arg Assert(pfErrorDisplayed); // Invalid Arg if (NULL == pwszCmd || L'\0' == *pwszCmd) { hr = TraceResult(E_INVALIDARG); goto exit; } // Does the file exist ? if (FALSE == PathFileExistsW(pwszCmd)) { // Locals REPORTERRORINFO rError={0}; // Set hr hr = TraceResult(MSOEAPI_E_FILE_NOT_FOUND); // Duplicate It LPSTR pszCmd = PszToANSI(CP_ACP, pwszCmd); if (pszCmd) { // Make the rror MAKEERROR(&rError, 0, IDS_ERROR_FILE_NOEXIST, 0, pszCmd); rError.nHelpIds = 0; // Show an error *pfErrorDisplayed = _ReportError(g_hLocRes, hr, 0, &rError); // Cleanup MemFree(pszCmd); } // Done goto exit; } initStruct.dwInitType = OEMSIT_FAT; initStruct.pwszFile = pwszCmd; hr = CreateAndShowNote(OENA_READ, dwCreateFlags, &initStruct); exit: // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::_HandleNewsArticleURL // -------------------------------------------------------------------------------- HRESULT COutlookExpress::_HandleNewsArticleURL(LPSTR pszServerIn, LPSTR pszArticle, UINT uPort, BOOL fSecure, BOOL *pfErrorDisplayed) { HRESULT hr=S_OK; CHAR szAccountId[CCHMAX_ACCOUNT_NAME]; LPSTR psz = NULL, pszBuf = NULL; IImnAccount *pAccount=NULL; INIT_MSGSITE_STRUCT initStruct; LPMIMEMESSAGE pMsg = NULL; Assert(pszServerIn); // Stack TraceCall("COutlookExpress::_HandleNewsArticleURL"); // Invalid Arg Assert(pfErrorDisplayed); // If a server was specified, then try to create a temp account for it if (FALSE == FIsEmptyA(pszServerIn) && SUCCEEDED(CreateTempNewsAccount(pszServerIn, uPort, fSecure, &pAccount))) { // Get the Account name IF_FAILEXIT(hr = pAccount->GetPropSz(AP_ACCOUNT_ID, szAccountId, ARRAYSIZE(szAccountId))); } // Otherwise, use the default news server else { // If a server wasn't specified, then use the default account IF_FAILEXIT(hr = GetDefaultNewsServer(szAccountId, ARRAYSIZE(szAccountId))); } // Bug #10555 - The URL shouldn't have <> around the article ID, but some lameoids probably will do it anyway, so deal with it. if (FALSE == IsDBCSLeadByte(*pszArticle) && '<' != *pszArticle) { ULONG cchArticle; cchArticle = lstrlen(pszArticle) + 4; if (!MemAlloc((void **)&pszBuf, cchArticle)) { hr = E_OUTOFMEMORY; goto exit; } wnsprintf(pszBuf, cchArticle, TEXT("<%s>"), pszArticle); psz = pszBuf; } else { psz = pszArticle; } hr = HrDownloadArticleDialog(szAccountId, psz, &pMsg); if (S_OK == (hr)) { Assert(pMsg != NULL); initStruct.dwInitType = OEMSIT_MSG; initStruct.pMsg = pMsg; initStruct.folderID = FOLDERID_INVALID; hr = CreateAndShowNote(OENA_READ, OENCF_NEWSFIRST, &initStruct); } else { // No errors if the user cancel'ed on purpose. if (HR_E_USER_CANCEL_CONNECT == hr || HR_E_OFFLINE == hr) hr = S_OK; else { AthMessageBoxW(g_hwndInit, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsNewsTaskArticleError), 0, MB_OK|MB_SETFOREGROUND); hr = S_OK; } } exit: // Cleanup MemFree(pszBuf); ReleaseObj(pAccount); ReleaseObj(pMsg); // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::_HandleNewsURL // -------------------------------------------------------------------------------- HRESULT COutlookExpress::_HandleNewsURL(INT nCmdShow, LPWSTR pwszCmd, BOOL *pfErrorDisplayed) { // Locals HWND hwnd; HRESULT hr=S_OK; LPSTR pszCmd=NULL, pszServer=NULL, pszGroup=NULL, pszArticle=NULL; UINT uPort=(UINT)-1; BOOL fSecure; FOLDERID idFolder; TCHAR szRes[CCHMAX_STRINGRES], szError[MAX_PATH + CCHMAX_STRINGRES]; // Stack TraceCall("COutlookExpress::_HandleNewsURL"); // Invalid Arg Assert(pfErrorDisplayed); Assert(pwszCmd != NULL); Assert(*pwszCmd != 0); // Since this is a URL, then don't need to worry about UNICODE IF_NULLEXIT(pszCmd = PszToANSI(CP_ACP, pwszCmd)); // Un-escape the Url UrlUnescapeInPlace(pszCmd, 0); // Figure out if the URL is valid and what type of URL it is. hr = URL_ParseNewsUrls(pszCmd, &pszServer, &uPort, &pszGroup, &pszArticle, &fSecure); if ((hr == INET_E_UNKNOWN_PROTOCOL || hr == INET_E_INVALID_URL) && LoadString(g_hLocRes, idsErrOpenUrlFmt, szRes, ARRAYSIZE(szRes))) { // if bad url format, warn user and return S_OK as we handled it // Outlook Express could not open the URL '%.100s' because it is not a recognized format. // we clip the URL to 100 chars, so it easily fits in the MAX_PATH buffer wnsprintf(szError, ARRAYSIZE(szError), szRes, pszCmd, lstrlen(pszCmd)>100?g_szEllipsis:c_szEmpty); AthMessageBox(g_hwndInit, MAKEINTRESOURCE(idsAthena), szError, 0, MB_OK|MB_SETFOREGROUND); return S_OK; } IF_FAILEXIT(hr); // Compute the correct port number if (uPort == -1) uPort = fSecure ? DEF_SNEWSPORT : DEF_NNTPPORT; // If we have an article, HandleNewsArticleURL if (pszArticle) { // Launch a read note onto the article id IF_FAILEXIT(hr = _HandleNewsArticleURL(pszServer, pszArticle, uPort, fSecure, pfErrorDisplayed)); } // Otheriwse, create a PIDL and browse to that pidl (its a newsgroup) else { // Locals FOLDERID idFolder; if (pszServer == NULL) { // If we have a browser, the its hwnd so that ICW has a parent if (g_pBrowser) g_pBrowser->GetWindow(&hwnd); else hwnd = NULL; // Run the ICW if necessary hr = ProcessICW(hwnd, FOLDER_NEWS, TRUE); if (hr != S_OK) goto exit; } // Create a PIDL for this newsgroup URL if (SUCCEEDED(hr = GetFolderIdFromNewsUrl(pszServer, uPort, pszGroup, fSecure, &idFolder))) { // Browse to that object IF_FAILEXIT(hr = BrowseToObject(nCmdShow, idFolder)); } } exit: // Cleanup SafeMemFree(pszCmd); SafeMemFree(pszServer); SafeMemFree(pszGroup); SafeMemFree(pszArticle); // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::_HandleMailURL // // PURPOSE: Provides an entry point into Thor that allows us to be // invoked from a URL. The pszCmdLine paramter must be a // valid Mail URL or nothing happens. // // -------------------------------------------------------------------------------- HRESULT COutlookExpress::_HandleMailURL(LPWSTR pwszCmdLine, BOOL *pfErrorDisplayed) { // Locals HRESULT hr=S_OK; LPMIMEMESSAGE pMsg=NULL; INIT_MSGSITE_STRUCT initStruct; TCHAR szRes[CCHMAX_STRINGRES], szError[MAX_PATH + CCHMAX_STRINGRES]; LPSTR pszCmdLine = NULL; // Stack TraceCall("COutlookExpress::_HandleMailURL"); // Invalid Arg Assert(pfErrorDisplayed); // No command line if (NULL == pwszCmdLine || L'\0' == *pwszCmdLine) { hr = TraceResult(E_INVALIDARG); goto exit; } // Since this is a URL, then don't need to worry about UNICODE IF_NULLEXIT(pszCmdLine = PszToANSI(CP_ACP, pwszCmdLine)); // Create a Message Object IF_FAILEXIT(hr = HrCreateMessage(&pMsg)); // NOTE: no URLUnescape in this function - it must be done in URL_ParseMailTo to handle // URLs of the format: // // mailto:foo@bar.com?subject=AT%26T%3dBell&cc=me@too.com // // so that the "AT%26T" is Unescaped into "AT&T=Bell" *AFTER* the "subject=AT%26T%3dBell&" blob is parsed. hr = URL_ParseMailTo(pszCmdLine, pMsg); if ((hr == INET_E_UNKNOWN_PROTOCOL || hr == INET_E_INVALID_URL) && LoadString(g_hLocRes, idsErrOpenUrlFmt, szRes, ARRAYSIZE(szRes))) { // if bad url format, warn user and return S_OK as we handled it // Outlook Express could not open the URL '%.100s' because it is not a recognized format. // we clip the URL to 100 chars, so it easily fits in the MAX_PATH buffer wnsprintf(szError, ARRAYSIZE(szError), szRes, pszCmdLine, lstrlen(pszCmdLine)>100?g_szEllipsis:c_szEmpty); AthMessageBox(g_hwndInit, MAKEINTRESOURCE(idsAthena), szError, 0, MB_OK|MB_SETFOREGROUND); return S_OK; } IF_FAILEXIT(hr); initStruct.dwInitType = OEMSIT_MSG; initStruct.pMsg = pMsg; initStruct.folderID = FOLDERID_INVALID; hr = CreateAndShowNote(OENA_COMPOSE, OENCF_SENDIMMEDIATE, &initStruct); exit: // Cleanup SafeRelease(pMsg); MemFree(pszCmdLine); // Done return hr; } // -------------------------------------------------------------------------------- // COutlookExpress::InitWndProc // -------------------------------------------------------------------------------- LRESULT EXPORT_16 CALLBACK COutlookExpress::InitWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { // Locals BOOL fRet; HRESULT hr; // Delegate to the Account Manager if (g_pAcctMan && g_pAcctMan->ProcessNotification(msg, wp, lp) == S_OK) return TRUE; // Handle the Message switch(msg) { case WM_ENDSESSION: // if we get forced down by window, we don't exit clean, so deinit global opt it not called. We obviously don't have a mailbomb so clear the regkey. SetDwOption(OPT_ATHENA_RUNNING, FALSE, NULL, 0); break; case WM_SETTINGCHANGE: Assert (g_lpIFontCache); if (g_lpIFontCache) { if (!wp || SPI_SETNONCLIENTMETRICS == wp || SPI_SETICONTITLELOGFONT == wp) g_lpIFontCache->OnOptionChange(); } break; case ITM_WAB_CO_DECREMENT: Wab_CoDecrement(); return 0; case ITM_BROWSETOOBJECT: return (LRESULT)g_pInstance->BrowseToObject((UINT)wp, (FOLDERID)lp); case ITM_SHUTDOWNTHREAD: return (LRESULT)g_pInstance->_CoDecrementInitMain((LPHINITREF)lp); case ITM_POSTCOPYDATA: if (lp) { g_pInstance->Start(MSOEAPI_START_ALREADY_RUNNING, (LPCWSTR)lp, SW_SHOWNORMAL); MemFree((LPWSTR)lp); } break; case WM_COPYDATA: { // Locals COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT *)lp; // Command-line if (pCopyData->dwData == MSOEAPI_ACDM_CMDLINE) { // #25238: On Win95, OLE get's pissed if we syncronously do stuff on the // WM_COPYDATA. On the most part it works, but if we show an error and pump messages // then some messages get run out of sequence and we deadlock between msimn.exe and // iexplore.exe. Now we post to ourselves as we don't care about the HRESULT anyway // we free the duped string on the post PostMessage(hwnd, ITM_POSTCOPYDATA, 0, (LPARAM)PszDupW((LPCWSTR)pCopyData->lpData)); return 0; } // Notification Thunk else if (pCopyData->dwData == MSOEAPI_ACDM_NOTIFY) { // Locals NOTIFYDATA rNotify; LRESULT lResult=0; // Crack the notification if (SUCCEEDED(CrackNotificationPackage(pCopyData, &rNotify))) { // Otherwise, its within this process... if (ISFLAGSET(rNotify.dwFlags, SNF_SENDMSG)) lResult = SendMessage(rNotify.hwndNotify, rNotify.msg, rNotify.wParam, rNotify.lParam); else PostMessage(rNotify.hwndNotify, rNotify.msg, rNotify.wParam, rNotify.lParam); // Done return lResult; } // Problems else Assert(FALSE); } } break; case MVM_NOTIFYICONEVENT: g_pInstance->_HandleTrayIconEvent(wp, lp); return (0); } // Delegate to default window procedure return DefWindowProc(hwnd, msg, wp, lp); } HRESULT COutlookExpress::UpdateTrayIcon(TRAYICONACTION type) { NOTIFYICONDATA nid; HWND hwnd = NULL; ULONG i; TraceCall("COutlookExpress::UpdateTrayIcon"); EnterCriticalSection(&m_cs); // Make sure we have the init window around first if (!g_hwndInit) goto exit; // Set up the struct nid.cbSize = sizeof(NOTIFYICONDATA); nid.uID = 0; nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; nid.uCallbackMessage = MVM_NOTIFYICONEVENT; if(m_hTrayIcon) { //Bug #86366 - (erici) Fixes leak. Don't create a new ICON each time COutlookExpress::UpdateTrayIcon is called. nid.hIcon = m_hTrayIcon; } else { nid.hIcon = (HICON) LoadImage(g_hLocRes, MAKEINTRESOURCE(idiNewMailNotify), IMAGE_ICON, 16, 16, 0); } nid.hWnd = g_hwndInit; LoadString(g_hLocRes, idsNewMailNotify, nid.szTip, sizeof(nid.szTip)); if (TRAYICONACTION_REMOVE == type) { Shell_NotifyIcon(NIM_DELETE, &nid); } // Add if (TRAYICONACTION_ADD == type) { Shell_NotifyIcon(NIM_ADD, &nid); } g_pBrowser->WriteUnreadCount(); exit: LeaveCriticalSection(&m_cs); return (S_OK); } void COutlookExpress::_HandleTrayIconEvent(WPARAM wParam, LPARAM lParam) { HWND hwnd; if (lParam == WM_LBUTTONDBLCLK) { if (g_pBrowser) { g_pBrowser->GetWindow(&hwnd); if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE); SetForegroundWindow(hwnd); PostMessage(hwnd, WM_COMMAND, ID_GO_INBOX, 0); } } }