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.

2009 lines
54 KiB

  1. #include "stock.h"
  2. #pragma hdrstop
  3. #include <idhidden.h>
  4. #include <regitemp.h>
  5. #include <shstr.h>
  6. #include <shlobjp.h>
  7. #include <lmcons.h>
  8. #include <validc.h>
  9. #include "ccstock2.h"
  10. // Alpha platform doesn't need unicode thunks, seems like this
  11. // should happen automatically in the headerfiles...
  12. //
  13. #if defined(_X86_) || defined(UNIX)
  14. #else
  15. #define NO_W95WRAPS_UNITHUNK
  16. #endif
  17. #include "wininet.h"
  18. #include "w95wraps.h"
  19. //------------------------------------------------------------------------
  20. // Random helpful functions
  21. //------------------------------------------------------------------------
  22. //
  23. STDAPI_(LPCTSTR) SkipServerSlashes(LPCTSTR pszName)
  24. {
  25. for (pszName; *pszName && *pszName == TEXT('\\'); pszName++);
  26. return pszName;
  27. }
  28. // pbIsNamed is true if the i-th item in hm is a named separator
  29. STDAPI_(BOOL) _SHIsMenuSeparator2(HMENU hm, int i, BOOL *pbIsNamed)
  30. {
  31. MENUITEMINFO mii;
  32. BOOL bLocal;
  33. if (!pbIsNamed)
  34. pbIsNamed = &bLocal;
  35. *pbIsNamed = FALSE;
  36. mii.cbSize = sizeof(mii);
  37. mii.fMask = MIIM_TYPE | MIIM_ID;
  38. mii.cch = 0; // WARNING: We MUST initialize it to 0!!!
  39. if (GetMenuItemInfo(hm, i, TRUE, &mii) && (mii.fType & MFT_SEPARATOR))
  40. {
  41. // NOTE that there is a bug in either 95 or NT user!!!
  42. // 95 returns 16 bit ID's and NT 32 bit therefore there is a
  43. // the following may fail, on win9x, to evaluate to false
  44. // without casting
  45. *pbIsNamed = ((WORD)mii.wID != (WORD)-1);
  46. return TRUE;
  47. }
  48. return FALSE;
  49. }
  50. STDAPI_(BOOL) _SHIsMenuSeparator(HMENU hm, int i)
  51. {
  52. return _SHIsMenuSeparator2(hm, i, NULL);
  53. }
  54. //
  55. // _SHPrettyMenu -- make this menu look darn purty
  56. //
  57. // Prune the separators in this hmenu to ensure there isn't one in the first or last
  58. // position and there aren't any runs of >1 separator.
  59. //
  60. // Named separators take precedence over regular separators.
  61. //
  62. STDAPI_(void) _SHPrettyMenu(HMENU hm)
  63. {
  64. BOOL bSeparated = TRUE;
  65. BOOL bWasNamed = TRUE;
  66. for (int i = GetMenuItemCount(hm) - 1; i > 0; --i)
  67. {
  68. BOOL bIsNamed;
  69. if (_SHIsMenuSeparator2(hm, i, &bIsNamed))
  70. {
  71. if (bSeparated)
  72. {
  73. // if we have two separators in a row, only one of which is named
  74. // remove the non named one!
  75. if (bIsNamed && !bWasNamed)
  76. {
  77. DeleteMenu(hm, i+1, MF_BYPOSITION);
  78. bWasNamed = bIsNamed;
  79. }
  80. else
  81. {
  82. DeleteMenu(hm, i, MF_BYPOSITION);
  83. }
  84. }
  85. else
  86. {
  87. bWasNamed = bIsNamed;
  88. bSeparated = TRUE;
  89. }
  90. }
  91. else
  92. {
  93. bSeparated = FALSE;
  94. }
  95. }
  96. // The above loop does not handle the case of many separators at
  97. // the beginning of the menu
  98. while (_SHIsMenuSeparator2(hm, 0, NULL))
  99. {
  100. DeleteMenu(hm, 0, MF_BYPOSITION);
  101. }
  102. }
  103. STDAPI_(DWORD) SHIsButtonObscured(HWND hwnd, PRECT prc, INT_PTR i)
  104. {
  105. ASSERT(IsWindow(hwnd));
  106. ASSERT(i < SendMessage(hwnd, TB_BUTTONCOUNT, 0, 0));
  107. DWORD dwEdge = 0;
  108. RECT rc, rcInt;
  109. SendMessage(hwnd, TB_GETITEMRECT, i, (LPARAM)&rc);
  110. if (!IntersectRect(&rcInt, prc, &rc))
  111. {
  112. dwEdge = EDGE_LEFT | EDGE_RIGHT | EDGE_TOP | EDGE_BOTTOM;
  113. }
  114. else
  115. {
  116. if (rc.top != rcInt.top)
  117. dwEdge |= EDGE_TOP;
  118. if (rc.bottom != rcInt.bottom)
  119. dwEdge |= EDGE_BOTTOM;
  120. if (rc.left != rcInt.left)
  121. dwEdge |= EDGE_LEFT;
  122. if (rc.right != rcInt.right)
  123. dwEdge |= EDGE_RIGHT;
  124. }
  125. return dwEdge;
  126. }
  127. STDAPI_(BYTE) SHBtnStateFromRestriction(DWORD dwRest, BYTE fsState)
  128. {
  129. if (dwRest == RESTOPT_BTN_STATE_VISIBLE)
  130. return (fsState & ~TBSTATE_HIDDEN);
  131. else if (dwRest == RESTOPT_BTN_STATE_HIDDEN)
  132. return (fsState | TBSTATE_HIDDEN);
  133. else {
  134. #ifdef DEBUG
  135. if (dwRest != RESTOPT_BTN_STATE_DEFAULT)
  136. TraceMsg(TF_ERROR, "bad toolbar button state policy %x", dwRest);
  137. #endif
  138. return fsState;
  139. }
  140. }
  141. //
  142. // SHIsDisplayable
  143. //
  144. // Figure out if this unicode string can be displayed by the system
  145. // (i.e., won't be turned into a string of question marks).
  146. //
  147. STDAPI_(BOOL) SHIsDisplayable(LPCWSTR pwszName, BOOL fRunOnFE, BOOL fRunOnNT5)
  148. {
  149. BOOL fNotDisplayable = FALSE;
  150. if (pwszName)
  151. {
  152. if (!fRunOnNT5)
  153. {
  154. // if WCtoMB has to use default characters in mapping pwszName to multibyte,
  155. // it sets fNotDisplayable == TRUE, in which case we have to use something
  156. // else for the title string.
  157. WideCharToMultiByte(CP_ACP, 0, pwszName, -1, NULL, 0, NULL, &fNotDisplayable);
  158. if (fNotDisplayable)
  159. {
  160. if (fRunOnFE)
  161. {
  162. WCHAR wzName[INTERNET_MAX_URL_LENGTH];
  163. BOOL fReplaceNbsp = FALSE;
  164. StrCpyNW(wzName, pwszName, ARRAYSIZE(wzName));
  165. for (int i = 0; i < ARRAYSIZE(wzName); i++)
  166. {
  167. if (0x00A0 == wzName[i]) // if &nbsp
  168. {
  169. wzName[i] = 0x0020; // replace to space
  170. fReplaceNbsp = TRUE;
  171. }
  172. else if (0 == wzName[i])
  173. break;
  174. }
  175. if (fReplaceNbsp)
  176. {
  177. pwszName = wzName;
  178. WideCharToMultiByte(CP_ACP, 0, pwszName, -1, NULL, 0, NULL, &fNotDisplayable);
  179. }
  180. }
  181. }
  182. }
  183. }
  184. return !fNotDisplayable;
  185. }
  186. // Trident will take URLs that don't indicate their source of
  187. // origin (about:, javascript:, & vbscript:) and will append
  188. // an URL turd and then the source URL. The turd will indicate
  189. // where the source URL begins and that source URL is needed
  190. // when the action needs to be Zone Checked.
  191. //
  192. // This function will remove that URL turd and everything behind
  193. // it so the URL is presentable for the user.
  194. #define URL_TURD ((TCHAR)0x01)
  195. STDAPI_(void) SHRemoveURLTurd(LPTSTR pszUrl)
  196. {
  197. if (!pszUrl)
  198. return;
  199. while (0 != pszUrl[0])
  200. {
  201. if (URL_TURD == pszUrl[0])
  202. {
  203. pszUrl[0] = 0;
  204. break;
  205. }
  206. pszUrl = CharNext(pszUrl);
  207. }
  208. }
  209. STDAPI_(BOOL) SetWindowZorder(HWND hwnd, HWND hwndInsertAfter)
  210. {
  211. return SetWindowPos(hwnd, hwndInsertAfter, 0, 0, 0, 0,
  212. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
  213. }
  214. BOOL CALLBACK _FixZorderEnumProc(HWND hwnd, LPARAM lParam)
  215. {
  216. HWND hwndTest = (HWND)lParam;
  217. HWND hwndOwner = hwnd;
  218. while (hwndOwner = GetWindow(hwndOwner, GW_OWNER))
  219. {
  220. if (hwndOwner == hwndTest)
  221. {
  222. TraceMsg(TF_WARNING, "_FixZorderEnumProc: Found topmost window %x owned by non-topmost window %x, fixing...", hwnd, hwndTest);
  223. SetWindowZorder(hwnd, HWND_NOTOPMOST);
  224. #ifdef DEBUG
  225. if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
  226. TraceMsg(TF_ERROR, "_FixZorderEnumProc: window %x is still topmost", hwnd);
  227. #endif
  228. break;
  229. }
  230. }
  231. return TRUE;
  232. }
  233. STDAPI_(BOOL) SHForceWindowZorder(HWND hwnd, HWND hwndInsertAfter)
  234. {
  235. BOOL fRet = SetWindowZorder(hwnd, hwndInsertAfter);
  236. if (fRet && hwndInsertAfter == HWND_TOPMOST)
  237. {
  238. if (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST))
  239. {
  240. //
  241. // user didn't actually move the hwnd to topmost
  242. //
  243. // According to GerardoB, this can happen if the window has
  244. // an owned window that somehow has become topmost while the
  245. // owner remains non-topmost, i.e., the two have become
  246. // separated in the z-order. In this state, when the owner
  247. // window tries to make itself topmost, the call will
  248. // silently fail.
  249. //
  250. // TERRIBLE HORRIBLE NO GOOD VERY BAD HACK
  251. //
  252. // Hacky fix is to enumerate the toplevel windows, check to see
  253. // if any are topmost and owned by hwnd, and if so, make them
  254. // non-topmost. Then, retry the SetWindowPos call.
  255. //
  256. TraceMsg(TF_WARNING, "SHForceWindowZorder: SetWindowPos(%x, HWND_TOPMOST) failed", hwnd);
  257. // Fix up the z-order
  258. EnumWindows(_FixZorderEnumProc, (LPARAM)hwnd);
  259. // Retry the set. (This should make all owned windows topmost as well.)
  260. SetWindowZorder(hwnd, HWND_TOPMOST);
  261. #ifdef DEBUG
  262. if (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST))
  263. TraceMsg(TF_ERROR, "SHForceWindowZorder: window %x is still not topmost", hwnd);
  264. #endif
  265. }
  266. }
  267. return fRet;
  268. }
  269. STDAPI_(LPITEMIDLIST) ILCloneParent(LPCITEMIDLIST pidl)
  270. {
  271. LPITEMIDLIST pidlParent = ILClone(pidl);
  272. if (pidlParent)
  273. ILRemoveLastID(pidlParent);
  274. return pidlParent;
  275. }
  276. // in:
  277. // psf OPTIONAL, if NULL assume psfDesktop
  278. // pidl to bind to from psfParent
  279. //
  280. STDAPI SHBindToObject(IShellFolder *psf, REFIID riid, LPCITEMIDLIST pidl, void **ppv)
  281. {
  282. // NOTE: callers should use SHBindToObjectEx!!!
  283. return SHBindToObjectEx(psf, pidl, NULL, riid, ppv);
  284. }
  285. // in:
  286. // psf OPTIONAL, if NULL assume psfDesktop
  287. // pidl to bind to from psfParent
  288. // pbc bind context
  289. STDAPI SHBindToObjectEx(IShellFolder *psf, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  290. {
  291. HRESULT hr;
  292. IShellFolder *psfRelease = NULL;
  293. if (!psf)
  294. {
  295. SHGetDesktopFolder(&psf);
  296. psfRelease = psf;
  297. }
  298. if (psf)
  299. {
  300. if (!pidl || ILIsEmpty(pidl))
  301. {
  302. hr = psf->QueryInterface(riid, ppv);
  303. }
  304. else
  305. {
  306. hr = psf->BindToObject(pidl, pbc, riid, ppv);
  307. }
  308. }
  309. else
  310. {
  311. *ppv = NULL;
  312. hr = E_FAIL;
  313. }
  314. if (psfRelease)
  315. {
  316. psfRelease->Release();
  317. }
  318. if (SUCCEEDED(hr) && (*ppv == NULL))
  319. {
  320. // Some shell extensions (eg WS_FTP) will return success and a null out pointer
  321. TraceMsg(TF_WARNING, "SHBindToObjectEx: BindToObject succeeded but returned null ppv!!");
  322. hr = E_FAIL;
  323. }
  324. return hr;
  325. }
  326. // psfRoot is the base of the bind. If NULL, then we use the shell desktop.
  327. // If you want to bind relative to the explorer root (e.g., CabView, MSN),
  328. // then use SHBindToIDListParent.
  329. STDAPI SHBindToFolderIDListParent(IShellFolder *psfRoot, LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlLast)
  330. {
  331. HRESULT hr;
  332. // Old shell32 code in some cases simply whacked the pidl,
  333. // but this is unsafe. Do what shdocvw does and clone/remove:
  334. //
  335. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  336. if (pidlParent)
  337. {
  338. hr = SHBindToObjectEx(psfRoot, pidlParent, NULL, riid, ppv);
  339. ILFree(pidlParent);
  340. }
  341. else
  342. hr = E_OUTOFMEMORY;
  343. if (ppidlLast)
  344. *ppidlLast = ILFindLastID(pidl);
  345. return hr;
  346. }
  347. //
  348. // Warning! brutil.cpp overrides this function
  349. //
  350. STDAPI SHBindToIDListParent(LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlLast)
  351. {
  352. return SHBindToFolderIDListParent(NULL, pidl, riid, ppv, ppidlLast);
  353. }
  354. // should be IUnknown_GetIDList()
  355. STDAPI SHGetIDListFromUnk(IUnknown *punk, LPITEMIDLIST *ppidl)
  356. {
  357. *ppidl = NULL;
  358. HRESULT hr = E_NOINTERFACE;
  359. if (punk)
  360. {
  361. IPersistFolder2 *ppf;
  362. IPersistIDList *pperid;
  363. if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistIDList, &pperid))))
  364. {
  365. hr = pperid->GetIDList(ppidl);
  366. pperid->Release();
  367. }
  368. else if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf))))
  369. {
  370. hr = ppf->GetCurFolder(ppidl);
  371. ppf->Release();
  372. }
  373. }
  374. return hr;
  375. }
  376. //
  377. // generically useful to hide.
  378. //
  379. #pragma pack(1)
  380. typedef struct _HIDDENCLSID
  381. {
  382. HIDDENITEMID hid;
  383. CLSID clsid;
  384. } HIDDENCLSID;
  385. #pragma pack()
  386. typedef UNALIGNED HIDDENCLSID *PHIDDENCLSID;
  387. typedef const UNALIGNED HIDDENCLSID *PCHIDDENCLSID;
  388. STDAPI_(LPITEMIDLIST) ILAppendHiddenClsid(LPITEMIDLIST pidl, IDLHID id, CLSID *pclsid)
  389. {
  390. HIDDENCLSID hc = {{sizeof(hc), 0, id}};
  391. hc.clsid = *pclsid;
  392. // WARNING - cannot use hid.wVersion for compat reasons - ZekeL - 23-OCT-2000
  393. // on win2k and winMe we appended clsid's with wVersion
  394. // as stack garbage. this means we cannot use it for anything
  395. return ILAppendHiddenID(pidl, &hc.hid);
  396. }
  397. STDAPI_(BOOL) ILGetHiddenClsid(LPCITEMIDLIST pidl, IDLHID id, CLSID *pclsid)
  398. {
  399. PCHIDDENCLSID phc = (PCHIDDENCLSID) ILFindHiddenID(pidl, id);
  400. // WARNING - cannot use hid.wVersion for compat reasons - ZekeL - 23-OCT-2000
  401. // on win2k and winMe we appended clsid's with wVersion
  402. // as stack garbage. this means we cannot use it for anything
  403. if (phc)
  404. {
  405. *pclsid = phc->clsid;
  406. return TRUE;
  407. }
  408. return FALSE;
  409. }
  410. #pragma pack(1)
  411. typedef struct _HIDDENSTRINGA
  412. {
  413. HIDDENITEMID hid;
  414. WORD type;
  415. CHAR sz[1]; // variable length string
  416. } HIDDENSTRINGA;
  417. #pragma pack()
  418. typedef UNALIGNED HIDDENSTRINGA *PHIDDENSTRINGA;
  419. typedef const UNALIGNED HIDDENSTRINGA *PCHIDDENSTRINGA;
  420. #pragma pack(1)
  421. typedef struct _HIDDENSTRINGW
  422. {
  423. HIDDENITEMID hid;
  424. WORD type;
  425. WCHAR sz[1]; // canonical name to be passed to ISTRING
  426. } HIDDENSTRINGW;
  427. #pragma pack()
  428. typedef UNALIGNED HIDDENSTRINGW *PHIDDENSTRINGW;
  429. typedef const UNALIGNED HIDDENSTRINGW *PCHIDDENSTRINGW;
  430. #define HIDSTRTYPE_ANSI 0x0001
  431. #define HIDSTRTYPE_WIDE 0x0002
  432. STDAPI_(LPITEMIDLIST) ILAppendHiddenStringW(LPITEMIDLIST pidl, IDLHID id, LPCWSTR psz)
  433. {
  434. // terminator is included in the ID definition
  435. USHORT cb = (USHORT)sizeof(HIDDENSTRINGW) + CbFromCchW(lstrlenW(psz));
  436. //
  437. // Use HIDDENSTRINGW* here instead of PHIDDENSTRINGW which is defined
  438. // as UNALIGNED.
  439. //
  440. HIDDENSTRINGW *phs = (HIDDENSTRINGW *) LocalAlloc(LPTR, cb);
  441. if (phs)
  442. {
  443. phs->hid.cb = cb;
  444. phs->hid.id = id;
  445. phs->type = HIDSTRTYPE_WIDE;
  446. StrCpyW(phs->sz, psz);
  447. pidl = ILAppendHiddenID(pidl, &phs->hid);
  448. LocalFree(phs);
  449. return pidl;
  450. }
  451. return NULL;
  452. }
  453. STDAPI_(LPITEMIDLIST) ILAppendHiddenStringA(LPITEMIDLIST pidl, IDLHID id, LPCSTR psz)
  454. {
  455. // terminator is included in the ID definition
  456. USHORT cb = (USHORT)sizeof(HIDDENSTRINGA) + CbFromCchA(lstrlenA(psz));
  457. //
  458. // Use HIDDENSTRINGA* here instead of PHIDDENSTRINGW which is defined
  459. // as UNALIGNED.
  460. //
  461. HIDDENSTRINGA *phs = (HIDDENSTRINGA *) LocalAlloc(LPTR, cb);
  462. if (phs)
  463. {
  464. phs->hid.cb = cb;
  465. phs->hid.id = id;
  466. phs->type = HIDSTRTYPE_ANSI;
  467. StrCpyA(phs->sz, psz);
  468. pidl = ILAppendHiddenID(pidl, &phs->hid);
  469. LocalFree(phs);
  470. return pidl;
  471. }
  472. return NULL;
  473. }
  474. STDAPI_(void *) _MemDupe(const UNALIGNED void *pv, DWORD cb)
  475. {
  476. void *pvRet = LocalAlloc(LPTR, cb);
  477. if (pvRet)
  478. {
  479. CopyMemory(pvRet, pv, cb);
  480. }
  481. return pvRet;
  482. }
  483. STDAPI_(BOOL) ILGetHiddenStringW(LPCITEMIDLIST pidl, IDLHID id, LPWSTR psz, DWORD cch)
  484. {
  485. PCHIDDENSTRINGW phs = (PCHIDDENSTRINGW) ILFindHiddenID(pidl, id);
  486. RIP(psz);
  487. if (phs)
  488. {
  489. if (phs->type == HIDSTRTYPE_WIDE)
  490. {
  491. ualstrcpynW(psz, phs->sz, cch);
  492. return TRUE;
  493. }
  494. else
  495. {
  496. ASSERT(phs->type == HIDSTRTYPE_ANSI);
  497. SHAnsiToUnicode((LPSTR)phs->sz, psz, cch);
  498. return TRUE;
  499. }
  500. }
  501. return FALSE;
  502. }
  503. STDAPI_(BOOL) ILGetHiddenStringA(LPCITEMIDLIST pidl, IDLHID id, LPSTR psz, DWORD cch)
  504. {
  505. PCHIDDENSTRINGW phs = (PCHIDDENSTRINGW) ILFindHiddenID(pidl, id);
  506. RIP(psz);
  507. if (phs)
  508. {
  509. if (phs->type == HIDSTRTYPE_ANSI)
  510. {
  511. ualstrcpynA(psz, (LPSTR)phs->sz, cch);
  512. return TRUE;
  513. }
  514. else
  515. {
  516. ASSERT(phs->type == HIDSTRTYPE_WIDE);
  517. // we need to handle the unalignment here...
  518. LPWSTR pszT = (LPWSTR) _MemDupe(phs->sz, CbFromCch(ualstrlenW(phs->sz) +1));
  519. if (pszT)
  520. {
  521. SHUnicodeToAnsi(pszT, psz, cch);
  522. LocalFree(pszT);
  523. return TRUE;
  524. }
  525. }
  526. }
  527. return FALSE;
  528. }
  529. STDAPI_(int) ILCompareHiddenString(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, IDLHID id)
  530. {
  531. // if there are fragments in here, then they might
  532. // differentiate the two pidls
  533. PCHIDDENSTRINGW ps1 = (PCHIDDENSTRINGW)ILFindHiddenID(pidl1, id);
  534. PCHIDDENSTRINGW ps2 = (PCHIDDENSTRINGW)ILFindHiddenID(pidl2, id);
  535. if (ps1 && ps2)
  536. {
  537. if (ps1->type == ps2->type)
  538. {
  539. if (ps1->type == HIDSTRTYPE_WIDE)
  540. return ualstrcmpW(ps1->sz, ps2->sz);
  541. ASSERT(ps1->type == HIDSTRTYPE_ANSI);
  542. return lstrcmpA((LPCSTR)ps1->sz, (LPCSTR)ps2->sz);
  543. }
  544. else
  545. {
  546. SHSTRW str;
  547. if (ps1->type == HIDSTRTYPE_ANSI)
  548. {
  549. str.SetStr((LPCSTR)ps1->sz);
  550. return ualstrcmpW(str, ps2->sz);
  551. }
  552. else
  553. {
  554. ASSERT(ps2->type == HIDSTRTYPE_ANSI);
  555. str.SetStr((LPCSTR)ps2->sz);
  556. return ualstrcmpW(ps1->sz, str);
  557. }
  558. }
  559. }
  560. if (ps1)
  561. return 1;
  562. if (ps2)
  563. return -1;
  564. return 0;
  565. }
  566. STDAPI_(OBJCOMPATFLAGS) SHGetObjectCompatFlagsFromIDList(LPCITEMIDLIST pidl)
  567. {
  568. OBJCOMPATFLAGS ocf = 0;
  569. CLSID clsid;
  570. // APPCOMPAT: FileNet IDMDS (Panagon)'s shell folder extension returns
  571. // E_NOTIMPL for IPersistFolder::GetClassID, so to detect the application,
  572. // we have to crack the pidl. (B#359464: tracysh)
  573. if (!ILIsEmpty(pidl)
  574. && pidl->mkid.cb >= sizeof(IDREGITEM)
  575. && pidl->mkid.abID[0] == SHID_ROOT_REGITEM)
  576. {
  577. clsid = ((LPCIDLREGITEM)pidl)->idri.clsid;
  578. ocf = SHGetObjectCompatFlags(NULL, &clsid);
  579. }
  580. return ocf;
  581. }
  582. STDAPI_(LPITEMIDLIST) _ILCreate(UINT cbSize)
  583. {
  584. LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbSize);
  585. if (pidl)
  586. memset(pidl, 0, cbSize); // zero-init for external task allocator
  587. return pidl;
  588. }
  589. //
  590. // ILClone using Task allocator
  591. //
  592. STDAPI SHILClone(LPCITEMIDLIST pidl, LPITEMIDLIST * ppidlOut)
  593. {
  594. *ppidlOut = ILClone(pidl);
  595. return *ppidlOut ? S_OK : E_OUTOFMEMORY;
  596. }
  597. //
  598. // ILCombine using Task allocator
  599. //
  600. STDAPI SHILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, LPITEMIDLIST * ppidlOut)
  601. {
  602. *ppidlOut = ILCombine(pidl1, pidl2);
  603. return *ppidlOut ? S_OK : E_OUTOFMEMORY;
  604. }
  605. //
  606. // rooted helpers
  607. //
  608. LPCIDREGITEM _IsRooted(LPCITEMIDLIST pidl)
  609. {
  610. LPCIDREGITEM pidlr = (LPCIDREGITEM)pidl;
  611. if (!ILIsEmpty(pidl)
  612. && pidlr->cb > sizeof(IDREGITEM)
  613. && pidlr->bFlags == SHID_ROOTEDREGITEM)
  614. return pidlr;
  615. return NULL;
  616. }
  617. STDAPI_(BOOL) ILIsRooted(LPCITEMIDLIST pidl)
  618. {
  619. return (NULL != _IsRooted(pidl));
  620. }
  621. #define _ROOTEDPIDL(pidlr) (LPITEMIDLIST)(((LPBYTE)pidlr)+sizeof(IDREGITEM))
  622. STDAPI_(LPCITEMIDLIST) ILRootedFindIDList(LPCITEMIDLIST pidl)
  623. {
  624. LPCIDREGITEM pidlr = _IsRooted(pidl);
  625. if (pidlr && pidlr->cb > sizeof(IDREGITEM))
  626. {
  627. // then we have a rooted IDList in there
  628. return _ROOTEDPIDL(pidlr);
  629. }
  630. return NULL;
  631. }
  632. STDAPI_(BOOL) ILRootedGetClsid(LPCITEMIDLIST pidl, CLSID *pclsid)
  633. {
  634. LPCIDREGITEM pidlr = _IsRooted(pidl);
  635. *pclsid = pidlr ? pidlr->clsid : CLSID_NULL;
  636. return (NULL != pidlr);
  637. }
  638. STDAPI_(LPITEMIDLIST) ILRootedCreateIDList(CLSID *pclsid, LPCITEMIDLIST pidl)
  639. {
  640. UINT cbPidl = ILGetSize(pidl);
  641. UINT cbTotal = sizeof(IDREGITEM) + cbPidl;
  642. LPIDREGITEM pidlr = (LPIDREGITEM) SHAlloc(cbTotal + sizeof(WORD));
  643. if (pidlr)
  644. {
  645. pidlr->cb = (WORD)cbTotal;
  646. pidlr->bFlags = SHID_ROOTEDREGITEM;
  647. pidlr->bOrder = 0; // Nobody uses this (yet)
  648. if (pclsid)
  649. pidlr->clsid = *pclsid;
  650. else
  651. pidlr->clsid = CLSID_ShellDesktop;
  652. MoveMemory(_ROOTEDPIDL(pidlr), pidl, cbPidl);
  653. // terminate
  654. _ILNext((LPITEMIDLIST)pidlr)->mkid.cb = 0;
  655. }
  656. return (LPITEMIDLIST) pidlr;
  657. }
  658. int CompareGUID(REFGUID guid1, REFGUID guid2)
  659. {
  660. TCHAR sz1[GUIDSTR_MAX];
  661. TCHAR sz2[GUIDSTR_MAX];
  662. SHStringFromGUIDW(guid1, sz1, SIZECHARS(sz1));
  663. SHStringFromGUIDW(guid2, sz2, SIZECHARS(sz2));
  664. return lstrcmp(sz1, sz2);
  665. }
  666. STDAPI_(int) ILRootedCompare(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  667. {
  668. int iRet;
  669. LPCIDREGITEM pidlr1 = _IsRooted(pidl1);
  670. LPCIDREGITEM pidlr2 = _IsRooted(pidl2);
  671. if (pidlr1 && pidlr2)
  672. {
  673. CLSID clsid1 = pidlr1->clsid;
  674. CLSID clsid2 = pidlr2->clsid;
  675. iRet = CompareGUID(clsid1, clsid2);
  676. if (0 == iRet)
  677. {
  678. if (!ILIsEqual(_ROOTEDPIDL(pidl1), _ROOTEDPIDL(pidl2)))
  679. {
  680. IShellFolder *psfDesktop;
  681. if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop)))
  682. {
  683. HRESULT hr = psfDesktop->CompareIDs(0, _ROOTEDPIDL(pidl1), _ROOTEDPIDL(pidl2));
  684. psfDesktop->Release();
  685. iRet = ShortFromResult(hr);
  686. }
  687. }
  688. }
  689. }
  690. else if (pidlr1)
  691. {
  692. iRet = -1;
  693. }
  694. else if (pidlr2)
  695. {
  696. iRet = 1;
  697. }
  698. else
  699. {
  700. // if neither are rootes, then they share the desktop
  701. // as the same root...
  702. iRet = 0;
  703. }
  704. return iRet;
  705. }
  706. LPITEMIDLIST ILRootedTranslate(LPCITEMIDLIST pidlRooted, LPCITEMIDLIST pidlTrans)
  707. {
  708. LPCITEMIDLIST pidlChild = ILFindChild(ILRootedFindIDList(pidlRooted), pidlTrans);
  709. if (pidlChild)
  710. {
  711. LPITEMIDLIST pidlRoot = ILCloneFirst(pidlRooted);
  712. if (pidlRoot)
  713. {
  714. LPITEMIDLIST pidlRet = ILCombine(pidlRoot, pidlChild);
  715. ILFree(pidlRoot);
  716. return pidlRet;
  717. }
  718. }
  719. return NULL;
  720. }
  721. const ITEMIDLIST s_idlNULL = { 0 } ;
  722. HRESULT ILRootedBindToRoot(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  723. {
  724. HRESULT hr;
  725. CLSID clsid;
  726. ASSERT(ILIsRooted(pidl));
  727. ILRootedGetClsid(pidl, &clsid);
  728. pidl = ILRootedFindIDList(pidl);
  729. if (!pidl)
  730. pidl = &s_idlNULL;
  731. if (IsEqualGUID(clsid, CLSID_ShellDesktop))
  732. {
  733. hr = SHBindToObjectEx(NULL, pidl, NULL, riid, ppv);
  734. }
  735. else
  736. {
  737. IPersistFolder* ppf;
  738. hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IPersistFolder, &ppf));
  739. if (SUCCEEDED(hr))
  740. {
  741. hr = ppf->Initialize(pidl);
  742. if (SUCCEEDED(hr))
  743. {
  744. hr = ppf->QueryInterface(riid, ppv);
  745. }
  746. ppf->Release();
  747. }
  748. }
  749. return hr;
  750. }
  751. HRESULT ILRootedBindToObject(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  752. {
  753. IShellFolder *psf;
  754. HRESULT hr = ILRootedBindToRoot(pidl, IID_PPV_ARG(IShellFolder, &psf));
  755. if (SUCCEEDED(hr))
  756. {
  757. pidl = _ILNext(pidl);
  758. if (ILIsEmpty(pidl))
  759. hr = psf->QueryInterface(riid, ppv);
  760. else
  761. hr = psf->BindToObject(pidl, NULL, riid, ppv);
  762. }
  763. return hr;
  764. }
  765. HRESULT ILRootedBindToParentFolder(LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlChild)
  766. {
  767. //
  768. // there are three different cases to handle
  769. //
  770. // 1. Rooted pidl Alone
  771. // [ rooted id [ target pidl ] ]
  772. // return the parent folder of the target pidl
  773. // and return its last id in ppidlChild
  774. //
  775. // 2. Rooted pidl with One Child
  776. // [ rooted id [ target pidl ] ][ child id ]
  777. // return the rooted id as the parent folder
  778. // and the child id in ppidlChild
  779. //
  780. // 3. rooted pidl with many children
  781. // [ rooted id [ target pidl ] ][ parent id ][ child id ]
  782. // return rooted id bound to parent id as the folder
  783. // and the child id in ppidlchild
  784. //
  785. HRESULT hr;
  786. ASSERT(ILIsRooted(pidl));
  787. //
  788. // if this is a rooted pidl and it is just the root
  789. // then we can bind to the target pidl of the root instead
  790. //
  791. if (ILIsEmpty(_ILNext(pidl)))
  792. {
  793. hr = SHBindToIDListParent(ILRootedFindIDList(pidl), riid, ppv, ppidlChild);
  794. }
  795. else
  796. {
  797. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  798. if (pidlParent)
  799. {
  800. hr = ILRootedBindToObject(pidlParent, riid, ppv);
  801. ILFree(pidlParent);
  802. }
  803. else
  804. hr = E_OUTOFMEMORY;
  805. if (ppidlChild)
  806. *ppidlChild = ILFindLastID(pidl);
  807. }
  808. return hr;
  809. }
  810. #define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
  811. #define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
  812. STDAPI_(LPITEMIDLIST) IDA_ILClone(LPIDA pida, UINT i)
  813. {
  814. if (i < pida->cidl)
  815. return ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, i));
  816. return NULL;
  817. }
  818. STDAPI_(void) EnableOKButtonFromString(HWND hDlg, LPTSTR pszText)
  819. {
  820. BOOL bNonEmpty;
  821. PathRemoveBlanks(pszText); // REVIEW, should we not remove from the end of
  822. bNonEmpty = lstrlen(pszText); // Not a BOOL, but okay
  823. EnableWindow(GetDlgItem(hDlg, IDOK), bNonEmpty);
  824. if (bNonEmpty)
  825. {
  826. SendMessage(hDlg, DM_SETDEFID, IDOK, 0L);
  827. }
  828. }
  829. STDAPI_(void) EnableOKButtonFromID(HWND hDlg, int id)
  830. {
  831. TCHAR szText[MAX_PATH];
  832. if (!GetDlgItemText(hDlg, id, szText, ARRAYSIZE(szText)))
  833. {
  834. szText[0] = 0;
  835. }
  836. EnableOKButtonFromString(hDlg, szText);
  837. }
  838. //
  839. // C-callable versions of the ATL string conversion functions.
  840. //
  841. STDAPI_(LPWSTR) SHA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars)
  842. {
  843. ASSERT(lpa != NULL);
  844. ASSERT(lpw != NULL);
  845. // verify that no illegal character present
  846. // since lpw was allocated based on the size of lpa
  847. // don't worry about the number of chars
  848. lpw[0] = '\0';
  849. MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars);
  850. return lpw;
  851. }
  852. STDAPI_(LPSTR) SHW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars)
  853. {
  854. ASSERT(lpw != NULL);
  855. ASSERT(lpa != NULL);
  856. // verify that no illegal character present
  857. // since lpa was allocated based on the size of lpw
  858. // don't worry about the number of chars
  859. lpa[0] = '\0';
  860. WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL);
  861. return lpa;
  862. }
  863. //
  864. // Helper functions for SHChangeMenuAsIDList
  865. //
  866. // See comment in declaration of SHChangeMenuAsIDList for caveats about
  867. // the pSender member.
  868. //
  869. // This is tricky because IE 5.0 shipped with a Win64-unfriendly version
  870. // of this notification, so we have to sniff the structure and see if
  871. // this is an IE 5.0 style notification or a new Win64 style notification.
  872. // If an IE 5.0 style notification, then it was not sent by us because
  873. // we send the new Win64-style notification.
  874. //
  875. STDAPI_(BOOL) SHChangeMenuWasSentByMe(void * self, LPCITEMIDLIST pidlNotify)
  876. {
  877. SHChangeMenuAsIDList UNALIGNED * pcmidl = (SHChangeMenuAsIDList UNALIGNED *)pidlNotify;
  878. return pcmidl->cb >= FIELD_OFFSET(SHChangeMenuAsIDList, cbZero) &&
  879. pcmidl->pSender == (INT64)self &&
  880. pcmidl->dwProcessID == GetCurrentProcessId();
  881. }
  882. //
  883. //
  884. // Send out an extended event changenotify, using a SHChangeMenuAsIDList
  885. // as the pidl1 so recipients can identify whether they were the
  886. // sender or not.
  887. //
  888. // It's okay to pass self==NULL here. It means you don't care about
  889. // detecting whether it was sent by you or not.
  890. //
  891. STDAPI_(void) SHSendChangeMenuNotify(void * self, DWORD shcnee, DWORD shcnf, LPCITEMIDLIST pidl2)
  892. {
  893. SHChangeMenuAsIDList cmidl;
  894. cmidl.cb = FIELD_OFFSET(SHChangeMenuAsIDList, cbZero);
  895. cmidl.dwItem1 = shcnee;
  896. cmidl.pSender = (INT64)self;
  897. cmidl.dwProcessID = self ? GetCurrentProcessId() : 0;
  898. cmidl.cbZero = 0;
  899. // Nobody had better have specified a type; the type must be
  900. // SHCNF_IDLIST.
  901. ASSERT((shcnf & SHCNF_TYPE) == 0);
  902. SHChangeNotify(SHCNE_EXTENDED_EVENT, shcnf | SHCNF_IDLIST, (LPCITEMIDLIST)&cmidl, pidl2);
  903. }
  904. // Return FALSE if out of memory
  905. STDAPI_(BOOL) Pidl_Set(LPITEMIDLIST* ppidl, LPCITEMIDLIST pidl)
  906. {
  907. BOOL bRet = TRUE;
  908. LPITEMIDLIST pidlNew;
  909. ASSERT(IS_VALID_WRITE_PTR(ppidl, LPITEMIDLIST));
  910. ASSERT(NULL == *ppidl || IS_VALID_PIDL(*ppidl));
  911. ASSERT(NULL == pidl || IS_VALID_PIDL(pidl));
  912. if (pidl)
  913. {
  914. pidlNew = ILClone(pidl);
  915. if (!pidlNew)
  916. {
  917. bRet = FALSE; // failed to clone the pidl (out of memory)
  918. }
  919. }
  920. else
  921. {
  922. pidlNew = NULL;
  923. }
  924. LPITEMIDLIST pidlToFree = (LPITEMIDLIST)InterlockedExchangePointer((void **)ppidl, (void *)pidlNew);
  925. if (pidlToFree)
  926. {
  927. ILFree(pidlToFree);
  928. }
  929. return bRet;
  930. }
  931. // this needs to be the last thing in the file that uses ILClone, because everywhere
  932. // else, ILClone becomes SafeILClone
  933. #undef ILClone
  934. STDAPI_(LPITEMIDLIST) SafeILClone(LPCITEMIDLIST pidl)
  935. {
  936. // the shell32 implementation of ILClone is different for win95 an ie4.
  937. // it doesnt check for NULL in the old version, but it does in the new...
  938. // so we need to always check
  939. return pidl ? ILClone(pidl) : NULL;
  940. }
  941. //
  942. // retrieves the UIObject interface for the specified full pidl.
  943. //
  944. STDAPI SHGetUIObjectFromFullPIDL(LPCITEMIDLIST pidl, HWND hwnd, REFIID riid, void **ppv)
  945. {
  946. *ppv = NULL;
  947. LPCITEMIDLIST pidlChild;
  948. IShellFolder* psf;
  949. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
  950. if (SUCCEEDED(hr))
  951. {
  952. hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv);
  953. psf->Release();
  954. }
  955. return hr;
  956. }
  957. STDAPI LoadFromFileW(REFCLSID clsid, LPCWSTR pszFile, REFIID riid, void **ppv)
  958. {
  959. *ppv = NULL;
  960. IPersistFile *ppf;
  961. HRESULT hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IPersistFile, &ppf));
  962. if (SUCCEEDED(hr))
  963. {
  964. hr = ppf->Load(pszFile, STGM_READ);
  965. if (SUCCEEDED(hr))
  966. hr = ppf->QueryInterface(riid, ppv);
  967. ppf->Release();
  968. }
  969. return hr;
  970. }
  971. STDAPI LoadFromIDList(REFCLSID clsid, LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  972. {
  973. *ppv = NULL;
  974. IPersistFolder *ppf;
  975. HRESULT hr = SHCoCreateInstanceAC(clsid, NULL, CLSCTX_INPROC, IID_PPV_ARG(IPersistFolder, &ppf));
  976. if (SUCCEEDED(hr))
  977. {
  978. hr = ppf->Initialize(pidl);
  979. if (SUCCEEDED(hr))
  980. {
  981. hr = ppf->QueryInterface(riid, ppv);
  982. }
  983. ppf->Release();
  984. }
  985. return hr;
  986. }
  987. //
  988. // This is a helper function for finding a specific verb's index in a context menu
  989. //
  990. STDAPI_(UINT) GetMenuIndexForCanonicalVerb(HMENU hMenu, IContextMenu *pcm, UINT idCmdFirst, LPCWSTR pwszVerb)
  991. {
  992. int cMenuItems = GetMenuItemCount(hMenu);
  993. for (int iItem = 0; iItem < cMenuItems; iItem++)
  994. {
  995. MENUITEMINFO mii = {0};
  996. mii.cbSize = sizeof(mii);
  997. mii.fMask = MIIM_TYPE | MIIM_ID;
  998. // IS_INTRESOURCE guards against mii.wID == -1 **and** against
  999. // buggy shell extensions which set their menu item IDs out of range.
  1000. if (GetMenuItemInfo(hMenu, iItem, MF_BYPOSITION, &mii) &&
  1001. !(mii.fType & MFT_SEPARATOR) && IS_INTRESOURCE(mii.wID) &&
  1002. (mii.wID >= idCmdFirst))
  1003. {
  1004. union {
  1005. WCHAR szItemNameW[80];
  1006. char szItemNameA[80];
  1007. };
  1008. CHAR aszVerb[80];
  1009. // try both GCS_VERBA and GCS_VERBW in case it only supports one of them
  1010. SHUnicodeToAnsi(pwszVerb, aszVerb, ARRAYSIZE(aszVerb));
  1011. if (SUCCEEDED(pcm->GetCommandString(mii.wID - idCmdFirst, GCS_VERBA, NULL, szItemNameA, ARRAYSIZE(szItemNameA))))
  1012. {
  1013. if (StrCmpICA(szItemNameA, aszVerb) == 0)
  1014. {
  1015. break; // found it
  1016. }
  1017. }
  1018. else
  1019. {
  1020. if (SUCCEEDED(pcm->GetCommandString(mii.wID - idCmdFirst, GCS_VERBW, NULL, (LPSTR)szItemNameW, ARRAYSIZE(szItemNameW))) &&
  1021. (StrCmpICW(szItemNameW, pwszVerb) == 0))
  1022. {
  1023. break; // found it
  1024. }
  1025. }
  1026. }
  1027. }
  1028. if (iItem == cMenuItems)
  1029. {
  1030. iItem = -1; // went through all the menuitems and didn't find it
  1031. }
  1032. return iItem;
  1033. }
  1034. // deal with GCS_VERBW/GCS_VERBA maddness
  1035. STDAPI ContextMenu_GetCommandStringVerb(IContextMenu *pcm, UINT idCmd, LPWSTR pszVerb, int cchVerb)
  1036. {
  1037. // Ulead SmartSaver Pro has a 60 character verb, and
  1038. // over writes out stack, ignoring the cch param and we fault.
  1039. // so make sure this buffer is at least 60 chars
  1040. TCHAR wszVerb[64];
  1041. wszVerb[0] = 0;
  1042. HRESULT hr = pcm->GetCommandString(idCmd, GCS_VERBW, NULL, (LPSTR)wszVerb, ARRAYSIZE(wszVerb));
  1043. if (FAILED(hr))
  1044. {
  1045. // be extra paranoid about requesting the ansi version -- we've
  1046. // found IContextMenu implementations that return a UNICODE buffer
  1047. // even though we ask for an ANSI string on NT systems -- hopefully
  1048. // they will have answered the above request already, but just in
  1049. // case let's not let them overrun our stack!
  1050. char szVerbAnsi[128];
  1051. hr = pcm->GetCommandString(idCmd, GCS_VERBA, NULL, szVerbAnsi, ARRAYSIZE(szVerbAnsi) / 2);
  1052. if (SUCCEEDED(hr))
  1053. {
  1054. SHAnsiToUnicode(szVerbAnsi, wszVerb, ARRAYSIZE(wszVerb));
  1055. }
  1056. }
  1057. StrCpyNW(pszVerb, wszVerb, cchVerb);
  1058. return hr;
  1059. }
  1060. //
  1061. // Purpose: Deletes the menu item specified by name
  1062. //
  1063. // Parameters: pcm - Context menu interface
  1064. // hpopup - Context menu handle
  1065. // idFirst - Beginning of id range
  1066. // pszCommand - Command to look for
  1067. //
  1068. STDAPI ContextMenu_DeleteCommandByName(IContextMenu *pcm, HMENU hpopup, UINT idFirst, LPCWSTR pszCommand)
  1069. {
  1070. UINT ipos = GetMenuIndexForCanonicalVerb(hpopup, pcm, idFirst, pszCommand);
  1071. if (ipos != -1)
  1072. {
  1073. DeleteMenu(hpopup, ipos, MF_BYPOSITION);
  1074. return S_OK;
  1075. }
  1076. else
  1077. {
  1078. return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  1079. }
  1080. }
  1081. //
  1082. // Helpers to banish STRRET's into the realm of darkness
  1083. //
  1084. STDAPI DisplayNameOf(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD flags, LPTSTR psz, UINT cch)
  1085. {
  1086. *psz = 0;
  1087. STRRET sr;
  1088. HRESULT hr = psf->GetDisplayNameOf(pidl, flags, &sr);
  1089. if (SUCCEEDED(hr))
  1090. hr = StrRetToBuf(&sr, pidl, psz, cch);
  1091. return hr;
  1092. }
  1093. STDAPI DisplayNameOfAsOLESTR(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD flags, LPWSTR *ppsz)
  1094. {
  1095. *ppsz = NULL;
  1096. STRRET sr;
  1097. HRESULT hr = psf->GetDisplayNameOf(pidl, flags, &sr);
  1098. if (SUCCEEDED(hr))
  1099. hr = StrRetToStrW(&sr, pidl, ppsz);
  1100. return hr;
  1101. }
  1102. // get the target pidl for a folder pidl. this deals with the case where a folder
  1103. // is an alias to a real folder, Folder Shortcuts, etc.
  1104. STDAPI SHGetTargetFolderIDList(LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidl)
  1105. {
  1106. *ppidl = NULL;
  1107. // likely should ASSERT() that pidlFolder has SFGAO_FOLDER
  1108. IShellLink *psl;
  1109. HRESULT hr = SHGetUIObjectFromFullPIDL(pidlFolder, NULL, IID_PPV_ARG(IShellLink, &psl));
  1110. if (SUCCEEDED(hr))
  1111. {
  1112. hr = psl->GetIDList(ppidl);
  1113. psl->Release();
  1114. }
  1115. // No its not a folder shortcut. Get the pidl normally.
  1116. if (FAILED(hr))
  1117. hr = SHILClone(pidlFolder, ppidl);
  1118. return hr;
  1119. }
  1120. // get the target folder for a folder pidl. this deals with the case where a folder
  1121. // is an alias to a real folder, Folder Shortcuts, MyDocs, etc.
  1122. STDAPI SHGetTargetFolderPathW(LPCITEMIDLIST pidlFolder, LPWSTR pszPath, UINT cchPath)
  1123. {
  1124. *pszPath = 0;
  1125. LPITEMIDLIST pidlTarget;
  1126. if (SUCCEEDED(SHGetTargetFolderIDList(pidlFolder, &pidlTarget)))
  1127. {
  1128. SHGetPathFromIDListW(pidlTarget, pszPath); // make sure it is a path
  1129. ILFree(pidlTarget);
  1130. }
  1131. return *pszPath ? S_OK : E_FAIL;
  1132. }
  1133. STDAPI SHGetTargetFolderPathA(LPCITEMIDLIST pidlFolder, LPSTR pszPath, UINT cchPath)
  1134. {
  1135. *pszPath = 0;
  1136. WCHAR szPath[MAX_PATH];
  1137. HRESULT hr = SHGetTargetFolderPathW(pidlFolder, szPath, ARRAYSIZE(szPath));
  1138. if (SUCCEEDED(hr))
  1139. SHAnsiToUnicode(pszPath, szPath, cchPath);
  1140. return hr;
  1141. }
  1142. STDAPI SHBuildDisplayMachineName(LPCWSTR pszMachineName, LPCWSTR pszComment, LPWSTR pszDisplayName, DWORD cchDisplayName)
  1143. {
  1144. HRESULT hr = E_FAIL;
  1145. if (pszComment && pszComment[0])
  1146. {
  1147. // encorporate the comment into the display name
  1148. LPCWSTR pszNoSlashes = SkipServerSlashes(pszMachineName);
  1149. int i = wnsprintfW(pszDisplayName, cchDisplayName, L"%s (%s)", pszComment, pszNoSlashes);
  1150. hr = (i < 0) ? E_FAIL : S_OK;
  1151. }
  1152. else
  1153. {
  1154. // Return failure here so netfldr can do smarter things to build a display name
  1155. hr = E_FAIL;
  1156. }
  1157. return hr;
  1158. }
  1159. // create objects from registered under a key value, uses the per user per machine
  1160. // reg services to do this.
  1161. STDAPI CreateFromRegKey(LPCWSTR pszKey, LPCWSTR pszValue, REFIID riid, void **ppv)
  1162. {
  1163. HRESULT hr = E_FAIL;
  1164. WCHAR szCLSID[MAX_PATH];
  1165. DWORD cbSize = sizeof(szCLSID);
  1166. if (SHRegGetUSValueW(pszKey, pszValue, NULL, szCLSID, &cbSize, FALSE, NULL, 0) == ERROR_SUCCESS)
  1167. {
  1168. CLSID clsid;
  1169. if (GUIDFromString(szCLSID, &clsid))
  1170. {
  1171. hr = SHCoCreateInstanceAC(clsid, NULL, CLSCTX_INPROC_SERVER, riid, ppv);
  1172. }
  1173. }
  1174. return hr;
  1175. }
  1176. //
  1177. // SHProcessMessagesUntilEvent:
  1178. //
  1179. // this executes message loop until an event or a timeout occurs
  1180. //
  1181. STDAPI_(DWORD) SHProcessMessagesUntilEventEx(HWND hwnd, HANDLE hEvent, DWORD dwTimeout, DWORD dwWakeMask)
  1182. {
  1183. DWORD dwEndTime = GetTickCount() + dwTimeout;
  1184. LONG lWait = (LONG)dwTimeout;
  1185. DWORD dwReturn;
  1186. if (!hEvent && (dwTimeout == INFINITE))
  1187. {
  1188. ASSERTMSG(FALSE, "SHProcessMessagesUntilEvent: caller passed a NULL hEvent and an INFINITE timeout!!");
  1189. return -1;
  1190. }
  1191. for (;;)
  1192. {
  1193. DWORD dwCount = hEvent ? 1 : 0;
  1194. dwReturn = MsgWaitForMultipleObjects(dwCount, &hEvent, FALSE, lWait, dwWakeMask);
  1195. // were we signalled or did we time out?
  1196. if (dwReturn != (WAIT_OBJECT_0 + dwCount))
  1197. {
  1198. break;
  1199. }
  1200. // we woke up because of messages.
  1201. MSG msg;
  1202. while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
  1203. {
  1204. ASSERT(msg.message != WM_QUIT);
  1205. TranslateMessage(&msg);
  1206. if (msg.message == WM_SETCURSOR)
  1207. {
  1208. SetCursor(LoadCursor(NULL, IDC_WAIT));
  1209. }
  1210. else
  1211. {
  1212. DispatchMessage(&msg);
  1213. }
  1214. }
  1215. // calculate new timeout value
  1216. if (dwTimeout != INFINITE)
  1217. {
  1218. lWait = (LONG)dwEndTime - GetTickCount();
  1219. }
  1220. }
  1221. return dwReturn;
  1222. }
  1223. // deals with goofyness of IShellFolder::GetAttributesOf() including
  1224. // in/out param issue
  1225. // failures
  1226. // goofy cast for 1 item case
  1227. // masks off results to only return what you asked for
  1228. STDAPI_(DWORD) SHGetAttributes(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD dwAttribs)
  1229. {
  1230. // like SHBindToObject, if psf is NULL, use absolute pidl
  1231. LPCITEMIDLIST pidlChild;
  1232. if (!psf)
  1233. {
  1234. SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
  1235. }
  1236. else
  1237. {
  1238. psf->AddRef();
  1239. pidlChild = pidl;
  1240. }
  1241. DWORD dw = 0;
  1242. if (psf)
  1243. {
  1244. dw = dwAttribs;
  1245. dw = SUCCEEDED(psf->GetAttributesOf(1, (LPCITEMIDLIST *)&pidlChild, &dw)) ? (dwAttribs & dw) : 0;
  1246. if ((dw & SFGAO_FOLDER) && (dw & SFGAO_CANMONIKER) && !(dw & SFGAO_STORAGEANCESTOR) && (dwAttribs & SFGAO_STORAGEANCESTOR))
  1247. {
  1248. if (OBJCOMPATF_NEEDSSTORAGEANCESTOR & SHGetObjectCompatFlags(psf, NULL))
  1249. {
  1250. // switch SFGAO_CANMONIKER -> SFGAO_STORAGEANCESTOR
  1251. dw |= SFGAO_STORAGEANCESTOR;
  1252. dw &= ~SFGAO_CANMONIKER;
  1253. }
  1254. }
  1255. }
  1256. if (psf)
  1257. {
  1258. psf->Release();
  1259. }
  1260. return dw;
  1261. }
  1262. //===========================================================================
  1263. // IDLARRAY stuff
  1264. //===========================================================================
  1265. STDAPI_(HIDA) HIDA_Create(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST * apidl)
  1266. {
  1267. UINT offset = sizeof(CIDA) + sizeof(UINT) * cidl;
  1268. UINT cbTotal = offset + ILGetSize(pidlFolder);
  1269. for (UINT i = 0; i<cidl ; i++)
  1270. {
  1271. cbTotal += ILGetSize(apidl[i]);
  1272. }
  1273. HIDA hida = GlobalAlloc(GPTR, cbTotal); // This MUST be GlobalAlloc!!!
  1274. if (hida)
  1275. {
  1276. LPIDA pida = (LPIDA)hida; // no need to lock
  1277. LPCITEMIDLIST pidlNext;
  1278. pida->cidl = cidl;
  1279. for (i = 0, pidlNext = pidlFolder; ; pidlNext = apidl[i++])
  1280. {
  1281. UINT cbSize = ILGetSize(pidlNext);
  1282. pida->aoffset[i] = offset;
  1283. CopyMemory(((LPBYTE)pida) + offset, pidlNext, cbSize);
  1284. offset += cbSize;
  1285. ASSERT(ILGetSize(HIDA_GetPIDLItem(pida,i-1)) == cbSize);
  1286. if (i == cidl)
  1287. break;
  1288. }
  1289. ASSERT(offset == cbTotal);
  1290. }
  1291. return hida;
  1292. }
  1293. STDAPI_(void) HIDA_Free(HIDA hida)
  1294. {
  1295. GlobalFree(hida);
  1296. }
  1297. STDAPI_(HIDA) HIDA_Clone(HIDA hida)
  1298. {
  1299. SIZE_T cbTotal = GlobalSize(hida);
  1300. HIDA hidaCopy = GlobalAlloc(GMEM_FIXED, cbTotal);
  1301. if (hidaCopy)
  1302. {
  1303. CopyMemory(hidaCopy, GlobalLock(hida), cbTotal);
  1304. GlobalUnlock(hida);
  1305. }
  1306. return hidaCopy;
  1307. }
  1308. STDAPI_(UINT) HIDA_GetCount(HIDA hida)
  1309. {
  1310. UINT count = 0;
  1311. LPIDA pida = (LPIDA)GlobalLock(hida);
  1312. if (pida)
  1313. {
  1314. count = pida->cidl;
  1315. GlobalUnlock(hida);
  1316. }
  1317. return count;
  1318. }
  1319. STDAPI_(UINT) HIDA_GetIDList(HIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax)
  1320. {
  1321. LPIDA pida = (LPIDA)GlobalLock(hida);
  1322. if (pida)
  1323. {
  1324. LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida);
  1325. LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem(pida, i);
  1326. UINT cbFolder = ILGetSize(pidlFolder) - sizeof(USHORT);
  1327. UINT cbItem = ILGetSize(pidlItem);
  1328. if (cbMax < cbFolder+cbItem)
  1329. {
  1330. if (pidlOut)
  1331. pidlOut->mkid.cb = 0;
  1332. }
  1333. else
  1334. {
  1335. MoveMemory(pidlOut, pidlFolder, cbFolder);
  1336. MoveMemory(((LPBYTE)pidlOut) + cbFolder, pidlItem, cbItem);
  1337. }
  1338. GlobalUnlock(hida);
  1339. return cbFolder + cbItem;
  1340. }
  1341. return 0;
  1342. }
  1343. STDAPI_(BOOL) PathIsImage(LPCTSTR pszFile)
  1344. {
  1345. BOOL fPicture = FALSE;
  1346. LPTSTR pszExt = PathFindExtension(pszFile);
  1347. if (pszExt)
  1348. {
  1349. // there's no ASSOCSTR_PERCEIVED so pick it up from the registry.
  1350. TCHAR szPerceivedType[MAX_PATH];
  1351. DWORD cb = ARRAYSIZE(szPerceivedType) * sizeof(TCHAR);
  1352. if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, pszExt, TEXT("PerceivedType"), NULL, szPerceivedType, &cb))
  1353. {
  1354. fPicture = (StrCmpI(szPerceivedType, TEXT("image")) == 0);
  1355. }
  1356. }
  1357. return fPicture;
  1358. }
  1359. // helper function to create a stream or storage in a storage.
  1360. HRESULT CreateStreamOrStorage(IStorage * pStorageParent, LPCTSTR pszName, REFIID riid, void **ppv)
  1361. {
  1362. DWORD grfModeCreated = STGM_READWRITE;
  1363. HRESULT hr = E_INVALIDARG;
  1364. if (IsEqualGUID(riid, IID_IStorage))
  1365. {
  1366. IStorage * pStorageCreated;
  1367. hr = pStorageParent->CreateStorage(pszName, grfModeCreated, 0, 0, &pStorageCreated);
  1368. if (SUCCEEDED(hr))
  1369. {
  1370. hr = pStorageParent->Commit(STGC_DEFAULT);
  1371. *ppv = pStorageCreated;
  1372. }
  1373. }
  1374. else if (IsEqualGUID(riid, IID_IStream))
  1375. {
  1376. IStream * pStreamCreated;
  1377. hr = pStorageParent->CreateStream(pszName, grfModeCreated, 0, 0, &pStreamCreated);
  1378. if (SUCCEEDED(hr))
  1379. {
  1380. hr = pStorageParent->Commit(STGC_DEFAULT);
  1381. *ppv = pStreamCreated;
  1382. }
  1383. }
  1384. return hr;
  1385. }
  1386. // same as PathMakeUniqueNameEx but it works on storages.
  1387. // Note: LFN only!
  1388. STDAPI StgMakeUniqueNameWithCount(IStorage *pStorageParent, LPCWSTR pszTemplate,
  1389. int iMinLong, REFIID riid, void **ppv)
  1390. {
  1391. HRESULT hr = E_INVALIDARG;
  1392. RIPMSG(pszTemplate && IS_VALID_STRING_PTR(pszTemplate, -1) && lstrlen(pszTemplate)<(MAX_PATH-6), "StgMakeUniqueNameWithCount: invalid pszTemplate");
  1393. if (pszTemplate && lstrlen(pszTemplate)<(MAX_PATH-6)) // -6 for " (999)"
  1394. {
  1395. WCHAR szBuffer[MAX_PATH];
  1396. WCHAR szFormat[MAX_PATH];
  1397. int cchStem;
  1398. // Set up:
  1399. // cchStem : length of pszTemplate we're going to use w/o wsprintf
  1400. // szFormat : format string to wsprintf the number with, catenates on to pszTemplate[0..cchStem]
  1401. // Has template already been uniquified?
  1402. //
  1403. LPWSTR pszRest = StrChr(pszTemplate, L'(');
  1404. while (pszRest)
  1405. {
  1406. // First validate that this is the right one
  1407. LPWSTR pszEndUniq = CharNext(pszRest);
  1408. while (*pszEndUniq && *pszEndUniq >= L'0' && *pszEndUniq <= L'9')
  1409. {
  1410. pszEndUniq++;
  1411. }
  1412. if (*pszEndUniq == L')')
  1413. break; // We have the right one!
  1414. pszRest = StrChr(CharNext(pszRest), L'(');
  1415. }
  1416. if (!pszRest)
  1417. {
  1418. // if no (, then tack it on at the end. (but before the extension)
  1419. // eg. New Link yields New Link (1)
  1420. pszRest = PathFindExtension(pszTemplate);
  1421. cchStem = (int)(pszRest - pszTemplate);
  1422. wsprintf(szFormat, L" (%%d)%s", pszRest ? pszRest : L"");
  1423. }
  1424. else
  1425. {
  1426. // Template has been uniquified, remove uniquing digits
  1427. // eg. New Link (1) yields New Link (2)
  1428. //
  1429. pszRest++; // step over the (
  1430. cchStem = (int) (pszRest - pszTemplate);
  1431. while (*pszRest && *pszRest >= L'0' && *pszRest <= L'9')
  1432. {
  1433. pszRest++;
  1434. }
  1435. // we are guaranteed enough room because we don't include
  1436. // the stuff before the # in this format
  1437. wsprintf(szFormat, L"%%d%s", pszRest);
  1438. }
  1439. // copy the fixed portion into the buffer
  1440. //
  1441. StrCpyN(szBuffer, pszTemplate, cchStem+1);
  1442. // Iterate on the uniquifying szFormat portion until we find a unique name:
  1443. //
  1444. LPTSTR pszDigit = szBuffer + cchStem;
  1445. hr = STG_E_FILEALREADYEXISTS;
  1446. for (int i = iMinLong; (i < 1000) && (STG_E_FILEALREADYEXISTS == hr); i++)
  1447. {
  1448. wsprintf(pszDigit, szFormat, i);
  1449. // okay, we have the unique name, so create it in the storage.
  1450. hr = CreateStreamOrStorage(pStorageParent, szBuffer, riid, ppv);
  1451. }
  1452. }
  1453. return hr;
  1454. }
  1455. STDAPI StgMakeUniqueName(IStorage *pStorageParent, LPCTSTR pszFileSpec, REFIID riid, void **ppv)
  1456. {
  1457. HRESULT hr = S_OK;
  1458. TCHAR szTemp[MAX_PATH];
  1459. LPTSTR psz;
  1460. LPTSTR pszNew;
  1461. // try it without the ( if there's a space after it
  1462. psz = StrChr(pszFileSpec, L'(');
  1463. while (psz)
  1464. {
  1465. if (*(CharNext(psz)) == L')')
  1466. break;
  1467. psz = StrChr(CharNext(psz), L'(');
  1468. }
  1469. if (psz)
  1470. {
  1471. // We have the (). See if we have either x () y or x ().y in which case
  1472. // we probably want to get rid of one of the blanks...
  1473. int ichSkip = 2;
  1474. LPTSTR pszT = CharPrev(pszFileSpec, psz);
  1475. if (*pszT == L' ')
  1476. {
  1477. ichSkip = 3;
  1478. psz = pszT;
  1479. }
  1480. pszNew = szTemp;
  1481. lstrcpy(pszNew, pszFileSpec);
  1482. pszNew += (psz - pszFileSpec);
  1483. lstrcpy(pszNew, psz + ichSkip);
  1484. }
  1485. else
  1486. {
  1487. // 1taro registers its document with '/'.
  1488. if (psz=StrChr(pszFileSpec, '/'))
  1489. {
  1490. LPTSTR pszT = CharNext(psz);
  1491. pszNew = szTemp;
  1492. lstrcpy(pszNew, pszFileSpec);
  1493. pszNew += (psz - pszFileSpec);
  1494. lstrcpy(pszNew, pszT);
  1495. }
  1496. else
  1497. {
  1498. lstrcpy(szTemp, pszFileSpec);
  1499. }
  1500. }
  1501. hr = CreateStreamOrStorage(pStorageParent, szTemp, riid, ppv);
  1502. if (FAILED(hr))
  1503. {
  1504. hr = StgMakeUniqueNameWithCount(pStorageParent, pszFileSpec, 2, riid, ppv);
  1505. }
  1506. return hr;
  1507. }
  1508. STDAPI SHInvokeCommandOnPidl(HWND hwnd, IUnknown* punk, LPCITEMIDLIST pidl, UINT uFlags, LPCSTR lpVerb)
  1509. {
  1510. IShellFolder* psf;
  1511. LPCITEMIDLIST pidlChild;
  1512. HRESULT hr = SHBindToFolderIDListParent(NULL, pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
  1513. if (SUCCEEDED(hr))
  1514. {
  1515. hr = SHInvokeCommandOnPidlArray(hwnd, punk, psf, &pidlChild, 1, uFlags, lpVerb);
  1516. psf->Release();
  1517. }
  1518. return hr;
  1519. }
  1520. STDAPI SHInvokeCommandOnPidlArray(HWND hwnd, IUnknown* punk, IShellFolder* psf, LPCITEMIDLIST *ppidlItem, UINT cItems, UINT uFlags, LPCSTR lpVerb)
  1521. {
  1522. IContextMenu *pcm;
  1523. HRESULT hr = psf->GetUIObjectOf(hwnd, cItems, ppidlItem, IID_X_PPV_ARG(IContextMenu, 0, &pcm));
  1524. if (SUCCEEDED(hr) && pcm)
  1525. {
  1526. hr = SHInvokeCommandOnContextMenu(hwnd, punk, pcm, uFlags, lpVerb);
  1527. pcm->Release();
  1528. }
  1529. return hr;
  1530. }
  1531. STDAPI SHInvokeCommandOnDataObject(HWND hwnd, IUnknown* punk, IDataObject* pdtobj, UINT uFlags, LPCSTR pszVerb)
  1532. {
  1533. HRESULT hr = E_FAIL;
  1534. STGMEDIUM medium;
  1535. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1536. if (pida)
  1537. {
  1538. IShellFolder *psf;
  1539. LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1);
  1540. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlParent, &psf))))
  1541. {
  1542. LPCITEMIDLIST *ppidl = (LPCITEMIDLIST *)LocalAlloc(LPTR, pida->cidl * sizeof(LPCITEMIDLIST));
  1543. if (ppidl)
  1544. {
  1545. for (UINT i = 0; i < pida->cidl; i++)
  1546. {
  1547. ppidl[i] = IDA_GetIDListPtr(pida, i);
  1548. }
  1549. hr = SHInvokeCommandOnPidlArray(hwnd, punk, psf, ppidl, pida->cidl, uFlags, pszVerb);
  1550. LocalFree(ppidl);
  1551. }
  1552. psf->Release();
  1553. }
  1554. HIDA_ReleaseStgMedium(pida, &medium);
  1555. }
  1556. return hr;
  1557. }
  1558. STDAPI_(LPCITEMIDLIST) IDA_GetIDListPtr(LPIDA pida, UINT i)
  1559. {
  1560. LPCITEMIDLIST pidl = NULL;
  1561. if (pida && ((i == (UINT)-1) || i < pida->cidl))
  1562. {
  1563. pidl = HIDA_GetPIDLItem(pida, i);
  1564. }
  1565. return pidl;
  1566. }
  1567. STDAPI_(void) IEPlaySound(LPCTSTR pszSound, BOOL fSysSound)
  1568. {
  1569. TCHAR szKey[256];
  1570. // check the registry first
  1571. // if there's nothing registered, we blow off the play,
  1572. // but we don't set the MM_DONTLOAD flag so that if they register
  1573. // something we will play it
  1574. wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("AppEvents\\Schemes\\Apps\\%s\\%s\\.current"),
  1575. (fSysSound ? TEXT(".Default") : TEXT("Explorer")), pszSound);
  1576. TCHAR szFileName[MAX_PATH];
  1577. szFileName[0] = 0;
  1578. DWORD cbSize = sizeof(szFileName);
  1579. // note the test for an empty string, PlaySound will play the Default Sound if we
  1580. // give it a sound it cannot find...
  1581. if ((SHGetValue(HKEY_CURRENT_USER, szKey, NULL, NULL, szFileName, &cbSize) == ERROR_SUCCESS)
  1582. && cbSize && szFileName[0] != 0)
  1583. {
  1584. DWORD dwFlags = SND_FILENAME | SND_NODEFAULT | SND_ASYNC | SND_NOSTOP | SND_ALIAS;
  1585. // This flag only works on Win95
  1586. if (IsOS(OS_WIN95GOLD))
  1587. {
  1588. #define SND_LOPRIORITY 0x10000000l
  1589. dwFlags |= SND_LOPRIORITY;
  1590. }
  1591. // Unlike SHPlaySound in shell32.dll, we get the registry value
  1592. // above and pass it to PlaySound with SND_FILENAME instead of
  1593. // SDN_APPLICATION, so that we play sound even if the application
  1594. // is not Explroer.exe (such as IExplore.exe or WebBrowserOC).
  1595. PlaySoundWrapW(szFileName, NULL, dwFlags);
  1596. }
  1597. }
  1598. STDAPI IUnknown_DragEnter(IUnknown* punk, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1599. {
  1600. HRESULT hr = E_FAIL;
  1601. if (punk)
  1602. {
  1603. IDropTarget* pdt;
  1604. hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt));
  1605. if (SUCCEEDED(hr))
  1606. {
  1607. hr = pdt->DragEnter(pdtobj, grfKeyState, pt, pdwEffect);
  1608. pdt->Release();
  1609. }
  1610. }
  1611. if (FAILED(hr))
  1612. *pdwEffect = DROPEFFECT_NONE;
  1613. return hr;
  1614. }
  1615. STDAPI IUnknown_DragOver(IUnknown* punk, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1616. {
  1617. HRESULT hr = E_FAIL;
  1618. if (punk)
  1619. {
  1620. IDropTarget* pdt;
  1621. hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt));
  1622. if (SUCCEEDED(hr))
  1623. {
  1624. hr = pdt->DragOver(grfKeyState, pt, pdwEffect);
  1625. pdt->Release();
  1626. }
  1627. }
  1628. if (FAILED(hr))
  1629. *pdwEffect = DROPEFFECT_NONE;
  1630. return hr;
  1631. }
  1632. STDAPI IUnknown_DragLeave(IUnknown* punk)
  1633. {
  1634. HRESULT hr = E_FAIL;
  1635. if (punk)
  1636. {
  1637. IDropTarget* pdt;
  1638. hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt));
  1639. if (SUCCEEDED(hr))
  1640. {
  1641. hr = pdt->DragLeave();
  1642. pdt->Release();
  1643. }
  1644. }
  1645. return hr;
  1646. }
  1647. STDAPI IUnknown_Drop(IUnknown* punk, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1648. {
  1649. HRESULT hr = E_FAIL;
  1650. if (punk)
  1651. {
  1652. IDropTarget* pdt;
  1653. hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt));
  1654. if (SUCCEEDED(hr))
  1655. {
  1656. hr = pdt->Drop(pdtobj, grfKeyState, pt, pdwEffect);
  1657. pdt->Release();
  1658. }
  1659. }
  1660. if (FAILED(hr))
  1661. *pdwEffect = DROPEFFECT_NONE;
  1662. return hr;
  1663. }
  1664. __int64 MakeFileVersion(WORD wMajor, WORD wMinor, WORD wBuild, WORD wQfe)
  1665. {
  1666. return ((__int64)(wMajor) << 48) | ((__int64)(wMinor) << 32) |
  1667. ((__int64)(wBuild) << 16) | ((__int64)(wQfe));
  1668. }
  1669. HRESULT GetVersionFromString64(LPCWSTR psz, __int64 *pVer)
  1670. {
  1671. HRESULT hr = S_OK;
  1672. int idx = 0;
  1673. WORD nums[4] = { 0, 0, 0, 0 };
  1674. while (*psz && SUCCEEDED(hr))
  1675. {
  1676. if (L',' == *psz)
  1677. {
  1678. idx++;
  1679. if (idx >= ARRAYSIZE(nums))
  1680. {
  1681. hr = E_INVALIDARG;
  1682. break;
  1683. }
  1684. }
  1685. else if ((*psz >= L'0') && (*psz <= L'9'))
  1686. {
  1687. nums[idx] = (nums[idx] * 10) + (*psz - L'0');
  1688. }
  1689. else
  1690. {
  1691. hr = E_INVALIDARG;
  1692. break;
  1693. }
  1694. psz++;
  1695. }
  1696. if (SUCCEEDED(hr))
  1697. {
  1698. *pVer = MakeFileVersion(nums[0], nums[1], nums[2], nums[3]);
  1699. }
  1700. else
  1701. {
  1702. *pVer = 0i64;
  1703. }
  1704. return hr;
  1705. }