1836 lines
51 KiB
C++
1836 lines
51 KiB
C++
//
|
|
//
|
|
// assocapi.cpp
|
|
//
|
|
// Association APIs
|
|
//
|
|
//
|
|
//
|
|
|
|
|
|
#include "priv.h"
|
|
#include "apithk.h"
|
|
#include <shstr.h>
|
|
#include <w95wraps.h>
|
|
#include <msi.h>
|
|
#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;
|
|
}
|