Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

754 lines
22 KiB

  1. //
  2. //
  3. // assocapi.cpp
  4. //
  5. // Association APIs
  6. //
  7. //
  8. //
  9. #include "priv.h"
  10. #include "apithk.h"
  11. #include <shstr.h>
  12. #ifdef _X86_
  13. #include <w95wraps.h>
  14. #endif
  15. #include <msi.h>
  16. #include "assoc.h"
  17. #include <filetype.h>
  18. BOOL _PathAppend(LPCTSTR pszBase, LPCTSTR pszAppend, LPTSTR pszOut, DWORD cchOut);
  19. void _MakeAppPathKey(LPCTSTR pszApp, LPTSTR pszKey, DWORD cchKey)
  20. {
  21. if (_PathAppend(REGSTR_PATH_APPPATHS, pszApp, pszKey, cchKey))
  22. {
  23. // Currently we will only look up .EXE if an extension is not
  24. // specified
  25. if (*PathFindExtension(pszApp) == 0)
  26. {
  27. StrCatBuff(pszKey, TEXT(".exe"), cchKey);
  28. }
  29. }
  30. }
  31. void _MakeApplicationsKey(LPCWSTR pszApp, LPWSTR pszKey, DWORD cchKey)
  32. {
  33. if (_PathAppend(TEXT("Applications"), pszApp, pszKey, cchKey))
  34. {
  35. // Currently we will only look up .EXE if an extension is not
  36. // specified
  37. if (*PathFindExtension(pszApp) == 0)
  38. {
  39. StrCatBuff(pszKey, TEXT(".exe"), cchKey);
  40. }
  41. }
  42. }
  43. HRESULT _AssocOpenRegKey(HKEY hk, LPCTSTR pszSub, HKEY *phkOut, BOOL fCreate)
  44. {
  45. ASSERT(phkOut);
  46. *phkOut = NULL;
  47. if (!hk)
  48. return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  49. DWORD err;
  50. if (!fCreate)
  51. err = RegOpenKeyEx(hk, pszSub, 0, MAXIMUM_ALLOWED, phkOut);
  52. else
  53. err = RegCreateKeyEx(hk, pszSub, 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, phkOut, NULL);
  54. if (ERROR_SUCCESS != err)
  55. {
  56. ASSERT(!*phkOut);
  57. return HRESULT_FROM_WIN32(err);
  58. }
  59. return S_OK;
  60. }
  61. LWSTDAPI AssocCreate(CLSID clsid, REFIID riid, LPVOID *ppvOut)
  62. {
  63. HRESULT hr = E_INVALIDARG;
  64. if (ppvOut)
  65. {
  66. if (IsEqualGUID(clsid, CLSID_QueryAssociations)
  67. || IsEqualGUID(clsid, IID_IQueryAssociations))
  68. {
  69. if (IsOS(OS_WHISTLERORGREATER))
  70. hr = SHCoCreateInstance(NULL, &CLSID_QueryAssociations, NULL, riid, ppvOut);
  71. else
  72. {
  73. hr = AssocCreateW2k(riid, ppvOut);
  74. }
  75. }
  76. else
  77. hr = AssocCreateElement(clsid, riid, ppvOut);
  78. }
  79. return hr;
  80. }
  81. #define ASSOCF_INIT_ALL (ASSOCF_INIT_BYEXENAME | ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_DEFAULTTOSTAR | ASSOCF_INIT_NOREMAPCLSID)
  82. LWSTDAPI AssocQueryStringW(ASSOCF flags, ASSOCSTR str, LPCWSTR pszAssoc, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut)
  83. {
  84. IQueryAssociations *passoc;
  85. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&passoc);
  86. if (SUCCEEDED(hr))
  87. {
  88. hr = passoc->Init(flags & ASSOCF_INIT_ALL, pszAssoc, NULL, NULL);
  89. if (SUCCEEDED(hr))
  90. hr = passoc->GetString(flags, str, pszExtra, pszOut, pcchOut);
  91. passoc->Release();
  92. }
  93. return hr;
  94. }
  95. LWSTDAPI AssocQueryStringA(ASSOCF flags, ASSOCSTR str, LPCSTR pszAssoc, LPCSTR pszExtra, LPSTR pszOut, DWORD *pcchOut)
  96. {
  97. if (!pcchOut)
  98. return E_INVALIDARG;
  99. HRESULT hr = E_OUTOFMEMORY;
  100. SHSTRW strAssoc, strExtra;
  101. if (SUCCEEDED(strAssoc.SetStr(pszAssoc))
  102. && SUCCEEDED(strExtra.SetStr(pszExtra)))
  103. {
  104. SHSTRW strOut;
  105. DWORD cchIn = IS_INTRESOURCE(pcchOut) ? PtrToUlong(pcchOut) : *pcchOut;
  106. DWORD cch;
  107. LPTSTR pszTemp = NULL;
  108. if (pszOut)
  109. {
  110. strOut.SetSize(cchIn);
  111. cch = strOut.GetSize();
  112. pszTemp = strOut.GetInplaceStr();
  113. }
  114. hr = AssocQueryStringW(flags, str, strAssoc, strExtra, pszTemp, &cch);
  115. if (SUCCEEDED(hr))
  116. {
  117. cch = SHUnicodeToAnsi(strOut, pszOut, cchIn);
  118. if (!IS_INTRESOURCE(pcchOut))
  119. *pcchOut = cch;
  120. }
  121. }
  122. return hr;
  123. }
  124. LWSTDAPI AssocQueryStringByKeyW(ASSOCF flags, ASSOCSTR str, HKEY hkAssoc, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut)
  125. {
  126. IQueryAssociations *passoc;
  127. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&passoc);
  128. if (SUCCEEDED(hr))
  129. {
  130. hr = passoc->Init(flags & ASSOCF_INIT_ALL, NULL, hkAssoc, NULL);
  131. if (SUCCEEDED(hr))
  132. hr = passoc->GetString(flags, str, pszExtra, pszOut, pcchOut);
  133. passoc->Release();
  134. }
  135. return hr;
  136. }
  137. LWSTDAPI AssocQueryStringByKeyA(ASSOCF flags, ASSOCSTR str, HKEY hkAssoc, LPCSTR pszExtra, LPSTR pszOut, DWORD *pcchOut)
  138. {
  139. if (!pcchOut)
  140. return E_INVALIDARG;
  141. HRESULT hr = E_OUTOFMEMORY;
  142. SHSTRW strExtra;
  143. if (SUCCEEDED(strExtra.SetStr(pszExtra)))
  144. {
  145. SHSTRW strOut;
  146. DWORD cchIn = IS_INTRESOURCE(pcchOut) ? PtrToUlong(pcchOut) : *pcchOut;
  147. DWORD cch;
  148. LPWSTR pszTemp = NULL;
  149. if (pszOut)
  150. {
  151. strOut.SetSize(cchIn);
  152. cch = strOut.GetSize();
  153. pszTemp = strOut.GetInplaceStr();
  154. }
  155. hr = AssocQueryStringByKeyW(flags, str, hkAssoc, strExtra, pszTemp, &cch);
  156. if (SUCCEEDED(hr))
  157. {
  158. cch = SHUnicodeToAnsi(strOut, pszOut, cchIn);
  159. if (!IS_INTRESOURCE(pcchOut))
  160. *pcchOut = cch;
  161. }
  162. }
  163. return hr;
  164. }
  165. LWSTDAPI AssocQueryKeyW(ASSOCF flags, ASSOCKEY key, LPCWSTR pszAssoc, LPCWSTR pszExtra, HKEY *phkey)
  166. {
  167. IQueryAssociations *passoc;
  168. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&passoc);
  169. if (SUCCEEDED(hr))
  170. {
  171. hr = passoc->Init(flags & ASSOCF_INIT_ALL, pszAssoc, NULL, NULL);
  172. if (SUCCEEDED(hr))
  173. hr = passoc->GetKey(flags, key, pszExtra, phkey);
  174. passoc->Release();
  175. }
  176. return hr;
  177. }
  178. LWSTDAPI AssocQueryKeyA(ASSOCF flags, ASSOCKEY key, LPCSTR pszAssoc, LPCSTR pszExtra, HKEY *phkey)
  179. {
  180. HRESULT hr = E_OUTOFMEMORY;
  181. SHSTRW strAssoc, strExtra;
  182. if (SUCCEEDED(strAssoc.SetStr(pszAssoc))
  183. && SUCCEEDED(strExtra.SetStr(pszExtra)))
  184. {
  185. hr = AssocQueryKeyW(flags, key, strAssoc, strExtra, phkey);
  186. }
  187. return hr;
  188. }
  189. #define ISQUOTED(s) (TEXT('"') == *(s) && TEXT('"') == *((s) + lstrlen(s) - 1))
  190. BOOL _TrySubst(SHSTR& str, LPCTSTR psz)
  191. {
  192. BOOL fRet = FALSE;
  193. TCHAR szVar[MAX_PATH];
  194. DWORD cch = GetEnvironmentVariable(psz, szVar, SIZECHARS(szVar));
  195. if (cch && cch <= SIZECHARS(szVar))
  196. {
  197. if (0 == StrCmpNI(str, szVar, cch))
  198. {
  199. // we got a match.
  200. // size the buffer for the env var... +3 = (% + % + \0)
  201. SHSTR strT;
  202. if (S_OK == strT.SetStr(str.GetStr() + cch)
  203. && S_OK == str.SetSize(str.GetLen() - cch + lstrlen(psz) + 3)
  204. )
  205. {
  206. wnsprintf(str.GetInplaceStr(), str.GetSize(), TEXT("%%%s%%%s"), psz, strT.GetStr());
  207. fRet = TRUE;
  208. }
  209. }
  210. }
  211. return fRet;
  212. }
  213. BOOL _TryEnvSubst(SHSTR& str)
  214. {
  215. static LPCTSTR rgszEnv[] = {
  216. TEXT("USERPROFILE"),
  217. TEXT("ProgramFiles"),
  218. TEXT("SystemRoot"),
  219. TEXT("SystemDrive"),
  220. TEXT("windir"),
  221. NULL
  222. };
  223. LPCTSTR *ppsz = rgszEnv;
  224. BOOL fRet = FALSE;
  225. while (*ppsz && !fRet)
  226. {
  227. fRet = _TrySubst(str, *ppsz++);
  228. }
  229. return fRet;
  230. }
  231. HRESULT _MakeCommandString(ASSOCF *pflags, LPCTSTR pszExe, LPCTSTR pszArgs, SHSTR& str)
  232. {
  233. SHSTR strArgs;
  234. HRESULT hr;
  235. if (!pszArgs || !*pszArgs)
  236. {
  237. // default to just passing the
  238. // file name right in.
  239. // NOTE 16bit apps might have a problem with
  240. // this, but i request that the caller
  241. // specify that this is the case....
  242. pszArgs = TEXT("\"%1\"");
  243. }
  244. // else NO _ParseCommand()
  245. hr = str.SetStr(pszExe);
  246. if (S_OK == hr)
  247. {
  248. // check for quotes before doing env subst
  249. BOOL fNeedQuotes = (!ISQUOTED(str.GetStr()) && PathIsLFNFileSpec(str));
  250. // this will put environment vars into the string...
  251. if ((*pflags & ASSOCMAKEF_SUBSTENV) && _TryEnvSubst(str))
  252. {
  253. *pflags |= ASSOCMAKEF_USEEXPAND;
  254. }
  255. str.Trim();
  256. if (fNeedQuotes)
  257. {
  258. // 3 = " + " + \0
  259. if (S_OK == str.SetSize(str.GetLen() + 3))
  260. PathQuoteSpaces(str.GetInplaceStr());
  261. }
  262. hr = str.Append(TEXT(' '));
  263. if (S_OK == hr)
  264. {
  265. hr = str.Append(pszArgs);
  266. }
  267. }
  268. return hr;
  269. }
  270. HRESULT _AssocMakeCommand(ASSOCMAKEF flags, HKEY hkVerb, LPCWSTR pszExe, LPCWSTR pszArgs)
  271. {
  272. ASSERT(hkVerb && pszExe);
  273. SHSTR str;
  274. HRESULT hr = _MakeCommandString(&flags, pszExe, pszArgs, str);
  275. if (S_OK == hr)
  276. {
  277. DWORD dw = (flags & ASSOCMAKEF_USEEXPAND) ? REG_EXPAND_SZ : REG_SZ;
  278. DWORD err = SHSetValue(hkVerb, TEXT("command"), NULL, dw, (LPVOID) str.GetStr(), CbFromCch(str.GetLen() +1));
  279. hr = HRESULT_FROM_WIN32(err);
  280. }
  281. return hr;
  282. }
  283. LWSTDAPI AssocMakeShell(ASSOCMAKEF flags, HKEY hkProgid, LPCWSTR pszApplication, ASSOCSHELL *pShell)
  284. {
  285. HRESULT hr = E_INVALIDARG;
  286. if (hkProgid && pszApplication && pShell)
  287. {
  288. for (DWORD c = 0; c < pShell->cVerbs; c++)
  289. {
  290. ASSOCVERB *pverb = &pShell->rgVerbs[c];
  291. if (pverb->pszVerb)
  292. {
  293. TCHAR szVerbKey[MAX_PATH];
  294. HKEY hkVerb;
  295. _PathAppend(TEXT("shell"), pverb->pszVerb, szVerbKey, SIZECHARS(szVerbKey));
  296. if (c == pShell->iDefaultVerb)
  297. SHSetValue(hkProgid, TEXT("shell"), NULL, REG_SZ, pverb->pszVerb, CbFromCch(lstrlen(pverb->pszVerb) +1));
  298. // ASSOCMAKEF_FAILIFEXIST check if its ok to overwrite
  299. if (SUCCEEDED(_AssocOpenRegKey(hkProgid, szVerbKey, &hkVerb, FALSE)))
  300. {
  301. RegCloseKey(hkVerb);
  302. SHDeleteKey(hkProgid, szVerbKey);
  303. }
  304. if (SUCCEEDED(_AssocOpenRegKey(hkProgid, szVerbKey, &hkVerb, TRUE)))
  305. {
  306. if (pverb->pszTitle)
  307. SHSetValue(hkVerb, NULL, NULL, REG_SZ, pverb->pszTitle, CbFromCch(lstrlen(pverb->pszTitle) +1));
  308. hr = _AssocMakeCommand(flags, hkVerb, pverb->pszApplication ? pverb->pszApplication : pszApplication , pverb->pszParams);
  309. // if (SUCCEEDED(hr) && pverb->pDDEExec)
  310. // hr = _AssocMakeDDEExec(flags, hkVerb, pverb->pDDEExec);
  311. RegCloseKey(hkVerb);
  312. }
  313. }
  314. }
  315. }
  316. return hr;
  317. }
  318. HRESULT _OpenClasses(HKEY *phkOut)
  319. {
  320. *phkOut = NULL;
  321. DWORD err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\classes"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, phkOut, NULL);
  322. if (err)
  323. err = RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\classes"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, phkOut, NULL);
  324. return HRESULT_FROM_WIN32(err);
  325. }
  326. LWSTDAPI AssocMakeProgid(ASSOCMAKEF flags, LPCWSTR pszApplication, ASSOCPROGID *pProgid, HKEY *phkProgid)
  327. {
  328. HRESULT hr = E_INVALIDARG;
  329. if (pszApplication
  330. && pProgid
  331. && pProgid->cbSize >= sizeof(ASSOCPROGID)
  332. && pProgid->pszProgid
  333. && *pProgid->pszProgid)
  334. {
  335. HKEY hkRoot;
  336. if ((!(flags & ASSOCMAKEF_VERIFY) || PathFileExists(pszApplication))
  337. && SUCCEEDED(_OpenClasses(&hkRoot)))
  338. {
  339. HKEY hkProgid;
  340. // need to add support for ASSOCMAKEF_VOLATILE...
  341. hr = _AssocOpenRegKey(hkRoot, pProgid->pszProgid, &hkProgid, TRUE);
  342. if (SUCCEEDED(hr))
  343. {
  344. if (pProgid->pszFriendlyDocName)
  345. SHSetValue(hkProgid, NULL, NULL, REG_SZ, pProgid->pszFriendlyDocName, CbFromCch(lstrlen(pProgid->pszFriendlyDocName) +1));
  346. if (pProgid->pszDefaultIcon)
  347. SHSetValue(hkProgid, TEXT("DefaultIcon"), NULL, REG_SZ, pProgid->pszDefaultIcon, CbFromCch(lstrlen(pProgid->pszDefaultIcon) +1));
  348. if (pProgid->pShellKey)
  349. hr = AssocMakeShell(flags, hkProgid, pszApplication, pProgid->pShellKey);
  350. if (SUCCEEDED(hr) && pProgid->pszExtensions)
  351. {
  352. LPCTSTR psz = pProgid->pszExtensions;
  353. DWORD err = NOERROR;
  354. while (*psz && NOERROR == err)
  355. {
  356. err = SHSetValue(hkRoot, psz, NULL, REG_SZ, pProgid->pszProgid, CbFromCch(lstrlen(pProgid->pszProgid) + 1));
  357. psz += lstrlen(psz) + 1;
  358. }
  359. if (NOERROR != err)
  360. HRESULT_FROM_WIN32(err);
  361. }
  362. if (SUCCEEDED(hr) && phkProgid)
  363. *phkProgid = hkProgid;
  364. else
  365. RegCloseKey(hkProgid);
  366. }
  367. RegCloseKey(hkRoot);
  368. }
  369. }
  370. return hr;
  371. }
  372. HRESULT _AssocCopyVerb(HKEY hkSrc, HKEY hkDst, LPCTSTR pszVerb)
  373. {
  374. HRESULT hr = S_OK;
  375. TCHAR szKey[MAX_PATH];
  376. HKEY hkVerb;
  377. DWORD dwDisp;
  378. // only copy the verb component
  379. wnsprintf(szKey, SIZECHARS(szKey), TEXT("shell\\%s"), pszVerb);
  380. RegCreateKeyEx(hkDst, szKey, 0, NULL, 0,
  381. MAXIMUM_ALLOWED, NULL, &hkVerb, &dwDisp);
  382. // create a failure state here...
  383. if (hkVerb)
  384. {
  385. // we avoid overwriting old keys by checking the dwDisp
  386. if ((dwDisp == REG_CREATED_NEW_KEY) && SHCopyKey(hkSrc, pszVerb, hkVerb, 0L))
  387. hr = E_UNEXPECTED;
  388. RegCloseKey(hkVerb);
  389. }
  390. return hr;
  391. }
  392. typedef BOOL (*PFNALLOWVERB)(LPCWSTR psz, LPARAM param);
  393. LWSTDAPI _AssocCopyVerbs(HKEY hkSrc, HKEY hkDst, PFNALLOWVERB pfnAllow, LPARAM lParam)
  394. {
  395. HRESULT hr = E_INVALIDARG;
  396. HKEY hkEnum;
  397. if (SUCCEEDED(_AssocOpenRegKey(hkSrc, TEXT("shell"), &hkEnum, FALSE)))
  398. {
  399. TCHAR szVerb[MAX_PATH];
  400. DWORD cchVerb = SIZECHARS(szVerb);
  401. for (DWORD i = 0
  402. ; (NOERROR == RegEnumKeyEx(hkEnum, i, szVerb, &cchVerb, NULL, NULL, NULL, NULL))
  403. ; (cchVerb = SIZECHARS(szVerb)), i++)
  404. {
  405. if (!pfnAllow || pfnAllow(szVerb, lParam))
  406. hr = _AssocCopyVerb(hkEnum, hkDst, szVerb);
  407. }
  408. // switch to cbVerb here
  409. cchVerb = sizeof(szVerb);
  410. if (NOERROR == SHGetValue(hkEnum, NULL, NULL, NULL, szVerb, &cchVerb))
  411. {
  412. SHSetValue(hkDst, TEXT("shell"), NULL, REG_SZ, szVerb, cchVerb);
  413. }
  414. RegCloseKey(hkEnum);
  415. }
  416. return hr;
  417. }
  418. LWSTDAPI AssocCopyVerbs(HKEY hkSrc, HKEY hkDst)
  419. {
  420. return _AssocCopyVerbs(hkSrc, hkDst, NULL, NULL);
  421. }
  422. BOOL _IsMSIPerUserInstall(IQueryAssociations *pqa, ASSOCF flags, LPCWSTR pszVerb)
  423. {
  424. WCHAR sz[MAX_PATH];
  425. DWORD cb = sizeof(sz);
  426. if (SUCCEEDED(pqa->GetData(flags, ASSOCDATA_MSIDESCRIPTOR, pszVerb, sz, &cb)))
  427. {
  428. WCHAR szOut[3]; // bit enough for "1" or "0"
  429. cb = SIZECHARS(szOut);
  430. if (NOERROR == MsiGetProductInfoW(sz, INSTALLPROPERTY_ASSIGNMENTTYPE, szOut, &cb))
  431. {
  432. // The string "1" for the value represents machine installations,
  433. // while "0" represents user installations.
  434. if (0 == StrCmpW(szOut, L"0"))
  435. return TRUE;
  436. }
  437. }
  438. return FALSE;
  439. }
  440. typedef struct {
  441. IQueryAssociations *pqa;
  442. ASSOCF Qflags;
  443. LPCWSTR pszExe;
  444. BOOL fAllowPerUser;
  445. } QUERYEXECB;
  446. BOOL _AllowExeVerb(LPCWSTR pszVerb, QUERYEXECB *pqcb)
  447. {
  448. BOOL fRet = FALSE;
  449. WCHAR sz[MAX_PATH];
  450. if (SUCCEEDED(pqcb->pqa->GetString(pqcb->Qflags, ASSOCSTR_EXECUTABLE, pszVerb,
  451. sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
  452. {
  453. if (0 == StrCmpIW(PathFindFileNameW(sz), pqcb->pszExe))
  454. {
  455. //
  456. // EXEs match so we should copy this verb.
  457. // but we need to block per-user installs by darwin being added to the
  458. // applications key, since other users wont be able to use them
  459. //
  460. if (_IsMSIPerUserInstall(pqcb->pqa, pqcb->Qflags, pszVerb))
  461. fRet = pqcb->fAllowPerUser;
  462. else
  463. fRet = TRUE;
  464. }
  465. }
  466. // todo mask off DARWIN per-user installs
  467. return fRet;
  468. }
  469. HRESULT _AssocCreateAppKey(LPCWSTR pszExe, BOOL fPerUser, HKEY *phk)
  470. {
  471. WCHAR szKey[MAX_PATH];
  472. wnsprintf(szKey, SIZECHARS(szKey), L"software\\classes\\applications\\%s", pszExe);
  473. if (*PathFindExtension(pszExe) == 0)
  474. {
  475. StrCatBuff(szKey, TEXT(".exe"), SIZECHARS(szKey));
  476. }
  477. return _AssocOpenRegKey((IsOS(OS_NT) && fPerUser) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szKey, phk, TRUE);
  478. }
  479. LWSTDAPI AssocMakeApplicationByKeyW(ASSOCMAKEF flags, HKEY hkSrc, LPCWSTR pszVerb)
  480. {
  481. WCHAR szPath[MAX_PATH];
  482. HRESULT hr = HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  483. ASSOCF Qflags = (flags & ASSOCMAKEF_VERIFY) ? ASSOCF_VERIFY : 0;
  484. IQueryAssociations *pqa;
  485. AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&pqa);
  486. if (!pqa)
  487. return E_OUTOFMEMORY;
  488. if (SUCCEEDED(pqa->Init(0, NULL, hkSrc, NULL)))
  489. {
  490. if (SUCCEEDED(pqa->GetString(Qflags, ASSOCSTR_EXECUTABLE, pszVerb,
  491. szPath, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szPath))))
  492. && (0 != StrCmpW(szPath, TEXT("%1"))))
  493. {
  494. LPCWSTR pszExe = PathFindFileNameW(szPath);
  495. BOOL fPerUser = _IsMSIPerUserInstall(pqa, Qflags, pszVerb);
  496. HKEY hkDst;
  497. ASSERT(pszExe && *pszExe);
  498. // we have an exe to use
  499. // check to see if this Application already has
  500. // this verb installed
  501. DWORD cch;
  502. hr = AssocQueryString(Qflags | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_COMMAND, pszExe,
  503. pszVerb, NULL, &cch);
  504. if (FAILED(hr) && SUCCEEDED(_AssocCreateAppKey(pszExe, fPerUser, &hkDst)))
  505. {
  506. QUERYEXECB qcb = {pqa, Qflags, pszExe, fPerUser};
  507. if (pszVerb)
  508. {
  509. if (_AllowExeVerb(pszVerb, &qcb))
  510. {
  511. HKEY hkSrcVerbs;
  512. if (SUCCEEDED(_AssocOpenRegKey(hkSrc, TEXT("shell"), &hkSrcVerbs, FALSE)))
  513. {
  514. hr = _AssocCopyVerb(hkSrcVerbs, hkDst, pszVerb);
  515. RegCloseKey(hkSrcVerbs);
  516. }
  517. else
  518. {
  519. hr = E_UNEXPECTED;
  520. }
  521. }
  522. }
  523. else
  524. {
  525. hr = _AssocCopyVerbs(hkSrc, hkDst, (PFNALLOWVERB)_AllowExeVerb, (LPARAM)&qcb);
  526. }
  527. RegCloseKey(hkDst);
  528. }
  529. // init the friendly name for later
  530. if ((flags & ASSOCMAKEF_VERIFY) && SUCCEEDED(hr))
  531. {
  532. AssocQueryString(ASSOCF_OPEN_BYEXENAME | Qflags, ASSOCSTR_FRIENDLYAPPNAME,
  533. pszExe, NULL, NULL, &cch);
  534. }
  535. }
  536. pqa->Release();
  537. }
  538. return hr;
  539. }
  540. LWSTDAPI AssocMakeApplicationByKeyA(ASSOCMAKEF flags, HKEY hkAssoc, LPCSTR pszVerb)
  541. {
  542. // convert pszVerb to wide char but preserve difference
  543. // between NULL and "" for AssocMakeApplicationByKeyW
  544. if (! pszVerb)
  545. return AssocMakeApplicationByKeyW(flags, hkAssoc, NULL);
  546. SHSTRW strVerb;
  547. HRESULT hr = strVerb.SetStr(pszVerb);
  548. if (SUCCEEDED(hr))
  549. hr = AssocMakeApplicationByKeyW(flags, hkAssoc, strVerb);
  550. return hr;
  551. }
  552. // This list needs to continue to be updated and we should try to keep parity with Office
  553. const LPCTSTR c_arszUnsafeExts[] =
  554. {
  555. TEXT(".ade"), TEXT(".adp"), TEXT(".asp"), TEXT(".bas"), TEXT(".bat"), TEXT(".chm"),
  556. TEXT(".cmd"), TEXT(".com"), TEXT(".cpl"), TEXT(".crt"), TEXT(".exe"), TEXT(".hlp"),
  557. TEXT(".hta"), TEXT(".inf"), TEXT(".ins"), TEXT(".isp"), TEXT(".its"), TEXT(".js"),
  558. TEXT(".jse"), TEXT(".lnk"), TEXT(".mdb"), TEXT(".mde"), TEXT(".mdt"), TEXT(".mdw"),
  559. TEXT(".msc"), TEXT(".msi"), TEXT(".msp"), TEXT(".mst"), TEXT(".pcd"), TEXT(".pif"),
  560. TEXT(".reg"), TEXT(".scr"), TEXT(".sct"), TEXT(".shb"), TEXT(".shs"), TEXT(".tmp"),
  561. TEXT(".url"), TEXT(".vb"), TEXT(".vbe"), TEXT(".vbs"), TEXT(".vsd"), TEXT(".vsmacros"),
  562. TEXT(".vss"), TEXT(".vst"), TEXT(".vsw"), TEXT(".ws"), TEXT(".wsc"), TEXT(".wsf"), TEXT(".wsh"),
  563. };
  564. typedef BOOL (*PFNSAFERIISEXECUTABLEFILETYPE)(LPCWSTR szFullPathname, BOOLEAN bFromShellExecute);
  565. LWSTDAPI_(BOOL) AssocIsDangerous(PCWSTR pszType)
  566. {
  567. #ifdef DEBUG
  568. // make sure our sort is good.
  569. static BOOL fCheckedUnsafe = FALSE;
  570. if (!fCheckedUnsafe)
  571. {
  572. for (int i = 1; i < ARRAYSIZE(c_arszUnsafeExts); i++)
  573. {
  574. ASSERT(0 > StrCmpIW(c_arszUnsafeExts[i-1], c_arszUnsafeExts[i]));
  575. }
  576. fCheckedUnsafe = TRUE;
  577. }
  578. #endif // DEBUG
  579. BOOL fDangerous = IsTypeInList(pszType, c_arszUnsafeExts, ARRAYSIZE(c_arszUnsafeExts));
  580. if (!fDangerous && pszType)
  581. {
  582. IQueryAssociations *passoc;
  583. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &passoc));
  584. if (SUCCEEDED(hr))
  585. {
  586. hr = passoc->Init(NULL, pszType, NULL, NULL);
  587. if (SUCCEEDED(hr))
  588. {
  589. DWORD dwEditFlags;
  590. ULONG cb = sizeof(dwEditFlags);
  591. hr = passoc->GetData(NULL, ASSOCDATA_EDITFLAGS, NULL, &dwEditFlags, &cb);
  592. if (SUCCEEDED(hr))
  593. {
  594. fDangerous = dwEditFlags & FTA_AlwaysUnsafe;
  595. }
  596. }
  597. passoc->Release();
  598. }
  599. if (!fDangerous && IsOS(OS_WHISTLERORGREATER) && *pszType)
  600. {
  601. HMODULE hmod = LoadLibrary(TEXT("advapi32.dll"));
  602. if (hmod)
  603. {
  604. PFNSAFERIISEXECUTABLEFILETYPE pfnSaferiIsExecutableFileType =
  605. (PFNSAFERIISEXECUTABLEFILETYPE)GetProcAddress(hmod, "SaferiIsExecutableFileType");
  606. if (pfnSaferiIsExecutableFileType)
  607. fDangerous = pfnSaferiIsExecutableFileType(pszType, TRUE);
  608. FreeLibrary(hmod);
  609. }
  610. }
  611. }
  612. return fDangerous;
  613. }