// shimgvw.cpp : Implementation of DLL Exports. #include "precomp.h" #include "resource.h" #include "cfdefs.h" #include "advpub.h" #include "initguid.h" #include "shimgvw.h" #include "guids.h" #include "shutil.h" #include #define DECLARE_DEBUG #define SZ_DEBUGINI "ccshell.ini" #define SZ_DEBUGSECTION "Shell Image View" #define SZ_MODULE "SHIMGVW" #include #define DECL_CRTFREE #include #include "prevCtrl.h" // for CPreview #define IIDSTR_IExtractImage "{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}" #define REGSTR_APPROVED "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved" LPCSTR const c_rgszDocFileExts[] = { ".doc", ".dot", ".xls", ".xlt", ".obd", ".obt", ".ppt", ".pot", ".mic", ".mix", ".fpx", ".mpp" }; LPCSTR const c_rgszHtmlExts[] = { ".html", ".htm", ".url", ".mhtml", ".mht", ".xml", ".nws", ".eml" }; CComModule _Module; BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_Preview, CPreview) OBJECT_ENTRY(CLSID_GdiThumbnailExtractor, CGdiPlusThumb) OBJECT_ENTRY(CLSID_DocfileThumbnailHandler, CDocFileThumb) OBJECT_ENTRY(CLSID_HtmlThumbnailExtractor, CHtmlThumb) END_OBJECT_MAP() CF_TABLE_BEGIN(g_ObjectInfo) CF_TABLE_ENTRY(&CLSID_ShellImageDataFactory, CImageDataFactory_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_PhotoVerbs, CPhotoVerbs_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_AutoplayForSlideShow, CAutoplayForSlideShow_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ImagePropertyHandler, CImageData_CreateInstance, COCREATEONLY), CF_TABLE_ENTRY(&CLSID_ImageRecompress, CImgRecompress_CreateInstance, COCREATEONLY), CF_TABLE_END(g_ObjectInfo) ///////////////////////////////////////////////////////////////////////////// // DLL Entry Point extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { SHFusionInitializeFromModule(hInstance); _Module.Init(ObjectMap, hInstance); DisableThreadLibraryCalls(hInstance); } else if (dwReason == DLL_PROCESS_DETACH) { _Module.Term(); SHFusionUninitialize(); } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // Used to determine whether the DLL can be unloaded by OLE STDAPI DllCanUnloadNow(void) { return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; } // some types can only wallpaper on win32 #ifdef _WIN64 #define IMAGEOPTION_CANWALLPAPER_WIN32 0 #else #define IMAGEOPTION_CANWALLPAPER_WIN32 IMAGEOPTION_CANWALLPAPER #endif #define IMGOPT_ALL (IMAGEOPTION_CANWALLPAPER | IMAGEOPTION_CANROTATE) #define IMGOPT_ALLW32 (IMAGEOPTION_CANWALLPAPER_WIN32 | IMAGEOPTION_CANROTATE) #define IMGOPT_NONE 0 ///////////////////////////////////////////////////////////////////////////// // Returns a class factory to create an object of the requested type typedef struct { LPTSTR szExt; LPTSTR pszContentType; LPTSTR szProgId; LPTSTR szProgram; DWORD dwOpts;} FILETYPEINFO; // { ".EXT", "progid", old program, IMGOPT FILETYPEINFO g_rgExtentions[] = { { TEXT(".bmp"), TEXT("image/bmp"), TEXT("Paint.Picture"), TEXT("mspaint.exe"), IMGOPT_ALL, }, { TEXT(".dib"), TEXT("image/bmp"), TEXT("Paint.Picture"), TEXT("mspaint.exe"), IMGOPT_ALL, }, { TEXT(".emf"), NULL, TEXT("emffile"), TEXT(""), IMGOPT_NONE, }, { TEXT(".gif"), TEXT("image/gif"), TEXT("giffile"), TEXT("iexplore.exe"), IMGOPT_ALLW32 }, { TEXT(".jfif"),TEXT("image/jpeg"), TEXT("pjpegfile"), TEXT("iexplore.exe"), IMGOPT_ALLW32 }, { TEXT(".jpg"), TEXT("image/jpeg"), TEXT("jpegfile"), TEXT("iexplore.exe"), IMGOPT_ALLW32 }, { TEXT(".jpe"), TEXT("image/jpeg"), TEXT("jpegfile"), TEXT("iexplore.exe"), IMGOPT_ALLW32 }, { TEXT(".jpeg"),TEXT("image/jpeg"), TEXT("jpegfile"), TEXT("iexplore.exe"), IMGOPT_ALLW32 }, { TEXT(".png"), TEXT("image/png"), TEXT("pngfile"), TEXT("iexplore.exe"), IMGOPT_ALLW32 }, { TEXT(".tif"), TEXT("image/tiff"), TEXT("TIFImage.Document"), TEXT("KodakPrv.exe"), IMGOPT_NONE, }, { TEXT(".tiff"),TEXT("image/tiff"), TEXT("TIFImage.Document"), TEXT("KodakPrv.exe"), IMGOPT_NONE, }, { TEXT(".wmf"), NULL, TEXT("wmffile"), TEXT(""), IMGOPT_NONE, }, }; const TCHAR c_szBitmapIcon[] = TEXT("shimgvw.dll,1"); const TCHAR c_szDefaultIcon[] = TEXT("shimgvw.dll,2"); const TCHAR c_szJPegIcon[] = TEXT("shimgvw.dll,3"); const TCHAR c_szTifIcon[] = TEXT("shimgvw.dll,4"); // we might have to create a missing Prog ID so we need to know its default info. typedef struct { LPCTSTR szProgID; int iResId; LPCTSTR szIcon; BOOL fDone; } PROGIDINFO; // { "ProgID" "Description" "DefaultIcon" done? } PROGIDINFO g_rgProgIDs[] = { { TEXT("emffile"), IDS_EMFIMAGE, c_szDefaultIcon, FALSE }, { TEXT("giffile"), IDS_GIFIMAGE, c_szDefaultIcon, FALSE }, { TEXT("jpegfile"), IDS_JPEGIMAGE, c_szJPegIcon, FALSE }, { TEXT("Paint.Picture"), IDS_BITMAPIMAGE, c_szBitmapIcon, FALSE }, { TEXT("pjpegfile"), IDS_JPEGIMAGE, c_szJPegIcon, FALSE }, { TEXT("pngfile"), IDS_PNGIMAGE, c_szDefaultIcon, FALSE }, { TEXT("TIFImage.Document"),IDS_TIFIMAGE, c_szTifIcon, FALSE }, { TEXT("wmffile"), IDS_WMFIMAGE, c_szDefaultIcon, FALSE }, }; BOOL _ShouldSlamVerb(HKEY hkProgid, BOOL fForce, PCWSTR pszKey, PCWSTR pszApp, PCWSTR pszModule) { if (!fForce) { TCHAR szOld[MAX_PATH*2]; DWORD cbOld = sizeof(szOld); if (ERROR_SUCCESS == SHGetValue(hkProgid, pszKey, NULL, NULL, szOld, &cbOld) && *szOld) { // if we know about this app, then blow it away if ((*pszApp && StrStrI(szOld, pszApp)) || StrStrI(szOld, pszModule) || StrStrI(szOld, TEXT("wangimg.exe"))) // NT4 app { fForce = TRUE; } } else { fForce = TRUE; } } return fForce; } BOOL _ExtIsProgid(PCTSTR pszExt, PCTSTR pszProgid, DWORD dwOpts) { // default to take-over BOOL fRet = TRUE; TCHAR sz[MAX_PATH]; // make sure this is in the openwith list wnsprintf(sz, ARRAYSIZE(sz), TEXT("%s\\OpenWithProgids"), pszExt); SHSetValue(HKEY_CLASSES_ROOT, sz, pszProgid, REG_NONE, NULL, NULL); // make sure the flags are set for our verbs to show up wnsprintf(sz, ARRAYSIZE(sz), TEXT("SystemFileAssociations\\%s"), pszExt); SHSetValue(HKEY_CLASSES_ROOT, sz, TEXT("ImageOptionFlags"), REG_DWORD, &dwOpts, sizeof(dwOpts)); SHSetValue(HKEY_CLASSES_ROOT, pszExt, TEXT("PerceivedType"), REG_SZ, TEXT("image"), sizeof(TEXT("image"))); DWORD cb = sizeof(sz); if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, sz, &cb)) { // empty or match is good fRet = (!*sz || 0 == StrCmpI(sz, pszProgid)); } // always remove bogus Trident IExtractImage entries wnsprintf(sz, ARRAYSIZE(sz), TEXT("%s\\shellex\\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}"), pszExt); SHDeleteKey(HKEY_CLASSES_ROOT, sz); PathRemoveFileSpec(sz); SHDeleteEmptyKey(HKEY_CLASSES_ROOT, sz); return fRet; } PROGIDINFO *_ShouldSetupProgid(FILETYPEINFO *pfti, BOOL fForce) { PROGIDINFO *ppi = NULL; if (_ExtIsProgid(pfti->szExt, pfti->szProgId, pfti->dwOpts) || fForce) { // take it over SHSetValue(HKEY_CLASSES_ROOT, pfti->szExt, NULL, REG_SZ, pfti->szProgId, CbFromCch(lstrlen(pfti->szProgId)+1)); if (pfti->pszContentType) { SHSetValue(HKEY_CLASSES_ROOT, pfti->szExt, TEXT("Content Type"), REG_SZ, pfti->pszContentType, CbFromCch(lstrlen(pfti->pszContentType)+1)); } // we now know that szProgID is the ProgID for this extention. // look up the index into the ProgID table to see if we did this one already. int iProgIdIndex; for (iProgIdIndex=0; iProgIdIndexszProgId)) { if (!g_rgProgIDs[iProgIdIndex].fDone) ppi = &g_rgProgIDs[iProgIdIndex]; break; } } } return ppi; } void RegisterFileType(FILETYPEINFO *pfti, BOOL fForce) { PROGIDINFO *ppi = _ShouldSetupProgid(pfti, fForce); if (ppi) { // this ProgID is in our table HKEY hkeyProgId; LRESULT lres = RegCreateKeyEx(HKEY_CLASSES_ROOT, pfti->szProgId, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE, NULL, &hkeyProgId, NULL); if (ERROR_SUCCESS == lres) { LPCTSTR pszIcon = ppi->szIcon; TCHAR szModPath[MAX_PATH]; GetModuleFileName(_Module.GetModuleInstance(), szModPath, ARRAYSIZE(szModPath)); PCWSTR pszModule = PathFindFileName(szModPath); TCHAR szCmd[MAX_PATH * 2]; // should we slam the default value? // to progid = "EXT file" if (_ShouldSlamVerb(hkeyProgId, fForce, L"shell\\open\\command", pfti->szProgram, pszModule)) { SHDeleteKey(hkeyProgId, TEXT("shell\\open")); wnsprintf(szCmd, ARRAYSIZE(szCmd), TEXT("rundll32.exe %s,ImageView_Fullscreen %%1"), szModPath); SHRegSetPath(hkeyProgId, TEXT("shell\\open\\command"), NULL, szCmd, 0); SHStringFromGUID(CLSID_PhotoVerbs, szCmd, ARRAYSIZE(szCmd)); SHSetValue(hkeyProgId, TEXT("shell\\open\\DropTarget"), TEXT("Clsid"), REG_SZ, szCmd, CbFromCch(lstrlen(szCmd)+1)); wnsprintf(szCmd, ARRAYSIZE(szCmd), TEXT("@%s,%d"), pszModule, -IDS_PREVIEW_CTX); SHSetValue(hkeyProgId, TEXT("shell\\open"), TEXT("MuiVerb"), REG_SZ, szCmd, CbFromCch(lstrlen(szCmd)+1)); SHRegSetPath(hkeyProgId, TEXT("DefaultIcon"), NULL, pszIcon, 0); } if (_ShouldSlamVerb(hkeyProgId, fForce, L"shell\\printto\\command", TEXT("mspaint.exe"), pszModule)) { SHDeleteKey(hkeyProgId, TEXT("shell\\printto")); wnsprintf(szCmd, ARRAYSIZE(szCmd), TEXT("rundll32.exe %s,ImageView_PrintTo /pt \"%%1\" \"%%2\" \"%%3\" \"%%4\""), szModPath); SHRegSetPath(hkeyProgId, TEXT("shell\\printto\\command"), NULL, szCmd, 0); } // this will delete print verb // print is added in selfreg under HKCR\SystemFileAssociations\image\Print if (_ShouldSlamVerb(hkeyProgId, fForce, L"shell\\print\\command", TEXT("mspaint.exe"), pszModule)) { SHDeleteKey(hkeyProgId, TEXT("shell\\print")); } // Modify the EditFlags: it's okay to run these without prompting... DWORD dwEditFlags = 0; DWORD cbEditFlags = sizeof(dwEditFlags); SHGetValue(hkeyProgId, NULL, TEXT("EditFlags"), NULL, &dwEditFlags, &cbEditFlags); dwEditFlags |= 0x00010000; // turn on the "okay to run without prompt" flag SHSetValue(hkeyProgId, NULL, TEXT("EditFlags"), REG_DWORD, &dwEditFlags, sizeof(dwEditFlags)); RegCloseKey(hkeyProgId); } ppi->fDone = TRUE; } } HRESULT _CallRegInstall(LPCSTR szSection, BOOL bUninstall) { HRESULT hr = E_FAIL; HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL")); if (hinstAdvPack) { REGINSTALL pfnri = (REGINSTALL)GetProcAddress(hinstAdvPack, "RegInstall"); if (pfnri) { STRENTRY seReg[] = { { "25", "%SystemRoot%" }, { "11", "%SystemRoot%\\system32" }, }; STRTABLE stReg = { ARRAYSIZE(seReg), seReg }; hr = pfnri(_Module.GetModuleInstance(), szSection, &stReg); if (bUninstall) { // ADVPACK will return E_UNEXPECTED if you try to uninstall // (which does a registry restore) on an INF section that was // never installed. We uninstall sections that may never have // been installed, so ignore this error hr = ((E_UNEXPECTED == hr) ? S_OK : hr); } } FreeLibrary(hinstAdvPack); } return hr; } void _FixupProgid(PCSTR pszExt, PCSTR pszProgid) { HKEY hk; DWORD dwRet = RegOpenKeyExA(HKEY_CLASSES_ROOT, pszExt, 0, KEY_QUERY_VALUE, &hk); if (dwRet == ERROR_SUCCESS) { // if its empty, then we need to fix it up. CHAR sz[MAX_PATH]; DWORD cb = sizeof(sz); if (ERROR_SUCCESS == SHGetValueA(hk, NULL, NULL, NULL, sz, &cb) && !*sz) { SHSetValueA(hk, NULL, NULL, REG_SZ, pszProgid, CbFromCchA(lstrlenA(pszProgid)+1)); } } } HRESULT RegisterHandler(const LPCSTR *ppszExts, UINT cExts, LPCSTR pszIID, LPCSTR pszCLSID) { for (UINT cKey = 0; cKey < cExts; cKey ++) { CHAR szKey[MAX_PATH]; wnsprintfA(szKey, ARRAYSIZE(szKey), "SystemFileAssociations\\%s\\shellex\\%s", *ppszExts, pszIID); SHDeleteKeyA(HKEY_CLASSES_ROOT, szKey); SHSetValueA(HKEY_CLASSES_ROOT, szKey, NULL, REG_SZ, pszCLSID, CbFromCch(lstrlenA(pszCLSID)+1)); ppszExts++; } return S_OK; } HRESULT UnregisterHandler(const LPCSTR *ppszExts, UINT cExts, LPCSTR pszIID, LPCSTR pszCLSID) { for (UINT cKey = 0; cKey < cExts; cKey ++) { CHAR szKey[MAX_PATH]; CHAR szCLSID[256]; DWORD dwSize = sizeof(szCLSID); wnsprintfA(szKey, ARRAYSIZE(szKey), "SystemFileAssociations\\%s\\shellex\\%s", *ppszExts, pszIID); if (ERROR_SUCCESS == SHGetValueA(HKEY_CLASSES_ROOT, szKey, NULL, NULL, &szCLSID, &dwSize)) { if (!StrCmpIA(szCLSID, pszCLSID)) { SHDeleteKeyA(HKEY_CLASSES_ROOT, szKey); } } dwSize = sizeof(szCLSID); wnsprintfA(szKey, ARRAYSIZE(szKey), "%s\\shellex\\%s", *ppszExts, pszIID); if (ERROR_SUCCESS == SHGetValueA(HKEY_CLASSES_ROOT, szKey, NULL, NULL, &szCLSID, &dwSize)) { if (!StrCmpIA(szCLSID, pszCLSID)) { SHDeleteKeyA(HKEY_CLASSES_ROOT, szKey); } } ppszExts++; } return S_OK; } STDAPI DllRegisterServer(void) { // REVIEW: should this be done only in DLLInstall? for (int i=0; idwClassFactFlags & 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(); } return hres; } } STDMETHODIMP CClassFactory::LockServer(BOOL fLock) { if (fLock) _Module.Lock(); else _Module.Unlock(); return S_OK; } // Object constructor STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { // handle non-ATL objects first 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; _Module.Lock(); return S_OK; } } } // Try the ATL way.... return _Module.GetClassObject(rclsid, riid, ppv); }