windows-nt/Source/XPSP1/NT/shell/shlwapi/assocapi.cpp
2020-09-26 16:20:57 +08:00

755 lines
22 KiB
C++

//
//
// assocapi.cpp
//
// Association APIs
//
//
//
#include "priv.h"
#include "apithk.h"
#include <shstr.h>
#ifdef _X86_
#include <w95wraps.h>
#endif
#include <msi.h>
#include "assoc.h"
#include <filetype.h>
BOOL _PathAppend(LPCTSTR pszBase, LPCTSTR pszAppend, LPTSTR pszOut, DWORD cchOut);
void _MakeAppPathKey(LPCTSTR pszApp, LPTSTR pszKey, DWORD cchKey)
{
if (_PathAppend(REGSTR_PATH_APPPATHS, pszApp, pszKey, cchKey))
{
// Currently we will only look up .EXE if an extension is not
// specified
if (*PathFindExtension(pszApp) == 0)
{
StrCatBuff(pszKey, TEXT(".exe"), cchKey);
}
}
}
void _MakeApplicationsKey(LPCWSTR pszApp, LPWSTR pszKey, DWORD cchKey)
{
if (_PathAppend(TEXT("Applications"), pszApp, pszKey, cchKey))
{
// Currently we will only look up .EXE if an extension is not
// specified
if (*PathFindExtension(pszApp) == 0)
{
StrCatBuff(pszKey, TEXT(".exe"), cchKey);
}
}
}
HRESULT _AssocOpenRegKey(HKEY hk, LPCTSTR pszSub, HKEY *phkOut, BOOL fCreate)
{
ASSERT(phkOut);
*phkOut = NULL;
if (!hk)
return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
DWORD err;
if (!fCreate)
err = RegOpenKeyEx(hk, pszSub, 0, MAXIMUM_ALLOWED, phkOut);
else
err = RegCreateKeyEx(hk, pszSub, 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, phkOut, NULL);
if (ERROR_SUCCESS != err)
{
ASSERT(!*phkOut);
return HRESULT_FROM_WIN32(err);
}
return S_OK;
}
LWSTDAPI AssocCreate(CLSID clsid, REFIID riid, LPVOID *ppvOut)
{
HRESULT hr = E_INVALIDARG;
if (ppvOut)
{
if (IsEqualGUID(clsid, CLSID_QueryAssociations)
|| IsEqualGUID(clsid, IID_IQueryAssociations))
{
if (IsOS(OS_WHISTLERORGREATER))
hr = SHCoCreateInstance(NULL, &CLSID_QueryAssociations, NULL, riid, ppvOut);
else
{
hr = AssocCreateW2k(riid, ppvOut);
}
}
else
hr = AssocCreateElement(clsid, riid, ppvOut);
}
return hr;
}
#define ASSOCF_INIT_ALL (ASSOCF_INIT_BYEXENAME | ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_DEFAULTTOSTAR | ASSOCF_INIT_NOREMAPCLSID)
LWSTDAPI AssocQueryStringW(ASSOCF flags, ASSOCSTR str, LPCWSTR pszAssoc, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut)
{
IQueryAssociations *passoc;
HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&passoc);
if (SUCCEEDED(hr))
{
hr = passoc->Init(flags & ASSOCF_INIT_ALL, pszAssoc, NULL, NULL);
if (SUCCEEDED(hr))
hr = passoc->GetString(flags, str, pszExtra, pszOut, pcchOut);
passoc->Release();
}
return hr;
}
LWSTDAPI AssocQueryStringA(ASSOCF flags, ASSOCSTR str, LPCSTR pszAssoc, LPCSTR pszExtra, LPSTR pszOut, DWORD *pcchOut)
{
if (!pcchOut)
return E_INVALIDARG;
HRESULT hr = E_OUTOFMEMORY;
SHSTRW strAssoc, strExtra;
if (SUCCEEDED(strAssoc.SetStr(pszAssoc))
&& SUCCEEDED(strExtra.SetStr(pszExtra)))
{
SHSTRW strOut;
DWORD cchIn = IS_INTRESOURCE(pcchOut) ? PtrToUlong(pcchOut) : *pcchOut;
DWORD cch;
LPTSTR pszTemp = NULL;
if (pszOut)
{
strOut.SetSize(cchIn);
cch = strOut.GetSize();
pszTemp = strOut.GetInplaceStr();
}
hr = AssocQueryStringW(flags, str, strAssoc, strExtra, pszTemp, &cch);
if (SUCCEEDED(hr))
{
cch = SHUnicodeToAnsi(strOut, pszOut, cchIn);
if (!IS_INTRESOURCE(pcchOut))
*pcchOut = cch;
}
}
return hr;
}
LWSTDAPI AssocQueryStringByKeyW(ASSOCF flags, ASSOCSTR str, HKEY hkAssoc, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut)
{
IQueryAssociations *passoc;
HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&passoc);
if (SUCCEEDED(hr))
{
hr = passoc->Init(flags & ASSOCF_INIT_ALL, NULL, hkAssoc, NULL);
if (SUCCEEDED(hr))
hr = passoc->GetString(flags, str, pszExtra, pszOut, pcchOut);
passoc->Release();
}
return hr;
}
LWSTDAPI AssocQueryStringByKeyA(ASSOCF flags, ASSOCSTR str, HKEY hkAssoc, LPCSTR pszExtra, LPSTR pszOut, DWORD *pcchOut)
{
if (!pcchOut)
return E_INVALIDARG;
HRESULT hr = E_OUTOFMEMORY;
SHSTRW strExtra;
if (SUCCEEDED(strExtra.SetStr(pszExtra)))
{
SHSTRW strOut;
DWORD cchIn = IS_INTRESOURCE(pcchOut) ? PtrToUlong(pcchOut) : *pcchOut;
DWORD cch;
LPWSTR pszTemp = NULL;
if (pszOut)
{
strOut.SetSize(cchIn);
cch = strOut.GetSize();
pszTemp = strOut.GetInplaceStr();
}
hr = AssocQueryStringByKeyW(flags, str, hkAssoc, strExtra, pszTemp, &cch);
if (SUCCEEDED(hr))
{
cch = SHUnicodeToAnsi(strOut, pszOut, cchIn);
if (!IS_INTRESOURCE(pcchOut))
*pcchOut = cch;
}
}
return hr;
}
LWSTDAPI AssocQueryKeyW(ASSOCF flags, ASSOCKEY key, LPCWSTR pszAssoc, LPCWSTR pszExtra, HKEY *phkey)
{
IQueryAssociations *passoc;
HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&passoc);
if (SUCCEEDED(hr))
{
hr = passoc->Init(flags & ASSOCF_INIT_ALL, pszAssoc, NULL, NULL);
if (SUCCEEDED(hr))
hr = passoc->GetKey(flags, key, pszExtra, phkey);
passoc->Release();
}
return hr;
}
LWSTDAPI AssocQueryKeyA(ASSOCF flags, ASSOCKEY key, LPCSTR pszAssoc, LPCSTR pszExtra, HKEY *phkey)
{
HRESULT hr = E_OUTOFMEMORY;
SHSTRW strAssoc, strExtra;
if (SUCCEEDED(strAssoc.SetStr(pszAssoc))
&& SUCCEEDED(strExtra.SetStr(pszExtra)))
{
hr = AssocQueryKeyW(flags, key, strAssoc, strExtra, phkey);
}
return hr;
}
#define ISQUOTED(s) (TEXT('"') == *(s) && TEXT('"') == *((s) + lstrlen(s) - 1))
BOOL _TrySubst(SHSTR& str, LPCTSTR psz)
{
BOOL fRet = FALSE;
TCHAR szVar[MAX_PATH];
DWORD cch = GetEnvironmentVariable(psz, szVar, SIZECHARS(szVar));
if (cch && cch <= SIZECHARS(szVar))
{
if (0 == StrCmpNI(str, szVar, cch))
{
// we got a match.
// size the buffer for the env var... +3 = (% + % + \0)
SHSTR strT;
if (S_OK == strT.SetStr(str.GetStr() + cch)
&& S_OK == str.SetSize(str.GetLen() - cch + lstrlen(psz) + 3)
)
{
wnsprintf(str.GetInplaceStr(), str.GetSize(), TEXT("%%%s%%%s"), psz, strT.GetStr());
fRet = TRUE;
}
}
}
return fRet;
}
BOOL _TryEnvSubst(SHSTR& str)
{
static LPCTSTR rgszEnv[] = {
TEXT("USERPROFILE"),
TEXT("ProgramFiles"),
TEXT("SystemRoot"),
TEXT("SystemDrive"),
TEXT("windir"),
NULL
};
LPCTSTR *ppsz = rgszEnv;
BOOL fRet = FALSE;
while (*ppsz && !fRet)
{
fRet = _TrySubst(str, *ppsz++);
}
return fRet;
}
HRESULT _MakeCommandString(ASSOCF *pflags, LPCTSTR pszExe, LPCTSTR pszArgs, SHSTR& str)
{
SHSTR strArgs;
HRESULT hr;
if (!pszArgs || !*pszArgs)
{
// default to just passing the
// file name right in.
// NOTE 16bit apps might have a problem with
// this, but i request that the caller
// specify that this is the case....
pszArgs = TEXT("\"%1\"");
}
// else NO _ParseCommand()
hr = str.SetStr(pszExe);
if (S_OK == hr)
{
// check for quotes before doing env subst
BOOL fNeedQuotes = (!ISQUOTED(str.GetStr()) && PathIsLFNFileSpec(str));
// this will put environment vars into the string...
if ((*pflags & ASSOCMAKEF_SUBSTENV) && _TryEnvSubst(str))
{
*pflags |= ASSOCMAKEF_USEEXPAND;
}
str.Trim();
if (fNeedQuotes)
{
// 3 = " + " + \0
if (S_OK == str.SetSize(str.GetLen() + 3))
PathQuoteSpaces(str.GetInplaceStr());
}
hr = str.Append(TEXT(' '));
if (S_OK == hr)
{
hr = str.Append(pszArgs);
}
}
return hr;
}
HRESULT _AssocMakeCommand(ASSOCMAKEF flags, HKEY hkVerb, LPCWSTR pszExe, LPCWSTR pszArgs)
{
ASSERT(hkVerb && pszExe);
SHSTR str;
HRESULT hr = _MakeCommandString(&flags, pszExe, pszArgs, str);
if (S_OK == hr)
{
DWORD dw = (flags & ASSOCMAKEF_USEEXPAND) ? REG_EXPAND_SZ : REG_SZ;
DWORD err = SHSetValue(hkVerb, TEXT("command"), NULL, dw, (LPVOID) str.GetStr(), CbFromCch(str.GetLen() +1));
hr = HRESULT_FROM_WIN32(err);
}
return hr;
}
LWSTDAPI AssocMakeShell(ASSOCMAKEF flags, HKEY hkProgid, LPCWSTR pszApplication, ASSOCSHELL *pShell)
{
HRESULT hr = E_INVALIDARG;
if (hkProgid && pszApplication && pShell)
{
for (DWORD c = 0; c < pShell->cVerbs; c++)
{
ASSOCVERB *pverb = &pShell->rgVerbs[c];
if (pverb->pszVerb)
{
TCHAR szVerbKey[MAX_PATH];
HKEY hkVerb;
_PathAppend(TEXT("shell"), pverb->pszVerb, szVerbKey, SIZECHARS(szVerbKey));
if (c == pShell->iDefaultVerb)
SHSetValue(hkProgid, TEXT("shell"), NULL, REG_SZ, pverb->pszVerb, CbFromCch(lstrlen(pverb->pszVerb) +1));
// ASSOCMAKEF_FAILIFEXIST check if its ok to overwrite
if (SUCCEEDED(_AssocOpenRegKey(hkProgid, szVerbKey, &hkVerb, FALSE)))
{
RegCloseKey(hkVerb);
SHDeleteKey(hkProgid, szVerbKey);
}
if (SUCCEEDED(_AssocOpenRegKey(hkProgid, szVerbKey, &hkVerb, TRUE)))
{
if (pverb->pszTitle)
SHSetValue(hkVerb, NULL, NULL, REG_SZ, pverb->pszTitle, CbFromCch(lstrlen(pverb->pszTitle) +1));
hr = _AssocMakeCommand(flags, hkVerb, pverb->pszApplication ? pverb->pszApplication : pszApplication , pverb->pszParams);
// if (SUCCEEDED(hr) && pverb->pDDEExec)
// hr = _AssocMakeDDEExec(flags, hkVerb, pverb->pDDEExec);
RegCloseKey(hkVerb);
}
}
}
}
return hr;
}
HRESULT _OpenClasses(HKEY *phkOut)
{
*phkOut = NULL;
DWORD err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\classes"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, phkOut, NULL);
if (err)
err = RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\classes"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, phkOut, NULL);
return HRESULT_FROM_WIN32(err);
}
LWSTDAPI AssocMakeProgid(ASSOCMAKEF flags, LPCWSTR pszApplication, ASSOCPROGID *pProgid, HKEY *phkProgid)
{
HRESULT hr = E_INVALIDARG;
if (pszApplication
&& pProgid
&& pProgid->cbSize >= sizeof(ASSOCPROGID)
&& pProgid->pszProgid
&& *pProgid->pszProgid)
{
HKEY hkRoot;
if ((!(flags & ASSOCMAKEF_VERIFY) || PathFileExists(pszApplication))
&& SUCCEEDED(_OpenClasses(&hkRoot)))
{
HKEY hkProgid;
// need to add support for ASSOCMAKEF_VOLATILE...
hr = _AssocOpenRegKey(hkRoot, pProgid->pszProgid, &hkProgid, TRUE);
if (SUCCEEDED(hr))
{
if (pProgid->pszFriendlyDocName)
SHSetValue(hkProgid, NULL, NULL, REG_SZ, pProgid->pszFriendlyDocName, CbFromCch(lstrlen(pProgid->pszFriendlyDocName) +1));
if (pProgid->pszDefaultIcon)
SHSetValue(hkProgid, TEXT("DefaultIcon"), NULL, REG_SZ, pProgid->pszDefaultIcon, CbFromCch(lstrlen(pProgid->pszDefaultIcon) +1));
if (pProgid->pShellKey)
hr = AssocMakeShell(flags, hkProgid, pszApplication, pProgid->pShellKey);
if (SUCCEEDED(hr) && pProgid->pszExtensions)
{
LPCTSTR psz = pProgid->pszExtensions;
DWORD err = NOERROR;
while (*psz && NOERROR == err)
{
err = SHSetValue(hkRoot, psz, NULL, REG_SZ, pProgid->pszProgid, CbFromCch(lstrlen(pProgid->pszProgid) + 1));
psz += lstrlen(psz) + 1;
}
if (NOERROR != err)
HRESULT_FROM_WIN32(err);
}
if (SUCCEEDED(hr) && phkProgid)
*phkProgid = hkProgid;
else
RegCloseKey(hkProgid);
}
RegCloseKey(hkRoot);
}
}
return hr;
}
HRESULT _AssocCopyVerb(HKEY hkSrc, HKEY hkDst, LPCTSTR pszVerb)
{
HRESULT hr = S_OK;
TCHAR szKey[MAX_PATH];
HKEY hkVerb;
DWORD dwDisp;
// only copy the verb component
wnsprintf(szKey, SIZECHARS(szKey), TEXT("shell\\%s"), pszVerb);
RegCreateKeyEx(hkDst, szKey, 0, NULL, 0,
MAXIMUM_ALLOWED, NULL, &hkVerb, &dwDisp);
// create a failure state here...
if (hkVerb)
{
// we avoid overwriting old keys by checking the dwDisp
if ((dwDisp == REG_CREATED_NEW_KEY) && SHCopyKey(hkSrc, pszVerb, hkVerb, 0L))
hr = E_UNEXPECTED;
RegCloseKey(hkVerb);
}
return hr;
}
typedef BOOL (*PFNALLOWVERB)(LPCWSTR psz, LPARAM param);
LWSTDAPI _AssocCopyVerbs(HKEY hkSrc, HKEY hkDst, PFNALLOWVERB pfnAllow, LPARAM lParam)
{
HRESULT hr = E_INVALIDARG;
HKEY hkEnum;
if (SUCCEEDED(_AssocOpenRegKey(hkSrc, TEXT("shell"), &hkEnum, FALSE)))
{
TCHAR szVerb[MAX_PATH];
DWORD cchVerb = SIZECHARS(szVerb);
for (DWORD i = 0
; (NOERROR == RegEnumKeyEx(hkEnum, i, szVerb, &cchVerb, NULL, NULL, NULL, NULL))
; (cchVerb = SIZECHARS(szVerb)), i++)
{
if (!pfnAllow || pfnAllow(szVerb, lParam))
hr = _AssocCopyVerb(hkEnum, hkDst, szVerb);
}
// switch to cbVerb here
cchVerb = sizeof(szVerb);
if (NOERROR == SHGetValue(hkEnum, NULL, NULL, NULL, szVerb, &cchVerb))
{
SHSetValue(hkDst, TEXT("shell"), NULL, REG_SZ, szVerb, cchVerb);
}
RegCloseKey(hkEnum);
}
return hr;
}
LWSTDAPI AssocCopyVerbs(HKEY hkSrc, HKEY hkDst)
{
return _AssocCopyVerbs(hkSrc, hkDst, NULL, NULL);
}
BOOL _IsMSIPerUserInstall(IQueryAssociations *pqa, ASSOCF flags, LPCWSTR pszVerb)
{
WCHAR sz[MAX_PATH];
DWORD cb = sizeof(sz);
if (SUCCEEDED(pqa->GetData(flags, ASSOCDATA_MSIDESCRIPTOR, pszVerb, sz, &cb)))
{
WCHAR szOut[3]; // bit enough for "1" or "0"
cb = SIZECHARS(szOut);
if (NOERROR == MsiGetProductInfoW(sz, INSTALLPROPERTY_ASSIGNMENTTYPE, szOut, &cb))
{
// The string "1" for the value represents machine installations,
// while "0" represents user installations.
if (0 == StrCmpW(szOut, L"0"))
return TRUE;
}
}
return FALSE;
}
typedef struct {
IQueryAssociations *pqa;
ASSOCF Qflags;
LPCWSTR pszExe;
BOOL fAllowPerUser;
} QUERYEXECB;
BOOL _AllowExeVerb(LPCWSTR pszVerb, QUERYEXECB *pqcb)
{
BOOL fRet = FALSE;
WCHAR sz[MAX_PATH];
if (SUCCEEDED(pqcb->pqa->GetString(pqcb->Qflags, ASSOCSTR_EXECUTABLE, pszVerb,
sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
{
if (0 == StrCmpIW(PathFindFileNameW(sz), pqcb->pszExe))
{
//
// EXEs match so we should copy this verb.
// but we need to block per-user installs by darwin being added to the
// applications key, since other users wont be able to use them
//
if (_IsMSIPerUserInstall(pqcb->pqa, pqcb->Qflags, pszVerb))
fRet = pqcb->fAllowPerUser;
else
fRet = TRUE;
}
}
// todo mask off DARWIN per-user installs
return fRet;
}
HRESULT _AssocCreateAppKey(LPCWSTR pszExe, BOOL fPerUser, HKEY *phk)
{
WCHAR szKey[MAX_PATH];
wnsprintf(szKey, SIZECHARS(szKey), L"software\\classes\\applications\\%s", pszExe);
if (*PathFindExtension(pszExe) == 0)
{
StrCatBuff(szKey, TEXT(".exe"), SIZECHARS(szKey));
}
return _AssocOpenRegKey((IsOS(OS_NT) && fPerUser) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szKey, phk, TRUE);
}
LWSTDAPI AssocMakeApplicationByKeyW(ASSOCMAKEF flags, HKEY hkSrc, LPCWSTR pszVerb)
{
WCHAR szPath[MAX_PATH];
HRESULT hr = HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
ASSOCF Qflags = (flags & ASSOCMAKEF_VERIFY) ? ASSOCF_VERIFY : 0;
IQueryAssociations *pqa;
AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&pqa);
if (!pqa)
return E_OUTOFMEMORY;
if (SUCCEEDED(pqa->Init(0, NULL, hkSrc, NULL)))
{
if (SUCCEEDED(pqa->GetString(Qflags, ASSOCSTR_EXECUTABLE, pszVerb,
szPath, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szPath))))
&& (0 != StrCmpW(szPath, TEXT("%1"))))
{
LPCWSTR pszExe = PathFindFileNameW(szPath);
BOOL fPerUser = _IsMSIPerUserInstall(pqa, Qflags, pszVerb);
HKEY hkDst;
ASSERT(pszExe && *pszExe);
// we have an exe to use
// check to see if this Application already has
// this verb installed
DWORD cch;
hr = AssocQueryString(Qflags | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_COMMAND, pszExe,
pszVerb, NULL, &cch);
if (FAILED(hr) && SUCCEEDED(_AssocCreateAppKey(pszExe, fPerUser, &hkDst)))
{
QUERYEXECB qcb = {pqa, Qflags, pszExe, fPerUser};
if (pszVerb)
{
if (_AllowExeVerb(pszVerb, &qcb))
{
HKEY hkSrcVerbs;
if (SUCCEEDED(_AssocOpenRegKey(hkSrc, TEXT("shell"), &hkSrcVerbs, FALSE)))
{
hr = _AssocCopyVerb(hkSrcVerbs, hkDst, pszVerb);
RegCloseKey(hkSrcVerbs);
}
else
{
hr = E_UNEXPECTED;
}
}
}
else
{
hr = _AssocCopyVerbs(hkSrc, hkDst, (PFNALLOWVERB)_AllowExeVerb, (LPARAM)&qcb);
}
RegCloseKey(hkDst);
}
// init the friendly name for later
if ((flags & ASSOCMAKEF_VERIFY) && SUCCEEDED(hr))
{
AssocQueryString(ASSOCF_OPEN_BYEXENAME | Qflags, ASSOCSTR_FRIENDLYAPPNAME,
pszExe, NULL, NULL, &cch);
}
}
pqa->Release();
}
return hr;
}
LWSTDAPI AssocMakeApplicationByKeyA(ASSOCMAKEF flags, HKEY hkAssoc, LPCSTR pszVerb)
{
// convert pszVerb to wide char but preserve difference
// between NULL and "" for AssocMakeApplicationByKeyW
if (! pszVerb)
return AssocMakeApplicationByKeyW(flags, hkAssoc, NULL);
SHSTRW strVerb;
HRESULT hr = strVerb.SetStr(pszVerb);
if (SUCCEEDED(hr))
hr = AssocMakeApplicationByKeyW(flags, hkAssoc, strVerb);
return hr;
}
// This list needs to continue to be updated and we should try to keep parity with Office
const LPCTSTR c_arszUnsafeExts[] =
{
TEXT(".ade"), TEXT(".adp"), TEXT(".asp"), TEXT(".bas"), TEXT(".bat"), TEXT(".chm"),
TEXT(".cmd"), TEXT(".com"), TEXT(".cpl"), TEXT(".crt"), TEXT(".exe"), TEXT(".hlp"),
TEXT(".hta"), TEXT(".inf"), TEXT(".ins"), TEXT(".isp"), TEXT(".its"), TEXT(".js"),
TEXT(".jse"), TEXT(".lnk"), TEXT(".mdb"), TEXT(".mde"), TEXT(".mdt"), TEXT(".mdw"),
TEXT(".msc"), TEXT(".msi"), TEXT(".msp"), TEXT(".mst"), TEXT(".pcd"), TEXT(".pif"),
TEXT(".reg"), TEXT(".scr"), TEXT(".sct"), TEXT(".shb"), TEXT(".shs"), TEXT(".tmp"),
TEXT(".url"), TEXT(".vb"), TEXT(".vbe"), TEXT(".vbs"), TEXT(".vsd"), TEXT(".vsmacros"),
TEXT(".vss"), TEXT(".vst"), TEXT(".vsw"), TEXT(".ws"), TEXT(".wsc"), TEXT(".wsf"), TEXT(".wsh"),
};
typedef BOOL (*PFNSAFERIISEXECUTABLEFILETYPE)(LPCWSTR szFullPathname, BOOLEAN bFromShellExecute);
LWSTDAPI_(BOOL) AssocIsDangerous(PCWSTR pszType)
{
#ifdef DEBUG
// make sure our sort is good.
static BOOL fCheckedUnsafe = FALSE;
if (!fCheckedUnsafe)
{
for (int i = 1; i < ARRAYSIZE(c_arszUnsafeExts); i++)
{
ASSERT(0 > StrCmpIW(c_arszUnsafeExts[i-1], c_arszUnsafeExts[i]));
}
fCheckedUnsafe = TRUE;
}
#endif // DEBUG
BOOL fDangerous = IsTypeInList(pszType, c_arszUnsafeExts, ARRAYSIZE(c_arszUnsafeExts));
if (!fDangerous && pszType)
{
IQueryAssociations *passoc;
HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &passoc));
if (SUCCEEDED(hr))
{
hr = passoc->Init(NULL, pszType, NULL, NULL);
if (SUCCEEDED(hr))
{
DWORD dwEditFlags;
ULONG cb = sizeof(dwEditFlags);
hr = passoc->GetData(NULL, ASSOCDATA_EDITFLAGS, NULL, &dwEditFlags, &cb);
if (SUCCEEDED(hr))
{
fDangerous = dwEditFlags & FTA_AlwaysUnsafe;
}
}
passoc->Release();
}
if (!fDangerous && IsOS(OS_WHISTLERORGREATER) && *pszType)
{
HMODULE hmod = LoadLibrary(TEXT("advapi32.dll"));
if (hmod)
{
PFNSAFERIISEXECUTABLEFILETYPE pfnSaferiIsExecutableFileType =
(PFNSAFERIISEXECUTABLEFILETYPE)GetProcAddress(hmod, "SaferiIsExecutableFileType");
if (pfnSaferiIsExecutableFileType)
fDangerous = pfnSaferiIsExecutableFileType(pszType, TRUE);
FreeLibrary(hmod);
}
}
}
return fDangerous;
}