// // // assocapi.cpp // // Association APIs // // // #include "priv.h" #include "apithk.h" #include #include #include #include "ids.h" #include "assoc.h" #define ISEXTENSION(psz) (TEXT('.') == *(psz)) #define ISGUID(psz) (TEXT('{') == *(psz)) inline BOOL IsEmptyStr(SHSTR &str) { return (!*(LPCTSTR)str); } HRESULT _AssocGetRegString(HKEY hk, LPCTSTR pszSub, LPCTSTR pszVal, SHSTR &strOut) { if (!hk) { return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); } DWORD cbOut = CbFromCch(strOut.GetSize()); DWORD err = SHGetValue(hk, pszSub, pszVal, NULL, strOut.GetInplaceStr(), &cbOut); if (err == ERROR_SUCCESS) return S_OK; // else try to resize the buffer if (cbOut > CbFromCch(strOut.GetSize())) { strOut.SetSize(cbOut / sizeof(TCHAR)); err = SHGetValue(hk, pszSub, pszVal, NULL, strOut.GetInplaceStr(), &cbOut); } return HRESULT_FROM_WIN32(err); } HRESULT _AssocGetRegUIString(HKEY hk, LPCTSTR pszSub, LPCTSTR pszVal, SHSTR &strOut) { if (!hk) return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); HKEY hkSub; DWORD err; HRESULT hres; err = RegOpenKeyEx(hk, pszSub, 0, MAXIMUM_ALLOWED, &hkSub); if (err == ERROR_SUCCESS) { // Unfortunately, SHLoadRegUIString doesn't have a way to query the // buffer size, so we just have to assume INFOTIPSIZE is good enough. LPTSTR pszOut = strOut.GetModifyableStr(INFOTIPSIZE); if (pszOut == NULL) pszOut = strOut.GetInplaceStr(); hres = SHLoadRegUIString(hkSub, pszVal, pszOut, strOut.GetSize()); RegCloseKey(hkSub); } else { hres = HRESULT_FROM_WIN32(err); } return hres; } HRESULT _AssocGetRegData(HKEY hk, LPCTSTR pszSubKey, LPCTSTR pszValue, LPDWORD pdwType, LPBYTE pbOut, LPDWORD pcbOut) { if (!hk) return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); DWORD err; if (pszSubKey || pbOut || pcbOut || pdwType) err = SHGetValue(hk, pszSubKey, pszValue, pdwType, pbOut, pcbOut); else err = RegQueryValueEx(hk, pszValue, NULL, NULL, NULL, NULL); return HRESULT_FROM_WIN32(err); } BOOL _GetAppPath(LPCTSTR pszApp, SHSTR& strPath) { TCHAR sz[MAX_PATH]; _MakeAppPathKey(pszApp, sz, SIZECHARS(sz)); return SUCCEEDED(_AssocGetRegString(HKEY_LOCAL_MACHINE, sz, NULL, strPath)); } // // THE NEW WAY! // class CAssocW2k : public IQueryAssociations { public: STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef () ; STDMETHODIMP_(ULONG) Release (); // IQueryAssociations methods STDMETHODIMP Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd); STDMETHODIMP GetString(ASSOCF flags, ASSOCSTR str, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut); STDMETHODIMP GetKey(ASSOCF flags, ASSOCKEY, LPCWSTR pszExtra, HKEY *phkeyOut); STDMETHODIMP GetData(ASSOCF flags, ASSOCDATA data, LPCWSTR pszExtra, LPVOID pvOut, DWORD *pcbOut); STDMETHODIMP GetEnum(ASSOCF flags, ASSOCENUM assocenum, LPCWSTR pszExtra, REFIID riid, LPVOID *ppvOut); CAssocW2k(); protected: virtual ~CAssocW2k(); // static methods static HRESULT _CopyOut(BOOL fNoTruncate, SHSTR& str, LPTSTR psz, DWORD *pcch); static void _DefaultShellVerb(HKEY hk, LPTSTR pszVerb, DWORD cchVerb, HKEY *phkOut); typedef enum { KEYCACHE_INVALID = 0, KEYCACHE_HKCU = 1, KEYCACHE_HKLM, KEYCACHE_APP, KEYCACHE_FIXED, PSZCACHE_BASE, PSZCACHE_HKCU = PSZCACHE_BASE + KEYCACHE_HKCU, PSZCACHE_HKLM, PSZCACHE_APP, PSZCACHE_FIXED, } KEYCACHETYPE; typedef struct { LPTSTR pszCache; HKEY hkCache; LPTSTR psz; KEYCACHETYPE type; } KEYCACHE; static BOOL _CanUseCache(KEYCACHE &kc, LPCTSTR psz, KEYCACHETYPE type); static void _CacheFree(KEYCACHE &kc); static void _CacheKey(KEYCACHE &kc, HKEY hkCache, LPCTSTR pszName, KEYCACHETYPE type); static void _CacheString(KEYCACHE &kc, LPCTSTR pszCache, LPCTSTR pszName, KEYCACHETYPE type); void _Reset(void); BOOL _UseBaseClass(void); // // retrieve the appropriate cached keys // HKEY _RootKey(BOOL fForceLM); HKEY _AppKey(LPCTSTR pszApp, BOOL fCreate = FALSE); HKEY _ExtensionKey(BOOL fForceLM); HKEY _OpenProgidKey(LPCTSTR pszProgid); HKEY _ProgidKey(BOOL fDefaultToExtension); HKEY _UserProgidKey(void); HKEY _ClassKey(BOOL fForceLM); HKEY _ShellVerbKey(HKEY hkey, KEYCACHETYPE type, LPCTSTR pszVerb); HKEY _ShellVerbKey(BOOL fForceLM, LPCTSTR pszVerb); HKEY _ShellNewKey(HKEY hkExt); HKEY _ShellNewKey(BOOL fForceLM); HKEY _DDEKey(BOOL fForceLM, LPCTSTR pszVerb); // // actual worker routines // HRESULT _GetCommandString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut); HRESULT _ParseCommand(ASSOCF flags, LPCTSTR pszCommand, SHSTR& strExe, PSHSTR pstrArgs); HRESULT _GetExeString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut); HRESULT _GetFriendlyAppByVerb(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut); HRESULT _GetFriendlyAppByApp(LPCTSTR pszApp, ASSOCF flags, SHSTR& strOut); HRESULT _GetFriendlyAppString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut); HRESULT _GetTipString(LPCWSTR pwszValueName, BOOL fForceLM, SHSTR& strOut); HRESULT _GetInfoTipString(BOOL fForceLM, SHSTR& strOut); HRESULT _GetQuickTipString(BOOL fForceLM, SHSTR& strOut); HRESULT _GetTileInfoString(BOOL fForceLM, SHSTR& strOut); HRESULT _GetWebViewDisplayPropsString(BOOL fForceLM, SHSTR& strOut); HRESULT _GetShellNewValueString(BOOL fForceLM, BOOL fQueryOnly, LPCTSTR pszValue, SHSTR& strOut); HRESULT _GetDDEApplication(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut); HRESULT _GetDDETopic(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut); HRESULT _GetContentType(SHSTR& strOut); HRESULT _GetMSIDescriptor(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, LPBYTE pbOut, LPDWORD pcbOut); HRESULT _GetShellExecKey(ASSOCF flags, BOOL fForceLM, LPCWSTR pszVerb, HKEY *phkey); HRESULT _CloneKey(HKEY hk, HKEY *phkey); HRESULT _GetShellExtension(ASSOCF flags, BOOL fForceLM, LPCTSTR pszShellEx, SHSTR& strOut); HRESULT _GetFriendlyDocName(SHSTR& strOut); // // Members // LONG _cRef; TCHAR _szInit[MAX_PATH]; ASSOCF _assocfBaseClass; HWND _hwndInit; BITBOOL _fInited:1; BITBOOL _fAppOnly:1; BITBOOL _fAppPath:1; BITBOOL _fInitedBaseClass:1; BITBOOL _fIsClsid:1; BITBOOL _fNoRemapClsid:1; BITBOOL _fBaseClassOnly:1; HKEY _hkFileExtsCU; HKEY _hkExtensionCU; HKEY _hkExtensionLM; KEYCACHE _kcProgid; KEYCACHE _kcShellVerb; KEYCACHE _kcApp; KEYCACHE _kcCommand; KEYCACHE _kcExecutable; KEYCACHE _kcShellNew; KEYCACHE _kcDDE; IQueryAssociations *_pqaBaseClass; }; CAssocW2k::CAssocW2k() : _cRef(1) { }; HRESULT CAssocW2k::Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd) { // // pszAssoc can be: // .Ext // Detectable // {Clsid} // Detectable // Progid // Ambiguous // Default! // ExeName // Ambiguous // Requires ASSOCF_OPEN_BYEXENAME // MimeType // Ambiguous // NOT IMPLEMENTED... // if (!pszAssoc && !hkProgid) return E_INVALIDARG; HKEY hk = NULL; if (_fInited) _Reset(); if (pszAssoc) { _fAppOnly = BOOLIFY(flags & ASSOCF_OPEN_BYEXENAME); if (StrChr(pszAssoc, TEXT('\\'))) { // this is a path if (_fAppOnly) _fAppPath = TRUE; else { // we need the extension pszAssoc = PathFindExtension(pszAssoc); if (!*pszAssoc) pszAssoc = NULL; } } if (pszAssoc && *pszAssoc) { if (ISGUID(pszAssoc)) { _PathAppend(TEXT("CLSID"), pszAssoc, _szInit, SIZECHARS(_szInit)); _fIsClsid = TRUE; // for legacy reasons we dont always // want to remap the clsid. if (flags & ASSOCF_INIT_NOREMAPCLSID) _fNoRemapClsid = TRUE; } else { StrCpyN(_szInit , pszAssoc, SIZECHARS(_szInit)); // if we initializing to folder dont default to folder. if (0 == StrCmpI(_szInit, TEXT("Folder"))) flags &= ~ASSOCF_INIT_DEFAULTTOFOLDER; } hk = _ClassKey(FALSE); } else if (flags & ASSOCF_INIT_DEFAULTTOSTAR) { // this is a file without an extension // but we still allow file association on HKCR\. _szInit[0] = '.'; _szInit[1] = 0; hk = _ClassKey(FALSE); } } else { ASSERT(hkProgid); hk = SHRegDuplicateHKey(hkProgid); if (hk) _CacheKey(_kcProgid, hk, NULL, KEYCACHE_FIXED); } _assocfBaseClass = (flags & (ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_DEFAULTTOSTAR)); // // NOTE - we can actually do some work even if // we were unable to create the applications // key. so we want to succeed in the case // of an app only association. // if (hk || _fAppOnly) { _fInited = TRUE; return S_OK; } else if (_UseBaseClass()) { _fBaseClassOnly = TRUE; _fInited = TRUE; return S_OK; } return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); } CAssocW2k::~CAssocW2k() { _Reset(); } #define REGFREE(hk) if (hk) { RegCloseKey(hk); (hk) = NULL; } else { } void CAssocW2k::_Reset(void) { _CacheFree(_kcProgid); _CacheFree(_kcApp); _CacheFree(_kcShellVerb); _CacheFree(_kcCommand); _CacheFree(_kcExecutable); _CacheFree(_kcShellNew); _CacheFree(_kcDDE); REGFREE(_hkFileExtsCU); REGFREE(_hkExtensionCU); REGFREE(_hkExtensionLM); *_szInit = 0; _assocfBaseClass = 0; _hwndInit = NULL; _fInited = FALSE; _fAppOnly = FALSE; _fAppPath = FALSE; _fInitedBaseClass = FALSE; _fIsClsid = FALSE; _fBaseClassOnly = FALSE; ATOMICRELEASE(_pqaBaseClass); } STDMETHODIMP CAssocW2k::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CAssocW2k, IQueryAssociations), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CAssocW2k::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CAssocW2k::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } BOOL CAssocW2k::_UseBaseClass(void) { if (!_pqaBaseClass && !_fInitedBaseClass) { // try to init the base class IQueryAssociations *pqa; AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa)); if (pqa) { SHSTR strBase; if (_fInited && SUCCEEDED(_AssocGetRegString(_ClassKey(TRUE), NULL, TEXT("BaseClass"), strBase))) { if (SUCCEEDED(pqa->Init(_assocfBaseClass, strBase, NULL, _hwndInit))) _pqaBaseClass = pqa; } if (!_pqaBaseClass) { if ((_assocfBaseClass & ASSOCF_INIT_DEFAULTTOFOLDER) && (SUCCEEDED(pqa->Init(0, L"Folder", NULL, _hwndInit)))) _pqaBaseClass = pqa; else if ((_assocfBaseClass & ASSOCF_INIT_DEFAULTTOSTAR) && (SUCCEEDED(pqa->Init(0, L"*", NULL, _hwndInit)))) _pqaBaseClass = pqa; } // if we couldnt init the BaseClass, then kill the pqa if (!_pqaBaseClass) pqa->Release(); } _fInitedBaseClass = TRUE; } return (_pqaBaseClass != NULL); } HRESULT CAssocW2k::_CopyOut(BOOL fNoTruncate, SHSTR& str, LPTSTR psz, DWORD *pcch) { // if caller doesnt want any return size, // the incoming pointer is actually the size of the buffer ASSERT(pcch); ASSERT(psz || !IS_INTRESOURCE(pcch)); HRESULT hr; DWORD cch = IS_INTRESOURCE(pcch) ? PtrToUlong(pcch) : *pcch; DWORD cchStr = str.GetLen(); if (psz) { if (!fNoTruncate || cch > cchStr) { StrCpyN(psz, str, cch); hr = S_OK; } else hr = E_POINTER; } else hr = S_FALSE; // return the number of chars written/required if (!IS_INTRESOURCE(pcch)) *pcch = (hr == S_OK) ? lstrlen(psz) + 1 : cchStr + 1; return hr; } BOOL CAssocW2k::_CanUseCache(KEYCACHE &kc, LPCTSTR psz, KEYCACHETYPE type) { if (KEYCACHE_FIXED == kc.type) return TRUE; if (KEYCACHE_INVALID != kc.type && type == kc.type) { return ((!psz && !kc.psz) || (psz && kc.psz && 0 == StrCmpC(psz, kc.psz))); } return FALSE; } void CAssocW2k::_CacheFree(KEYCACHE &kc) { if (kc.pszCache) LocalFree(kc.pszCache); if (kc.hkCache) RegCloseKey(kc.hkCache); if (kc.psz) LocalFree(kc.psz); ZeroMemory(&kc, sizeof(kc)); } void CAssocW2k::_CacheKey(KEYCACHE &kc, HKEY hkCache, LPCTSTR pszName, KEYCACHETYPE type) { _CacheFree(kc); ASSERT(hkCache); kc.hkCache = hkCache; if (pszName) kc.psz = StrDup(pszName); if (!pszName || kc.psz) kc.type = type; } void CAssocW2k::_CacheString(KEYCACHE &kc, LPCTSTR pszCache, LPCTSTR pszName, KEYCACHETYPE type) { _CacheFree(kc); ASSERT(pszCache && *pszCache); kc.pszCache = StrDup(pszCache); if (kc.pszCache) { if (pszName) kc.psz = StrDup(pszName); if (!pszName || kc.psz) kc.type = type; } } void CAssocW2k::_DefaultShellVerb(HKEY hk, LPTSTR pszVerb, DWORD cchVerb, HKEY *phkOut) { // default to "open" BOOL fDefaultSpecified = FALSE; TCHAR sz[MAX_PATH]; DWORD cb = sizeof(sz); *phkOut = NULL; // see if something is specified... if (ERROR_SUCCESS == SHGetValue(hk, TEXT("shell"), NULL, NULL, (LPVOID)sz, &cb) && *sz) fDefaultSpecified = TRUE; else StrCpy(sz, TEXT("open")); HKEY hkShell; if (SUCCEEDED(_AssocOpenRegKey(hk, TEXT("shell"), &hkShell))) { HKEY hkVerb; if (FAILED(_AssocOpenRegKey(hkShell, sz, &hkVerb))) { if (fDefaultSpecified) { // try to find one of the ordered verbs int c = StrCSpn(sz, TEXT(" ,")); sz[c] = 0; _AssocOpenRegKey(hkShell, sz, &hkVerb); } else { // otherwise just use the first key we find.... cb = SIZECHARS(sz); if (ERROR_SUCCESS == RegEnumKeyEx(hkShell, 0, sz, &cb, NULL, NULL, NULL, NULL)) _AssocOpenRegKey(hkShell, sz, &hkVerb); } } if (hkVerb) { if (phkOut) *phkOut = hkVerb; else RegCloseKey(hkVerb); } RegCloseKey(hkShell); } if (pszVerb) StrCpyN(pszVerb, sz, cchVerb); } HKEY CAssocW2k::_RootKey(BOOL fForceLM) { // // this is one of the few places where there is no fallback to LM // if there is no CU, then we return NULL // we need to use a local for CU, but we can use a global for LM // if (!fForceLM) { if (!_hkFileExtsCU) { _AssocOpenRegKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts"), &_hkFileExtsCU); } return _hkFileExtsCU; } return HKEY_CLASSES_ROOT; } HKEY CAssocW2k::_AppKey(LPCTSTR pszApp, BOOL fCreate) { // right now we should only get NULL for the pszApp // when we are initialized with an EXE. ASSERT(_fAppOnly || pszApp); // else if (!_fAppOnly) TODO: handle getting it from default verb...or not if (!pszApp) pszApp = _fAppPath ? PathFindFileName(_szInit) : _szInit; if (_CanUseCache(_kcApp, pszApp, KEYCACHE_APP)) return _kcApp.hkCache; else { HKEY hk; TCHAR sz[MAX_PATH]; _MakeApplicationsKey(pszApp, sz, SIZECHARS(sz)); _AssocOpenRegKey(HKEY_CLASSES_ROOT, sz, &hk, fCreate); if (hk) { _CacheKey(_kcApp, hk, pszApp, KEYCACHE_APP); } return hk; } } HKEY CAssocW2k::_ExtensionKey(BOOL fForceLM) { if (_fAppOnly) return _AppKey(NULL); if (!ISEXTENSION(_szInit) && !_fIsClsid) return NULL; if (!fForceLM) { if (!_hkExtensionCU) _AssocOpenRegKey(_RootKey(FALSE), _szInit, &_hkExtensionCU); // NOTE there is no fallback here return _hkExtensionCU; } if (!_hkExtensionLM) _AssocOpenRegKey(_RootKey(TRUE), _szInit, &_hkExtensionLM); return _hkExtensionLM; } HKEY CAssocW2k::_OpenProgidKey(LPCTSTR pszProgid) { HKEY hkOut; if (SUCCEEDED(_AssocOpenRegKey(_RootKey(TRUE), pszProgid, &hkOut))) { // Check for a newer version of the ProgID TCHAR sz[MAX_PATH]; DWORD cb = sizeof(sz); // // APPCOMPAT LEGACY - Quattro Pro 2000 and Excel 2000 dont get along - ZekeL - 7-MAR-2000 // mill bug #129525. the problem is if Quattro is installed // first, then excel picks up quattro's CurVer key for some // reason. then we end up using Quattro.Worksheet as the current // version of the Excel.Sheet. this is bug in both of their code. // since quattro cant even open the file when we give it to them, // they never should take the assoc in the first place, and when excel // takes over it shouldnt have preserved the CurVer key from the // previous association. we could add some code to insure that the // CurVer key follows the OLE progid naming conventions and that it must // be derived from the same app name as the progid in order to take // precedence but for now we will block CurVer from working whenever // the progid is excel.sheet.8 (excel 2000) // if (StrCmpI(TEXT("Excel.Sheet.8"), pszProgid) && ERROR_SUCCESS == SHGetValue(hkOut, TEXT("CurVer"), NULL, NULL, sz, &cb) && (cb > sizeof(TCHAR))) { // cache this bubby HKEY hkTemp = hkOut; if (SUCCEEDED(_AssocOpenRegKey(_RootKey(TRUE), sz, &hkOut))) { // // APPCOMPAT LEGACY - order of preference - ZekeL - 22-JUL-99 // this is to support associations that installed empty curver // keys, like microsoft project. // // 1. curver with shell subkey // 2. progid with shell subkey // 3. curver without shell subkey // 4. progid without shell subkey // HKEY hkShell; if (SUCCEEDED(_AssocOpenRegKey(hkOut, TEXT("shell"), &hkShell))) { RegCloseKey(hkShell); RegCloseKey(hkTemp); // close old ProgID key } else if (SUCCEEDED(_AssocOpenRegKey(hkTemp, TEXT("shell"), &hkShell))) { RegCloseKey(hkShell); RegCloseKey(hkOut); hkOut = hkTemp; } else RegCloseKey(hkTemp); } else // reset! hkOut = hkTemp; } } return hkOut; } // we only need to build this once, so build it for // the lowest common denominator... HKEY CAssocW2k::_ProgidKey(BOOL fDefaultToExtension) { HKEY hkExt = _ExtensionKey(TRUE); TCHAR sz[MAX_PATH]; ULONG cb = sizeof(sz); LPCTSTR psz; HKEY hkRet = NULL; if (!hkExt && !ISEXTENSION(_szInit)) { psz = _szInit; } else if (!_fNoRemapClsid && hkExt && (ERROR_SUCCESS == SHGetValue(hkExt, _fIsClsid ? TEXT("ProgID") : NULL, NULL, NULL, sz, &cb)) && (cb > sizeof(TCHAR))) { psz = sz; } else psz = NULL; if (psz && *psz) { hkRet = _OpenProgidKey(psz); } if (!hkRet && fDefaultToExtension && hkExt) hkRet = SHRegDuplicateHKey(hkExt); return hkRet; } HKEY CAssocW2k::_UserProgidKey(void) { SHSTR strApp; if (SUCCEEDED(_AssocGetRegString(_ExtensionKey(FALSE), NULL, TEXT("Application"), strApp))) { HKEY hkRet = _AppKey(strApp); if (hkRet) return SHRegDuplicateHKey(hkRet); } return NULL; } HKEY CAssocW2k::_ClassKey(BOOL fForceLM) { // REARCHITECT - we are not supporting clsids correctly here. HKEY hkRet = NULL; if (_fAppOnly) return _AppKey(NULL); else { KEYCACHETYPE type; if (!fForceLM) type = KEYCACHE_HKCU; else type = KEYCACHE_HKLM; if (_CanUseCache(_kcProgid, NULL, type)) hkRet = _kcProgid.hkCache; else { if (!fForceLM) hkRet = _UserProgidKey(); if (!hkRet) hkRet = _ProgidKey(TRUE); // cache the value off if (hkRet) _CacheKey(_kcProgid, hkRet, NULL, type); } } return hkRet; } HKEY CAssocW2k::_ShellVerbKey(HKEY hkey, KEYCACHETYPE type, LPCTSTR pszVerb) { HKEY hkRet = NULL; // check our cache if (_CanUseCache(_kcShellVerb, pszVerb, type)) hkRet = _kcShellVerb.hkCache; else if (hkey) { // NO cache hit if (!pszVerb) _DefaultShellVerb(hkey, NULL, 0, &hkRet); else { TCHAR szKey[MAX_PATH]; _PathAppend(TEXT("shell"), pszVerb, szKey, SIZECHARS(szKey)); _AssocOpenRegKey(hkey, szKey, &hkRet); } // only replace the cache if we got something if (hkRet) _CacheKey(_kcShellVerb, hkRet, pszVerb, type); } return hkRet; } HKEY CAssocW2k::_ShellVerbKey(BOOL fForceLM, LPCTSTR pszVerb) { HKEY hk = NULL; if (!fForceLM) { hk = _ShellVerbKey(_ClassKey(FALSE), KEYCACHE_HKCU, pszVerb); if (!hk && _szInit[0]) // szInit[0] = NULL, if Iqa is inited by key. hk = _ShellVerbKey(_ExtensionKey(FALSE), KEYCACHE_HKCU, pszVerb); } if (!hk) { KEYCACHETYPE type = (_fAppOnly) ? KEYCACHE_APP : KEYCACHE_HKLM; hk = _ShellVerbKey(_ClassKey(TRUE), type, pszVerb); if (!hk && _szInit[0]) // szInit[0] = NULL, if Iqa is inited by key. hk = _ShellVerbKey(_ExtensionKey(TRUE), type, pszVerb); } return hk; } HKEY CAssocW2k::_ShellNewKey(HKEY hkExt) { // // shellnew keys look like this // \.ext // @ = "progid" // \progid // \shellnew // -- OR -- // \.ext // \shellnew // HKEY hk = NULL; SHSTR strProgid; if (SUCCEEDED(_AssocGetRegString(hkExt, NULL, NULL, strProgid))) { strProgid.Append(TEXT("\\shellnew")); _AssocOpenRegKey(hkExt, TEXT("shellnew"), &hk); } if (!hk) _AssocOpenRegKey(hkExt, TEXT("shellnew"), &hk); return hk; } HKEY CAssocW2k::_ShellNewKey(BOOL fForceLM) { ASSERT(!_fAppOnly); if (_CanUseCache(_kcShellNew, NULL, KEYCACHE_HKLM)) return _kcShellNew.hkCache; HKEY hk = _ShellNewKey(_ExtensionKey(TRUE)); if (hk) _CacheKey(_kcShellNew, hk, NULL, KEYCACHE_HKLM); return hk; } HRESULT CAssocW2k::_GetCommandString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut) { HRESULT hr = E_INVALIDARG; KEYCACHETYPE type; if (!fForceLM) type = PSZCACHE_HKCU; else if (_fAppOnly) type = PSZCACHE_APP; else type = PSZCACHE_HKLM; if (pszVerb && !*pszVerb) pszVerb = NULL; if (_CanUseCache(_kcCommand, pszVerb, type)) { hr = strOut.SetStr(_kcCommand.pszCache); } if (FAILED(hr)) { hr = _AssocGetRegString(_ShellVerbKey(fForceLM, pszVerb), TEXT("command"), NULL, strOut); if (SUCCEEDED(hr)) { _CacheString(_kcCommand, strOut, pszVerb, type); } } return hr; } BOOL _PathIsFile(LPCTSTR pszPath) { DWORD attrs = GetFileAttributes(pszPath); return ((DWORD)-1 != attrs && !(attrs & FILE_ATTRIBUTE_DIRECTORY)); } HRESULT CAssocW2k::_ParseCommand(ASSOCF flags, LPCTSTR pszCommand, SHSTR& strExe, PSHSTR pstrArgs) { // we just need to find where the params begin, and the exe ends... LPTSTR pch = PathGetArgs(pszCommand); if (*pch) *(--pch) = TEXT('\0'); else pch = NULL; HRESULT hr = strExe.SetStr(pszCommand); // to prevent brace proliferation if (S_OK != hr) goto quit; strExe.Trim(); PathUnquoteSpaces(strExe.GetInplaceStr()); // // WARNING: Expensive disk hits all over! // // We check for %1 since it is what appears under (for example) HKCR\exefile\shell\open\command // This will save us a chain of 35 calls to _PathIsFile("%1") when launching or getting a // context menu on a shortcut to an .exe or .bat file. if ((ASSOCF_VERIFY & flags) && (0 != StrCmp(strExe, TEXT("%1"))) && (!_PathIsFile(strExe)) ) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); if (PathIsFileSpec(strExe)) { if (_GetAppPath(strExe, strExe)) { if (_PathIsFile(strExe)) hr = S_OK; } else { LPTSTR pszTemp = strExe.GetModifyableStr(MAX_PATH); if (pszTemp == NULL) hr = E_OUTOFMEMORY; else { if (PathFindOnPathEx(pszTemp, NULL, PFOPEX_DEFAULT | PFOPEX_OPTIONAL)) { // the find does a disk check for us... hr = S_OK; } } } } else { // // sometimes the path is not properly quoted. // these keys will still work because of the // way CreateProcess works, but we need to do // some fiddling to figure that out. // // if we found args, put them back... // and try some different args while (pch) { *pch++ = TEXT(' '); if (pch = StrChr(pch, TEXT(' '))) *pch = TEXT('\0'); if (S_OK == strExe.SetStr(pszCommand)) { strExe.Trim(); if (_PathIsFile(strExe)) { hr = S_FALSE; // this means that we found something // but the command line was kinda screwed break; } // this is where we loop again } else { hr = E_OUTOFMEMORY; break; } }// while (pch) } } if (SUCCEEDED(hr) && pch) { // currently right before the args, on a NULL terminator ASSERT(!*pch); pch++; if ((ASSOCF_REMAPRUNDLL & flags) && 0 == StrCmpNIW(PathFindFileName(strExe), TEXT("rundll"), SIZECHARS(TEXT("rundll")) -1)) { LPTSTR pchComma = StrChr(pch, TEXT(',')); // make the comma the beginning of the args if (pchComma) *pchComma = TEXT('\0'); if (!*(PathFindExtension(pch)) && lstrlen(++pchComma) > SIZECHARS(TEXT(".dll"))) StrCat(pch, TEXT(".dll")); // recurse :P hr = _ParseCommand(flags, pch, strExe, pstrArgs); } // set the args if we got'em else if (pstrArgs) pstrArgs->SetStr(pch); } quit: return hr; } HRESULT CAssocW2k::_GetFriendlyDocName(SHSTR& strOut) { HRESULT hr = E_FAIL; HKEY hkProgId = _ProgidKey(_fIsClsid); if (hkProgId) { // first try the MUI friendly version of the string // if that fails fall back to the default value of the progid. hr = _AssocGetRegUIString(hkProgId, NULL, L"FriendlyTypeName", strOut); if (FAILED(hr)) hr = _AssocGetRegString(hkProgId, NULL, NULL, strOut); RegCloseKey(hkProgId); } if (FAILED(hr) || IsEmptyStr(strOut)) { hr = E_FAIL; if (!_fIsClsid) { // fallback code TCHAR szDesc[MAX_PATH]; *szDesc = 0; if (_assocfBaseClass & ASSOCF_INIT_DEFAULTTOFOLDER || 0 == StrCmpIW(L"Folder", _szInit)) { // load the folder description "Folder" LoadString(HINST_THISDLL, IDS_FOLDERTYPENAME, szDesc, ARRAYSIZE(szDesc)); } else if (ISEXTENSION(_szInit) && _szInit[1]) { TCHAR szTemplate[128]; // "%s File" CharUpper(_szInit); LoadString(HINST_THISDLL, IDS_EXTTYPETEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); wnsprintf(szDesc, ARRAYSIZE(szDesc), szTemplate, _szInit + 1); } else if (_assocfBaseClass & ASSOCF_INIT_DEFAULTTOSTAR) { // load the file description "File" LoadString(HINST_THISDLL, IDS_FILETYPENAME, szDesc, ARRAYSIZE(szDesc)); } else if (_szInit[0]) { StrCpyN(szDesc, _szInit, ARRAYSIZE(szDesc)); CharUpper(szDesc); } if (*szDesc) hr = strOut.SetStr(szDesc); } } return hr; } HRESULT CAssocW2k::_GetExeString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut) { HRESULT hr = E_FAIL; KEYCACHETYPE type; if (!fForceLM) type = PSZCACHE_HKCU; else if (_fAppOnly) type = PSZCACHE_APP; else type = PSZCACHE_HKLM; if (_CanUseCache(_kcExecutable, pszVerb, type)) hr = strOut.SetStr(_kcExecutable.pszCache); if (FAILED(hr)) { SHSTR strCommand; hr = _GetCommandString(flags, fForceLM, pszVerb, strCommand); if (S_OK == hr) { SHSTR strArgs; strCommand.Trim(); hr = _ParseCommand(flags | ASSOCF_REMAPRUNDLL, strCommand, strOut, &strArgs); if (S_FALSE == hr) { hr = S_OK; // if (!ASSOCF_NOFIXUPS & flags) // AssocSetCommandByKey(ASSOCF_SET_SUBSTENV, hkey, pszVerb, strExe.GetStr(), strArgs.GetStr()); } } if (SUCCEEDED(hr)) _CacheString(_kcExecutable, strOut, pszVerb, type); } return hr; } HRESULT _AssocGetDarwinProductString(LPCTSTR pszDarwinCommand, SHSTR& strOut) { DWORD cch = strOut.GetSize(); UINT err = MsiGetProductInfo(pszDarwinCommand, INSTALLPROPERTY_PRODUCTNAME, strOut.GetInplaceStr(), &cch); if (err == ERROR_MORE_DATA && cch > strOut.GetSize()) { if (SUCCEEDED(strOut.SetSize(cch))) err = MsiGetProductInfo(pszDarwinCommand, INSTALLPROPERTY_PRODUCTNAME, strOut.GetInplaceStr(), &cch); else return E_OUTOFMEMORY; } if (err) return HRESULT_FROM_WIN32(err); return ERROR_SUCCESS; } HRESULT CAssocW2k::_GetFriendlyAppByVerb(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut) { if (pszVerb && !*pszVerb) pszVerb = NULL; HKEY hk = _ShellVerbKey(fForceLM, pszVerb); if (hk) { HRESULT hr = _AssocGetRegUIString(hk, NULL, TEXT("FriendlyAppName"), strOut); if (FAILED(hr)) { SHSTR str; // check the appkey, for this executeables friendly // name. this should be the most common case hr = _GetExeString(flags, fForceLM, pszVerb, str); if (SUCCEEDED(hr)) { hr = _GetFriendlyAppByApp(str, flags, strOut); } // if the EXE isnt on the System, then Darwin might // be able to tell us something about it... if (FAILED(hr)) { hr = _AssocGetRegString(hk, TEXT("command"), TEXT("command"), str); if (SUCCEEDED(hr)) { hr = _AssocGetDarwinProductString(str, strOut); } } } return hr; } return E_FAIL; } HRESULT _GetFriendlyAppByCache(HKEY hkApp, LPCTSTR pszApp, BOOL fVerifyCache, BOOL fNoFixUps, SHSTR& strOut) { HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); if (pszApp) { FILETIME ftCurr; if (MyGetLastWriteTime(pszApp, &ftCurr)) { if (fVerifyCache) { FILETIME ftCache = {0}; DWORD cbCache = sizeof(ftCache); SHGetValue(hkApp, TEXT("shell"), TEXT("FriendlyCacheCTime"), NULL, &ftCache, &cbCache); if (0 == CompareFileTime(&ftCurr, &ftCache)) hr = S_OK; } if (FAILED(hr)) { // need to get this from the file itself LPTSTR pszOut = strOut.GetModifyableStr(MAX_PATH); // How big is big enough? UINT cch = strOut.GetSize(); if (pszOut == NULL) pszOut = strOut.GetInplaceStr(); if (SHGetFileDescription(pszApp, NULL, NULL, pszOut, &cch)) hr = S_OK; if (SUCCEEDED(hr) && !(fNoFixUps)) { SHSetValue(hkApp, TEXT("shell"), TEXT("FriendlyCache"), REG_SZ, strOut, CbFromCch(strOut.GetLen() +1)); SHSetValue(hkApp, TEXT("shell"), TEXT("FriendlyCacheCTime"), REG_BINARY, &ftCurr, sizeof(ftCurr)); } } } } return hr; } HRESULT CAssocW2k::_GetFriendlyAppByApp(LPCTSTR pszApp, ASSOCF flags, SHSTR& strOut) { HKEY hk = _AppKey(pszApp ? PathFindFileName(pszApp) : NULL, TRUE); HRESULT hr = _AssocGetRegUIString(hk, NULL, TEXT("FriendlyAppName"), strOut); ASSERT(hk == _kcApp.hkCache); if (FAILED(hr)) { // we have now tried the default // we need to try our private cache hr = _AssocGetRegUIString(hk, TEXT("shell"), TEXT("FriendlyCache"), strOut); if (flags & ASSOCF_VERIFY) { SHSTR strExe; if (!pszApp) { ASSERT(_fAppOnly); if (_fAppPath) { pszApp = _szInit; } else if (SUCCEEDED(_GetExeString(flags, FALSE, NULL, strExe))) { pszApp = strExe; } } hr = _GetFriendlyAppByCache(hk, pszApp, SUCCEEDED(hr), (flags & ASSOCF_NOFIXUPS), strOut); } } return hr; } HRESULT CAssocW2k::_GetFriendlyAppString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut) { // Algorithm: // if there is a named value "friendly", return its value; // if it is a darwin app, return darwin product name; // if there is app key, return its named value "friendly" // o/w, get friendly name from the exe, and cache it in its app key // check the verb first. that overrides the // general exe case HRESULT hr; if (_fAppOnly) { hr = _GetFriendlyAppByApp(NULL, flags, strOut); } else { hr = _GetFriendlyAppByVerb(flags, fForceLM, pszVerb, strOut); } return hr; } HRESULT CAssocW2k::_GetShellExtension(ASSOCF flags, BOOL fForceLM, LPCTSTR pszShellEx, SHSTR& strOut) { HRESULT hr = E_FAIL; if (pszShellEx && *pszShellEx) { // Try to get the extension handler under ProgID first. HKEY hk = _ClassKey(fForceLM); TCHAR szHandler[140] = TEXT("ShellEx\\"); StrCatBuff(szHandler, pszShellEx, ARRAYSIZE(szHandler)); if (hk) { hr = _AssocGetRegString(hk, szHandler, NULL, strOut); } // Else try to get the extension handler under file extension. if (FAILED(hr) && _szInit[0]) // szInit[0] = NULL, if Iqa is inited by key. { // reuse hk here hk = _ExtensionKey(fForceLM); if (hk) { hr = _AssocGetRegString(hk, szHandler, NULL, strOut); } } } return hr; } HRESULT CAssocW2k::_GetTipString(LPCWSTR pwszValueName, BOOL fForceLM, SHSTR& strOut) { HRESULT hr = _AssocGetRegUIString(_ClassKey(fForceLM), NULL, pwszValueName, strOut); if (FAILED(hr)) hr = _AssocGetRegUIString(_ExtensionKey(fForceLM), NULL, pwszValueName, strOut); if (FAILED(hr) && !fForceLM) hr = _AssocGetRegUIString(_ExtensionKey(TRUE), NULL, pwszValueName, strOut); return hr; } HRESULT CAssocW2k::_GetInfoTipString(BOOL fForceLM, SHSTR& strOut) { return _GetTipString(L"InfoTip", fForceLM, strOut); } HRESULT CAssocW2k::_GetQuickTipString(BOOL fForceLM, SHSTR& strOut) { return _GetTipString(L"QuickTip", fForceLM, strOut); } HRESULT CAssocW2k::_GetTileInfoString(BOOL fForceLM, SHSTR& strOut) { return _GetTipString(L"TileInfo", fForceLM, strOut); } HRESULT CAssocW2k::_GetWebViewDisplayPropsString(BOOL fForceLM, SHSTR& strOut) { return _GetTipString(L"WebViewDisplayProperties", fForceLM, strOut); } HRESULT CAssocW2k::_GetShellNewValueString(BOOL fForceLM, BOOL fQueryOnly, LPCTSTR pszValue, SHSTR& strOut) { HRESULT hr = E_FAIL; HKEY hk = _ShellNewKey(fForceLM); if (hk) { TCHAR sz[MAX_PATH]; if (!pszValue) { // get the default value.... DWORD cch = SIZECHARS(sz); // we want a pszValue.... if (ERROR_SUCCESS == RegEnumValue(hk, 0, sz, &cch, NULL, NULL, NULL, NULL)) pszValue = sz; } hr = _AssocGetRegString(hk, NULL, pszValue, strOut); } return hr; } HKEY CAssocW2k::_DDEKey(BOOL fForceLM, LPCTSTR pszVerb) { HKEY hkRet = NULL; KEYCACHETYPE type; if (!fForceLM) { type = KEYCACHE_HKCU; } else { type = KEYCACHE_HKLM; } if (_CanUseCache(_kcDDE, pszVerb, type)) { hkRet = _kcDDE.hkCache; } else { if (SUCCEEDED(_AssocOpenRegKey(_ShellVerbKey(fForceLM, pszVerb), TEXT("ddeexec"), &hkRet))) { _CacheKey(_kcDDE, hkRet, pszVerb, type); } } return hkRet; } HRESULT CAssocW2k::_GetDDEApplication(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut) { HRESULT hr = E_FAIL; HKEY hk = _DDEKey(fForceLM, pszVerb); if (hk) { hr = _AssocGetRegString(hk, TEXT("Application"), NULL, strOut); if (FAILED(hr) || IsEmptyStr(strOut)) { hr = E_FAIL; // this means we should figure it out if (SUCCEEDED(_GetExeString(flags, fForceLM, pszVerb, strOut))) { PathRemoveExtension(strOut.GetInplaceStr()); PathStripPath(strOut.GetInplaceStr()); if (!IsEmptyStr(strOut)) { // we have a useful app name hr = S_OK; if (!(flags & ASSOCF_NOFIXUPS)) { // lets put it back! SHSetValue(_DDEKey(fForceLM, pszVerb), TEXT("Application"), NULL, REG_SZ, strOut.GetStr(), CbFromCch(strOut.GetLen() +1)); } } } } } return hr; } HRESULT CAssocW2k::_GetDDETopic(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut) { HRESULT hr = E_FAIL; HKEY hk = _DDEKey(fForceLM, pszVerb); if (hk) { hr = _AssocGetRegString(hk, TEXT("Topic"), NULL, strOut); if (FAILED(hr) || IsEmptyStr(strOut)) hr = strOut.SetStr(TEXT("System")); } return hr; } HRESULT CAssocW2k::_GetContentType(SHSTR& strOut) { HRESULT hr = E_FAIL; if (ISEXTENSION(_szInit)) { HKEY hk = _ExtensionKey(TRUE); if (hk) { hr = _AssocGetRegString(hk, NULL, TEXT("Content Type"), strOut); } } return hr; } HRESULT CAssocW2k::GetString(ASSOCF flags, ASSOCSTR str, LPCTSTR pszExtra, LPTSTR pszOut, DWORD *pcchOut) { RIP(_fInited); if (!_fInited) return E_UNEXPECTED; HRESULT hr = E_INVALIDARG; SHSTR strOut; if (str && str < ASSOCSTR_MAX && pcchOut && (pszOut || !IS_INTRESOURCE(pcchOut))) { BOOL fForceLM = (_fAppOnly) || (flags & ASSOCF_NOUSERSETTINGS); if (!_fBaseClassOnly || ASSOCSTR_FRIENDLYDOCNAME == str) { switch(str) { case ASSOCSTR_COMMAND: hr = _GetCommandString(flags, fForceLM, pszExtra, strOut); break; case ASSOCSTR_EXECUTABLE: hr = _GetExeString(flags, fForceLM, pszExtra, strOut); break; case ASSOCSTR_FRIENDLYAPPNAME: hr = _GetFriendlyAppString(flags, fForceLM, pszExtra, strOut); break; case ASSOCSTR_SHELLNEWVALUE: if (!_fAppOnly) hr = _GetShellNewValueString(fForceLM, (pszOut == NULL), pszExtra, strOut); break; case ASSOCSTR_NOOPEN: if (!_fAppOnly) hr = _AssocGetRegString(_ClassKey(fForceLM), NULL, TEXT("NoOpen"), strOut); break; case ASSOCSTR_FRIENDLYDOCNAME: if (!_fAppOnly) hr = _GetFriendlyDocName(strOut); break; case ASSOCSTR_DDECOMMAND: hr = _AssocGetRegString(_DDEKey(fForceLM, pszExtra), NULL, NULL, strOut); break; case ASSOCSTR_DDEIFEXEC: hr = _AssocGetRegString(_DDEKey(fForceLM, pszExtra), TEXT("IfExec"), NULL, strOut); break; case ASSOCSTR_DDEAPPLICATION: hr = _GetDDEApplication(flags, fForceLM, pszExtra, strOut); break; case ASSOCSTR_DDETOPIC: hr = _GetDDETopic(flags, fForceLM, pszExtra, strOut); break; case ASSOCSTR_INFOTIP: hr = _GetInfoTipString(fForceLM, strOut); break; case ASSOCSTR_QUICKTIP: hr = _GetQuickTipString(fForceLM, strOut); break; case ASSOCSTR_TILEINFO: hr = _GetTileInfoString(fForceLM, strOut); break; case ASSOCSTR_CONTENTTYPE: hr = _GetContentType(strOut); break; case ASSOCSTR_DEFAULTICON: hr = _AssocGetRegString(_ClassKey(fForceLM), TEXT("DefaultIcon"), NULL, strOut); break; case ASSOCSTR_SHELLEXTENSION: hr = _GetShellExtension(flags, fForceLM, pszExtra, strOut); if (FAILED(hr) && !fForceLM) hr = _GetShellExtension(flags, TRUE, pszExtra, strOut); break; default: // // Turn off this assert message until we have a clean way to support new ASSOCSTR types // in both shell32 and shlwapi // #if 0 AssertMsg(FALSE, TEXT("CAssocW2k::GetString() mismatched headers - ZekeL")); #endif hr = E_INVALIDARG; break; } } if (SUCCEEDED(hr)) hr = _CopyOut(flags & ASSOCF_NOTRUNCATE, strOut, pszOut, pcchOut); else if (!(flags & ASSOCF_IGNOREBASECLASS) && _UseBaseClass()) { HRESULT hrT = _pqaBaseClass->GetString(flags, str, pszExtra, pszOut, pcchOut); if (SUCCEEDED(hrT)) hr = hrT; } } return hr; } HRESULT CAssocW2k::_GetMSIDescriptor(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, LPBYTE pbOut, LPDWORD pcbOut) { // what do we do with A/W thunks of REG_MULTI_SZ // the darwin ID is always a value name that is the same as the name of the parent key, // so instead of reading the default value we read the value with the name of the // parent key. // // shell // | // -- Open // | // -- Command // (Default) = "%SystemRoot%\system32\normal_app.exe" <-- this is the normal app value // Command = "[DarwinID] /c" <-- this is the darwin ID value // // HACK! Access 95 (shipping product) creates a "Command" value under // the Command key but it is >>not<< a Darwin ID. I don't know what // they were smoking. So we also check the key type and it must be // REG_MULTI_SZ or we will ignore it. // // DWORD dwType; HRESULT hr = _AssocGetRegData(_ShellVerbKey(fForceLM, pszVerb), TEXT("command"), TEXT("command"), &dwType, pbOut, pcbOut); if (SUCCEEDED(hr) && dwType != REG_MULTI_SZ) hr = E_UNEXPECTED; return hr; } HRESULT CAssocW2k::GetData(ASSOCF flags, ASSOCDATA data, LPCWSTR pszExtra, LPVOID pvOut, DWORD *pcbOut) { RIP(_fInited); if (!_fInited) return E_UNEXPECTED; HRESULT hr = E_INVALIDARG; if (data && data < ASSOCSTR_MAX) { BOOL fForceLM = (_fAppOnly) || (flags & ASSOCF_NOUSERSETTINGS); DWORD cbReal; if (pcbOut && IS_INTRESOURCE(pcbOut)) { cbReal = PtrToUlong(pcbOut); pcbOut = &cbReal; } if (!_fBaseClassOnly) { switch(data) { case ASSOCDATA_MSIDESCRIPTOR: hr = _GetMSIDescriptor(flags, fForceLM, pszExtra, (LPBYTE)pvOut, pcbOut); break; case ASSOCDATA_NOACTIVATEHANDLER: hr = _AssocGetRegData(_DDEKey(fForceLM, pszExtra), NULL, TEXT("NoActivateHandler"), NULL, (LPBYTE) pvOut, pcbOut); break; case ASSOCDATA_QUERYCLASSSTORE: hr = _AssocGetRegData(_ClassKey(fForceLM), NULL, TEXT("QueryClassStore"), NULL, (LPBYTE) pvOut, pcbOut); break; case ASSOCDATA_HASPERUSERASSOC: { HKEY hk = _UserProgidKey(); if (hk && _ShellVerbKey(hk, KEYCACHE_HKCU, pszExtra)) hr = S_OK; else hr = S_FALSE; REGFREE(hk); } break; case ASSOCDATA_EDITFLAGS: hr = _AssocGetRegData(_ClassKey(fForceLM), NULL, TEXT("EditFlags"), NULL, (LPBYTE) pvOut, pcbOut); break; default: AssertMsg(FALSE, TEXT("CAssocW2k::GetString() mismatched headers - ZekeL")); hr = E_INVALIDARG; break; } } if (FAILED(hr) && !(flags & ASSOCF_IGNOREBASECLASS) && _UseBaseClass()) { HRESULT hrT = _pqaBaseClass->GetData(flags, data, pszExtra, pvOut, pcbOut); if (SUCCEEDED(hrT)) hr = hrT; } } return hr; } HRESULT CAssocW2k::GetEnum(ASSOCF flags, ASSOCENUM assocenum, LPCTSTR pszExtra, REFIID riid, LPVOID *ppvOut) { return E_NOTIMPL; } HRESULT CAssocW2k::_GetShellExecKey(ASSOCF flags, BOOL fForceLM, LPCWSTR pszVerb, HKEY *phkey) { HKEY hkProgid = NULL; if (pszVerb && !*pszVerb) pszVerb = NULL; if (!fForceLM) { hkProgid = _ClassKey(FALSE); if (hkProgid && (!(flags & ASSOCF_VERIFY) || _ShellVerbKey(hkProgid, KEYCACHE_HKCU, pszVerb))) *phkey = SHRegDuplicateHKey(hkProgid); } if (!*phkey) { KEYCACHETYPE type = (_fAppOnly) ? KEYCACHE_APP : KEYCACHE_HKLM; hkProgid = _ClassKey(TRUE); if (hkProgid && (!(flags & ASSOCF_VERIFY) || _ShellVerbKey(hkProgid, type, pszVerb))) *phkey = SHRegDuplicateHKey(hkProgid); } return *phkey ? S_OK : HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); } HRESULT CAssocW2k::_CloneKey(HKEY hk, HKEY *phkey) { if (hk) *phkey = SHRegDuplicateHKey(hk); return *phkey ? S_OK : HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); } HRESULT CAssocW2k::GetKey(ASSOCF flags, ASSOCKEY key, LPCTSTR pszExtra, HKEY *phkey) { RIP(_fInited); if (!_fInited) return E_UNEXPECTED; HRESULT hr = E_INVALIDARG; if (key && key < ASSOCKEY_MAX && phkey) { BOOL fForceLM = (_fAppOnly) || (flags & ASSOCF_NOUSERSETTINGS); *phkey = NULL; if (!_fBaseClassOnly) { switch (key) { case ASSOCKEY_SHELLEXECCLASS: hr = _GetShellExecKey(flags, fForceLM, pszExtra, phkey); break; case ASSOCKEY_APP: hr = _fAppOnly ? _CloneKey(_AppKey(NULL), phkey) : E_INVALIDARG; break; case ASSOCKEY_CLASS: hr = _CloneKey(_ClassKey(fForceLM), phkey); break; case ASSOCKEY_BASECLASS: // fall through and it is handled by the BaseClass handling break; default: AssertMsg(FALSE, TEXT("CAssocW2k::GetKey() mismatched headers - ZekeL")); hr = E_INVALIDARG; break; } } if (FAILED(hr) && !(flags & ASSOCF_IGNOREBASECLASS) && _UseBaseClass()) { // it is possible to indicate the depth of the // base class by pszExtra being an INT if (key == ASSOCKEY_BASECLASS) { int depth = IS_INTRESOURCE(pszExtra) ? LOWORD(pszExtra) : 0; if (depth) { // go deeper than this depth--; hr = _pqaBaseClass->GetKey(flags, key, MAKEINTRESOURCE(depth), phkey); } else { // just return this baseclass hr = _pqaBaseClass->GetKey(flags, ASSOCKEY_CLASS, pszExtra, phkey); } } else { // forward to the base class hr = _pqaBaseClass->GetKey(flags, key, pszExtra, phkey); } } } return hr; } HRESULT AssocCreateW2k(REFIID riid, LPVOID *ppvOut) { HRESULT hr = E_OUTOFMEMORY; CAssocW2k *passoc = new CAssocW2k(); if (passoc) { hr = passoc->QueryInterface(riid, ppvOut); passoc->Release(); } return hr; }