Files
reactos/dll/win32/shell32/evalcmd/elements.cpp
T
Katayama Hirofumi MZ 79761d88fd [SHELL32][SHLWAPI][SHELL32_APITEST][SDK] Support AssocCreateElement (#9215)
Prepare for CLSID_QueryAssociations.
JIRA issue: CORE-20467
- Add dll/win32/shell32/evalcmd/
  elements.cpp.
- Implement AssocCreateElement
  function in elements.cpp.
- Modify shell32.spec.
- Fix shlwapi!AssocCreate by using
  AssocCreateElement.
- Add SKGetValueW and
  SKSetValueW prototypes into
  <shlwapi_undoc.h>.
- Add AssocCreateElement
  prototype into <shlwapi_undoc.h>.
- Add some CLSIDs into
  <shlguid_undoc.h>.
- Add some resource strings into
  shlwapi.
- Add AssocGetPerceivedType
  prototype into <shlwapi.h>.
- Add AssocCreateElement
  testcase into shell32_apitest.
2026-07-04 01:10:31 +09:00

1700 lines
47 KiB
C++

/*
* PROJECT: ReactOS Shell
* LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
* PURPOSE: AssocCreateElement
* COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ <[email protected]>
*/
#include <windef.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <undocshell.h>
#include <shlobj_undoc.h>
#include <shlguid_undoc.h>
#include <shlwapi_undoc.h>
#include <pathcch.h>
#include <strsafe.h>
#include <shellutils.h>
#include <new>
#include "evalcmd.h"
#include "resource.h"
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(evalcmd);
typedef HRESULT (CALLBACK* QUERY_CALLBACK)(IQuerySourceOld*, ASSOCQUERY query, PCWSTR keyName,
PCWSTR valueName, PVOID pValue);
typedef struct QUERYKEYVAL
{
ASSOCQUERY query;
PCWSTR key;
PCWSTR value;
} QUERYKEYVAL, *PQUERYKEYVAL;
#define ASSOCQUERY_DE (ASSOCQUERY_DIRECT | ASSOCQUERY_EXISTS)
#define ASSOCQUERY_SDE (ASSOCQUERY_STRING | ASSOCQUERY_DE)
#define ASSOCQUERY_SDED (ASSOCQUERY_SDE | ASSOCQUERY_DWORD)
#define ASSOCQUERY_SDEI (ASSOCQUERY_SDE | ASSOCQUERY_INDIRECT)
#define ASSOCQUERY_SDEEV (ASSOCQUERY_SDE | ASSOCQUERY_EXTRA_VERB)
#define ASSOCQUERY_DEFAULTICON (ASSOCQUERY_SDE | 0x1)
#define ASSOCQUERY_CLSID (ASSOCQUERY_SDE | 0x3)
#define ASSOCQUERY_PROGID (ASSOCQUERY_SDE | 0x4)
#define ASSOCQUERY_SHELLEX (ASSOCQUERY_SDE | ASSOCQUERY_FALLBACK | ASSOCQUERY_EXTRA_NON_VERB | 0x2)
#define ASSOCQUERY_NOACTIVATEHANDLER (ASSOCQUERY_DE | ASSOCQUERY_EXTRA_VERB | 0x5)
#define ASSOCQUERY_COMMAND_COMMAND (ASSOCQUERY_DE | ASSOCQUERY_EXTRA_VERB | 0x6)
#define ASSOCQUERY_FRIENDLYAPPNAME ASSOCQUERY_SDEI
#define ASSOCQUERY_VERB_FRIENDLYAPPNAME (ASSOCQUERY_SDEI | ASSOCQUERY_EXTRA_VERB | 0x8)
#define ASSOCQUERY_COMMAND ASSOCQUERY_SDEEV
#define ASSOCQUERY_DDEEXEC (ASSOCQUERY_SDEEV | 0x1)
#define ASSOCQUERY_DDEEXEC_IFEXEC (ASSOCQUERY_SDEEV | 0x2)
#define ASSOCQUERY_DDEEXEC_APPLICATION (ASSOCQUERY_SDEEV | 0x3)
#define ASSOCQUERY_DDEEXEC_TOPIC (ASSOCQUERY_SDEEV | 0x4)
#define ASSOCQUERY_LOCALIZEDSTRING (ASSOCQUERY_SDEI | ASSOCQUERY_EXTRA_NON_VERB | 0x1)
#define ASSOCQUERY_SEV_7 (ASSOCQUERY_STRING | ASSOCQUERY_EXTRA_VERB | 0x7)
#define ASSOCQUERY_APPKEY_FRIENDLYAPPNAME (ASSOCQUERY_SDEEV | ASSOCQUERY_INDIRECT | 0x8)
#define ASSOCQUERY_CONTENT_TYPE (ASSOCQUERY_SDE | ASSOCQUERY_FALLBACK | 0x2)
#define ASSOCQUERY_DELEGATE (ASSOCQUERY_STRING | ASSOCQUERY_EXTRA_VERB | 0x7)
#define ASSOCQUERY_OBJECT_EV_1 (ASSOCQUERY_OBJECT | ASSOCQUERY_EXTRA_VERB | 0x1)
static const QUERYKEYVAL g_perceivedKeyVals[] =
{
{ ASSOCQUERY_FRIENDLYAPPNAME, NULL, L"FriendlyTypeName" },
{ ASSOCQUERY_DEFAULTICON, L"DefaultIcon", NULL },
};
static const QUERYKEYVAL g_shellKeyVals[] =
{
{ ASSOCQUERY_FRIENDLYAPPNAME, NULL, L"FriendlyTypeName" },
{ ASSOCQUERY_DEFAULTICON, L"DefaultIcon", NULL },
{ ASSOCQUERY_CLSID, L"Clsid", NULL },
{ ASSOCQUERY_PROGID, L"Progid", NULL },
{ ASSOCQUERY_SHELLEX, L"ShellEx\\%s", NULL },
};
static const QUERYKEYVAL g_shellVerbKeyVals[] =
{
{ ASSOCQUERY_COMMAND, L"command", NULL },
{ ASSOCQUERY_DDEEXEC, L"ddeexec", NULL },
{ ASSOCQUERY_DDEEXEC_IFEXEC, L"ddeexec\\ifexec", NULL },
{ ASSOCQUERY_DDEEXEC_APPLICATION, L"ddeexec\\application", NULL },
{ ASSOCQUERY_DDEEXEC_TOPIC, L"ddeexec\\topic", NULL },
{ ASSOCQUERY_NOACTIVATEHANDLER, L"ddeexec", L"NoActivateHandler" },
{ ASSOCQUERY_COMMAND_COMMAND, L"command", L"command" },
{ ASSOCQUERY_VERB_FRIENDLYAPPNAME, NULL, L"FriendlyAppName" },
};
static const QUERYKEYVAL g_shellAppKeyVals[] =
{
{ ASSOCQUERY_APPKEY_FRIENDLYAPPNAME, NULL, L"FriendlyAppName" },
};
static const QUERYKEYVAL g_progidKeyVals[2] =
{
{ ASSOCQUERY_SHELLEX, L"ShellEx\\%s", NULL },
{ ASSOCQUERY_CONTENT_TYPE, NULL, L"Content Type" },
};
static const QUERYKEYVAL*
_FindKeyVal(ASSOCQUERY query, const QUERYKEYVAL* pItems, UINT cItems)
{
for (UINT iItem = 0; iItem < cItems; ++iItem)
{
if (pItems[iItem].query == query)
return &pItems[iItem];
}
return NULL;
}
static HRESULT SHAllocMUI(PWSTR* ppwsz)
{
WCHAR szOutBuf[1024];
HRESULT hr = SHLoadIndirectString(*ppwsz, szOutBuf, _countof(szOutBuf), NULL);
CoTaskMemFree(*ppwsz);
if (FAILED_UNEXPECTEDLY(hr))
{
*ppwsz = NULL;
return hr;
}
return SHStrDupW(szOutBuf, ppwsz);
}
static inline HRESULT _SHAllocLoadString(HINSTANCE hInstance, UINT uID, PWSTR* ppwsz)
{
WCHAR szBuff[MAX_PATH];
LoadStringW(hInstance, uID, szBuff, _countof(szBuff));
return SHStrDupW(szBuff, ppwsz);
}
static HRESULT QSOpen2(
IQuerySourceOld* pSource,
PCWSTR key,
PCWSTR pszSrc,
BOOL bCreate,
IQuerySourceOld** ppSource)
{
WCHAR szDest[MAX_PATH];
PathCchCombineEx(szDest, _countof(szDest), key, pszSrc, PATHCCH_NONE);
return pSource->OpenSource(szDest, bCreate, ppSource);
}
static HRESULT _GetFileTypeName(PWSTR lpsz, PWSTR* ppwsz)
{
WCHAR szDest[MAX_PATH], szBuff[128];
HINSTANCE hSHLWAPI = GetModuleHandleW(L"shlwapi.dll");
if (!lpsz || *lpsz != '.' || !lpsz[1])
return _SHAllocLoadString(hSHLWAPI, IDS_FILE, ppwsz);
CharUpperW(lpsz);
LoadStringW(hSHLWAPI, IDS_ANY_FILE, szBuff, _countof(szBuff));
StringCchPrintfW(szDest, _countof(szDest), szBuff, lpsz + 1);
return SHStrDupW(szDest, ppwsz);
}
static inline LSTATUS
_RegQueryString(HKEY hKey, PCWSTR lpSubKey, PWSTR lpData, LONG cchData)
{
LONG cbData = cchData * sizeof(WCHAR);
return RegQueryValueW(hKey, lpSubKey, lpData, &cbData);
}
static LSTATUS
_RegSetVolatileString(HKEY hKey, PCWSTR lpSubKey, PCWSTR lpString)
{
LSTATUS error;
if (lpSubKey && *lpSubKey)
error = RegCreateKeyExW(hKey, lpSubKey, 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE,
NULL, &hKey, NULL);
else
error = RegOpenKeyExW(hKey, NULL, 0, KEY_WRITE, &hKey);
if (error)
return error;
INT cch = lstrlenW(lpString);
error = RegSetValueExW(hKey, NULL, 0, REG_SZ, (PBYTE)lpString, (cch + 1) * sizeof(WCHAR));
RegCloseKey(hKey);
return error;
}
static HRESULT CALLBACK
_QuerySourceDirect(
IQuerySourceOld* pSource,
ASSOCQUERY query,
PCWSTR keyName,
PCWSTR valueName,
PVOID pValue)
{
return pSource->QueryValueDirect(keyName, valueName, (FLAGGED_BYTE_BLOB**)pValue);
}
static HRESULT CALLBACK
_QuerySourceDword(
IQuerySourceOld* pSource,
ASSOCQUERY query,
PCWSTR keyName,
PCWSTR valueName,
PVOID pValue)
{
return pSource->QueryValueDword(keyName, valueName, (PDWORD)pValue);
}
static HRESULT CALLBACK
_QuerySourceExists(
IQuerySourceOld* pSource,
ASSOCQUERY query,
PCWSTR keyName,
PCWSTR valueName,
PVOID pValue)
{
return pSource->QueryValueExists(keyName, valueName);
}
static HRESULT CALLBACK
_QuerySourceString(
IQuerySourceOld* pSource,
ASSOCQUERY query,
PCWSTR keyName,
PCWSTR valueName,
PVOID pValue)
{
HRESULT hr = pSource->QueryValueString(keyName, valueName, static_cast<PWSTR*>(pValue));
if (FAILED_UNEXPECTEDLY(hr) || !(query & ASSOCQUERY_INDIRECT))
return hr;
return SHAllocMUI(static_cast<PWSTR*>(pValue));
}
static inline HRESULT
_QuerySourceCreateFromKey(HKEY hKey, PCWSTR lpSubKey, BOOL bCreate, IQuerySourceOld** ppSource)
{
return QuerySourceCreateFromKey(hKey, lpSubKey, bCreate,
IID_PPV_ARG(IQuerySourceOld, ppSource));
}
static HRESULT
_QuerySourceCreateFromKey2(
HKEY hKey,
PCWSTR key1,
PCWSTR key2,
IQuerySourceOld** ppSource)
{
WCHAR szBuff[MAX_PATH];
if (key1)
PathCchCombineEx(szBuff, _countof(szBuff), key1, key2, PATHCCH_NONE);
else
StringCchCopyW(szBuff, _countof(szBuff), key2);
return _QuerySourceCreateFromKey(hKey, szBuff, FALSE, ppSource);
}
static HRESULT
_AssocOpenRegKey(HKEY hKey, PCWSTR lpSubKey, PHKEY phkResult, BOOL bCreate)
{
*phkResult = NULL;
if (!hKey)
return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
LSTATUS error;
if ( bCreate )
error = RegCreateKeyExW(hKey, lpSubKey, 0, NULL, 0, MAXIMUM_ALLOWED, NULL,
phkResult, NULL);
else
error = RegOpenKeyExW(hKey, lpSubKey, 0, MAXIMUM_ALLOWED, phkResult);
if (error == ERROR_SUCCESS)
return S_OK;
return HRESULT_FROM_WIN32(error);
}
static HKEY _OpenProgidKey(PCWSTR psz)
{
HKEY hKeyBase;
HRESULT hr = _AssocOpenRegKey(HKEY_CLASSES_ROOT, psz, &hKeyBase, FALSE);
if (FAILED_UNEXPECTEDLY(hr))
return NULL;
WCHAR szData[64];
DWORD cbData = sizeof(szData);
if (StrCmpIW(L"Excel.Sheet.8", psz) == 0)
return hKeyBase;
LSTATUS error = SHGetValueW(hKeyBase, L"CurVer", NULL, NULL, szData, &cbData);
if (error || cbData <= 2)
return hKeyBase;
HKEY hKeyCurVer;
hr = _AssocOpenRegKey(HKEY_CLASSES_ROOT, szData, &hKeyCurVer, FALSE);
if (FAILED_UNEXPECTEDLY(hr))
return hKeyBase;
HKEY hKeyShell;
hr = _AssocOpenRegKey(hKeyCurVer, L"shell", &hKeyShell, FALSE);
if (FAILED_UNEXPECTEDLY(hr))
{
hr = _AssocOpenRegKey(hKeyBase, L"shell", &hKeyShell, FALSE);
if (FAILED_UNEXPECTEDLY(hr))
{
RegCloseKey(hKeyBase);
return hKeyCurVer;
}
RegCloseKey(hKeyShell);
RegCloseKey(hKeyCurVer);
return hKeyBase;
}
RegCloseKey(hKeyShell);
RegCloseKey(hKeyBase);
return hKeyCurVer;
}
static inline void _MakeApplicationsKey(PCWSTR pszPath, PWSTR pszDest, UINT cchDest)
{
HRESULT hr = PathCchCombineEx(pszDest, cchDest, L"Applications", pszPath, PATHCCH_NONE);
if (FAILED_UNEXPECTEDLY(hr))
return;
PathCchAddExtension(pszDest, cchDest, L".exe");
}
static inline BOOL _ParamIsApp(PCWSTR psz)
{
return !StrCmpNW(psz, L"%1", 2) || !StrCmpNW(psz, L"\"%1\"", 4);
}
static HRESULT _PathFileExists(PCWSTR lpFileName)
{
DWORD attrs;
if ( !PathFileExistsAndAttributesW(lpFileName, &attrs) || (attrs & FILE_ATTRIBUTE_DIRECTORY))
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
return S_OK;
}
static HRESULT _ExeFromCmd(PCWSTR pszCmdTemplate, PWSTR* ppszApplication)
{
if (_ParamIsApp(pszCmdTemplate))
return SHStrDupW(L"%1", ppszApplication);
PWSTR pszParameters = NULL;
HRESULT hr = SHEvaluateSystemCommandTemplate(pszCmdTemplate, ppszApplication,
NULL, &pszParameters);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (hr != S_OK || StrCmpIW(PathFindFileNameW(*ppszApplication), L"rundll32.exe") != 0)
{
CoTaskMemFree(pszParameters);
return hr;
}
CoTaskMemFree(*ppszApplication);
*ppszApplication = NULL;
PWSTR pchComma = StrChrW(pszParameters, L',');
if (!pchComma)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
CoTaskMemFree(pszParameters);
return hr;
}
WCHAR szExe[MAX_PATH];
hr = _PathCopyExeAndTrimWhiteSpaces(szExe, _countof(szExe), pszParameters,
pchComma - pszParameters);
if (FAILED_UNEXPECTEDLY(hr))
{
CoTaskMemFree(pszParameters);
return hr;
}
PathUnquoteSpacesW(szExe);
PathCchAddExtension(szExe, _countof(szExe), L".dll");
if (PathIsAbsolute(szExe))
{
hr = _PathFileExists(szExe);
}
else if (PathIsFileSpecW(szExe))
{
hr = _PathFindInSystem(szExe, _countof(szExe));
}
else
{
CoTaskMemFree(pszParameters);
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
if (SUCCEEDED(hr))
hr = SHStrDupW(szExe, ppszApplication);
CoTaskMemFree(pszParameters);
return hr;
}
/******************************************************************************
* CAssocElement
*/
class CAssocElement
: public IObjectWithQuerySourceOld
, public IAssociationElementOld
{
protected:
LONG m_cRefs = 1;
IQuerySourceOld* m_pSource = NULL;
HRESULT _QueryKeyValAny(
QUERY_CALLBACK callback,
const QUERYKEYVAL* pItems,
UINT cItems,
IQuerySourceOld* pQS,
ASSOCQUERY query,
PCWSTR valueName,
PVOID pValue);
HRESULT _QuerySourceAny(
QUERY_CALLBACK callback,
IQuerySourceOld* pSource,
DWORD dwFlags,
ASSOCQUERY query,
PCWSTR valueName,
PVOID pValue);
public:
virtual ~CAssocElement();
/*** IUnknown ***/
STDMETHODIMP QueryInterface(REFIID riid, PVOID* ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
/*** IObjectWithQuerySourceOld ***/
STDMETHODIMP SetSource(IQuerySourceOld* pSource) override;
STDMETHODIMP GetSource(REFIID riid, PVOID* ppSource) override;
/*** IAssociationElementOld ***/
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue) override;
STDMETHODIMP QueryDword(ASSOCQUERY query, PCWSTR key, DWORD* pdwValue) override;
STDMETHODIMP QueryExists(ASSOCQUERY query, PCWSTR key) override;
STDMETHODIMP QueryDirect(ASSOCQUERY query, PCWSTR key, FLAGGED_BYTE_BLOB** ppBlob) override;
STDMETHODIMP QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppvObj) override;
protected:
virtual UINT _GetQueryKeyVal(const QUERYKEYVAL** ppItems);
};
/******************************************************************************
* CAssocShellElement
*/
class CAssocShellElement
: public CAssocElement
, public IPersistString2
{
protected:
PWSTR m_pszName = NULL;
WCHAR m_szBuff[64] = L"";
UINT _GetQueryKeyVal(const QUERYKEYVAL** ppItems) override;
HRESULT _DefaultVerbSource(IQuerySourceOld** ppSource);
public:
~CAssocShellElement();
/*** IUnknown ***/
STDMETHODIMP QueryInterface(REFIID riid, PVOID* ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
/*** IAssociationElementOld ***/
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue) override;
STDMETHODIMP QueryDword(ASSOCQUERY query, PCWSTR key, DWORD* pdwValue) override;
STDMETHODIMP QueryExists(ASSOCQUERY query, PCWSTR key) override;
STDMETHODIMP QueryDirect(ASSOCQUERY query, PCWSTR key, FLAGGED_BYTE_BLOB** ppBlob) override;
STDMETHODIMP QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppvObj) override;
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
/*** IPersistString2 ***/
STDMETHODIMP SetString(PCWSTR psz) override;
STDMETHODIMP GetString(PWSTR* ppsz) override;
virtual BOOL _UseEnumForDefaultVerb();
virtual HRESULT _InitSource();
virtual BOOL _IsAppSource();
virtual HRESULT _GetVerbDelegate(PCWSTR pszSrc, IAssociationElementOld** ppElement);
};
/******************************************************************************
* CAssocShellVerbElement
*/
class CAssocShellVerbElement
: public CAssocElement
{
protected:
BOOL m_bAppSource = FALSE;
UINT _GetQueryKeyVal(const QUERYKEYVAL** ppItems) override;
HRESULT _GetAppDelegate(REFIID riid, PVOID* ppv);
public:
CAssocShellVerbElement(BOOL bAppSource);
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppwsz) override;
STDMETHODIMP QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppvObj) override;
};
/******************************************************************************
* CAssocApplicationElement
*/
class CAssocApplicationElement
: public CAssocShellElement
{
protected:
BOOL m_bHasDir = FALSE;
UINT _GetQueryKeyVal(const QUERYKEYVAL** ppItems) override;
HRESULT _InitSource() override;
HRESULT _GetAppDisplayName(PWSTR* ppsz);
public:
STDMETHODIMP GetClassID(CLSID* pClsid) override;
STDMETHODIMP QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppv) override;
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppsz) override;
};
/******************************************************************************
* CAssocProgidElement
*/
class CAssocProgidElement
: public CAssocShellElement
{
IQuerySourceOld* m_pSource2 = NULL;
public:
~CAssocProgidElement() override;
/*** IAssociationElementOld ***/
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue) override;
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
BOOL _UseEnumForDefaultVerb() override;
HRESULT _InitSource() override;
};
/******************************************************************************
* CAssocClsidElement
*/
class CAssocClsidElement
: public CAssocShellElement
{
public:
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
HRESULT _InitSource() override;
};
/******************************************************************************
* CAssocSystemExtElement
*/
class CAssocSystemExtElement
: public CAssocShellElement
{
public:
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
HRESULT _InitSource() override;
};
/******************************************************************************
* CAssocFolderElement
*/
class CAssocFolderElement
: public CAssocShellElement
{
protected:
public:
/*** IAssociationElementOld ***/
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue) override;
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
HRESULT _InitSource() override;
};
/******************************************************************************
* CAssocStarElement
*/
class CAssocStarElement
: public CAssocShellElement
{
protected:
public:
/*** IAssociationElementOld ***/
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue) override;
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
HRESULT _InitSource() override;
};
/******************************************************************************
* CAssocPerceivedElement
*/
class CAssocPerceivedElement
: public CAssocShellElement
{
protected:
BOOL m_bPerceived = FALSE;
UINT _GetQueryKeyVal(const QUERYKEYVAL** ppItems) override;
public:
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
/*** IObjectWithQuerySourceOld ***/
STDMETHODIMP GetSource(REFIID riid, PVOID* ppSource) override;
HRESULT _InitSource() override;
HRESULT _GetVerbDelegate(PCWSTR pszSrc, IAssociationElementOld** ppElement) override;
};
/******************************************************************************
* CAssocClientElement
*/
class CAssocClientElement
: public CAssocShellElement
{
public:
/*** IAssociationElementOld ***/
STDMETHODIMP QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue) override;
/*** IPersist ***/
STDMETHODIMP GetClassID(CLSID* pClassID) override;
HRESULT _InitSource() override;
HRESULT _FixNetscapeRegistration();
BOOL _CreateRepairedNetscapeRegistration(HKEY hKey);
HRESULT _InitSourceFromKey(HKEY hKey, PCWSTR lpSubKey, DWORD dwFlags);
};
/******************************************************************************
* CAssocElement methods
*/
CAssocElement::~CAssocElement()
{
if (m_pSource)
{
m_pSource->Release();
m_pSource = NULL;
}
}
UINT CAssocElement::_GetQueryKeyVal(const QUERYKEYVAL** ppItems)
{
*ppItems = NULL;
return 0;
}
HRESULT
CAssocElement::_QueryKeyValAny(
QUERY_CALLBACK callback,
const QUERYKEYVAL* pItems,
UINT cItems,
IQuerySourceOld* pQS,
ASSOCQUERY query,
PCWSTR valueName,
PVOID pValue)
{
const QUERYKEYVAL* pItem = _FindKeyVal(query, pItems, cItems);
if (!pItem)
return E_INVALIDARG;
WCHAR szBuff[128];
PCWSTR key = pItem->key;
if ((query & ASSOCQUERY_EXTRA_NON_VERB) && key)
{
StringCchPrintfW(szBuff, _countof(szBuff), key, valueName);
key = szBuff;
}
return callback(pQS, query, key, pItem->value, pValue);
}
HRESULT
CAssocElement::_QuerySourceAny(
QUERY_CALLBACK callback,
IQuerySourceOld* pSource,
DWORD dwFlags,
ASSOCQUERY query,
PCWSTR valueName,
PVOID pValue)
{
if (!pSource)
return E_INVALIDARG;
if (query == (ASSOCQUERY_SDED | ASSOCQUERY_EXTRA_NON_VERB) ||
query == ASSOCQUERY_LOCALIZEDSTRING)
{
return callback(pSource, query, NULL, valueName, pValue);
}
if ((query & dwFlags) != dwFlags)
return E_INVALIDARG;
const QUERYKEYVAL* pItems = NULL;
UINT cItems = _GetQueryKeyVal(&pItems);
if (!cItems)
return E_INVALIDARG;
return _QueryKeyValAny(callback, pItems, cItems, pSource, query, valueName, pValue);
}
STDMETHODIMP CAssocElement::QueryInterface(REFIID riid, PVOID* ppv)
{
if (riid == IID_IObjectWithQuerySourceOld)
{
*ppv = static_cast<IObjectWithQuerySourceOld*>(this);
AddRef();
return S_OK;
}
if (riid == IID_IAssociationElementOld)
{
*ppv = static_cast<IAssociationElementOld*>(this);
AddRef();
return S_OK;
}
ERR("E_NOINTERFACE: %s\n", wine_dbgstr_guid(&riid));
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CAssocElement::AddRef()
{
return InterlockedIncrement(&m_cRefs);
}
STDMETHODIMP_(ULONG) CAssocElement::Release()
{
LONG refs = InterlockedDecrement(&m_cRefs);
if (!refs)
delete this;
return refs;
}
STDMETHODIMP CAssocElement::SetSource(IQuerySourceOld* pSource)
{
if (m_pSource)
return E_UNEXPECTED;
m_pSource = pSource;
m_pSource->AddRef();
return S_OK;
}
STDMETHODIMP CAssocElement::GetSource(REFIID riid, PVOID* ppSource)
{
if (!m_pSource)
{
*ppSource = NULL;
return E_NOINTERFACE;
}
return m_pSource->QueryInterface(riid, ppSource);
}
STDMETHODIMP CAssocElement::QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue)
{
*ppszValue = NULL;
return _QuerySourceAny(_QuerySourceString, m_pSource, (ASSOCQUERY_STRING | ASSOCQUERY_DIRECT),
query, key, ppszValue);
}
STDMETHODIMP CAssocElement::QueryDword(ASSOCQUERY query, PCWSTR key, DWORD* pdwValue)
{
return _QuerySourceAny(_QuerySourceDword, m_pSource, (ASSOCQUERY_DWORD | ASSOCQUERY_DIRECT),
query, key, pdwValue);
}
STDMETHODIMP CAssocElement::QueryExists(ASSOCQUERY query, PCWSTR key)
{
return _QuerySourceAny(_QuerySourceExists, m_pSource, (ASSOCQUERY_STRING | ASSOCQUERY_DIRECT),
query, key, NULL);
}
STDMETHODIMP CAssocElement::QueryDirect(ASSOCQUERY query, PCWSTR key, FLAGGED_BYTE_BLOB** ppBlob)
{
*ppBlob = NULL;
return _QuerySourceAny(_QuerySourceDirect, m_pSource, ASSOCQUERY_DIRECT, query, key, ppBlob);
}
STDMETHODIMP CAssocElement::QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppvObj)
{
*ppvObj = NULL;
return E_NOTIMPL;
}
/******************************************************************************
* CAssocShellElement methods
*/
CAssocShellElement::~CAssocShellElement()
{
if (m_pszName && m_pszName != m_szBuff)
LocalFree(m_pszName);
}
STDMETHODIMP CAssocShellElement::QueryInterface(REFIID riid, PVOID* ppv)
{
if (riid == IID_IPersist)
{
*ppv = static_cast<IPersist*>(this);
AddRef();
return S_OK;
}
if (riid == IID_IPersistString2)
{
*ppv = static_cast<IPersistString2*>(this);
AddRef();
return S_OK;
}
return CAssocElement::QueryInterface(riid, ppv);
}
STDMETHODIMP_(ULONG) CAssocShellElement::AddRef()
{
return CAssocElement::AddRef();
}
STDMETHODIMP_(ULONG) CAssocShellElement::Release()
{
return CAssocElement::Release();
}
HRESULT CAssocShellElement::_InitSource()
{
return _QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, m_pszName, 0, &m_pSource);
}
UINT CAssocShellElement::_GetQueryKeyVal(const QUERYKEYVAL** ppItems)
{
*ppItems = g_shellKeyVals;
return _countof(g_shellKeyVals);
}
BOOL CAssocShellElement::_UseEnumForDefaultVerb()
{
return FALSE;
}
BOOL CAssocShellElement::_IsAppSource()
{
return FALSE;
}
HRESULT CAssocShellElement::_GetVerbDelegate(PCWSTR pszSrc, IAssociationElementOld** ppElement)
{
if (!m_pSource)
return E_FAIL;
HRESULT hr;
IQuerySourceOld* pSource;
if (pszSrc)
hr = QSOpen2(m_pSource, L"shell", pszSrc, 0, &pSource);
else
hr = _DefaultVerbSource(&pSource);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
BOOL bAppSource = _IsAppSource();
auto pElement = new(std::nothrow) CAssocShellVerbElement(bAppSource);
if (!pElement)
{
ERR("E_OUTOFMEMORY\n");
pSource->Release();
return E_OUTOFMEMORY;
}
hr = pElement->SetSource(pSource);
*ppElement = static_cast<IAssociationElementOld*>(pElement);
pSource->Release();
return hr;
}
STDMETHODIMP CAssocShellElement::QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue)
{
if (!(query & ASSOCQUERY_EXTRA_VERB))
return CAssocElement::QueryString(query, key, ppszValue);
IAssociationElementOld* pDelegate;
HRESULT hr = _GetVerbDelegate(key, &pDelegate);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = pDelegate->QueryString(query, NULL, ppszValue);
pDelegate->Release();
return hr;
}
STDMETHODIMP CAssocShellElement::QueryDword(ASSOCQUERY query, PCWSTR key, DWORD* pdwValue)
{
if (!(query & ASSOCQUERY_EXTRA_VERB))
return CAssocElement::QueryDword(query, key, pdwValue);
IAssociationElementOld* pDelegate;
HRESULT hr = _GetVerbDelegate(key, &pDelegate);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = pDelegate->QueryDword(query, NULL, pdwValue);
pDelegate->Release();
return hr;
}
STDMETHODIMP CAssocShellElement::QueryExists(ASSOCQUERY query, PCWSTR key)
{
if (!(query & ASSOCQUERY_EXTRA_VERB))
return CAssocElement::QueryExists(query, key);
IAssociationElementOld* pDelegate;
HRESULT hr = _GetVerbDelegate(key, &pDelegate);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = pDelegate->QueryExists(query, NULL);
pDelegate->Release();
return hr;
}
STDMETHODIMP
CAssocShellElement::QueryDirect(ASSOCQUERY query, PCWSTR key, FLAGGED_BYTE_BLOB** ppBlob)
{
if (!(query & ASSOCQUERY_EXTRA_VERB))
return CAssocElement::QueryDirect(query, key, ppBlob);
IAssociationElementOld* pDelegate;
HRESULT hr = _GetVerbDelegate(key, &pDelegate);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = pDelegate->QueryDirect(query, NULL, ppBlob);
pDelegate->Release();
return hr;
}
STDMETHODIMP
CAssocShellElement::QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppvObj)
{
if (!(query & ASSOCQUERY_EXTRA_VERB))
{
*ppvObj = NULL;
return E_INVALIDARG;
}
IAssociationElementOld* pDelegate;
HRESULT hr = _GetVerbDelegate(key, &pDelegate);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (query == (ASSOCQUERY_OBJECT | ASSOCQUERY_EXTRA_VERB))
hr = pDelegate->QueryInterface(riid, ppvObj);
else
hr = pDelegate->QueryObject(query, NULL, riid, ppvObj);
pDelegate->Release();
return hr;
}
STDMETHODIMP CAssocShellElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocShellElement;
return S_OK;
}
STDMETHODIMP CAssocShellElement::SetString(PCWSTR psz)
{
if (m_pszName)
return E_UNEXPECTED;
UINT cchBuff, cchString = lstrlenW(psz);
PWSTR pszBuff;
if (cchString >= _countof(m_szBuff))
{
cchBuff = cchString + 1;
pszBuff = (PWSTR)LocalAlloc(LPTR, cchBuff * sizeof(WCHAR));
}
else
{
pszBuff = m_szBuff;
cchBuff = _countof(m_szBuff);
}
m_pszName = pszBuff;
if (!m_pszName)
return E_UNEXPECTED;
StringCchCopyW(pszBuff, cchBuff, psz);
return _InitSource();
}
STDMETHODIMP CAssocShellElement::GetString(PWSTR* ppsz)
{
return SHStrDupW(m_pszName, ppsz);
}
HRESULT CAssocShellElement::_DefaultVerbSource(IQuerySourceOld** ppSource)
{
IQuerySourceOld* pShellSource;
HRESULT hr = m_pSource->OpenSource(L"shell", FALSE, &pShellSource);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
PWSTR pszValue;
PCWSTR pszVerb = L"open";
if (SUCCEEDED(pShellSource->QueryValueString(NULL, NULL, &pszValue)))
pszVerb = pszValue;
hr = pShellSource->OpenSource(pszVerb, FALSE, ppSource);
if (FAILED_UNEXPECTEDLY(hr))
{
if (pszValue)
{
INT nDelim = StrCSpnW(pszValue, L" ,");
if (nDelim != lstrlenW(pszValue))
{
pszValue[nDelim] = UNICODE_NULL;
hr = pShellSource->OpenSource(pszValue, FALSE, ppSource);
}
}
else
{
IEnumString* pEnum;
if (_UseEnumForDefaultVerb() && SUCCEEDED(pShellSource->EnumSources(&pEnum)))
{
PWSTR pszFirst = NULL;
ULONG cFetched = 0;
if (SUCCEEDED(pEnum->Next(1, &pszFirst, &cFetched)))
{
hr = pShellSource->OpenSource(pszFirst, FALSE, ppSource);
CoTaskMemFree(pszFirst);
}
pEnum->Release();
}
}
}
if (pszValue)
CoTaskMemFree(pszValue);
pShellSource->Release();
return hr;
}
/******************************************************************************
* CAssocShellVerbElement methods
*/
CAssocShellVerbElement::CAssocShellVerbElement(BOOL bAppSource)
: m_bAppSource(bAppSource)
{
}
STDMETHODIMP CAssocShellVerbElement::QueryString(
ASSOCQUERY query,
PCWSTR key,
PWSTR* ppwsz)
{
HRESULT hr = CAssocElement::QueryString(query, key, ppwsz);
if (SUCCEEDED(hr))
return hr;
PWSTR psz;
if (query == ASSOCQUERY_SEV_7)
{
hr = CAssocElement::QueryString(ASSOCQUERY_COMMAND, 0, (PWSTR*)&psz);
if (SUCCEEDED(hr))
hr = _ExeFromCmd(psz, ppwsz);
if (psz)
CoTaskMemFree(psz);
return hr;
}
if (query == ASSOCQUERY_DDEEXEC_APPLICATION)
{
hr = QueryString(ASSOCQUERY_DELEGATE, NULL, &psz);
if (SUCCEEDED(hr))
{
PathStripPathW(psz);
PathRemoveExtensionW(psz);
}
*ppwsz = psz;
return hr;
}
if (query == ASSOCQUERY_DDEEXEC_TOPIC)
return SHStrDupW(L"System", ppwsz);
if (!m_bAppSource && query == ASSOCQUERY_VERB_FRIENDLYAPPNAME)
{
IAssociationElementOld* pElement;
hr = _GetAppDelegate(IID_PPV_ARG(IAssociationElementOld, &pElement));
if (SUCCEEDED(hr))
{
hr = pElement->QueryString(ASSOCQUERY_APPKEY_FRIENDLYAPPNAME, NULL, ppwsz);
pElement->Release();
}
}
return hr;
}
UINT CAssocShellVerbElement::_GetQueryKeyVal(const QUERYKEYVAL** ppItems)
{
*ppItems = g_shellVerbKeyVals;
return _countof(g_shellVerbKeyVals);
}
HRESULT CAssocShellVerbElement::_GetAppDelegate(REFIID riid, PVOID* ppv)
{
PWSTR psz = NULL;
HRESULT hr = CAssocElement::QueryString(ASSOCQUERY_DELEGATE, NULL, &psz);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
IPersistString2* pPS2;
hr = AssocCreateElement(CLSID_AssocApplicationElement, IID_PPV_ARG(IPersistString2, &pPS2));
if (SUCCEEDED(hr))
{
hr = pPS2->SetString(psz);
if (SUCCEEDED(hr))
hr = pPS2->QueryInterface(riid, ppv);
pPS2->Release();
}
if (psz)
CoTaskMemFree(psz);
return hr;
}
STDMETHODIMP
CAssocShellVerbElement::QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppvObj)
{
if (query != ASSOCQUERY_OBJECT_EV_1)
return E_INVALIDARG;
return _GetAppDelegate(riid, ppvObj);
}
/******************************************************************************
* CAssocApplicationElement methods
*/
UINT CAssocApplicationElement::_GetQueryKeyVal(const QUERYKEYVAL** ppItems)
{
*ppItems = g_shellAppKeyVals;
return _countof(g_shellAppKeyVals);
}
HRESULT CAssocApplicationElement::_InitSource()
{
PWSTR pchFileName = PathFindFileNameW(m_pszName);
WCHAR szSubKey[MAX_PATH];
_MakeApplicationsKey(pchFileName, szSubKey, _countof(szSubKey));
HRESULT hr = QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, szSubKey, FALSE,
IID_PPV_ARG(IQuerySourceOld, &m_pSource));
m_bHasDir = (pchFileName != m_pszName);
if (FAILED_UNEXPECTEDLY(hr) && m_bHasDir && PathFileExistsW(m_pszName))
return S_FALSE;
return hr;
}
STDMETHODIMP CAssocApplicationElement::GetClassID(CLSID* pClsid)
{
*pClsid = CLSID_AssocApplicationElement;
return S_OK;
}
STDMETHODIMP
CAssocApplicationElement::QueryObject(ASSOCQUERY query, PCWSTR key, REFIID riid, PVOID* ppv)
{
if (query == ASSOCQUERY_OBJECT_EV_1)
return CAssocElement::QueryInterface(riid, ppv);
return CAssocShellElement::QueryObject(query, key, riid, ppv);
}
STDMETHODIMP CAssocApplicationElement::QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppsz)
{
HRESULT hr = CAssocShellElement::QueryString(query, key, ppsz);
if (FAILED_UNEXPECTEDLY(hr) && query == ASSOCQUERY_APPKEY_FRIENDLYAPPNAME)
return _GetAppDisplayName(ppsz);
return hr;
}
HRESULT CAssocApplicationElement::_GetAppDisplayName(PWSTR* ppsz)
{
PWSTR pszName;
if (!m_bHasDir)
{
HRESULT hr = QueryString(ASSOCQUERY_DELEGATE, NULL, &pszName);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
}
else
{
pszName = m_pszName;
}
WCHAR szData[MAX_PATH];
DWORD cbData = sizeof(szData);
DWORD flags = SHKEY_Subkey_MUICache | SHKEY_Key_ShellNoRoam | SHKEY_Root_HKCU;
HRESULT hr = SKGetValueW(flags, NULL, pszName, 0, szData, &cbData);
if (FAILED_UNEXPECTEDLY(hr))
{
UINT cchData = MAX_PATH;
if (SHGetFileDescriptionW(pszName, NULL, NULL, szData, &cchData))
{
INT cchName = lstrlenW(szData);
UINT cbName = (cchName + 1) * sizeof(WCHAR);
SKSetValueW(flags, NULL, pszName, REG_SZ, (PBYTE)szData, cbName);
hr = S_OK;
}
}
if (SUCCEEDED(hr))
hr = SHStrDupW(szData, ppsz);
if (!m_bHasDir)
CoTaskMemFree(pszName);
return hr;
}
/******************************************************************************
* CAssocProgidElement methods
*/
CAssocProgidElement::~CAssocProgidElement()
{
if (m_pSource2)
{
m_pSource2->Release();
m_pSource2 = NULL;
}
}
STDMETHODIMP CAssocProgidElement::QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue)
{
HRESULT hr = CAssocShellElement::QueryString(query, key, ppszValue);
if (SUCCEEDED(hr))
return hr;
if (m_pSource2 && (query & ASSOCQUERY_FALLBACK))
{
return _QueryKeyValAny(_QuerySourceString,
g_progidKeyVals, _countof(g_progidKeyVals),
m_pSource2,
query,
key,
ppszValue);
}
if (m_pSource && query == ASSOCQUERY_FRIENDLYAPPNAME)
return m_pSource->QueryValueString(NULL, NULL, ppszValue);
return hr;
}
STDMETHODIMP CAssocProgidElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocProgidElement;
return S_OK;
}
BOOL CAssocProgidElement::_UseEnumForDefaultVerb()
{
return TRUE;
}
HRESULT CAssocProgidElement::_InitSource()
{
HRESULT hr = S_OK;
PWSTR pszName;
if (*m_pszName == L'.')
{
hr = _QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, m_pszName, 0, &m_pSource2);
if (FAILED_UNEXPECTEDLY(hr))
goto Exit;
hr = m_pSource2->QueryValueString(NULL, NULL, &pszName);
}
else
{
pszName = m_pszName;
}
if (SUCCEEDED(hr))
{
HKEY hKey = _OpenProgidKey(pszName);
if (hKey)
{
hr = _QuerySourceCreateFromKey(hKey, NULL, NULL, &m_pSource);
RegCloseKey(hKey);
}
else
{
hr = E_UNEXPECTED;
}
if (pszName != m_pszName)
CoTaskMemFree(pszName);
}
Exit:
if (FAILED_UNEXPECTEDLY(hr) && m_pSource2)
{
m_pSource = m_pSource2;
m_pSource2 = NULL;
return S_FALSE;
}
return hr;
}
/******************************************************************************
* CAssocClsidElement methods
*/
STDMETHODIMP CAssocClsidElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocClsidElement;
return S_OK;
}
HRESULT CAssocClsidElement::_InitSource()
{
return _QuerySourceCreateFromKey2(HKEY_CLASSES_ROOT, L"CLSID", m_pszName, &m_pSource);
}
/******************************************************************************
* CAssocSystemExtElement methods
*/
STDMETHODIMP CAssocSystemExtElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocSystemElement;
return S_OK;
}
HRESULT CAssocSystemExtElement::_InitSource()
{
return _QuerySourceCreateFromKey2(HKEY_CLASSES_ROOT, L"SystemFileAssociations", m_pszName,
&m_pSource);
}
/******************************************************************************
* CAssocFolderElement methods
*/
STDMETHODIMP CAssocFolderElement::QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue)
{
if (query == ASSOCQUERY_FRIENDLYAPPNAME)
return _SHAllocLoadString(GetModuleHandleW(L"shlwapi.dll"), IDS_FOLDER, ppszValue);
return CAssocShellElement::QueryString(query, key, ppszValue);
}
STDMETHODIMP CAssocFolderElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocFolderElement;
return S_OK;
}
HRESULT CAssocFolderElement::_InitSource()
{
return QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, L"Folder", FALSE,
IID_PPV_ARG(IQuerySourceOld, &m_pSource));
}
/******************************************************************************
* CAssocStarElement methods
*/
STDMETHODIMP CAssocStarElement::QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue)
{
if (query == ASSOCQUERY_FRIENDLYAPPNAME)
return _GetFileTypeName(m_pszName, ppszValue);
return CAssocShellElement::QueryString(query, key, ppszValue);
}
STDMETHODIMP CAssocStarElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocStarElement;
return S_OK;
}
HRESULT CAssocStarElement::_InitSource()
{
return QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, L"*", FALSE,
IID_PPV_ARG(IQuerySourceOld, &m_pSource));
}
/******************************************************************************
* CAssocPerceivedElement methods
*/
UINT CAssocPerceivedElement::_GetQueryKeyVal(const QUERYKEYVAL** ppItems)
{
if (m_bPerceived)
{
*ppItems = g_perceivedKeyVals;
return _countof(g_perceivedKeyVals);
}
else
{
*ppItems = g_shellKeyVals;
return _countof(g_shellKeyVals);
}
}
STDMETHODIMP CAssocPerceivedElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocPerceivedElement;
return S_OK;
}
STDMETHODIMP CAssocPerceivedElement::GetSource(REFIID riid, PVOID* ppSource)
{
*ppSource = NULL;
if (m_bPerceived)
return E_FAIL;
return CAssocElement::GetSource(riid, ppSource);
}
HRESULT CAssocPerceivedElement::_InitSource()
{
PERCEIVED type;
PERCEIVEDFLAG flags;
PWSTR pszType;
HRESULT hr = AssocGetPerceivedType(m_pszName, &type, &flags, &pszType);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
m_bPerceived = !(flags & PERCEIVEDFLAG_NATIVESUPPORT);
hr = _QuerySourceCreateFromKey2(HKEY_CLASSES_ROOT, L"SystemFileAssociations", pszType,
&m_pSource);
CoTaskMemFree(pszType);
return hr;
}
HRESULT CAssocPerceivedElement::_GetVerbDelegate(PCWSTR pszSrc, IAssociationElementOld** ppElement)
{
if (m_bPerceived)
return E_FAIL;
return CAssocShellElement::_GetVerbDelegate(pszSrc, ppElement);
}
/******************************************************************************
* CAssocClientElement methods
*/
STDMETHODIMP CAssocClientElement::QueryString(ASSOCQUERY query, PCWSTR key, PWSTR* ppszValue)
{
HRESULT hr;
if (query == ASSOCQUERY_DEFAULTICON)
{
hr = CAssocElement::QueryString(ASSOCQUERY_DEFAULTICON, key, ppszValue);
if (SUCCEEDED(hr))
return hr;
IAssociationElementOld* pDelegate;
HRESULT hr = _GetVerbDelegate(key, &pDelegate);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = pDelegate->QueryString(ASSOCQUERY_DELEGATE, L"open", ppszValue);
pDelegate->Release();
return hr;
}
if (query == ASSOCQUERY_FRIENDLYAPPNAME)
{
hr = CAssocElement::QueryString(ASSOCQUERY_LOCALIZEDSTRING, L"LocalizedString", ppszValue);
if (SUCCEEDED(hr))
return hr;
return CAssocElement::QueryString((ASSOCQUERY_SDED | ASSOCQUERY_EXTRA_NON_VERB), NULL, ppszValue);
}
return CAssocShellElement::QueryString(query, key, ppszValue);
}
STDMETHODIMP CAssocClientElement::GetClassID(CLSID* pClassID)
{
*pClassID = CLSID_AssocClientElement;
return S_OK;
}
HRESULT CAssocClientElement::_InitSource()
{
WCHAR szBuff[MAX_PATH];
StringCchPrintfW(szBuff, _countof(szBuff), L"SOFTWARE\\Clients\\%s", m_pszName);
HRESULT hr = _InitSourceFromKey(HKEY_CURRENT_USER, szBuff, 0);
if (SUCCEEDED(hr))
return hr;
return _InitSourceFromKey(HKEY_LOCAL_MACHINE, szBuff, 0);
}
HRESULT CAssocClientElement::_FixNetscapeRegistration()
{
HKEY hKeyParent;
LSTATUS error = RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Clients\\Mail", 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hKeyParent, NULL);
if (error)
return E_FAIL;
DWORD dwDisposition;
HKEY hKey;
error = RegCreateKeyExW(hKeyParent, L"Netscape Messenger", 0, NULL, REG_OPTION_VOLATILE,
KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition);
if (error)
{
SHDeleteKeyW(hKeyParent, L"Netscape Messenger");
RegCloseKey(hKeyParent);
return E_FAIL;
}
HRESULT hr = E_FAIL;
if (dwDisposition == REG_OPENED_EXISTING_KEY || _CreateRepairedNetscapeRegistration(hKey))
{
IQuerySourceOld* pNewSource = NULL;
hr = _QuerySourceCreateFromKey(hKey, NULL, NULL, &pNewSource);
if (SUCCEEDED(hr))
{
if (m_pSource)
m_pSource->Release();
m_pSource = pNewSource;
}
}
RegCloseKey(hKey);
if (FAILED_UNEXPECTEDLY(hr))
SHDeleteKeyW(hKeyParent, L"Netscape Messenger");
RegCloseKey(hKeyParent);
return hr;
}
BOOL CAssocClientElement::_CreateRepairedNetscapeRegistration(HKEY hKey)
{
HKEY hkResult;
LSTATUS error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"Software\\Clients\\Mail\\Netscape Messenger",
0, KEY_READ, &hkResult);
if (error)
return FALSE;
WCHAR szIconFile[MAX_PATH];
error = _RegQueryString(hkResult, L"Protocols\\mailto\\DefaultIcon",
szIconFile, _countof(szIconFile));
if (error == ERROR_SUCCESS)
{
PathParseIconLocationW(szIconFile);
StringCchCatW(szIconFile, _countof(szIconFile), L",-1349");
_RegSetVolatileString(hKey, L"DefaultIcon", szIconFile);
}
BOOL ret = FALSE;
PWSTR lpString2 = NULL;
if (_RegQueryString(hkResult, 0, szIconFile, _countof(szIconFile)) == ERROR_SUCCESS &&
_RegSetVolatileString(hKey, 0, szIconFile) == ERROR_SUCCESS &&
_RegQueryString(hkResult, L"Protocols\\mailto\\shell\\open\\command",
szIconFile, _countof(szIconFile)) == ERROR_SUCCESS &&
SUCCEEDED(_ExeFromCmd(szIconFile, &lpString2)))
{
StringCchCopyW(szIconFile, _countof(szIconFile), lpString2);
CoTaskMemFree(lpString2);
PathQuoteSpacesW(szIconFile);
StringCchCatW(szIconFile, _countof(szIconFile), L" -mail");
if (_RegSetVolatileString(hKey, L"shell\\open\\command", szIconFile) == ERROR_SUCCESS)
ret = TRUE;
}
RegCloseKey(hkResult);
return ret;
}
HRESULT CAssocClientElement::_InitSourceFromKey(HKEY hKey, PCWSTR lpSubKey, DWORD dwFlags)
{
HKEY hKey2;
LSTATUS error = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ | dwFlags, &hKey2);
if (error)
return E_INVALIDARG;
HRESULT hr = E_INVALIDARG;
HKEY hkeySrc = NULL;
WCHAR szData[80];
DWORD dwType, cbData = sizeof(szData);
error = RegQueryValueExW(hKey2, NULL, NULL, &dwType, (PBYTE)szData, &cbData);
if (error == ERROR_SUCCESS && dwType == REG_SZ && szData[0] &&
RegOpenKeyExW(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &hkeySrc) == ERROR_SUCCESS)
{
hr = _QuerySourceCreateFromKey2(hkeySrc, NULL, szData, &m_pSource);
RegCloseKey(hkeySrc);
}
RegCloseKey(hKey2);
if (FAILED_UNEXPECTEDLY(hr) ||
lstrcmpiW(m_pszName, L"mail") != 0 ||
lstrcmpiW(szData, L"Netscape Messenger") != 0 ||
SUCCEEDED(QueryExists(ASSOCQUERY_COMMAND, L"open")))
{
return hr;
}
return _FixNetscapeRegistration();
}
/******************************************************************************
* AssocCreateElement [SHELL32.764] (Vista+)
* AssocCreateElement [SHLWAPI internal] (XP)
*
* @see AssocCreate
* @see https://www.geoffchappell.com/studies/windows/shell/shell32/api/assocelem/createelement.htm
*/
EXTERN_C HRESULT WINAPI
AssocCreateElement(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ PVOID* ppvObj)
{
IAssociationElementOld* pElement;
if (rclsid == CLSID_AssocShellElement)
{
auto pNewElement = new(std::nothrow) CAssocShellElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocApplicationElement)
{
auto pNewElement = new(std::nothrow) CAssocApplicationElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocProgidElement)
{
auto pNewElement = new(std::nothrow) CAssocProgidElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocClsidElement)
{
auto pNewElement = new(std::nothrow) CAssocClsidElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocSystemElement)
{
auto pNewElement = new(std::nothrow) CAssocSystemExtElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocFolderElement)
{
auto pNewElement = new(std::nothrow) CAssocFolderElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocStarElement)
{
auto pNewElement = new(std::nothrow) CAssocStarElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocPerceivedElement)
{
auto pNewElement = new(std::nothrow) CAssocPerceivedElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else if (rclsid == CLSID_AssocClientElement)
{
auto pNewElement = new(std::nothrow) CAssocClientElement();
pElement = static_cast<IAssociationElementOld*>(pNewElement);
}
else
{
ERR("rclsid: %s\n", wine_dbgstr_guid(&rclsid));
*ppvObj = NULL;
return CLASS_E_CLASSNOTAVAILABLE;
}
if (!pElement)
{
ERR("E_OUTOFMEMORY\n");
return E_OUTOFMEMORY;
}
HRESULT hr = pElement->QueryInterface(riid, ppvObj);
pElement->Release();
return hr;
}