Leaked source code of windows server 2003
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.

2465 lines
78 KiB

  1. #include "shellprv.h"
  2. #include "util.h"
  3. #include "ids.h"
  4. #include "ole2dup.h"
  5. #include "datautil.h"
  6. #include "filetbl.h"
  7. #include "copy.h"
  8. #include "prop.h"
  9. #include <pif.h>
  10. #include "fstreex.h" // GetIconOverlayManager()
  11. #include <runtask.h>
  12. extern void PathStripTrailingDots(LPTSTR szPath);
  13. HRESULT IExtractIcon_Extract(IExtractIcon *pei, LPCTSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize)
  14. {
  15. // wrapper to let us ask an IExtractIcon for only one icon (the large one)
  16. // since many implementations will fault if you pass NULL phiconSmall
  17. HICON hiconDummy;
  18. if (phiconSmall == NULL)
  19. {
  20. phiconSmall = &hiconDummy;
  21. nIconSize = MAKELONG(nIconSize, nIconSize);
  22. }
  23. HRESULT hr = pei->Extract(pszFile, nIconIndex, phiconLarge, phiconSmall, nIconSize);
  24. if (hr == S_OK && phiconSmall == &hiconDummy)
  25. {
  26. DestroyIcon(hiconDummy);
  27. }
  28. return hr;
  29. }
  30. HRESULT IExtractIconA_Extract(IExtractIconA *peia, LPCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize)
  31. {
  32. // wrapper to let us ask an IExtractIcon for only one icon (the large one)
  33. // since many dudes don't check for NULL phiconSmall
  34. HICON hiconDummy;
  35. if (phiconSmall == NULL)
  36. {
  37. phiconSmall = &hiconDummy;
  38. nIconSize = MAKELONG(nIconSize, nIconSize);
  39. }
  40. HRESULT hr = peia->Extract(pszFile, nIconIndex, phiconLarge, phiconSmall, nIconSize);
  41. if (hr == S_OK && phiconSmall == &hiconDummy)
  42. {
  43. DestroyIcon(hiconDummy);
  44. }
  45. return hr;
  46. }
  47. // try to figure out if this is an icon already
  48. // in our system image list, so that we dont re-add
  49. BOOL _HijackOfficeIcons(HICON hLarge, int iIndex)
  50. {
  51. BOOL fRet = FALSE;
  52. HIMAGELIST himl;
  53. ASSERT(hLarge);
  54. if (Shell_GetImageLists(NULL, &himl))
  55. {
  56. HICON hMaybe = ImageList_GetIcon(himl, iIndex, 0);
  57. if (hMaybe)
  58. {
  59. fRet = SHAreIconsEqual(hLarge, hMaybe);
  60. DestroyIcon(hMaybe);
  61. }
  62. }
  63. #ifdef DEBUG
  64. if (!fRet)
  65. TraceMsg(TF_WARNING, "_HijackOfficeIcons() called in suspicious circumstance");
  66. #endif
  67. return fRet;
  68. }
  69. HRESULT _GetILIndexGivenPXIcon(IExtractIcon *pxicon, UINT uFlags, LPCITEMIDLIST pidl, int *piImage, BOOL fAnsiCrossOver)
  70. {
  71. TCHAR szIconFile[MAX_PATH];
  72. CHAR szIconFileA[MAX_PATH];
  73. IExtractIconA *pxiconA = (IExtractIconA *)pxicon;
  74. int iIndex;
  75. int iImage = -1;
  76. UINT wFlags=0;
  77. HRESULT hr;
  78. if (fAnsiCrossOver)
  79. {
  80. szIconFileA[0] = 0;
  81. hr = pxiconA->GetIconLocation(uFlags | GIL_FORSHELL,
  82. szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags);
  83. SHAnsiToUnicode(szIconFileA, szIconFile, ARRAYSIZE(szIconFile));
  84. }
  85. else
  86. {
  87. szIconFile[0] = '\0';
  88. hr = pxicon->GetIconLocation(uFlags | GIL_FORSHELL,
  89. szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags);
  90. }
  91. //
  92. // "*" as the file name means iIndex is already a system
  93. // icon index, we are done.
  94. //
  95. // this is a hack for our own internal icon handler
  96. //
  97. if (SUCCEEDED(hr) && (wFlags & GIL_NOTFILENAME) &&
  98. szIconFile[0] == TEXT('*') && szIconFile[1] == 0)
  99. {
  100. *piImage = iIndex;
  101. return hr;
  102. }
  103. // Do not replace this with SUCCEEDED(hr). hr = S_FALSE means we need to use a default icon.
  104. if (hr == S_OK)
  105. {
  106. // If we have it in shell32, don't delay the extraction
  107. if (!(wFlags & GIL_NOTFILENAME) && lstrcmpi(PathFindFileName(szIconFile), c_szShell32Dll) == 0)
  108. {
  109. iImage = Shell_GetCachedImageIndex(szIconFile, iIndex, wFlags);
  110. }
  111. else
  112. {
  113. //
  114. // if GIL_DONTCACHE was returned by the icon handler, dont
  115. // lookup the previous icon, assume a cache miss.
  116. //
  117. if (!(wFlags & GIL_DONTCACHE) && *szIconFile)
  118. {
  119. iImage = LookupIconIndex(szIconFile, iIndex, wFlags);
  120. }
  121. }
  122. }
  123. // if we miss our cache...
  124. if (iImage == -1 && hr != S_FALSE)
  125. {
  126. if (uFlags & GIL_ASYNC)
  127. {
  128. // If we couldn't get the final icon, try to get a good temporary one
  129. if (fAnsiCrossOver)
  130. {
  131. szIconFileA[0] = 0;
  132. hr = pxiconA->GetIconLocation(uFlags | GIL_FORSHELL | GIL_DEFAULTICON,
  133. szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags);
  134. SHAnsiToUnicode(szIconFileA, szIconFile, ARRAYSIZE(szIconFile));
  135. }
  136. else
  137. {
  138. hr = pxicon->GetIconLocation(uFlags | GIL_FORSHELL | GIL_DEFAULTICON,
  139. szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags);
  140. }
  141. if (hr == S_OK)
  142. {
  143. iImage = LookupIconIndex(szIconFile, iIndex, wFlags);
  144. }
  145. // When all else fails...
  146. if (iImage == -1)
  147. {
  148. iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
  149. }
  150. // force a lookup incase we are not in explorer.exe
  151. *piImage = iImage;
  152. return E_PENDING;
  153. }
  154. // try getting it from the ExtractIcon member fuction
  155. HICON rghicon[ARRAYSIZE(g_rgshil)] = {0};
  156. BOOL fHandlerOk = FALSE;
  157. for (int i = 0; i < ARRAYSIZE(g_rgshil); i += 2)
  158. {
  159. // Ask for two at a time because
  160. //
  161. // (a) it's slightly more efficient, and
  162. //
  163. // (b) otherwise we break compatibility with IExtractIcon::Extract
  164. // implementions which ignore the size parameter (the Network
  165. // Connections folder is one). The SHIL_'s are conveniently
  166. // arranged in large/small alternating order for this purpose.
  167. //
  168. HICON *phiconSmall = NULL;
  169. HICON *phiconLarge = &rghicon[i];
  170. UINT nIconSize = g_rgshil[i].size.cx;
  171. if (i + 1 < ARRAYSIZE(g_rgshil))
  172. {
  173. phiconSmall = &rghicon[i+1];
  174. nIconSize = MAKELONG(nIconSize, g_rgshil[i+1].size.cx);
  175. }
  176. if (fAnsiCrossOver)
  177. {
  178. hr = IExtractIconA_Extract(pxiconA, szIconFileA, iIndex,
  179. phiconLarge, phiconSmall, nIconSize);
  180. }
  181. else
  182. {
  183. hr = IExtractIcon_Extract(pxicon, szIconFile, iIndex,
  184. phiconLarge, phiconSmall, nIconSize);
  185. }
  186. // S_FALSE means, can you please do it...Thanks
  187. if (hr == S_FALSE && !(wFlags & GIL_NOTFILENAME))
  188. {
  189. hr = SHDefExtractIcon(szIconFile, iIndex, wFlags,
  190. phiconLarge, phiconSmall, nIconSize);
  191. }
  192. if (SUCCEEDED(hr))
  193. {
  194. fHandlerOk = TRUE;
  195. }
  196. }
  197. // our belief knows no bounds
  198. if (!*szIconFile && rghicon[1] && iIndex > 0 && _HijackOfficeIcons(rghicon[1], iIndex))
  199. {
  200. // it lives!
  201. iImage = iIndex;
  202. }
  203. else
  204. {
  205. // if we extracted a icon add it to the cache.
  206. iImage = SHAddIconsToCache(rghicon, szIconFile, iIndex, wFlags);
  207. }
  208. _DestroyIcons(rghicon, ARRAYSIZE(rghicon));
  209. // if we failed in any way pick a default icon
  210. if (iImage == -1)
  211. {
  212. if (wFlags & GIL_SIMULATEDOC)
  213. {
  214. iImage = II_DOCUMENT;
  215. }
  216. else if ((wFlags & GIL_PERINSTANCE) && PathIsExe(szIconFile))
  217. {
  218. iImage = II_APPLICATION;
  219. }
  220. else
  221. {
  222. iImage = II_DOCNOASSOC;
  223. }
  224. // force a lookup incase we are not in explorer.exe
  225. iImage = Shell_GetCachedImageIndex(c_szShell32Dll, iImage, 0);
  226. // if the handler failed dont cache this default icon.
  227. // so we will try again later and maybe get the right icon.
  228. // handlers should only fail if they cant access the file
  229. // or something equally bad.
  230. //
  231. // if the handler succeeded then go ahead and assume this is
  232. // a usable icon, we must be in some low memory situation, or
  233. // something. So keep mapping to the same shell icon.
  234. //
  235. if (fHandlerOk)
  236. {
  237. if (iImage != -1 && *szIconFile && !(wFlags & (GIL_DONTCACHE | GIL_NOTFILENAME)))
  238. {
  239. AddToIconTable(szIconFile, iIndex, wFlags, iImage);
  240. }
  241. }
  242. else
  243. {
  244. TraceMsg(TF_DEFVIEW, "not caching icon for '%s' because cant access file", szIconFile);
  245. }
  246. }
  247. }
  248. if (iImage < 0)
  249. {
  250. iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
  251. }
  252. *piImage = iImage;
  253. return hr;
  254. }
  255. // given an IShellFolder and and an Idlist that is
  256. // contained in it, get back the index into the system image list.
  257. STDAPI SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage)
  258. {
  259. HRESULT hr;
  260. if (psi)
  261. {
  262. #ifdef DEBUG
  263. *piImage = -1;
  264. #endif
  265. hr = psi->GetIconOf(pidl, flags, piImage);
  266. if (hr == S_OK)
  267. {
  268. ASSERT(*piImage != -1);
  269. return hr;
  270. }
  271. if (hr == E_PENDING)
  272. {
  273. ASSERT(flags & GIL_ASYNC);
  274. ASSERT(*piImage != -1);
  275. return hr;
  276. }
  277. }
  278. *piImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
  279. // Be careful. Some shellfolders erroneously return S_OK when they fail
  280. IExtractIcon *pxi = NULL;
  281. hr = psf->GetUIObjectOf(NULL, pidl ? 1 : 0, pidl ? &pidl : NULL, IID_PPV_ARG_NULL(IExtractIcon, &pxi));
  282. if (SUCCEEDED(hr) && pxi)
  283. {
  284. hr = _GetILIndexGivenPXIcon(pxi, flags, pidl, piImage, FALSE);
  285. pxi->Release();
  286. }
  287. else
  288. {
  289. // Try the ANSI interface, see if we are dealing with an old set of code
  290. IExtractIconA *pxiA = NULL;
  291. hr = psf->GetUIObjectOf(NULL, pidl ? 1 : 0, pidl ? &pidl : NULL, IID_PPV_ARG_NULL(IExtractIconA, &pxiA));
  292. if (SUCCEEDED(hr))
  293. {
  294. if (pxiA)
  295. {
  296. hr = _GetILIndexGivenPXIcon((IExtractIcon *)pxiA, flags, pidl, piImage, TRUE);
  297. pxiA->Release();
  298. }
  299. else
  300. {
  301. // IShellFolder lied to us - returned S_OK even though it failed
  302. hr = E_FAIL;
  303. }
  304. }
  305. }
  306. return hr;
  307. }
  308. // given an IShellFolder and and an Idlist that is
  309. // contained in it, get back the index into the system image list.
  310. STDAPI_(int) SHMapPIDLToSystemImageListIndex(IShellFolder *psf, LPCITEMIDLIST pidl, int *piIndexSel)
  311. {
  312. int iIndex;
  313. if (piIndexSel)
  314. {
  315. SHGetIconFromPIDL(psf, NULL, pidl, GIL_OPENICON, piIndexSel);
  316. }
  317. SHGetIconFromPIDL(psf, NULL, pidl, 0, &iIndex);
  318. return iIndex;
  319. }
  320. class CGetIconTask : public CRunnableTask
  321. {
  322. public:
  323. STDMETHODIMP RunInitRT(void);
  324. CGetIconTask(HRESULT *phr, IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon,
  325. PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint);
  326. protected:
  327. ~CGetIconTask();
  328. IShellFolder *_psf;
  329. IShellIcon *_psi;
  330. LPITEMIDLIST _pidl;
  331. UINT _flags;
  332. BOOL _fGetOpenIcon;
  333. PFNASYNCICONTASKBALLBACK _pfn;
  334. void *_pvData;
  335. void *_pvHint;
  336. };
  337. CGetIconTask::CGetIconTask(HRESULT *phr, IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon,
  338. PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint) :
  339. CRunnableTask(RTF_DEFAULT), _psf(psf), _psi(psi), _flags(flags), _fGetOpenIcon(fGetOpenIcon), _pfn(pfn), _pvData(pvData), _pvHint(pvHint)
  340. {
  341. *phr = SHILClone(pidl, &_pidl);
  342. _psf->AddRef();
  343. if (_psi)
  344. _psi->AddRef();
  345. }
  346. CGetIconTask::~CGetIconTask()
  347. {
  348. ILFree(_pidl);
  349. _psf->Release();
  350. if (_psi)
  351. _psi->Release();
  352. }
  353. STDMETHODIMP CGetIconTask::RunInitRT()
  354. {
  355. int iIcon = -1;
  356. int iOpenIcon = -1;
  357. ASSERT(_pidl);
  358. if (_fGetOpenIcon)
  359. {
  360. SHGetIconFromPIDL(_psf, _psi, _pidl, _flags | GIL_OPENICON, &iOpenIcon);
  361. }
  362. // get the icon for this item.
  363. SHGetIconFromPIDL(_psf, _psi, _pidl, _flags, &iIcon);
  364. _pfn(_pidl, _pvData, _pvHint, iIcon, iOpenIcon);
  365. return S_OK;
  366. }
  367. HRESULT CGetIconTask_CreateInstance(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon,
  368. PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint, IRunnableTask **ppTask)
  369. {
  370. *ppTask = NULL;
  371. HRESULT hr;
  372. CGetIconTask * pNewTask = new CGetIconTask(&hr, psf, psi, pidl, flags, fGetOpenIcon, pfn, pvData, pvHint);
  373. if (pNewTask)
  374. {
  375. if (SUCCEEDED(hr))
  376. *ppTask = SAFECAST(pNewTask, IRunnableTask *);
  377. else
  378. pNewTask->Release();
  379. }
  380. else
  381. hr = E_OUTOFMEMORY;
  382. return hr;
  383. }
  384. // given an IShellFolder and and an Idlist that is
  385. // contained in it, get back a -possibly temporary - index into the system image list,
  386. // and get the final icon from the callback if necessary
  387. STDAPI SHMapIDListToImageListIndexAsync(IShellTaskScheduler* pts, IShellFolder *psf, LPCITEMIDLIST pidl, UINT flags,
  388. PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint, int *piIndex, int *piIndexSel)
  389. {
  390. HRESULT hr = S_OK;
  391. IShellIcon *psi = NULL;
  392. psf->QueryInterface(IID_PPV_ARG(IShellIcon, &psi));
  393. // We are doing all the ASYNC handling, not the caller.
  394. flags &= ~GIL_ASYNC;
  395. // Try asynchronous first
  396. if (pfn)
  397. {
  398. hr = SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_ASYNC, piIndex);
  399. if (piIndexSel)
  400. {
  401. HRESULT hr2 = SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_OPENICON | GIL_ASYNC, piIndexSel);
  402. if (SUCCEEDED(hr))
  403. {
  404. // Don't lose the result if the first GetIcon succeeds, but the second one is E_PENDING
  405. hr = hr2;
  406. }
  407. }
  408. if (hr == E_PENDING)
  409. {
  410. if (pts)
  411. {
  412. IRunnableTask *pTask;
  413. hr = CGetIconTask_CreateInstance(psf, psi, pidl, flags, (piIndexSel != NULL), pfn, pvData, pvHint, &pTask);
  414. if (SUCCEEDED(hr))
  415. {
  416. hr = pts->AddTask(pTask, TOID_DVIconExtract, 0, ITSAT_DEFAULT_PRIORITY);
  417. if (SUCCEEDED(hr))
  418. {
  419. hr = E_PENDING;
  420. }
  421. pTask->Release();
  422. }
  423. }
  424. else
  425. {
  426. hr = E_POINTER;
  427. }
  428. }
  429. else if (hr == S_OK)
  430. {
  431. goto cleanup;
  432. }
  433. }
  434. // If asynchronous get failed, try synchronous
  435. if (hr != E_PENDING)
  436. {
  437. if (piIndexSel)
  438. {
  439. SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_OPENICON, piIndexSel);
  440. }
  441. hr = SHGetIconFromPIDL(psf, psi, pidl, flags, piIndex);
  442. }
  443. cleanup:
  444. if (psi)
  445. {
  446. psi->Release();
  447. }
  448. return hr;
  449. }
  450. // returns the icon handle to be used to represent the specified
  451. // file. The caller should destroy the icon eventually.
  452. STDAPI_(HICON) SHGetFileIcon(HINSTANCE hinst, LPCTSTR pszPath, DWORD dwFileAttributes, UINT uFlags)
  453. {
  454. SHFILEINFO sfi;
  455. SHGetFileInfo(pszPath, dwFileAttributes, &sfi, sizeof(sfi), uFlags | SHGFI_ICON);
  456. return sfi.hIcon;
  457. }
  458. // Return 1 on success and 0 on failure.
  459. DWORD_PTR _GetFileInfoSections(LPITEMIDLIST pidl, SHFILEINFO *psfi, UINT uFlags)
  460. {
  461. DWORD_PTR dwResult = 1;
  462. IShellFolder *psf;
  463. LPCITEMIDLIST pidlLast;
  464. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  465. if (SUCCEEDED(hr))
  466. {
  467. // get attributes for file
  468. if (uFlags & SHGFI_ATTRIBUTES)
  469. {
  470. // [New in IE 4.0] If SHGFI_ATTR_SPECIFIED is set, we use psfi->dwAttributes as is
  471. if (!(uFlags & SHGFI_ATTR_SPECIFIED))
  472. psfi->dwAttributes = 0xFFFFFFFF; // get all of them
  473. if (FAILED(psf->GetAttributesOf(1, &pidlLast, &psfi->dwAttributes)))
  474. psfi->dwAttributes = 0;
  475. }
  476. //
  477. // get icon location, place the icon path into szDisplayName
  478. //
  479. if (uFlags & SHGFI_ICONLOCATION)
  480. {
  481. IExtractIcon *pxi;
  482. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractIcon, &pxi))))
  483. {
  484. UINT wFlags;
  485. pxi->GetIconLocation(0, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName),
  486. &psfi->iIcon, &wFlags);
  487. pxi->Release();
  488. // the returned location is not a filename we cant return it.
  489. // just give then nothing.
  490. if (wFlags & GIL_NOTFILENAME)
  491. {
  492. // special case one of our shell32.dll icons......
  493. if (psfi->szDisplayName[0] != TEXT('*'))
  494. psfi->iIcon = 0;
  495. psfi->szDisplayName[0] = 0;
  496. }
  497. }
  498. }
  499. HIMAGELIST himlLarge, himlSmall;
  500. // get the icon for the file.
  501. if ((uFlags & SHGFI_SYSICONINDEX) || (uFlags & SHGFI_ICON))
  502. {
  503. Shell_GetImageLists(&himlLarge, &himlSmall);
  504. if (uFlags & SHGFI_SYSICONINDEX)
  505. dwResult = (DWORD_PTR)((uFlags & SHGFI_SMALLICON) ? himlSmall : himlLarge);
  506. if (uFlags & SHGFI_OPENICON)
  507. SHMapPIDLToSystemImageListIndex(psf, pidlLast, &psfi->iIcon);
  508. else
  509. psfi->iIcon = SHMapPIDLToSystemImageListIndex(psf, pidlLast, NULL);
  510. }
  511. if (uFlags & SHGFI_ICON)
  512. {
  513. HIMAGELIST himl;
  514. UINT flags = 0;
  515. int cx, cy;
  516. if (uFlags & SHGFI_SMALLICON)
  517. {
  518. himl = himlSmall;
  519. cx = GetSystemMetrics(SM_CXSMICON);
  520. cy = GetSystemMetrics(SM_CYSMICON);
  521. }
  522. else
  523. {
  524. himl = himlLarge;
  525. cx = GetSystemMetrics(SM_CXICON);
  526. cy = GetSystemMetrics(SM_CYICON);
  527. }
  528. if (!(uFlags & SHGFI_ATTRIBUTES))
  529. {
  530. psfi->dwAttributes = SFGAO_LINK; // get link only
  531. psf->GetAttributesOf(1, &pidlLast, &psfi->dwAttributes);
  532. }
  533. //
  534. // check for a overlay image thing (link overlay)
  535. //
  536. if ((psfi->dwAttributes & SFGAO_LINK) || (uFlags & SHGFI_LINKOVERLAY))
  537. {
  538. IShellIconOverlayManager *psiom;
  539. HRESULT hrT = GetIconOverlayManager(&psiom);
  540. if (SUCCEEDED(hrT))
  541. {
  542. int iOverlayIndex = 0;
  543. hrT = psiom->GetReservedOverlayInfo(NULL, -1, &iOverlayIndex, SIOM_OVERLAYINDEX, SIOM_RESERVED_LINK);
  544. if (SUCCEEDED(hrT))
  545. flags |= INDEXTOOVERLAYMASK(iOverlayIndex);
  546. }
  547. }
  548. if ((uFlags & SHGFI_ADDOVERLAYS) || (uFlags & SHGFI_OVERLAYINDEX))
  549. {
  550. IShellIconOverlay * pio;
  551. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &pio))))
  552. {
  553. int iOverlayIndex = 0;
  554. if (SUCCEEDED(pio->GetOverlayIndex(pidlLast, &iOverlayIndex)))
  555. {
  556. if (uFlags & SHGFI_ADDOVERLAYS)
  557. {
  558. flags |= INDEXTOOVERLAYMASK(iOverlayIndex);
  559. }
  560. if (uFlags & SHGFI_OVERLAYINDEX)
  561. {
  562. // use the upper 16 bits for the overlayindex
  563. psfi->iIcon |= iOverlayIndex << 24;
  564. }
  565. }
  566. pio->Release();
  567. }
  568. }
  569. // check for selected state
  570. if (uFlags & SHGFI_SELECTED)
  571. flags |= ILD_BLEND50;
  572. psfi->hIcon = ImageList_GetIcon(himl, psfi->iIcon, flags);
  573. // if the caller does not want a "shell size" icon
  574. // convert the icon to the "system" icon size.
  575. if (psfi->hIcon && !(uFlags & SHGFI_SHELLICONSIZE))
  576. psfi->hIcon = (HICON)CopyImage(psfi->hIcon, IMAGE_ICON, cx, cy, LR_COPYRETURNORG | LR_COPYDELETEORG);
  577. }
  578. // get display name for the path
  579. if (uFlags & SHGFI_DISPLAYNAME)
  580. {
  581. DisplayNameOf(psf, pidlLast, SHGDN_NORMAL, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName));
  582. }
  583. if (uFlags & SHGFI_TYPENAME)
  584. {
  585. IShellFolder2 *psf2;
  586. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  587. {
  588. VARIANT var;
  589. VariantInit(&var);
  590. if (SUCCEEDED(psf2->GetDetailsEx(pidlLast, &SCID_TYPE, &var)))
  591. {
  592. VariantToStr(&var, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName));
  593. VariantClear(&var);
  594. }
  595. psf2->Release();
  596. }
  597. }
  598. psf->Release();
  599. }
  600. else
  601. dwResult = 0;
  602. return dwResult;
  603. }
  604. //
  605. // This function returns shell info about a given pathname.
  606. // a app can get the following:
  607. //
  608. // Icon (large or small)
  609. // Display Name
  610. // Name of File Type
  611. //
  612. // this function replaces SHGetFileIcon
  613. #define BUGGY_SHELL16_CBFILEINFO (sizeof(SHFILEINFO) - 4)
  614. STDAPI_(DWORD_PTR) SHGetFileInfo(LPCTSTR pszPath, DWORD dwFileAttributes, SHFILEINFO *psfi, UINT cbFileInfo, UINT uFlags)
  615. {
  616. LPITEMIDLIST pidlFull;
  617. DWORD_PTR res = 1;
  618. TCHAR szPath[MAX_PATH];
  619. // this was never enforced in the past
  620. // TODDB: The 16 to 32 bit thunking layer passes in the wrong value for cbFileInfo.
  621. // The size passed in looks to be the size of the 16 bit version of the structure
  622. // rather than the size of the 32 bit version, as such it is 4 bytes shorter.
  623. // TJGREEN: Special-case that size to keep the assertion from firing and party on.
  624. //
  625. ASSERT(!psfi || cbFileInfo == sizeof(*psfi) || cbFileInfo == BUGGY_SHELL16_CBFILEINFO);
  626. // You can't use both SHGFI_ATTR_SPECIFIED and SHGFI_ICON.
  627. ASSERT(uFlags & SHGFI_ATTR_SPECIFIED ? !(uFlags & SHGFI_ICON) : TRUE);
  628. if (pszPath == NULL)
  629. return 0;
  630. if (uFlags == SHGFI_EXETYPE)
  631. return GetExeType(pszPath); // funky way to get EXE type
  632. if (psfi == NULL)
  633. return 0;
  634. psfi->hIcon = 0;
  635. // Zip Pro 6.0 relies on the fact that if you don't ask for the icon,
  636. // the iIcon field doesn't change.
  637. //
  638. // psfi->iIcon = 0;
  639. psfi->szDisplayName[0] = 0;
  640. psfi->szTypeName[0] = 0;
  641. // do some simmple check on the input path.
  642. if (!(uFlags & SHGFI_PIDL))
  643. {
  644. // If the caller wants us to give them the file attributes, we can't trust
  645. // the attributes they gave us in the following two situations.
  646. if (uFlags & SHGFI_ATTRIBUTES)
  647. {
  648. if ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  649. (dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
  650. {
  651. DebugMsg(TF_FSTREE, TEXT("SHGetFileInfo cant use caller supplied file attribs for a sys/ro directory (possible junction)"));
  652. uFlags &= ~SHGFI_USEFILEATTRIBUTES;
  653. }
  654. else if (PathIsRoot(pszPath))
  655. {
  656. DebugMsg(TF_FSTREE, TEXT("SHGetFileInfo cant use caller supplied file attribs for a roots"));
  657. uFlags &= ~SHGFI_USEFILEATTRIBUTES;
  658. }
  659. }
  660. if (PathIsRelative(pszPath))
  661. {
  662. if (uFlags & SHGFI_USEFILEATTRIBUTES)
  663. {
  664. // get a shorter path than the current directory to support
  665. // long pszPath names (that might get truncated in the
  666. // long current dir case)
  667. GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
  668. }
  669. else
  670. {
  671. GetCurrentDirectory(ARRAYSIZE(szPath), szPath);
  672. }
  673. PathCombine(szPath, szPath, pszPath);
  674. pszPath = szPath;
  675. }
  676. }
  677. if (uFlags & SHGFI_PIDL)
  678. pidlFull = (LPITEMIDLIST)pszPath;
  679. else if (uFlags & SHGFI_USEFILEATTRIBUTES)
  680. {
  681. WIN32_FIND_DATA fd = {0};
  682. fd.dwFileAttributes = dwFileAttributes;
  683. SHSimpleIDListFromFindData(pszPath, &fd, &pidlFull);
  684. }
  685. else
  686. pidlFull = ILCreateFromPath(pszPath);
  687. if (pidlFull)
  688. {
  689. if (uFlags & (
  690. SHGFI_DISPLAYNAME |
  691. SHGFI_ATTRIBUTES |
  692. SHGFI_SYSICONINDEX |
  693. SHGFI_ICONLOCATION |
  694. SHGFI_ICON |
  695. SHGFI_TYPENAME))
  696. {
  697. res = _GetFileInfoSections(pidlFull, psfi, uFlags);
  698. }
  699. if (!(uFlags & SHGFI_PIDL))
  700. ILFree(pidlFull);
  701. }
  702. else
  703. res = 0;
  704. return res;
  705. }
  706. //===========================================================================
  707. //
  708. // SHGetFileInfoA Stub
  709. //
  710. // This function calls SHGetFileInfoW and then converts the returned
  711. // information back to ANSI.
  712. //
  713. //===========================================================================
  714. STDAPI_(DWORD_PTR) SHGetFileInfoA(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA *psfi, UINT cbFileInfo, UINT uFlags)
  715. {
  716. WCHAR szPathW[MAX_PATH];
  717. LPWSTR pszPathW;
  718. DWORD_PTR dwRet;
  719. if (uFlags & SHGFI_PIDL)
  720. {
  721. pszPathW = (LPWSTR)pszPath; // Its a pidl, fake it as a WSTR
  722. }
  723. else
  724. {
  725. SHAnsiToUnicode(pszPath, szPathW, ARRAYSIZE(szPathW));
  726. pszPathW = szPathW;
  727. }
  728. if (psfi)
  729. {
  730. SHFILEINFOW sfiw;
  731. ASSERT(cbFileInfo == sizeof(*psfi));
  732. // Zip Pro 6.0 sets SHGFI_SMALLICON | SHGFI_OPENICON but forgets to
  733. // pass SHGFI_ICON or SHGFI_SYSICONINDEX, even though they really
  734. // wanted the sys icon index.
  735. //
  736. // In Windows 95, fields of the SHFILEINFOA structure that you didn't
  737. // query for were left unchanged. They happened to have the icon for
  738. // a closed folder lying around there from a previous query, so they
  739. // got away with it by mistake. They got the wrong icon, but it was
  740. // close enough that nobody really complained.
  741. //
  742. // So pre-initialize the sfiw's iIcon with the app's iIcon. That
  743. // way, if it turns out the app didn't ask for the icon, he just
  744. // gets his old value back.
  745. //
  746. sfiw.iIcon = psfi->iIcon;
  747. sfiw.dwAttributes = psfi->dwAttributes;
  748. dwRet = SHGetFileInfoW(pszPathW, dwFileAttributes, &sfiw, sizeof(sfiw), uFlags);
  749. psfi->hIcon = sfiw.hIcon;
  750. psfi->iIcon = sfiw.iIcon;
  751. psfi->dwAttributes = sfiw.dwAttributes;
  752. SHUnicodeToAnsi(sfiw.szDisplayName, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName));
  753. SHUnicodeToAnsi(sfiw.szTypeName, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName));
  754. }
  755. else
  756. {
  757. dwRet = SHGetFileInfoW(pszPathW, dwFileAttributes, NULL, 0, uFlags);
  758. }
  759. return dwRet;
  760. }
  761. STDAPI ThunkFindDataWToA(WIN32_FIND_DATAW *pfd, WIN32_FIND_DATAA *pfda, int cb)
  762. {
  763. if (cb < sizeof(WIN32_FIND_DATAA))
  764. return DISP_E_BUFFERTOOSMALL;
  765. memcpy(pfda, pfd, FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
  766. SHUnicodeToAnsi(pfd->cFileName, pfda->cFileName, ARRAYSIZE(pfda->cFileName));
  767. SHUnicodeToAnsi(pfd->cAlternateFileName, pfda->cAlternateFileName, ARRAYSIZE(pfda->cAlternateFileName));
  768. return S_OK;
  769. }
  770. STDAPI ThunkNetResourceWToA(LPNETRESOURCEW pnrw, LPNETRESOURCEA pnra, UINT cb)
  771. {
  772. HRESULT hr;
  773. if (cb >= sizeof(NETRESOURCEA))
  774. {
  775. LPSTR psza, pszDest[4] = {NULL, NULL, NULL, NULL};
  776. CopyMemory(pnra, pnrw, FIELD_OFFSET(NETRESOURCE, lpLocalName));
  777. psza = (LPSTR)(pnra + 1); // Point just past the structure
  778. if (cb > sizeof(NETRESOURCE))
  779. {
  780. LPWSTR pszSource[4];
  781. UINT i, cchRemaining = cb - sizeof(NETRESOURCE);
  782. pszSource[0] = pnrw->lpLocalName;
  783. pszSource[1] = pnrw->lpRemoteName;
  784. pszSource[2] = pnrw->lpComment;
  785. pszSource[3] = pnrw->lpProvider;
  786. for (i = 0; i < 4; i++)
  787. {
  788. if (pszSource[i])
  789. {
  790. UINT cchItem;
  791. pszDest[i] = psza;
  792. cchItem = SHUnicodeToAnsi(pszSource[i], pszDest[i], cchRemaining);
  793. cchRemaining -= cchItem;
  794. psza += cchItem;
  795. }
  796. }
  797. }
  798. pnra->lpLocalName = pszDest[0];
  799. pnra->lpRemoteName = pszDest[1];
  800. pnra->lpComment = pszDest[2];
  801. pnra->lpProvider = pszDest[3];
  802. hr = S_OK;
  803. }
  804. else
  805. hr = DISP_E_BUFFERTOOSMALL;
  806. return hr;
  807. }
  808. STDAPI NetResourceWVariantToBuffer(const VARIANT* pvar, void* pv, UINT cb)
  809. {
  810. HRESULT hr;
  811. if (cb >= sizeof(NETRESOURCEW))
  812. {
  813. if (pvar && pvar->vt == (VT_ARRAY | VT_UI1))
  814. {
  815. int i;
  816. NETRESOURCEW* pnrw = (NETRESOURCEW*) pvar->parray->pvData;
  817. UINT cbOffsets[4] = { 0, 0, 0, 0 };
  818. UINT cbEnds[4] = { 0, 0, 0, 0 };
  819. LPWSTR pszPtrs[4] = { pnrw->lpLocalName, pnrw->lpRemoteName,
  820. pnrw->lpComment, pnrw->lpProvider };
  821. hr = S_OK;
  822. for (i = 0; i < ARRAYSIZE(pszPtrs); i++)
  823. {
  824. if (pszPtrs[i])
  825. {
  826. cbOffsets[i] = (UINT) ((BYTE*) pszPtrs[i] - (BYTE*) pnrw);
  827. cbEnds[i] = cbOffsets[i] + (sizeof(WCHAR) * (lstrlenW(pszPtrs[i]) + 1));
  828. // If any of the strings start or end too far into the buffer, then fail:
  829. if ((cbOffsets[i] >= cb) || (cbEnds[i] > cb))
  830. {
  831. hr = DISP_E_BUFFERTOOSMALL;
  832. break;
  833. }
  834. }
  835. }
  836. if (SUCCEEDED(hr))
  837. {
  838. hr = VariantToBuffer(pvar, pv, cb) ? S_OK : E_FAIL;
  839. pnrw = (NETRESOURCEW*) pv;
  840. if (SUCCEEDED(hr))
  841. {
  842. // Fixup pointers in structure to point into the output buffer,
  843. // instead of the variant buffer:
  844. LPWSTR* ppszPtrs[4] = { &(pnrw->lpLocalName), &(pnrw->lpRemoteName),
  845. &(pnrw->lpComment), &(pnrw->lpProvider) };
  846. for (i = 0; i < ARRAYSIZE(ppszPtrs); i++)
  847. {
  848. if (*ppszPtrs[i])
  849. {
  850. *ppszPtrs[i] = (LPWSTR) ((BYTE*) pnrw + cbOffsets[i]);
  851. }
  852. }
  853. }
  854. }
  855. }
  856. else
  857. {
  858. hr = E_FAIL;
  859. }
  860. }
  861. else
  862. {
  863. hr = DISP_E_BUFFERTOOSMALL;
  864. }
  865. return hr;
  866. }
  867. // This function will extract information that is cached in the pidl such
  868. // in the information that was returned from a FindFirst file. This function
  869. // is sortof a hack as t allow outside callers to be able to get at the infomation
  870. // without knowing how we store it in the pidl.
  871. // a app can get the following:
  872. STDAPI SHGetDataFromIDListW(IShellFolder *psf, LPCITEMIDLIST pidl, int nFormat, void *pv, int cb)
  873. {
  874. HRESULT hr = E_NOTIMPL;
  875. SHCOLUMNID* pscid;
  876. if (!pv || !psf || !pidl)
  877. return E_INVALIDARG;
  878. switch (nFormat)
  879. {
  880. case SHGDFIL_FINDDATA:
  881. if (cb < sizeof(WIN32_FIND_DATAW))
  882. return DISP_E_BUFFERTOOSMALL;
  883. else
  884. pscid = (SHCOLUMNID*)&SCID_FINDDATA;
  885. break;
  886. case SHGDFIL_NETRESOURCE:
  887. if (cb < sizeof(NETRESOURCEW))
  888. return DISP_E_BUFFERTOOSMALL;
  889. else
  890. pscid = (SHCOLUMNID*)&SCID_NETRESOURCE;
  891. break;
  892. case SHGDFIL_DESCRIPTIONID:
  893. pscid = (SHCOLUMNID*)&SCID_DESCRIPTIONID;
  894. break;
  895. default:
  896. return E_INVALIDARG;
  897. }
  898. IShellFolder2 *psf2;
  899. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  900. {
  901. VARIANT var;
  902. VariantInit(&var);
  903. hr = psf2->GetDetailsEx(pidl, pscid, &var);
  904. if (SUCCEEDED(hr))
  905. {
  906. if (SHGDFIL_NETRESOURCE == nFormat)
  907. {
  908. hr = NetResourceWVariantToBuffer(&var, pv, cb);
  909. }
  910. else
  911. {
  912. if (!VariantToBuffer(&var, pv, cb))
  913. hr = E_FAIL;
  914. }
  915. VariantClear(&var);
  916. }
  917. else
  918. {
  919. TraceMsg(TF_WARNING, "Trying to retrieve find data from unknown PIDL %s", DumpPidl(pidl));
  920. }
  921. psf2->Release();
  922. }
  923. return hr;
  924. }
  925. STDAPI SHGetDataFromIDListA(IShellFolder *psf, LPCITEMIDLIST pidl, int nFormat, void *pv, int cb)
  926. {
  927. HRESULT hr;
  928. WIN32_FIND_DATAW fdw;
  929. NETRESOURCEW *pnrw = NULL;
  930. void *pvData = pv;
  931. int cbData = cb;
  932. if (nFormat == SHGDFIL_FINDDATA)
  933. {
  934. cbData = sizeof(fdw);
  935. pvData = &fdw;
  936. }
  937. else if (nFormat == SHGDFIL_NETRESOURCE)
  938. {
  939. cbData = cb;
  940. pvData = pnrw = (NETRESOURCEW *)LocalAlloc(LPTR, cbData);
  941. if (pnrw == NULL)
  942. return E_OUTOFMEMORY;
  943. }
  944. hr = SHGetDataFromIDListW(psf, pidl, nFormat, pvData, cbData);
  945. if (SUCCEEDED(hr))
  946. {
  947. if (nFormat == SHGDFIL_FINDDATA)
  948. {
  949. hr = ThunkFindDataWToA(&fdw, (WIN32_FIND_DATAA *)pv, cb);
  950. }
  951. else if (nFormat == SHGDFIL_NETRESOURCE)
  952. {
  953. hr = ThunkNetResourceWToA(pnrw, (NETRESOURCEA *)pv, cb);
  954. }
  955. }
  956. if (pnrw)
  957. LocalFree(pnrw);
  958. return hr;
  959. }
  960. int g_iUseLinkPrefix = -1;
  961. #define INITIALLINKPREFIXCOUNT 20
  962. #define MAXLINKPREFIXCOUNT 30
  963. void LoadUseLinkPrefixCount()
  964. {
  965. TraceMsg(TF_FSTREE, "LoadUseLinkPrefixCount %d", g_iUseLinkPrefix);
  966. if (g_iUseLinkPrefix < 0)
  967. {
  968. DWORD cb = sizeof(g_iUseLinkPrefix);
  969. if (FAILED(SKGetValue(SHELLKEY_HKCU_EXPLORER, NULL, c_szLink, NULL, &g_iUseLinkPrefix, &cb))
  970. || g_iUseLinkPrefix < 0)
  971. {
  972. g_iUseLinkPrefix = INITIALLINKPREFIXCOUNT;
  973. }
  974. }
  975. }
  976. void SaveUseLinkPrefixCount()
  977. {
  978. if (g_iUseLinkPrefix >= 0)
  979. {
  980. SKSetValue(SHELLKEY_HKCU_EXPLORER, NULL, c_szLink, REG_BINARY, &g_iUseLinkPrefix, sizeof(g_iUseLinkPrefix));
  981. }
  982. }
  983. #define ISDIGIT(c) ((c) >= TEXT('0') && (c) <= TEXT('9'))
  984. // psz2 = destination
  985. // psz1 = source
  986. void _StripNumber(LPWSTR psz)
  987. {
  988. // strip out the '(' and the numbers after it
  989. // We need to verify that it is either simply () or (999) but not (A)
  990. for (; *psz; psz++)
  991. {
  992. if (*psz == TEXT('('))
  993. {
  994. LPCWSTR pszT = psz + 1;
  995. while (*pszT && ISDIGIT(*pszT))
  996. {
  997. pszT++;
  998. }
  999. if (*pszT == TEXT(')'))
  1000. {
  1001. // we got a match
  1002. if (*++pszT == TEXT(' '))
  1003. {
  1004. pszT++; // skip the extra space
  1005. }
  1006. int cch = lstrlen(pszT);
  1007. MoveMemory(psz, pszT, CbFromCchW(cch + 1));
  1008. return;
  1009. }
  1010. // else Continue to scan for the pattern
  1011. }
  1012. }
  1013. }
  1014. #define SHORTCUT_PREFIX_DECR 5
  1015. #define SHORTCUT_PREFIX_INCR 1
  1016. // this checks to see if you've renamed 'Shortcut #x To Foo' to 'Foo'
  1017. void CheckShortcutRename(LPCTSTR pszOldPath, LPCTSTR pszNewPath)
  1018. {
  1019. ASSERT(pszOldPath);
  1020. ASSERT(pszNewPath);
  1021. // already at 0.
  1022. if (g_iUseLinkPrefix)
  1023. {
  1024. LPCTSTR pszOldName = PathFindFileName(pszOldPath);
  1025. if (PathIsLnk(pszOldName))
  1026. {
  1027. TCHAR szBaseName[MAX_PATH];
  1028. TCHAR szLinkTo[80];
  1029. TCHAR szMockName[MAX_PATH];
  1030. StringCchCopy(szBaseName, ARRAYSIZE(szBaseName), PathFindFileName(pszNewPath));
  1031. PathRemoveExtension(szBaseName);
  1032. // mock up a name using the basename and the linkto template
  1033. LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo));
  1034. wnsprintf(szMockName, ARRAYSIZE(szMockName), szLinkTo, szBaseName);
  1035. StringCchCopy(szBaseName, ARRAYSIZE(szBaseName), pszOldName);
  1036. _StripNumber(szMockName);
  1037. _StripNumber(szBaseName);
  1038. // are the remaining gunk the same?
  1039. if (!lstrcmp(szMockName, szBaseName))
  1040. {
  1041. // yes! do the link count magic
  1042. LoadUseLinkPrefixCount();
  1043. ASSERT(g_iUseLinkPrefix >= 0);
  1044. g_iUseLinkPrefix -= SHORTCUT_PREFIX_DECR;
  1045. if (g_iUseLinkPrefix < 0)
  1046. g_iUseLinkPrefix = 0;
  1047. SaveUseLinkPrefixCount();
  1048. }
  1049. }
  1050. }
  1051. }
  1052. STDAPI_(int) SHRenameFileEx(HWND hwnd, IUnknown *punkEnableModless, LPCTSTR pszDir,
  1053. LPCTSTR pszOldName, LPCTSTR pszNewName)
  1054. {
  1055. int iRet = ERROR_CANCELLED; // user saw the error, don't report again
  1056. TCHAR szOldPathName[MAX_PATH + 1]; // +1 for double nul terminating on SHFileOperation
  1057. TCHAR szTempNewPath[MAX_PATH];
  1058. BOOL bEnableUI = hwnd || punkEnableModless;
  1059. IUnknown_EnableModless(punkEnableModless, FALSE);
  1060. PathCombine(szOldPathName, pszDir, pszOldName);
  1061. szOldPathName[lstrlen(szOldPathName) + 1] = 0;
  1062. StrCpyN(szTempNewPath, pszNewName, ARRAYSIZE(szTempNewPath));
  1063. int err = PathCleanupSpec(pszDir, szTempNewPath);
  1064. if (err)
  1065. {
  1066. if (bEnableUI)
  1067. {
  1068. ShellMessageBox(HINST_THISDLL, hwnd,
  1069. err & PCS_PATHTOOLONG ?
  1070. MAKEINTRESOURCE(IDS_REASONS_INVFILES) :
  1071. IsLFNDrive(pszDir) ?
  1072. MAKEINTRESOURCE(IDS_INVALIDFN) :
  1073. MAKEINTRESOURCE(IDS_INVALIDFNFAT),
  1074. MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND);
  1075. }
  1076. }
  1077. else
  1078. {
  1079. // strip off leading and trailing blanks off of the new file name.
  1080. StrCpyN(szTempNewPath, pszNewName, ARRAYSIZE(szTempNewPath));
  1081. PathRemoveBlanks(szTempNewPath);
  1082. if (!szTempNewPath[0] || (szTempNewPath[0] == TEXT('.')))
  1083. {
  1084. if (bEnableUI)
  1085. {
  1086. ShellMessageBox(HINST_THISDLL, hwnd,
  1087. MAKEINTRESOURCE(IDS_NONULLNAME),
  1088. MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND);
  1089. }
  1090. }
  1091. else
  1092. {
  1093. int idPrompt = IDYES;
  1094. TCHAR szNewPathName[MAX_PATH + 1]; // +1 for double nul terminating on SHFileOperation
  1095. PathCombine(szNewPathName, pszDir, szTempNewPath);
  1096. // if there was an old extension and the new and old don't match complain
  1097. LPTSTR pszExt = PathFindExtension(pszOldName);
  1098. if (*pszExt && lstrcmpi(pszExt, PathFindExtension(szTempNewPath)))
  1099. {
  1100. HKEY hk;
  1101. if (!PathIsDirectory(szOldPathName) &&
  1102. SUCCEEDED(AssocQueryKey(0, ASSOCKEY_SHELLEXECCLASS, pszExt, NULL, &hk)))
  1103. {
  1104. RegCloseKey(hk);
  1105. if (bEnableUI)
  1106. {
  1107. idPrompt = ShellMessageBox(HINST_THISDLL, hwnd,
  1108. MAKEINTRESOURCE(IDS_WARNCHANGEEXT),
  1109. MAKEINTRESOURCE(IDS_RENAME), MB_YESNO | MB_ICONEXCLAMATION);
  1110. }
  1111. }
  1112. }
  1113. if (IDYES == idPrompt)
  1114. {
  1115. szNewPathName[lstrlen(szNewPathName) + 1] = 0; // double NULL terminate
  1116. SHFILEOPSTRUCT fo = { hwnd, FO_RENAME, szOldPathName, szNewPathName, FOF_SILENT | FOF_ALLOWUNDO, };
  1117. iRet = SHFileOperation(&fo);
  1118. if (ERROR_SUCCESS == iRet)
  1119. CheckShortcutRename(szOldPathName, szNewPathName);
  1120. }
  1121. }
  1122. }
  1123. IUnknown_EnableModless(punkEnableModless, TRUE);
  1124. return iRet;
  1125. }
  1126. HKEY SHOpenShellFolderKey(const CLSID *pclsid)
  1127. {
  1128. HKEY hkey;
  1129. return SUCCEEDED(SHRegGetCLSIDKey(*pclsid, TEXT("ShellFolder"), FALSE, FALSE, &hkey)) ? hkey : NULL;
  1130. }
  1131. BOOL SHQueryShellFolderValue(const CLSID *pclsid, LPCTSTR pszValueName)
  1132. {
  1133. BOOL bRet = FALSE; // assume no
  1134. HKEY hkey = SHOpenShellFolderKey(pclsid);
  1135. if (hkey)
  1136. {
  1137. bRet = SHQueryValueEx(hkey, pszValueName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
  1138. RegCloseKey(hkey);
  1139. }
  1140. return bRet;
  1141. }
  1142. //
  1143. // The SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY key contains a bunch of values,
  1144. // each named after a GUID. The data associated with each value is a
  1145. // DWORD, which is either...
  1146. //
  1147. // 0 = no restriction on this CLSID
  1148. // 1 = unconditional restriction on this CLSID
  1149. // 0xFFFFFFFF = same as 1 (in case somebody got "creative")
  1150. // any other value = pass to SHRestricted() to see what the restriction is
  1151. //
  1152. // We support 0xFFFFFFFF only out of paranoia. This flag was only 0 or 1
  1153. // in Windows 2000, and somebody might've decided that "all bits set"
  1154. // is better than "just one bit set".
  1155. //
  1156. #define SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\NonEnum")
  1157. BOOL _IsNonEnumPolicySet(const CLSID *pclsid)
  1158. {
  1159. BOOL fPolicySet = FALSE;
  1160. TCHAR szCLSID[GUIDSTR_MAX];
  1161. DWORD dwDefault = 0;
  1162. RESTRICTIONS rest = REST_NONE;
  1163. DWORD cbSize = sizeof(rest);
  1164. if (EVAL(SHStringFromGUID(*pclsid, szCLSID, ARRAYSIZE(szCLSID))) &&
  1165. (ERROR_SUCCESS == SHRegGetUSValue(SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY, szCLSID, NULL, &rest, &cbSize, FALSE, &dwDefault, sizeof(dwDefault))) &&
  1166. rest)
  1167. {
  1168. fPolicySet = rest == 1 || rest == 0xFFFFFFFF || SHRestricted(rest);
  1169. }
  1170. return fPolicySet;
  1171. }
  1172. DWORD QueryCallForAttributes(HKEY hkey, const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested)
  1173. {
  1174. DWORD dwAttr = dwDefAttrs;
  1175. DWORD dwData, cbSize = sizeof(dwAttr);
  1176. // consider caching this folder to avoid creating over and over
  1177. // mydocs.dll uses this for compat with old apps
  1178. // See if this folder has asked us specifically to call and get
  1179. // the attributes...
  1180. //
  1181. if (SHQueryValueEx(hkey, TEXT("CallForAttributes"), NULL, NULL, &dwData, &cbSize) == ERROR_SUCCESS)
  1182. {
  1183. // CallForAttributes can be a masked value. See if it's being supplied in the value.
  1184. // NOTE: MyDocs.dll registers with a NULL String, so this check works.
  1185. DWORD dwMask = (DWORD)-1;
  1186. if (sizeof(dwData) == cbSize)
  1187. {
  1188. // There is a mask, Use this.
  1189. dwMask = dwData;
  1190. }
  1191. // Is the requested bit contained in the specified mask?
  1192. if (dwMask & dwRequested)
  1193. {
  1194. // Yes. Then CoCreate and Query.
  1195. IShellFolder *psf;
  1196. if (SUCCEEDED(SHExtCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IShellFolder, &psf))))
  1197. {
  1198. dwAttr = dwRequested;
  1199. psf->GetAttributesOf(0, NULL, &dwAttr);
  1200. psf->Release();
  1201. }
  1202. else
  1203. {
  1204. dwAttr |= SFGAO_FILESYSTEM;
  1205. }
  1206. }
  1207. }
  1208. return dwAttr;
  1209. }
  1210. // dwRequested is the bits you are explicitly looking for. This is an optimization that prevents reg hits.
  1211. STDAPI_(DWORD) SHGetAttributesFromCLSID2(const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested)
  1212. {
  1213. DWORD dwAttr = dwDefAttrs;
  1214. HKEY hkey = SHOpenShellFolderKey(pclsid);
  1215. if (hkey)
  1216. {
  1217. DWORD dwData, cbSize = sizeof(dwAttr);
  1218. // We are looking for some attributes on a shell folder. These attributes can be in two locations:
  1219. // 1) In the "Attributes" value in the registry.
  1220. // 2) Stored in a the shell folder's GetAttributesOf.
  1221. // First, Check to see if the reqested value is contained in the registry.
  1222. if (SHQueryValueEx(hkey, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwData, &cbSize) == ERROR_SUCCESS &&
  1223. cbSize == sizeof(dwData))
  1224. {
  1225. // We have data there, but it may not contain the data we are looking for
  1226. dwAttr = dwData & dwRequested;
  1227. // Does it contain the bit we are looking for?
  1228. if (((dwAttr & dwRequested) != dwRequested) && dwRequested != 0)
  1229. {
  1230. // No. Check to see if it is in the shell folder implementation
  1231. goto CallForAttributes;
  1232. }
  1233. }
  1234. else
  1235. {
  1236. CallForAttributes:
  1237. // See if we have to talk to the shell folder.
  1238. // I'm passing dwAttr, because if the previous case did not generate any attributes, then it's
  1239. // equal to dwDefAttrs. If the call to CallForAttributes fails, then it will contain the value of
  1240. // dwDefAttrs or whatever was in the shell folder's Attributes key
  1241. dwAttr = QueryCallForAttributes(hkey, pclsid, dwAttr, dwRequested);
  1242. }
  1243. RegCloseKey(hkey);
  1244. }
  1245. if (_IsNonEnumPolicySet(pclsid))
  1246. dwAttr |= SFGAO_NONENUMERATED;
  1247. if (SHGetObjectCompatFlags(NULL, pclsid) & OBJCOMPATF_NOTAFILESYSTEM)
  1248. dwAttr &= ~SFGAO_FILESYSTEM;
  1249. return dwAttr;
  1250. }
  1251. // _BuildLinkName
  1252. //
  1253. // Used during the creation of a shortcut, this function determines an appropriate name for the shortcut.
  1254. // This is not the exact name that will be used becuase it will usually contain "() " which will either
  1255. // get removed or replaced with "(x) " where x is a number that makes the name unique. This removal is done
  1256. // elsewhere (currently in PathYetAnotherMakeUniqueName).
  1257. //
  1258. // in:
  1259. // pszName file spec part
  1260. // pszDir path part of name to know how to limit the long name...
  1261. //
  1262. // out:
  1263. // pszLinkName - Full path to link name (May fit in 8.3...). Can be the same buffer as pszName.
  1264. //
  1265. // NOTES: If pszDir + pszLinkName is greater than MAX_PATH we will fail to create the shortcut.
  1266. // In an effort to prevent
  1267. void _BuildLinkName(LPTSTR pszLinkName, LPCTSTR pszName, LPCTSTR pszDir, BOOL fLinkTo)
  1268. {
  1269. TCHAR szLinkTo[40]; // "Shortcut to %s.lnk"
  1270. TCHAR szTemp[MAX_PATH + 40];
  1271. if (fLinkTo)
  1272. {
  1273. // check to see if we're in the "don't ever say 'shortcut to' mode"
  1274. LoadUseLinkPrefixCount();
  1275. if (!g_iUseLinkPrefix)
  1276. {
  1277. fLinkTo = FALSE;
  1278. }
  1279. else if (g_iUseLinkPrefix > 0)
  1280. {
  1281. if (g_iUseLinkPrefix < MAXLINKPREFIXCOUNT)
  1282. {
  1283. g_iUseLinkPrefix += SHORTCUT_PREFIX_INCR;
  1284. SaveUseLinkPrefixCount();
  1285. }
  1286. }
  1287. }
  1288. if (!fLinkTo)
  1289. {
  1290. // Generate the title of this link ("XX.lnk")
  1291. LoadString(HINST_THISDLL, IDS_LINKEXTENSION, szLinkTo, ARRAYSIZE(szLinkTo));
  1292. }
  1293. else
  1294. {
  1295. // Generate the title of this link ("Shortcut to XX.lnk")
  1296. LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo));
  1297. }
  1298. wnsprintf(szTemp, ARRAYSIZE(szTemp), szLinkTo, pszName);
  1299. PathCleanupSpecEx(pszDir, szTemp); // get rid of illegal chars AND ensure correct filename length
  1300. StrCpyN(pszLinkName, szTemp, MAX_PATH);
  1301. ASSERT(PathIsLnk(pszLinkName));
  1302. }
  1303. // return a new destination path for a link
  1304. //
  1305. // in:
  1306. // fErrorSoTryDesktop we are called because there was an error saving
  1307. // the shortcut and we want to prompt to see if the
  1308. // desktop should be used.
  1309. //
  1310. // in/out:
  1311. // pszPath on input the place being tried, on output the desktop folder
  1312. //
  1313. // returns:
  1314. //
  1315. // IDYES user said yes to creating a link at new place
  1316. // IDNO user said no to creating a link at new place
  1317. // -1 error
  1318. //
  1319. int _PromptTryDesktopLinks(HWND hwnd, LPTSTR pszPath, BOOL fErrorSoTryDesktop)
  1320. {
  1321. TCHAR szPath[MAX_PATH];
  1322. if (!SHGetSpecialFolderPath(hwnd, szPath, CSIDL_DESKTOPDIRECTORY, FALSE))
  1323. return -1; // fail no desktop dir
  1324. int idOk;
  1325. if (fErrorSoTryDesktop)
  1326. {
  1327. // Fail, if pszPath already points to the desktop directory.
  1328. if (lstrcmpi(szPath, pszPath) == 0)
  1329. return -1;
  1330. idOk = ShellMessageBox(HINST_THISDLL, hwnd,
  1331. MAKEINTRESOURCE(IDS_TRYDESKTOPLINK),
  1332. MAKEINTRESOURCE(IDS_LINKTITLE),
  1333. MB_YESNO | MB_ICONQUESTION);
  1334. }
  1335. else
  1336. {
  1337. ShellMessageBox(HINST_THISDLL, hwnd,
  1338. MAKEINTRESOURCE(IDS_MAKINGDESKTOPLINK),
  1339. MAKEINTRESOURCE(IDS_LINKTITLE),
  1340. MB_OK | MB_ICONASTERISK);
  1341. idOk = IDYES;
  1342. }
  1343. if (idOk == IDYES)
  1344. StrCpyN(pszPath, szPath, MAX_PATH); // output
  1345. return idOk; // return yes or no
  1346. }
  1347. // in:
  1348. // pszpdlLinkTo LPCITEMIDLIST or LPCTSTR, target of link to create
  1349. // pszDir where we will put the link
  1350. // uFlags SHGNLI_ flags
  1351. //
  1352. // out:
  1353. // pszName file name to create "c:\Shortcut to Foo.lnk"
  1354. // pfMustCopy pszpdlLinkTo was a link itself, make a copy of this
  1355. STDAPI_(BOOL) SHGetNewLinkInfo(LPCTSTR pszpdlLinkTo, LPCTSTR pszDir, LPTSTR pszName,
  1356. BOOL *pfMustCopy, UINT uFlags)
  1357. {
  1358. BOOL fDosApp = FALSE;
  1359. BOOL fLongFileNames = IsLFNDrive(pszDir);
  1360. SHFILEINFO sfi;
  1361. *pfMustCopy = FALSE;
  1362. sfi.dwAttributes = SFGAO_FILESYSTEM | SFGAO_LINK | SFGAO_FOLDER;
  1363. if (uFlags & SHGNLI_PIDL)
  1364. {
  1365. if (FAILED(SHGetNameAndFlags((LPCITEMIDLIST)pszpdlLinkTo, SHGDN_NORMAL,
  1366. pszName, MAX_PATH, &sfi.dwAttributes)))
  1367. return FALSE;
  1368. }
  1369. else
  1370. {
  1371. if (SHGetFileInfo(pszpdlLinkTo, 0, &sfi, sizeof(sfi),
  1372. SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED |
  1373. ((uFlags & SHGNLI_PIDL) ? SHGFI_PIDL : 0)))
  1374. StrCpyN(pszName, sfi.szDisplayName, MAX_PATH);
  1375. else
  1376. return FALSE;
  1377. }
  1378. if (PathCleanupSpecEx(pszDir, pszName) & PCS_FATAL)
  1379. return FALSE;
  1380. //
  1381. // WARNING: From this point on, sfi.szDisplayName may be re-used to
  1382. // contain the file path of the PIDL we are linking to. Don't rely on
  1383. // it containing the display name.
  1384. //
  1385. if (sfi.dwAttributes & SFGAO_FILESYSTEM)
  1386. {
  1387. LPTSTR pszPathSrc;
  1388. if (uFlags & SHGNLI_PIDL)
  1389. {
  1390. pszPathSrc = sfi.szDisplayName;
  1391. SHGetPathFromIDList((LPCITEMIDLIST)pszpdlLinkTo, pszPathSrc);
  1392. }
  1393. else
  1394. {
  1395. pszPathSrc = (LPTSTR)pszpdlLinkTo;
  1396. }
  1397. fDosApp = (lstrcmpi(PathFindExtension(pszPathSrc), TEXT(".pif")) == 0) ||
  1398. (LOWORD(GetExeType(pszPathSrc)) == 0x5A4D); // 'MZ'
  1399. if (sfi.dwAttributes & SFGAO_LINK)
  1400. {
  1401. *pfMustCopy = TRUE;
  1402. if (!(sfi.dwAttributes & SFGAO_FOLDER))
  1403. {
  1404. uFlags &= ~SHGNLI_NOLNK; // if copying the file then don't trim the extension
  1405. }
  1406. StrCpyN(pszName, PathFindFileName(pszPathSrc), MAX_PATH);
  1407. }
  1408. else
  1409. {
  1410. //
  1411. // when making a link to a drive root. special case a few things
  1412. //
  1413. // if we are not on a LFN drive, dont use the full name, just
  1414. // use the drive letter. "C.LNK" not "Label (C).LNK"
  1415. //
  1416. // if we are making a link to removable media, we dont want the
  1417. // label as part of the name, we want the media type.
  1418. //
  1419. // CD-ROM drives are currently the only removable media we
  1420. // show the volume label for, so we only need to special case
  1421. // cdrom drives here.
  1422. //
  1423. if (PathIsRoot(pszPathSrc) && !PathIsUNC(pszPathSrc))
  1424. {
  1425. if (!fLongFileNames)
  1426. StrCpyN(pszName, pszPathSrc, MAX_PATH);
  1427. else if (IsCDRomDrive(DRIVEID(pszPathSrc)))
  1428. LoadString(HINST_THISDLL, IDS_DRIVES_CDROM, pszName, MAX_PATH);
  1429. }
  1430. }
  1431. if (fLongFileNames && fDosApp)
  1432. {
  1433. HANDLE hPif = PifMgr_OpenProperties(pszPathSrc, NULL, 0, OPENPROPS_INHIBITPIF);
  1434. if (hPif)
  1435. {
  1436. PROPPRG PP = {0};
  1437. if (PifMgr_GetProperties(hPif, (LPCSTR)MAKELP(0, GROUP_PRG), &PP, sizeof(PP), 0) &&
  1438. ((PP.flPrgInit & PRGINIT_INFSETTINGS) ||
  1439. ((PP.flPrgInit & (PRGINIT_NOPIF | PRGINIT_DEFAULTPIF)) == 0)))
  1440. {
  1441. SHAnsiToTChar(PP.achTitle, pszName, MAX_PATH);
  1442. }
  1443. PifMgr_CloseProperties(hPif, 0);
  1444. }
  1445. }
  1446. }
  1447. if (!*pfMustCopy)
  1448. {
  1449. // create full dest path name. only use template iff long file names
  1450. // can be created and the caller requested it. _BuildLinkName will
  1451. // truncate files on non-lfn drives and clean up any invalid chars.
  1452. _BuildLinkName(pszName, pszName, pszDir,
  1453. (!(*pfMustCopy) && fLongFileNames && (uFlags & SHGNLI_PREFIXNAME)));
  1454. }
  1455. if (fDosApp)
  1456. PathRenameExtension(pszName, TEXT(".pif"));
  1457. if (uFlags & SHGNLI_NOLNK)
  1458. {
  1459. // Don't do PathRemoveExtension because pszName might contain
  1460. // internal dots ("Windows 3.1") and passing that to
  1461. // PathYetAnotherMakeUniqueName will result in
  1462. // "Windows 3 (2).1" which is wrong. We leave the dot at the
  1463. // end so we get "Windows 3.1 (2)." back. We will strip off the
  1464. // final dot later.
  1465. PathRenameExtension(pszName, TEXT("."));
  1466. }
  1467. // make sure the name is unique
  1468. // NOTE: PathYetAnotherMakeUniqueName will return the directory+filename in the pszName buffer.
  1469. // It returns FALSE if the name is not unique or the dir+filename is too long. If it returns
  1470. // false then this function should return false because creation will fail.
  1471. BOOL fSuccess;
  1472. if (!(uFlags & SHGNLI_NOUNIQUE))
  1473. fSuccess = PathYetAnotherMakeUniqueName(pszName, pszDir, pszName, pszName);
  1474. else
  1475. fSuccess = TRUE;
  1476. // Strip off any trailing dots that may have been generated by SHGNI_NOLNK
  1477. PathStripTrailingDots(pszName);
  1478. return fSuccess;
  1479. }
  1480. STDAPI_(BOOL) SHGetNewLinkInfoA(LPCSTR pszpdlLinkTo, LPCSTR pszDir, LPSTR pszName,
  1481. BOOL *pfMustCopy, UINT uFlags)
  1482. {
  1483. ThunkText * pThunkText;
  1484. BOOL bResult = FALSE;
  1485. if (uFlags & SHGNLI_PIDL)
  1486. {
  1487. // 1 string (pszpdlLinkTo is a pidl)
  1488. pThunkText = ConvertStrings(2, NULL, pszDir);
  1489. if (pThunkText)
  1490. pThunkText->m_pStr[0] = (LPWSTR)pszpdlLinkTo;
  1491. }
  1492. else
  1493. {
  1494. // 2 strings
  1495. pThunkText = ConvertStrings(2, pszpdlLinkTo, pszDir);
  1496. }
  1497. if (pThunkText)
  1498. {
  1499. WCHAR wszName[MAX_PATH];
  1500. bResult = SHGetNewLinkInfoW(pThunkText->m_pStr[0], pThunkText->m_pStr[1],
  1501. wszName, pfMustCopy, uFlags);
  1502. LocalFree(pThunkText);
  1503. if (bResult)
  1504. {
  1505. if (0 == WideCharToMultiByte(CP_ACP, 0, wszName, -1,
  1506. pszName, MAX_PATH, NULL, NULL))
  1507. {
  1508. SetLastError((DWORD)E_FAIL);
  1509. bResult = FALSE;
  1510. }
  1511. }
  1512. }
  1513. return bResult;
  1514. }
  1515. //
  1516. // in:
  1517. // pidlTo
  1518. STDAPI CreateLinkToPidl(LPCITEMIDLIST pidlTo, LPCTSTR pszDir, LPITEMIDLIST *ppidl, UINT uFlags)
  1519. {
  1520. HRESULT hr = E_FAIL;
  1521. TCHAR szPathDest[MAX_PATH];
  1522. BOOL fCopyLnk;
  1523. BOOL fUseLinkTemplate = (SHCL_USETEMPLATE & uFlags);
  1524. UINT uSHGNLI = fUseLinkTemplate ? SHGNLI_PIDL | SHGNLI_PREFIXNAME : SHGNLI_PIDL;
  1525. if (uFlags & SHCL_MAKEFOLDERSHORTCUT)
  1526. {
  1527. // Don't add ".lnk" to the folder shortcut name; that's just stupid
  1528. uSHGNLI |= SHGNLI_NOLNK;
  1529. }
  1530. if (uFlags & SHCL_NOUNIQUE)
  1531. {
  1532. uSHGNLI |= SHGNLI_NOUNIQUE;
  1533. }
  1534. if (SHGetNewLinkInfo((LPTSTR)pidlTo, pszDir, szPathDest, &fCopyLnk, uSHGNLI))
  1535. {
  1536. TCHAR szPathSrc[MAX_PATH];
  1537. IShellLink *psl = NULL;
  1538. // If we passed SHGNLI_NOUNIQUE then we need to do the PathCombine ourselves
  1539. // because SHGetNewLinkInfo won't
  1540. if (uFlags & SHCL_NOUNIQUE)
  1541. {
  1542. PathCombine(szPathDest, pszDir, szPathDest);
  1543. }
  1544. DWORD dwAttributes = SFGAO_FILESYSTEM | SFGAO_FOLDER;
  1545. SHGetNameAndFlags(pidlTo, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szPathSrc, ARRAYSIZE(szPathSrc), &dwAttributes);
  1546. if (fCopyLnk)
  1547. {
  1548. // if it is file system and not a folder (CopyFile does not work on folders)
  1549. // just copy it.
  1550. if (((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM) &&
  1551. CopyFile(szPathSrc, szPathDest, TRUE))
  1552. {
  1553. TouchFile(szPathDest);
  1554. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szPathDest, NULL);
  1555. SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPathDest, NULL);
  1556. hr = S_OK;
  1557. }
  1558. else
  1559. {
  1560. // load the source object that will be "copied" below (with the ::Save call)
  1561. hr = SHGetUIObjectFromFullPIDL(pidlTo, NULL, IID_PPV_ARG(IShellLink, &psl));
  1562. }
  1563. }
  1564. else
  1565. {
  1566. hr = SHCoCreateInstance(NULL, uFlags & SHCL_MAKEFOLDERSHORTCUT ?
  1567. &CLSID_FolderShortcut : &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl));
  1568. if (SUCCEEDED(hr))
  1569. {
  1570. hr = psl->SetIDList(pidlTo);
  1571. // set the working directory to the same path
  1572. // as the file we are linking too
  1573. if (szPathSrc[0] && ((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM))
  1574. {
  1575. PathRemoveFileSpec(szPathSrc);
  1576. psl->SetWorkingDirectory(szPathSrc);
  1577. }
  1578. }
  1579. }
  1580. if (psl)
  1581. {
  1582. if (SUCCEEDED(hr))
  1583. {
  1584. IPersistFile *ppf;
  1585. hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  1586. if (SUCCEEDED(hr))
  1587. {
  1588. hr = ppf->Save(szPathDest, TRUE);
  1589. if (SUCCEEDED(hr))
  1590. {
  1591. // in case ::Save translated the name of the
  1592. // file (.LNK -> .PIF, or Folder Shortcut)
  1593. WCHAR *pwsz;
  1594. if (SUCCEEDED(ppf->GetCurFile(&pwsz)) && pwsz)
  1595. {
  1596. SHUnicodeToTChar(pwsz, szPathDest, ARRAYSIZE(szPathDest));
  1597. SHFree(pwsz);
  1598. }
  1599. }
  1600. ppf->Release();
  1601. }
  1602. }
  1603. psl->Release();
  1604. }
  1605. }
  1606. if (ppidl)
  1607. {
  1608. *ppidl = SUCCEEDED(hr) ? SHSimpleIDListFromPath(szPathDest) : NULL;
  1609. }
  1610. return hr;
  1611. }
  1612. // in/out:
  1613. // pszDir inital folder to try, output new folder (desktop)
  1614. // out:
  1615. // ppidl optional output PIDL of thing created
  1616. HRESULT _CreateLinkRetryDesktop(HWND hwnd, LPCITEMIDLIST pidlTo, LPTSTR pszDir, UINT fFlags, LPITEMIDLIST *ppidl)
  1617. {
  1618. HRESULT hr;
  1619. if (ppidl)
  1620. *ppidl = NULL; // assume error
  1621. if (*pszDir && (fFlags & SHCL_CONFIRM))
  1622. {
  1623. hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags);
  1624. }
  1625. else
  1626. {
  1627. hr = E_FAIL;
  1628. }
  1629. // if we were unable to save, ask user if they want us to
  1630. // try it again but change the path to the desktop.
  1631. if (FAILED(hr))
  1632. {
  1633. int id;
  1634. if (hr == STG_E_MEDIUMFULL)
  1635. {
  1636. DebugMsg(TF_ERROR, TEXT("failed to create link because disk is full"));
  1637. id = IDYES;
  1638. }
  1639. else
  1640. {
  1641. if (fFlags & SHCL_CONFIRM)
  1642. {
  1643. id = _PromptTryDesktopLinks(hwnd, pszDir, (fFlags & SHCL_CONFIRM));
  1644. }
  1645. else
  1646. {
  1647. id = (SHGetSpecialFolderPath(hwnd, pszDir, CSIDL_DESKTOPDIRECTORY, FALSE)) ? IDYES : IDNO;
  1648. }
  1649. if (id == IDYES && *pszDir)
  1650. {
  1651. hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags);
  1652. }
  1653. }
  1654. // we failed to create the link complain to the user.
  1655. if (FAILED(hr) && id != IDNO)
  1656. {
  1657. ShellMessageBox(HINST_THISDLL, hwnd,
  1658. MAKEINTRESOURCE(IDS_CANNOTCREATELINK),
  1659. MAKEINTRESOURCE(IDS_LINKTITLE),
  1660. MB_OK | MB_ICONASTERISK);
  1661. }
  1662. }
  1663. #ifdef DEBUG
  1664. if (FAILED(hr) && ppidl)
  1665. ASSERT(*ppidl == NULL);
  1666. #endif
  1667. return hr;
  1668. }
  1669. //
  1670. // This function creates links to the stuff in the IDataObject
  1671. //
  1672. // Arguments:
  1673. // hwnd for any UI
  1674. // pszDir optional target directory (where to create links)
  1675. // pDataObj data object describing files (array of idlist)
  1676. // ppidl optional pointer to an array that receives pidls pointing to the new links
  1677. // or NULL if not interested
  1678. STDAPI SHCreateLinks(HWND hwnd, LPCTSTR pszDir, IDataObject *pDataObj, UINT fFlags, LPITEMIDLIST* ppidl)
  1679. {
  1680. DECLAREWAITCURSOR;
  1681. STGMEDIUM medium;
  1682. HRESULT hr;
  1683. SetWaitCursor();
  1684. LPIDA pida = DataObj_GetHIDA(pDataObj, &medium);
  1685. if (pida)
  1686. {
  1687. TCHAR szTargetDir[MAX_PATH];
  1688. hr = S_OK; // In case hida contains zero elements
  1689. szTargetDir[0] = 0;
  1690. if (pszDir)
  1691. StrCpyN(szTargetDir, pszDir, ARRAYSIZE(szTargetDir));
  1692. if (!(fFlags & SHCL_USEDESKTOP))
  1693. fFlags |= SHCL_CONFIRM;
  1694. for (UINT i = 0; i < pida->cidl; i++)
  1695. {
  1696. LPITEMIDLIST pidlTo = IDA_ILClone(pida, i);
  1697. if (pidlTo)
  1698. {
  1699. hr = _CreateLinkRetryDesktop(hwnd, pidlTo, szTargetDir, fFlags, ppidl ? &ppidl[i] : NULL);
  1700. ILFree(pidlTo);
  1701. if (FAILED(hr))
  1702. break;
  1703. }
  1704. }
  1705. HIDA_ReleaseStgMedium(pida, &medium);
  1706. }
  1707. else
  1708. hr = E_OUTOFMEMORY;
  1709. SHChangeNotifyHandleEvents();
  1710. ResetWaitCursor();
  1711. return hr;
  1712. }
  1713. HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts)
  1714. {
  1715. VARIANT var;
  1716. HRESULT hr = InitVariantFromIDList(&var, pidl);
  1717. if (SUCCEEDED(hr))
  1718. {
  1719. hr = psfv->SelectItem(&var, dwOpts);
  1720. VariantClear(&var);
  1721. }
  1722. return hr;
  1723. }
  1724. HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv)
  1725. {
  1726. *ppsfv = NULL;
  1727. IWebBrowserApp *pauto;
  1728. HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto);
  1729. if (SUCCEEDED(hr))
  1730. {
  1731. HWND hwnd;
  1732. if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd)))
  1733. {
  1734. // Make sure we make this the active window
  1735. SetForegroundWindow(hwnd);
  1736. ShowWindow(hwnd, SW_SHOWNORMAL);
  1737. }
  1738. IDispatch *pdoc;
  1739. hr = pauto->get_Document(&pdoc);
  1740. if (S_OK == hr) // careful, automation returns S_FALSE
  1741. {
  1742. hr = pdoc->QueryInterface(IID_PPV_ARG(IShellFolderViewDual, ppsfv));
  1743. pdoc->Release();
  1744. }
  1745. else
  1746. hr = E_FAIL;
  1747. pauto->Release();
  1748. }
  1749. return hr;
  1750. }
  1751. // pidlFolder - fully qualified pidl to the folder to open
  1752. // cidl/apidl - array of items in that folder to select
  1753. //
  1754. // if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's
  1755. // folder is opened and it is selected.
  1756. //
  1757. // dwFlags - optional flags, pass 0 for now
  1758. SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags)
  1759. {
  1760. HRESULT hr;
  1761. if (0 == cidl)
  1762. {
  1763. // overload the 0 item case to mean pidlFolder is the full pidl to the item
  1764. LPITEMIDLIST pidlTemp;
  1765. hr = SHILClone(pidlFolder, &pidlTemp);
  1766. if (SUCCEEDED(hr))
  1767. {
  1768. ILRemoveLastID(pidlTemp); // strip to the folder
  1769. LPCITEMIDLIST pidl = ILFindLastID(pidlFolder);
  1770. hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse
  1771. ILFree(pidlTemp);
  1772. }
  1773. }
  1774. else
  1775. {
  1776. IShellFolderViewDual *psfv;
  1777. hr = OpenFolderAndGetView(pidlFolder, &psfv);
  1778. if (SUCCEEDED(hr))
  1779. {
  1780. DWORD dwSelFlags = SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE;
  1781. for (UINT i = 0; i < cidl; i++)
  1782. {
  1783. hr = SelectPidlInSFV(psfv, apidl[i], dwSelFlags);
  1784. dwSelFlags = SVSI_SELECT; // second items append to sel
  1785. }
  1786. psfv->Release();
  1787. }
  1788. }
  1789. return hr;
  1790. }
  1791. SHSTDAPI SHCreateShellItem(LPCITEMIDLIST pidlParent, IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi)
  1792. {
  1793. *ppsi = NULL;
  1794. IShellItem *psi;
  1795. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellItem, NULL, IID_PPV_ARG(IShellItem, &psi));
  1796. if (SUCCEEDED(hr))
  1797. {
  1798. if (pidlParent || psfParent)
  1799. {
  1800. IParentAndItem *pinit;
  1801. ASSERT(pidl);
  1802. hr = psi->QueryInterface(IID_PPV_ARG(IParentAndItem, &pinit));
  1803. if (SUCCEEDED(hr))
  1804. {
  1805. hr = pinit->SetParentAndItem(pidlParent, psfParent, pidl);
  1806. pinit->Release();
  1807. }
  1808. }
  1809. else
  1810. {
  1811. IPersistIDList *pinit;
  1812. hr = psi->QueryInterface(IID_PPV_ARG(IPersistIDList, &pinit));
  1813. if (SUCCEEDED(hr))
  1814. {
  1815. hr = pinit->SetIDList(pidl);
  1816. pinit->Release();
  1817. }
  1818. }
  1819. if (SUCCEEDED(hr))
  1820. *ppsi = psi;
  1821. else
  1822. psi->Release();
  1823. }
  1824. return hr;
  1825. }
  1826. STDAPI SHCreateShellItemFromParent(IShellItem *psiParent, LPCWSTR pszName, IShellItem **ppsi)
  1827. {
  1828. *ppsi = NULL;
  1829. IShellFolder *psf;
  1830. HRESULT hr = psiParent->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder, &psf));
  1831. if (SUCCEEDED(hr))
  1832. {
  1833. LPITEMIDLIST pidl;
  1834. hr = SHGetIDListFromUnk(psiParent, &pidl);
  1835. if (SUCCEEDED(hr))
  1836. {
  1837. ULONG cchEaten;
  1838. LPITEMIDLIST pidlChild;
  1839. hr = psf->ParseDisplayName(NULL, NULL, (LPWSTR)pszName, &cchEaten, &pidlChild, NULL);
  1840. if (SUCCEEDED(hr))
  1841. {
  1842. hr = SHCreateShellItem(pidl, psf, pidlChild, ppsi);
  1843. ILFree(pidlChild);
  1844. }
  1845. ILFree(pidl);
  1846. }
  1847. psf->Release();
  1848. }
  1849. return hr;
  1850. }
  1851. SHSTDAPI SHSetLocalizedName(LPWSTR pszPath, LPCWSTR pszResModule, int idsRes)
  1852. {
  1853. IShellFolder *psfDesktop;
  1854. HRESULT hrInit = SHCoInitialize();
  1855. HRESULT hr = hrInit;
  1856. if (SUCCEEDED(hrInit))
  1857. {
  1858. hr = SHGetDesktopFolder(&psfDesktop);
  1859. if (SUCCEEDED(hr))
  1860. {
  1861. LPITEMIDLIST pidl;
  1862. hr = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL);
  1863. if (SUCCEEDED(hr))
  1864. {
  1865. LPCITEMIDLIST pidlChild;
  1866. IShellFolder *psf;
  1867. hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
  1868. if (SUCCEEDED(hr))
  1869. {
  1870. // WARNING - this is a stack sensitive function - ZekeL 29-Jan-2001
  1871. // since this function is called by winlogon/userenv
  1872. // we need to be sensitive to the stack limitations of those callers
  1873. // the shortname will be no larger than the long name
  1874. DWORD cchShort = lstrlenW(pszResModule) + 1;
  1875. PWSTR pszShort;
  1876. hr = SHLocalAlloc(CbFromCchW(cchShort), &pszShort);
  1877. if (SUCCEEDED(hr))
  1878. {
  1879. DWORD cch = GetShortPathName(pszResModule, pszShort, cchShort);
  1880. if (cch)
  1881. {
  1882. pszResModule = pszShort;
  1883. }
  1884. else
  1885. {
  1886. // GSPN() fails when the module passed in is a relative path
  1887. cch = cchShort;
  1888. }
  1889. cch += 14; // 11 for id + ',' + '@' + null
  1890. PWSTR pszName;
  1891. hr = SHLocalAlloc(CbFromCchW(cch), &pszName);
  1892. if (SUCCEEDED(hr))
  1893. {
  1894. wnsprintfW(pszName, cch, L"@%s,%d", pszResModule, (idsRes * -1));
  1895. hr = psf->SetNameOf(NULL, pidlChild, pszName, SHGDN_NORMAL, NULL);
  1896. LocalFree(pszName);
  1897. }
  1898. LocalFree(pszShort);
  1899. }
  1900. psf->Release();
  1901. }
  1902. SHFree(pidl);
  1903. }
  1904. psfDesktop->Release();
  1905. }
  1906. }
  1907. SHCoUninitialize(hrInit);
  1908. return hr;
  1909. }
  1910. // ShellHookProc was mistakenly exported in the original NT SHELL32.DLL when
  1911. // it didn't need to be (hookproc's, like wndproc's don't need to be exported
  1912. // in the 32-bit world). In order to maintain loadability of a app
  1913. // which might have linked to it, we stub it here. If some app ended up really
  1914. // using it, then we'll look into a specific fix for that app.
  1915. STDAPI_(LONG) ShellHookProc(int code, WPARAM wParam, LPARAM lParam)
  1916. {
  1917. return 0;
  1918. }
  1919. // RegisterShellHook - wrapper around RegisterShellHookWindow()/DeregisterShellHookWindow()
  1920. // the GetTaskmanWindow() stuff is legacy that I don't think is really needed
  1921. HWND g_hwndTaskMan = NULL;
  1922. STDAPI_(BOOL) RegisterShellHook(HWND hwnd, BOOL fInstall)
  1923. {
  1924. BOOL fOk = TRUE;
  1925. switch (fInstall)
  1926. {
  1927. case 0:
  1928. // un-installation of shell hooks
  1929. g_hwndTaskMan = GetTaskmanWindow();
  1930. if (hwnd == g_hwndTaskMan)
  1931. {
  1932. SetTaskmanWindow(NULL);
  1933. }
  1934. DeregisterShellHookWindow(hwnd);
  1935. return TRUE;
  1936. case 3:
  1937. // explorer.exe Tray uses this
  1938. if (g_hwndTaskMan != NULL)
  1939. {
  1940. SetTaskmanWindow(NULL);
  1941. g_hwndTaskMan = NULL;
  1942. }
  1943. fOk = SetTaskmanWindow(hwnd);
  1944. if (fOk)
  1945. {
  1946. g_hwndTaskMan = hwnd;
  1947. }
  1948. RegisterShellHookWindow(hwnd); // install
  1949. break;
  1950. }
  1951. return TRUE;
  1952. }
  1953. EXTERN_C DWORD g_dwThreadBindCtx;
  1954. class CThreadBindCtx : public IBindCtx
  1955. {
  1956. public:
  1957. CThreadBindCtx(IBindCtx *pbc) : _cRef(1) { _pbc = pbc; _pbc->AddRef(); }
  1958. ~CThreadBindCtx();
  1959. // *** IUnknown methods ***
  1960. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  1961. STDMETHODIMP_(ULONG) AddRef(void);
  1962. STDMETHODIMP_(ULONG) Release(void);
  1963. // *** IBindCtx methods ***
  1964. STDMETHODIMP RegisterObjectBound(IUnknown *punk)
  1965. { return _pbc->RegisterObjectBound(punk); }
  1966. STDMETHODIMP RevokeObjectBound(IUnknown *punk)
  1967. { return _pbc->RevokeObjectBound(punk); }
  1968. STDMETHODIMP ReleaseBoundObjects(void)
  1969. { return _pbc->ReleaseBoundObjects(); }
  1970. STDMETHODIMP SetBindOptions(BIND_OPTS *pbindopts)
  1971. { return _pbc->SetBindOptions(pbindopts); }
  1972. STDMETHODIMP GetBindOptions(BIND_OPTS *pbindopts)
  1973. { return _pbc->GetBindOptions(pbindopts); }
  1974. STDMETHODIMP GetRunningObjectTable(IRunningObjectTable **pprot)
  1975. { return _pbc->GetRunningObjectTable(pprot); }
  1976. STDMETHODIMP RegisterObjectParam(LPOLESTR pszKey, IUnknown *punk)
  1977. { return _pbc->RegisterObjectParam(pszKey, punk); }
  1978. STDMETHODIMP GetObjectParam(LPOLESTR pszKey, IUnknown **ppunk)
  1979. { return _pbc->GetObjectParam(pszKey, ppunk); }
  1980. STDMETHODIMP EnumObjectParam(IEnumString **ppenum)
  1981. { return _pbc->EnumObjectParam(ppenum); }
  1982. STDMETHODIMP RevokeObjectParam(LPOLESTR pszKey)
  1983. { return _pbc->RevokeObjectParam(pszKey); }
  1984. private:
  1985. LONG _cRef;
  1986. IBindCtx * _pbc;
  1987. };
  1988. CThreadBindCtx::~CThreadBindCtx()
  1989. {
  1990. ATOMICRELEASE(_pbc);
  1991. }
  1992. HRESULT CThreadBindCtx::QueryInterface(REFIID riid, void **ppvObj)
  1993. {
  1994. static const QITAB qit[] = {
  1995. QITABENT(CThreadBindCtx, IBindCtx),
  1996. { 0 },
  1997. };
  1998. return QISearch(this, qit, riid, ppvObj);
  1999. }
  2000. ULONG CThreadBindCtx::AddRef()
  2001. {
  2002. return InterlockedIncrement(&_cRef);
  2003. }
  2004. ULONG CThreadBindCtx::Release()
  2005. {
  2006. ASSERT( 0 != _cRef );
  2007. ULONG cRef = InterlockedDecrement(&_cRef);
  2008. if ( 0 == cRef )
  2009. {
  2010. delete this;
  2011. // clear ourselves out
  2012. TlsSetValue(g_dwThreadBindCtx, NULL);
  2013. }
  2014. return cRef;
  2015. }
  2016. STDAPI TBCGetBindCtx(BOOL fCreate, IBindCtx **ppbc)
  2017. {
  2018. HRESULT hr = E_UNEXPECTED;
  2019. *ppbc = NULL;
  2020. if ((DWORD) -1 != g_dwThreadBindCtx)
  2021. {
  2022. CThreadBindCtx *ptbc = (CThreadBindCtx *)TlsGetValue(g_dwThreadBindCtx);
  2023. if (ptbc)
  2024. {
  2025. ptbc->AddRef();
  2026. *ppbc = SAFECAST(ptbc, IBindCtx *);
  2027. hr = S_OK;
  2028. }
  2029. else if (fCreate)
  2030. {
  2031. IBindCtx *pbcInner;
  2032. hr = CreateBindCtx(0, &pbcInner);
  2033. if (SUCCEEDED(hr))
  2034. {
  2035. hr = E_OUTOFMEMORY;
  2036. ptbc = new CThreadBindCtx(pbcInner);
  2037. if (ptbc)
  2038. {
  2039. if (TlsSetValue(g_dwThreadBindCtx, ptbc))
  2040. {
  2041. *ppbc = SAFECAST(ptbc, IBindCtx *);
  2042. hr = S_OK;
  2043. }
  2044. else
  2045. delete ptbc;
  2046. }
  2047. pbcInner->Release();
  2048. }
  2049. }
  2050. }
  2051. return hr;
  2052. }
  2053. STDAPI TBCRegisterObjectParam(LPCOLESTR pszKey, IUnknown *punk, IBindCtx **ppbcLifetime)
  2054. {
  2055. IBindCtx *pbc;
  2056. HRESULT hr = TBCGetBindCtx(TRUE, &pbc);
  2057. if (SUCCEEDED(hr))
  2058. {
  2059. hr = BindCtx_RegisterObjectParam(pbc, pszKey, punk, ppbcLifetime);
  2060. pbc->Release();
  2061. }
  2062. else
  2063. *ppbcLifetime = 0;
  2064. return hr;
  2065. }
  2066. STDAPI TBCGetObjectParam(LPCOLESTR pszKey, REFIID riid, void **ppv)
  2067. {
  2068. IBindCtx *pbc;
  2069. HRESULT hr = TBCGetBindCtx(FALSE, &pbc);
  2070. if (SUCCEEDED(hr))
  2071. {
  2072. IUnknown *punk;
  2073. hr = pbc->GetObjectParam((LPOLESTR)pszKey, &punk);
  2074. if (SUCCEEDED(hr) )
  2075. {
  2076. if (ppv)
  2077. hr = punk->QueryInterface(riid, ppv);
  2078. punk->Release();
  2079. }
  2080. pbc->Release();
  2081. }
  2082. return hr;
  2083. }
  2084. #define TBCENVOBJECT L"ThreadEnvironmentVariables"
  2085. STDAPI TBCGetEnvironmentVariable(LPCWSTR pszVar, LPWSTR pszValue, DWORD cchValue)
  2086. {
  2087. IPropertyBag *pbag;
  2088. HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag));
  2089. if (SUCCEEDED(hr))
  2090. {
  2091. hr = SHPropertyBag_ReadStr(pbag, pszVar, pszValue, cchValue);
  2092. pbag->Release();
  2093. }
  2094. return hr;
  2095. }
  2096. STDAPI TBCSetEnvironmentVariable(LPCWSTR pszVar, LPCWSTR pszValue, IBindCtx **ppbcLifetime)
  2097. {
  2098. *ppbcLifetime = 0;
  2099. IPropertyBag *pbag;
  2100. HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag));
  2101. if (FAILED(hr))
  2102. hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &pbag));
  2103. if (SUCCEEDED(hr))
  2104. {
  2105. hr = SHPropertyBag_WriteStr(pbag, pszVar, pszValue);
  2106. if (SUCCEEDED(hr))
  2107. hr = TBCRegisterObjectParam(TBCENVOBJECT, pbag, ppbcLifetime);
  2108. pbag->Release();
  2109. }
  2110. return hr;
  2111. }
  2112. // create a stock IExtractIcon handler for a thing that is file like. this is typically
  2113. // used by name space extensiosn that display things that are like files in the
  2114. // file system. that is the extension, file attributes decrive all that is needed
  2115. // for a simple icon extractor
  2116. STDAPI SHCreateFileExtractIconW(LPCWSTR pszFile, DWORD dwFileAttributes, REFIID riid, void **ppv)
  2117. {
  2118. *ppv = NULL;
  2119. HRESULT hr = E_FAIL;
  2120. SHFILEINFO sfi = {0};
  2121. if (SHGetFileInfo(pszFile, dwFileAttributes, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES))
  2122. {
  2123. hr = SHCreateDefExtIcon(TEXT("*"), sfi.iIcon, sfi.iIcon, GIL_PERCLASS | GIL_NOTFILENAME, -1, riid, ppv);
  2124. DestroyIcon(sfi.hIcon);
  2125. }
  2126. return hr;
  2127. }