//#define DONT_USE_ATL #include "priv.h" #define ATL_ENABLED #include "atl.h" #include "sccls.h" #include #include // for CLSID_ACLMRU #include #include "shbrows2.h" // CWinInetNotify_szWindowClass #include "desktop.h" // DESKTOPPROXYCLASS #include "mluisupp.h" #define DECL_CRTFREE #include #include "shfusion.h" STDAPI_(void) InitURLIDs(UINT uPlatform); // from shdocfl.cpp STDAPI SHIsThereASystemScheduler(void); // from schedule.cpp STDAPI SHFreeSystemScheduler(void); // // Downlevel delay load support (we forward to shlwapi) // #include PfnDliHook __pfnDliFailureHook; HANDLE BaseDllHandle; LONG g_cRefThisDll = 0; // per-instance CRITICAL_SECTION g_csDll = {0}; // per-instance HINSTANCE g_hinst = NULL; HANDLE g_hMutexHistory = NULL; BOOL g_fNashInNewProcess = FALSE; // Are we running in a separate process BOOL g_fRunningOnNT = FALSE; BOOL g_bRunOnNT5 = FALSE; BOOL g_fRunOnWhistler = FALSE; BOOL g_bRunOnMemphis = FALSE; BOOL g_fRunOnFE = FALSE; DWORD g_dwStopWatchMode = 0; // Shell perf automation HANDLE g_hCabStateChange = NULL; BOOL g_fIE = FALSE; // Is Mirroring enabled BOOL g_bMirroredOS = FALSE; HPALETTE g_hpalHalftone = NULL; void DestroyZoneIconNameCache(void); // // This array holds information needed for ClassFacory. // OLEMISC_ flags are used by shembed and shocx. // // PERF: this table should be ordered in most-to-least used order // CF_TABLE_BEGIN(g_ObjectInfo) CF_TABLE_ENTRY(&CLSID_InternetToolbar, CInternetToolbar_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_BrandBand, CBrandBand_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_MenuBandSite, CMenuBandSite_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_MenuDeskBar, CMenuDeskBar_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_AugmentedShellFolder, CAugmentedISF_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_AugmentedShellFolder2, CAugmentedISF2_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_AddressBand, CAddressBand_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_AddressEditBox, CAddressEditBox_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_BandProxy, CBandProxy_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY_NOFLAGS( &CLSID_RebarBandSite, CBandSite_CreateInstance, COCREATEONLY_NOFLAGS, OIF_ALLOWAGGREGATION), CF_TABLE_ENTRY(&CLSID_DeskBarApp, CDeskBarApp_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_DeskBar, CDeskBar_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_AutoComplete, CAutoComplete_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ACLHistory, CACLHistory_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ACListISF, CACLIShellFolder_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ACLMRU, CACLMRU_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ACLMulti, CACLMulti_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY_NOFLAGS( &CLSID_CCommonBrowser, CCommonBrowser_CreateInstance, COCREATEONLY_NOFLAGS, OIF_ALLOWAGGREGATION), CF_TABLE_ENTRY(&CLSID_CDockingBarPropertyBag, CDockingBarPropertyBag_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_CRegTreeOptions, CRegTreeOptions_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_BrowserBand, CBrowserBand_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_SearchBand, CSearchBand_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_MediaBand, CMediaBand_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_CommBand, CCommBand_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_BandSiteMenu, CBandSiteMenu_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ComCatCacheTask, CComCatCacheTask_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ComCatConditionalCacheTask,CComCatConditionalCacheTask_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ImgCtxThumbnailExtractor, CImgCtxThumb_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ImageListCache, CImageListCache_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ShellTaskScheduler, CShellTaskScheduler_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_SharedTaskScheduler, CSharedTaskScheduler_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_BrowseuiPreloader, CBitmapPreload_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ShellSearchExt, CShellSearchExt_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_WebSearchExt, CWebSearchExt_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_OrderListExport, COrderList_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_UserAssist, CUserAssist_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_GlobalFolderSettings, CGlobalFolderSettings_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ProgressDialog, CProgressDialog_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ACLCustomMRU, CACLCustomMRU_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_AddressBarParser, CShellUrl_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY_NOFLAGS( &CLSID_MenuBand, CMenuBand_CreateInstance, COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl CF_TABLE_ENTRY_NOFLAGS( &CLSID_QuickLinks, CQuickLinks_CreateInstance, COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl CF_TABLE_ENTRY_NOFLAGS( &CLSID_ISFBand, CISFBand_CreateInstance, COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl CF_TABLE_ENTRY_NOFLAGS( &CLSID_Thumbnail, CThumbnail_CreateInstance, COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl CF_TABLE_ENTRY_NOFLAGS(&CLSID_TrackShellMenu, CTrackShellMenu_CreateInstance, COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl CF_TABLE_END(g_ObjectInfo) // constructor for CObjectInfo. CObjectInfo::CObjectInfo(CLSID const* pclsidin, LPFNCREATEOBJINSTANCE pfnCreatein, IID const* piidIn, IID const* piidEventsIn, long lVersionIn, DWORD dwOleMiscFlagsIn, DWORD dwClassFactFlagsIn) { pclsid = pclsidin; pfnCreateInstance = pfnCreatein; piid = piidIn; piidEvents = piidEventsIn; lVersion = lVersionIn; dwOleMiscFlags = dwOleMiscFlagsIn; dwClassFactFlags = dwClassFactFlagsIn; } // static class factory (no allocs!) STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void **ppvObj) { if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) { *ppvObj = (void *)GET_ICLASSFACTORY(this); DllAddRef(); return NOERROR; } *ppvObj = NULL; return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CClassFactory::AddRef() { DllAddRef(); return 2; } STDMETHODIMP_(ULONG) CClassFactory::Release() { DllRelease(); return 1; } STDMETHODIMP CClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { *ppv = NULL; if (punkOuter && !IsEqualIID(riid, IID_IUnknown)) { // It is technically illegal to aggregate an object and request // any interface other than IUnknown. Enforce this. // return CLASS_E_NOAGGREGATION; } else { LPOBJECTINFO pthisobj = (LPOBJECTINFO)this; if (punkOuter && !(pthisobj->dwClassFactFlags & OIF_ALLOWAGGREGATION)) return CLASS_E_NOAGGREGATION; IUnknown *punk; HRESULT hres = pthisobj->pfnCreateInstance(punkOuter, &punk, pthisobj); if (SUCCEEDED(hres)) { hres = punk->QueryInterface(riid, ppv); punk->Release(); } ASSERT(FAILED(hres) ? *ppv == NULL : TRUE); return hres; } } STDMETHODIMP CClassFactory::LockServer(BOOL fLock) { if (fLock) DllAddRef(); else DllRelease(); TraceMsg(DM_TRACE, "sccls: LockServer(%s) to %d", fLock ? TEXT("LOCK") : TEXT("UNLOCK"), g_cRefThisDll); return S_OK; } STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv) { TraceMsg(TF_SHDLIFE, "DllGetClassObject called with riid=%x (%x)", riid, &riid); if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) { for (LPCOBJECTINFO pcls = g_ObjectInfo; pcls->pclsid; pcls++) { if (IsEqualGUID(rclsid, *(pcls->pclsid))) { *ppv = (void*)pcls; DllAddRef(); // class factory holds DLL ref count return NOERROR; } } #ifdef ATL_ENABLED // Try the ATL class factory if (SUCCEEDED(AtlGetClassObject(rclsid, riid, ppv))) return NOERROR; #endif } *ppv = NULL; return CLASS_E_CLASSNOTAVAILABLE; } STDAPI DllCanUnloadNow(void) { #ifndef UNIX // special case for the system scheduler we hang onto if ( g_cRefThisDll == 1 && SHIsThereASystemScheduler() == S_OK ) { // this will drop the ref count by one to zero.... SHFreeSystemScheduler(); } #ifdef ATL_ENABLED if (0 != g_cRefThisDll || 0 != AtlGetLockCount()) return S_FALSE; #else if (0 != g_cRefThisDll) return S_FALSE; #endif #else if (g_cRefThisDll) return S_FALSE; #endif TraceMsg(DM_TRACE, "DllCanUnloadNow returning S_OK (bye, bye...)"); return S_OK; } // DllGetVersion // // All we have to do is declare this puppy and CCDllGetVersion does the rest // DLLVER_SINGLEBINARY(VER_PRODUCTVERSION_DW, VER_PRODUCTBUILD_QFE); UINT g_msgMSWheel; #ifdef DEBUG EXTERN_C DWORD g_TlsMem = 0xffffffff; #endif // imports from isfband.cpp STDAPI_(void) CLogoBase_Initialize( void ); STDAPI_(void) CLogoBase_Cleanup( void ); // // Table of all window classes we register so we can unregister them // at DLL unload. // const LPCTSTR c_rgszClasses[] = { TEXT("BaseBar"), // basebar.cpp TEXT("MenuSite"), // menusite.cpp DESKTOPPROXYCLASS, // proxy.cpp c_szExploreClass, // shbrows2.cpp c_szIExploreClass, // shbrows2.cpp c_szCabinetClass, // shbrows2.cpp c_szAutoSuggestClass, // autocomp.cpp TEXT("MediaPane"), //Mediaband.cpp TEXT("MediaPopupPane"), //Mediaband.cpp TEXT("MediaLayoutPane") //Mediaband.cpp }; // // Since we are single-binary, we have to play it safe and do // this cleanup (needed only on NT, but harmless on Win95). // #define UnregisterWindowClasses() \ SHUnregisterClasses(HINST_THISDLL, c_rgszClasses, ARRAYSIZE(c_rgszClasses)) void InitNFCtl() { INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_NATIVEFNTCTL_CLASS; InitCommonControlsEx(&icc); } const LPCTSTR s_aryExplorerFileName[] = { TEXT("iexplore.exe"), }; BOOL IsRootExeExplorer(void) { TCHAR szApp[MAX_PATH]; LPCTSTR pszApp; GetModuleFileName(NULL, szApp, ARRAYSIZE(szApp)); pszApp = PathFindFileName(szApp); if (pszApp) { for (int i = 0; i < ARRAYSIZE(s_aryExplorerFileName); i++) { if (!lstrcmpi(pszApp, s_aryExplorerFileName[i])) return TRUE; } } return FALSE; } // // we use shlwapi as our delayload error handler. // NOTE: this only works if we are statically linked to shlwapi! // void SetupDelayloadErrorHandler() { BaseDllHandle = GetModuleHandleA("shlwapi.dll"); ASSERTMSG(BaseDllHandle != NULL, "BROWSEUI must be statically linked to shlwapi.dll for delayload failure handling to work!"); __pfnDliFailureHook = (PfnDliHook)GetProcAddress((HMODULE)BaseDllHandle, "DelayLoadFailureHook"); } STDAPI_(BOOL) DllMain(HINSTANCE hDll, DWORD dwReason, void *fProcessUnload) { if (dwReason == DLL_PROCESS_ATTACH) { SHFusionInitializeFromModule(hDll); SetupDelayloadErrorHandler(); #ifdef ATL_ENABLED AtlInit(hDll); #endif DisableThreadLibraryCalls(hDll); // perf g_hinst = hDll; InitializeCriticalSection(&g_csDll); g_msgMSWheel = RegisterWindowMessage(TEXT("MSWHEEL_ROLLMSG")); MLLoadResources(g_hinst, TEXT("browselc.dll")); g_fIE = IsRootExeExplorer(); if (g_fIE) InitMUILanguage(MLGetUILanguage()); // Don't put it under #ifdef DEBUG CcshellGetDebugFlags(); #ifdef DEBUG g_TlsMem = TlsAlloc(); if (IsFlagSet(g_dwBreakFlags, BF_ONLOADED)) { TraceMsg(TF_ALWAYS, "DllMain() - SHDOCVW.DLL has just loaded"); DEBUG_BREAK; } #endif g_fRunningOnNT = IsOS(OS_NT); if (g_fRunningOnNT) { g_bRunOnNT5 = IsOS(OS_WIN2000ORGREATER); g_fRunOnWhistler = IsOS(OS_WHISTLERORGREATER); } else g_bRunOnMemphis = IsOS(OS_WIN98ORGREATER); g_fRunOnFE = GetSystemMetrics(SM_DBCSENABLED); g_bMirroredOS = IS_MIRRORING_ENABLED(); InitNFCtl(); // See if perfmode is enabled g_dwStopWatchMode = StopWatchMode(); // Cache a palette handle for use throughout shdocvw g_hpalHalftone = SHCreateShellPalette( NULL ); CLogoBase_Initialize( ); } else if (dwReason == DLL_PROCESS_DETACH) { #ifdef ATL_ENABLED AtlTerm(); #endif CBrandBand_CleanUp(); CInternetToolbar_CleanUp(); CUserAssist_CleanUp(dwReason, fProcessUnload); CLogoBase_Cleanup(); // let go of the resource DLL... MLFreeResources(g_hinst); ENTERCRITICAL; DESTROY_OBJ_WITH_HANDLE(g_hpalHalftone, DeletePalette); DESTROY_OBJ_WITH_HANDLE(g_hCabStateChange, SHGlobalCounterDestroy); DESTROY_OBJ_WITH_HANDLE(g_hMutexHistory, CloseHandle); DestroyZoneIconNameCache(); UnregisterWindowClasses(); LEAVECRITICAL; DeleteCriticalSection(&g_csDll); SHFusionUninitialize(); } return TRUE; } STDAPI_(void) DllAddRef(void) { InterlockedIncrement(&g_cRefThisDll); } STDAPI_(void) DllRelease(void) { InterlockedDecrement(&g_cRefThisDll); ASSERT(g_cRefThisDll >= 0); // don't underflow } // IEUNIX // CoCreateInstance is #defined as IECreateInstance #ifdef __cplusplus, // so I #undef it here to prevent the recursive call. // On Windows it works, because this file is C file. #ifdef CoCreateInstance #undef CoCreateInstance #endif HRESULT IECreateInstance(REFCLSID rclsid, IUnknown *pUnkOuter, DWORD dwClsContext, REFIID riid, void **ppv) { #ifndef NO_MARSHALLING if (dwClsContext == CLSCTX_INPROC_SERVER) #else if (dwClsContext & CLSCTX_INPROC_SERVER) #endif { LPCOBJECTINFO pcls; for (pcls = g_ObjectInfo; pcls->pclsid; pcls++) { // Note that we do pointer comparison (instead of IsEuqalGUID) if ((&rclsid == pcls->pclsid) && !(pcls->dwClassFactFlags & OIF_DONTIECREATE)) { // const -> non-const expclit casting (this is OK) IClassFactory* pcf = GET_ICLASSFACTORY(pcls); return pcf->CreateInstance(pUnkOuter, riid, ppv); } } } // Use SHCoCreateInstanceAC to go through the app compat layer return SHCoCreateInstanceAC(rclsid, pUnkOuter, dwClsContext, riid, ppv); } #ifdef DEBUG // // In DEBUG, make sure every class we register lives in the c_rgszClasses // table so we can clean up properly at DLL unload. NT does not automatically // unregister classes when a DLL unloads, so we have to do it manually. // STDAPI_(BOOL) SHRegisterClassD(CONST WNDCLASS *pwc) { for (int i = 0; i < ARRAYSIZE(c_rgszClasses); i++) { if (lstrcmpi(c_rgszClasses[i], pwc->lpszClassName) == 0) { return RealSHRegisterClass(pwc); } } AssertMsg(0, TEXT("Class %s needs to be added to the c_rgszClasses list"), pwc->lpszClassName); return 0; } STDAPI_(ATOM) RegisterClassD(CONST WNDCLASS *pwc) { for (int i = 0; i < ARRAYSIZE(c_rgszClasses); i++) { if (lstrcmpi(c_rgszClasses[i], pwc->lpszClassName) == 0) { return RealRegisterClass(pwc); } } AssertMsg(0, TEXT("Class %s needs to be added to the c_rgszClasses list"), pwc->lpszClassName); return 0; } // // In DEBUG, send FindWindow through a wrapper that ensures that the // critical section is not taken. FindWindow'ing for a window title // sends inter-thread WM_GETTEXT messages, which is not obvious. // STDAPI_(HWND) FindWindowD(LPCTSTR lpClassName, LPCTSTR lpWindowName) { return FindWindowExD(NULL, NULL, lpClassName, lpWindowName); } STDAPI_(HWND) FindWindowExD(HWND hwndParent, HWND hwndChildAfter, LPCTSTR lpClassName, LPCTSTR lpWindowName) { if (lpWindowName) { ASSERTNONCRITICAL; } return RealFindWindowEx(hwndParent, hwndChildAfter, lpClassName, lpWindowName); } #endif // DEBUG