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.

2543 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(LPTSTR psz2, LPCTSTR psz1)
  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 (; *psz1; psz1 = CharNext(psz1), psz2 = CharNext(psz2))
  991. {
  992. if (*psz1 == TEXT('('))
  993. {
  994. LPCTSTR pszT = psz1;
  995. do
  996. {
  997. psz1 = CharNext(psz1);
  998. } while (*psz1 && ISDIGIT(*psz1));
  999. if (*psz1 == TEXT(')'))
  1000. {
  1001. psz1 = CharNext(psz1);
  1002. if (*psz1 == TEXT(' '))
  1003. psz1 = CharNext(psz1); // skip the extra space
  1004. lstrcpy(psz2, psz1);
  1005. return;
  1006. }
  1007. // We have a (that does not match the format correctly!
  1008. psz1 = pszT; // restore pointer back to copy this char through and continue...
  1009. }
  1010. *psz2 = *psz1;
  1011. }
  1012. *psz2 = *psz1;
  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. LPCTSTR pszNewName = PathFindFileName(pszNewPath);
  1031. lstrcpy(szBaseName, pszNewName);
  1032. PathRemoveExtension(szBaseName);
  1033. // mock up a name using the basename and the linkto template
  1034. LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo));
  1035. wnsprintf(szMockName, ARRAYSIZE(szMockName), szLinkTo, szBaseName);
  1036. StripNumber(szMockName, szMockName);
  1037. StripNumber(szBaseName, pszOldName);
  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. //
  1173. // This function returns the attributes (to be returned IShellFolder::
  1174. // GetAttributesOf) of the junction point specified by the class ID.
  1175. //
  1176. STDAPI_(DWORD) SHGetAttributesFromCLSID(const CLSID *pclsid, DWORD dwDefault)
  1177. {
  1178. return SHGetAttributesFromCLSID2(pclsid, dwDefault, (DWORD)-1);
  1179. }
  1180. DWORD QueryCallForAttributes(HKEY hkey, const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested)
  1181. {
  1182. DWORD dwAttr = dwDefAttrs;
  1183. DWORD dwData, cbSize = sizeof(dwAttr);
  1184. // consider caching this folder to avoid creating over and over
  1185. // mydocs.dll uses this for compat with old apps
  1186. // See if this folder has asked us specifically to call and get
  1187. // the attributes...
  1188. //
  1189. if (SHQueryValueEx(hkey, TEXT("CallForAttributes"), NULL, NULL, &dwData, &cbSize) == ERROR_SUCCESS)
  1190. {
  1191. // CallForAttributes can be a masked value. See if it's being supplied in the value.
  1192. // NOTE: MyDocs.dll registers with a NULL String, so this check works.
  1193. DWORD dwMask = (DWORD)-1;
  1194. if (sizeof(dwData) == cbSize)
  1195. {
  1196. // There is a mask, Use this.
  1197. dwMask = dwData;
  1198. }
  1199. // Is the requested bit contained in the specified mask?
  1200. if (dwMask & dwRequested)
  1201. {
  1202. // Yes. Then CoCreate and Query.
  1203. IShellFolder *psf;
  1204. if (SUCCEEDED(SHExtCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IShellFolder, &psf))))
  1205. {
  1206. dwAttr = dwRequested;
  1207. psf->GetAttributesOf(0, NULL, &dwAttr);
  1208. psf->Release();
  1209. }
  1210. else
  1211. {
  1212. dwAttr |= SFGAO_FILESYSTEM;
  1213. }
  1214. }
  1215. }
  1216. return dwAttr;
  1217. }
  1218. // dwRequested is the bits you are explicitly looking for. This is an optimization that prevents reg hits.
  1219. STDAPI_(DWORD) SHGetAttributesFromCLSID2(const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested)
  1220. {
  1221. DWORD dwAttr = dwDefAttrs;
  1222. HKEY hkey = SHOpenShellFolderKey(pclsid);
  1223. if (hkey)
  1224. {
  1225. DWORD dwData, cbSize = sizeof(dwAttr);
  1226. // We are looking for some attributes on a shell folder. These attributes can be in two locations:
  1227. // 1) In the "Attributes" value in the registry.
  1228. // 2) Stored in a the shell folder's GetAttributesOf.
  1229. // First, Check to see if the reqested value is contained in the registry.
  1230. if (SHQueryValueEx(hkey, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwData, &cbSize) == ERROR_SUCCESS &&
  1231. cbSize == sizeof(dwData))
  1232. {
  1233. // We have data there, but it may not contain the data we are looking for
  1234. dwAttr = dwData & dwRequested;
  1235. // Does it contain the bit we are looking for?
  1236. if (((dwAttr & dwRequested) != dwRequested) && dwRequested != 0)
  1237. {
  1238. // No. Check to see if it is in the shell folder implementation
  1239. goto CallForAttributes;
  1240. }
  1241. }
  1242. else
  1243. {
  1244. CallForAttributes:
  1245. // See if we have to talk to the shell folder.
  1246. // I'm passing dwAttr, because if the previous case did not generate any attributes, then it's
  1247. // equal to dwDefAttrs. If the call to CallForAttributes fails, then it will contain the value of
  1248. // dwDefAttrs or whatever was in the shell folder's Attributes key
  1249. dwAttr = QueryCallForAttributes(hkey, pclsid, dwAttr, dwRequested);
  1250. }
  1251. RegCloseKey(hkey);
  1252. }
  1253. if (_IsNonEnumPolicySet(pclsid))
  1254. dwAttr |= SFGAO_NONENUMERATED;
  1255. if (SHGetObjectCompatFlags(NULL, pclsid) & OBJCOMPATF_NOTAFILESYSTEM)
  1256. dwAttr &= ~SFGAO_FILESYSTEM;
  1257. return dwAttr;
  1258. }
  1259. // _BuildLinkName
  1260. //
  1261. // Used during the creation of a shortcut, this function determines an appropriate name for the shortcut.
  1262. // This is not the exact name that will be used becuase it will usually contain "() " which will either
  1263. // get removed or replaced with "(x) " where x is a number that makes the name unique. This removal is done
  1264. // elsewhere (currently in PathYetAnotherMakeUniqueName).
  1265. //
  1266. // in:
  1267. // pszName file spec part
  1268. // pszDir path part of name to know how to limit the long name...
  1269. //
  1270. // out:
  1271. // pszLinkName - Full path to link name (May fit in 8.3...). Can be the same buffer as pszName.
  1272. //
  1273. // NOTES: If pszDir + pszLinkName is greater than MAX_PATH we will fail to create the shortcut.
  1274. // In an effort to prevent
  1275. void _BuildLinkName(LPTSTR pszLinkName, LPCTSTR pszName, LPCTSTR pszDir, BOOL fLinkTo)
  1276. {
  1277. TCHAR szLinkTo[40]; // "Shortcut to %s.lnk"
  1278. TCHAR szTemp[MAX_PATH + 40];
  1279. if (fLinkTo)
  1280. {
  1281. // check to see if we're in the "don't ever say 'shortcut to' mode"
  1282. LoadUseLinkPrefixCount();
  1283. if (!g_iUseLinkPrefix)
  1284. {
  1285. fLinkTo = FALSE;
  1286. }
  1287. else if (g_iUseLinkPrefix > 0)
  1288. {
  1289. if (g_iUseLinkPrefix < MAXLINKPREFIXCOUNT)
  1290. {
  1291. g_iUseLinkPrefix += SHORTCUT_PREFIX_INCR;
  1292. SaveUseLinkPrefixCount();
  1293. }
  1294. }
  1295. }
  1296. if (!fLinkTo)
  1297. {
  1298. // Generate the title of this link ("XX.lnk")
  1299. LoadString(HINST_THISDLL, IDS_LINKEXTENSION, szLinkTo, ARRAYSIZE(szLinkTo));
  1300. }
  1301. else
  1302. {
  1303. // Generate the title of this link ("Shortcut to XX.lnk")
  1304. LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo));
  1305. }
  1306. wnsprintf(szTemp, ARRAYSIZE(szTemp), szLinkTo, pszName);
  1307. PathCleanupSpecEx(pszDir, szTemp); // get rid of illegal chars AND ensure correct filename length
  1308. lstrcpyn(pszLinkName, szTemp, MAX_PATH);
  1309. ASSERT(PathIsLnk(pszLinkName));
  1310. }
  1311. // return a new destination path for a link
  1312. //
  1313. // in:
  1314. // fErrorSoTryDesktop we are called because there was an error saving
  1315. // the shortcut and we want to prompt to see if the
  1316. // desktop should be used.
  1317. //
  1318. // in/out:
  1319. // pszPath on input the place being tried, on output the desktop folder
  1320. //
  1321. // returns:
  1322. //
  1323. // IDYES user said yes to creating a link at new place
  1324. // IDNO user said no to creating a link at new place
  1325. // -1 error
  1326. //
  1327. int _PromptTryDesktopLinks(HWND hwnd, LPTSTR pszPath, BOOL fErrorSoTryDesktop)
  1328. {
  1329. TCHAR szPath[MAX_PATH];
  1330. if (!SHGetSpecialFolderPath(hwnd, szPath, CSIDL_DESKTOPDIRECTORY, FALSE))
  1331. return -1; // fail no desktop dir
  1332. int idOk;
  1333. if (fErrorSoTryDesktop)
  1334. {
  1335. // Fail, if pszPath already points to the desktop directory.
  1336. if (lstrcmpi(szPath, pszPath) == 0)
  1337. return -1;
  1338. idOk = ShellMessageBox(HINST_THISDLL, hwnd,
  1339. MAKEINTRESOURCE(IDS_TRYDESKTOPLINK),
  1340. MAKEINTRESOURCE(IDS_LINKTITLE),
  1341. MB_YESNO | MB_ICONQUESTION);
  1342. }
  1343. else
  1344. {
  1345. ShellMessageBox(HINST_THISDLL, hwnd,
  1346. MAKEINTRESOURCE(IDS_MAKINGDESKTOPLINK),
  1347. MAKEINTRESOURCE(IDS_LINKTITLE),
  1348. MB_OK | MB_ICONASTERISK);
  1349. idOk = IDYES;
  1350. }
  1351. if (idOk == IDYES)
  1352. lstrcpy(pszPath , szPath); // output
  1353. return idOk; // return yes or no
  1354. }
  1355. // in:
  1356. // pszpdlLinkTo LPCITEMIDLIST or LPCTSTR, target of link to create
  1357. // pszDir where we will put the link
  1358. // uFlags SHGNLI_ flags
  1359. //
  1360. // out:
  1361. // pszName file name to create "c:\Shortcut to Foo.lnk"
  1362. // pfMustCopy pszpdlLinkTo was a link itself, make a copy of this
  1363. STDAPI_(BOOL) SHGetNewLinkInfo(LPCTSTR pszpdlLinkTo, LPCTSTR pszDir, LPTSTR pszName,
  1364. BOOL *pfMustCopy, UINT uFlags)
  1365. {
  1366. BOOL fDosApp = FALSE;
  1367. BOOL fLongFileNames = IsLFNDrive(pszDir);
  1368. SHFILEINFO sfi;
  1369. *pfMustCopy = FALSE;
  1370. sfi.dwAttributes = SFGAO_FILESYSTEM | SFGAO_LINK | SFGAO_FOLDER;
  1371. if (uFlags & SHGNLI_PIDL)
  1372. {
  1373. if (FAILED(SHGetNameAndFlags((LPCITEMIDLIST)pszpdlLinkTo, SHGDN_NORMAL,
  1374. pszName, MAX_PATH, &sfi.dwAttributes)))
  1375. return FALSE;
  1376. }
  1377. else
  1378. {
  1379. if (SHGetFileInfo(pszpdlLinkTo, 0, &sfi, sizeof(sfi),
  1380. SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED |
  1381. ((uFlags & SHGNLI_PIDL) ? SHGFI_PIDL : 0)))
  1382. lstrcpy(pszName, sfi.szDisplayName);
  1383. else
  1384. return FALSE;
  1385. }
  1386. if (PathCleanupSpecEx(pszDir, pszName) & PCS_FATAL)
  1387. return FALSE;
  1388. //
  1389. // WARNING: From this point on, sfi.szDisplayName may be re-used to
  1390. // contain the file path of the PIDL we are linking to. Don't rely on
  1391. // it containing the display name.
  1392. //
  1393. if (sfi.dwAttributes & SFGAO_FILESYSTEM)
  1394. {
  1395. LPTSTR pszPathSrc;
  1396. if (uFlags & SHGNLI_PIDL)
  1397. {
  1398. pszPathSrc = sfi.szDisplayName;
  1399. SHGetPathFromIDList((LPCITEMIDLIST)pszpdlLinkTo, pszPathSrc);
  1400. }
  1401. else
  1402. {
  1403. pszPathSrc = (LPTSTR)pszpdlLinkTo;
  1404. }
  1405. fDosApp = (lstrcmpi(PathFindExtension(pszPathSrc), TEXT(".pif")) == 0) ||
  1406. (LOWORD(GetExeType(pszPathSrc)) == 0x5A4D); // 'MZ'
  1407. if (sfi.dwAttributes & SFGAO_LINK)
  1408. {
  1409. *pfMustCopy = TRUE;
  1410. if (!(sfi.dwAttributes & SFGAO_FOLDER))
  1411. {
  1412. uFlags &= ~SHGNLI_NOLNK; // if copying the file then don't trim the extension
  1413. }
  1414. lstrcpy(pszName, PathFindFileName(pszPathSrc));
  1415. }
  1416. else
  1417. {
  1418. //
  1419. // when making a link to a drive root. special case a few things
  1420. //
  1421. // if we are not on a LFN drive, dont use the full name, just
  1422. // use the drive letter. "C.LNK" not "Label (C).LNK"
  1423. //
  1424. // if we are making a link to removable media, we dont want the
  1425. // label as part of the name, we want the media type.
  1426. //
  1427. // CD-ROM drives are currently the only removable media we
  1428. // show the volume label for, so we only need to special case
  1429. // cdrom drives here.
  1430. //
  1431. if (PathIsRoot(pszPathSrc) && !PathIsUNC(pszPathSrc))
  1432. {
  1433. if (!fLongFileNames)
  1434. lstrcpy(pszName, pszPathSrc);
  1435. else if (IsCDRomDrive(DRIVEID(pszPathSrc)))
  1436. LoadString(HINST_THISDLL, IDS_DRIVES_CDROM, pszName, MAX_PATH);
  1437. }
  1438. }
  1439. if (fLongFileNames && fDosApp)
  1440. {
  1441. HANDLE hPif = PifMgr_OpenProperties(pszPathSrc, NULL, 0, OPENPROPS_INHIBITPIF);
  1442. if (hPif)
  1443. {
  1444. PROPPRG PP = {0};
  1445. if (PifMgr_GetProperties(hPif, (LPCSTR)MAKELP(0, GROUP_PRG), &PP, sizeof(PP), 0) &&
  1446. ((PP.flPrgInit & PRGINIT_INFSETTINGS) ||
  1447. ((PP.flPrgInit & (PRGINIT_NOPIF | PRGINIT_DEFAULTPIF)) == 0)))
  1448. {
  1449. SHAnsiToTChar(PP.achTitle, pszName, MAX_PATH);
  1450. }
  1451. PifMgr_CloseProperties(hPif, 0);
  1452. }
  1453. }
  1454. }
  1455. if (!*pfMustCopy)
  1456. {
  1457. // create full dest path name. only use template iff long file names
  1458. // can be created and the caller requested it. _BuildLinkName will
  1459. // truncate files on non-lfn drives and clean up any invalid chars.
  1460. _BuildLinkName(pszName, pszName, pszDir,
  1461. (!(*pfMustCopy) && fLongFileNames && (uFlags & SHGNLI_PREFIXNAME)));
  1462. }
  1463. if (fDosApp)
  1464. PathRenameExtension(pszName, TEXT(".pif"));
  1465. if (uFlags & SHGNLI_NOLNK)
  1466. {
  1467. // Don't do PathRemoveExtension because pszName might contain
  1468. // internal dots ("Windows 3.1") and passing that to
  1469. // PathYetAnotherMakeUniqueName will result in
  1470. // "Windows 3 (2).1" which is wrong. We leave the dot at the
  1471. // end so we get "Windows 3.1 (2)." back. We will strip off the
  1472. // final dot later.
  1473. PathRenameExtension(pszName, TEXT("."));
  1474. }
  1475. // make sure the name is unique
  1476. // NOTE: PathYetAnotherMakeUniqueName will return the directory+filename in the pszName buffer.
  1477. // It returns FALSE if the name is not unique or the dir+filename is too long. If it returns
  1478. // false then this function should return false because creation will fail.
  1479. BOOL fSuccess;
  1480. if (!(uFlags & SHGNLI_NOUNIQUE))
  1481. fSuccess = PathYetAnotherMakeUniqueName(pszName, pszDir, pszName, pszName);
  1482. else
  1483. fSuccess = TRUE;
  1484. // Strip off any trailing dots that may have been generated by SHGNI_NOLNK
  1485. PathStripTrailingDots(pszName);
  1486. return fSuccess;
  1487. }
  1488. STDAPI_(BOOL) SHGetNewLinkInfoA(LPCSTR pszpdlLinkTo, LPCSTR pszDir, LPSTR pszName,
  1489. BOOL *pfMustCopy, UINT uFlags)
  1490. {
  1491. ThunkText * pThunkText;
  1492. BOOL bResult = FALSE;
  1493. if (uFlags & SHGNLI_PIDL)
  1494. {
  1495. // 1 string (pszpdlLinkTo is a pidl)
  1496. pThunkText = ConvertStrings(2, NULL, pszDir);
  1497. if (pThunkText)
  1498. pThunkText->m_pStr[0] = (LPWSTR)pszpdlLinkTo;
  1499. }
  1500. else
  1501. {
  1502. // 2 strings
  1503. pThunkText = ConvertStrings(2, pszpdlLinkTo, pszDir);
  1504. }
  1505. if (pThunkText)
  1506. {
  1507. WCHAR wszName[MAX_PATH];
  1508. bResult = SHGetNewLinkInfoW(pThunkText->m_pStr[0], pThunkText->m_pStr[1],
  1509. wszName, pfMustCopy, uFlags);
  1510. LocalFree(pThunkText);
  1511. if (bResult)
  1512. {
  1513. if (0 == WideCharToMultiByte(CP_ACP, 0, wszName, -1,
  1514. pszName, MAX_PATH, NULL, NULL))
  1515. {
  1516. SetLastError((DWORD)E_FAIL);
  1517. bResult = FALSE;
  1518. }
  1519. }
  1520. }
  1521. return bResult;
  1522. }
  1523. //
  1524. // in:
  1525. // pidlTo
  1526. STDAPI CreateLinkToPidl(LPCITEMIDLIST pidlTo, LPCTSTR pszDir, LPITEMIDLIST *ppidl, UINT uFlags)
  1527. {
  1528. HRESULT hr = E_FAIL;
  1529. TCHAR szPathDest[MAX_PATH];
  1530. BOOL fCopyLnk;
  1531. BOOL fUseLinkTemplate = (SHCL_USETEMPLATE & uFlags);
  1532. UINT uSHGNLI = fUseLinkTemplate ? SHGNLI_PIDL | SHGNLI_PREFIXNAME : SHGNLI_PIDL;
  1533. if (uFlags & SHCL_MAKEFOLDERSHORTCUT)
  1534. {
  1535. // Don't add ".lnk" to the folder shortcut name; that's just stupid
  1536. uSHGNLI |= SHGNLI_NOLNK;
  1537. }
  1538. if (uFlags & SHCL_NOUNIQUE)
  1539. {
  1540. uSHGNLI |= SHGNLI_NOUNIQUE;
  1541. }
  1542. if (SHGetNewLinkInfo((LPTSTR)pidlTo, pszDir, szPathDest, &fCopyLnk, uSHGNLI))
  1543. {
  1544. TCHAR szPathSrc[MAX_PATH];
  1545. IShellLink *psl = NULL;
  1546. // If we passed SHGNLI_NOUNIQUE then we need to do the PathCombine ourselves
  1547. // because SHGetNewLinkInfo won't
  1548. if (uFlags & SHCL_NOUNIQUE)
  1549. {
  1550. PathCombine(szPathDest, pszDir, szPathDest);
  1551. }
  1552. DWORD dwAttributes = SFGAO_FILESYSTEM | SFGAO_FOLDER;
  1553. SHGetNameAndFlags(pidlTo, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szPathSrc, ARRAYSIZE(szPathSrc), &dwAttributes);
  1554. if (fCopyLnk)
  1555. {
  1556. // if it is file system and not a folder (CopyFile does not work on folders)
  1557. // just copy it.
  1558. if (((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM) &&
  1559. CopyFile(szPathSrc, szPathDest, TRUE))
  1560. {
  1561. TouchFile(szPathDest);
  1562. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szPathDest, NULL);
  1563. SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPathDest, NULL);
  1564. hr = S_OK;
  1565. }
  1566. else
  1567. {
  1568. // load the source object that will be "copied" below (with the ::Save call)
  1569. hr = SHGetUIObjectFromFullPIDL(pidlTo, NULL, IID_PPV_ARG(IShellLink, &psl));
  1570. }
  1571. }
  1572. else
  1573. {
  1574. hr = SHCoCreateInstance(NULL, uFlags & SHCL_MAKEFOLDERSHORTCUT ?
  1575. &CLSID_FolderShortcut : &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl));
  1576. if (SUCCEEDED(hr))
  1577. {
  1578. hr = psl->SetIDList(pidlTo);
  1579. // set the working directory to the same path
  1580. // as the file we are linking too
  1581. if (szPathSrc[0] && ((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM))
  1582. {
  1583. PathRemoveFileSpec(szPathSrc);
  1584. psl->SetWorkingDirectory(szPathSrc);
  1585. }
  1586. }
  1587. }
  1588. if (psl)
  1589. {
  1590. if (SUCCEEDED(hr))
  1591. {
  1592. IPersistFile *ppf;
  1593. hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  1594. if (SUCCEEDED(hr))
  1595. {
  1596. USES_CONVERSION;
  1597. hr = ppf->Save(T2CW(szPathDest), TRUE);
  1598. if (SUCCEEDED(hr))
  1599. {
  1600. // in case ::Save translated the name of the
  1601. // file (.LNK -> .PIF, or Folder Shortcut)
  1602. WCHAR *pwsz;
  1603. if (SUCCEEDED(ppf->GetCurFile(&pwsz)) && pwsz)
  1604. {
  1605. SHUnicodeToTChar(pwsz, szPathDest, ARRAYSIZE(szPathDest));
  1606. SHFree(pwsz);
  1607. }
  1608. }
  1609. ppf->Release();
  1610. }
  1611. }
  1612. psl->Release();
  1613. }
  1614. }
  1615. if (ppidl)
  1616. {
  1617. *ppidl = SUCCEEDED(hr) ? SHSimpleIDListFromPath(szPathDest) : NULL;
  1618. }
  1619. return hr;
  1620. }
  1621. // in/out:
  1622. // pszDir inital folder to try, output new folder (desktop)
  1623. // out:
  1624. // ppidl optional output PIDL of thing created
  1625. HRESULT _CreateLinkRetryDesktop(HWND hwnd, LPCITEMIDLIST pidlTo, LPTSTR pszDir, UINT fFlags, LPITEMIDLIST *ppidl)
  1626. {
  1627. HRESULT hr;
  1628. if (ppidl)
  1629. *ppidl = NULL; // assume error
  1630. if (*pszDir && (fFlags & SHCL_CONFIRM))
  1631. {
  1632. hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags);
  1633. }
  1634. else
  1635. {
  1636. hr = E_FAIL;
  1637. }
  1638. // if we were unable to save, ask user if they want us to
  1639. // try it again but change the path to the desktop.
  1640. if (FAILED(hr))
  1641. {
  1642. int id;
  1643. if (hr == STG_E_MEDIUMFULL)
  1644. {
  1645. DebugMsg(TF_ERROR, TEXT("failed to create link because disk is full"));
  1646. id = IDYES;
  1647. }
  1648. else
  1649. {
  1650. if (fFlags & SHCL_CONFIRM)
  1651. {
  1652. id = _PromptTryDesktopLinks(hwnd, pszDir, (fFlags & SHCL_CONFIRM));
  1653. }
  1654. else
  1655. {
  1656. id = (SHGetSpecialFolderPath(hwnd, pszDir, CSIDL_DESKTOPDIRECTORY, FALSE)) ? IDYES : IDNO;
  1657. }
  1658. if (id == IDYES && *pszDir)
  1659. {
  1660. hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags);
  1661. }
  1662. }
  1663. // we failed to create the link complain to the user.
  1664. if (FAILED(hr) && id != IDNO)
  1665. {
  1666. ShellMessageBox(HINST_THISDLL, hwnd,
  1667. MAKEINTRESOURCE(IDS_CANNOTCREATELINK),
  1668. MAKEINTRESOURCE(IDS_LINKTITLE),
  1669. MB_OK | MB_ICONASTERISK);
  1670. }
  1671. }
  1672. #ifdef DEBUG
  1673. if (FAILED(hr) && ppidl)
  1674. ASSERT(*ppidl == NULL);
  1675. #endif
  1676. return hr;
  1677. }
  1678. //
  1679. // This function creates links to the stuff in the IDataObject
  1680. //
  1681. // Arguments:
  1682. // hwnd for any UI
  1683. // pszDir optional target directory (where to create links)
  1684. // pDataObj data object describing files (array of idlist)
  1685. // ppidl optional pointer to an array that receives pidls pointing to the new links
  1686. // or NULL if not interested
  1687. STDAPI SHCreateLinks(HWND hwnd, LPCTSTR pszDir, IDataObject *pDataObj, UINT fFlags, LPITEMIDLIST* ppidl)
  1688. {
  1689. DECLAREWAITCURSOR;
  1690. STGMEDIUM medium;
  1691. HRESULT hr;
  1692. SetWaitCursor();
  1693. LPIDA pida = DataObj_GetHIDA(pDataObj, &medium);
  1694. if (pida)
  1695. {
  1696. TCHAR szTargetDir[MAX_PATH];
  1697. hr = S_OK; // In case hida contains zero elements
  1698. szTargetDir[0] = 0;
  1699. if (pszDir)
  1700. lstrcpyn(szTargetDir, pszDir, ARRAYSIZE(szTargetDir));
  1701. if (!(fFlags & SHCL_USEDESKTOP))
  1702. fFlags |= SHCL_CONFIRM;
  1703. for (UINT i = 0; i < pida->cidl; i++)
  1704. {
  1705. LPITEMIDLIST pidlTo = IDA_ILClone(pida, i);
  1706. if (pidlTo)
  1707. {
  1708. hr = _CreateLinkRetryDesktop(hwnd, pidlTo, szTargetDir, fFlags, ppidl ? &ppidl[i] : NULL);
  1709. ILFree(pidlTo);
  1710. if (FAILED(hr))
  1711. break;
  1712. }
  1713. }
  1714. HIDA_ReleaseStgMedium(pida, &medium);
  1715. }
  1716. else
  1717. hr = E_OUTOFMEMORY;
  1718. SHChangeNotifyHandleEvents();
  1719. ResetWaitCursor();
  1720. return hr;
  1721. }
  1722. #if 1
  1723. HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts)
  1724. {
  1725. VARIANT var;
  1726. HRESULT hr = InitVariantFromIDList(&var, pidl);
  1727. if (SUCCEEDED(hr))
  1728. {
  1729. hr = psfv->SelectItem(&var, dwOpts);
  1730. VariantClear(&var);
  1731. }
  1732. return hr;
  1733. }
  1734. HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv)
  1735. {
  1736. *ppsfv = NULL;
  1737. IWebBrowserApp *pauto;
  1738. HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto);
  1739. if (SUCCEEDED(hr))
  1740. {
  1741. HWND hwnd;
  1742. if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd)))
  1743. {
  1744. // Make sure we make this the active window
  1745. SetForegroundWindow(hwnd);
  1746. ShowWindow(hwnd, SW_SHOWNORMAL);
  1747. }
  1748. IDispatch *pdoc;
  1749. hr = pauto->get_Document(&pdoc);
  1750. if (S_OK == hr) // careful, automation returns S_FALSE
  1751. {
  1752. hr = pdoc->QueryInterface(IID_PPV_ARG(IShellFolderViewDual, ppsfv));
  1753. pdoc->Release();
  1754. }
  1755. else
  1756. hr = E_FAIL;
  1757. pauto->Release();
  1758. }
  1759. return hr;
  1760. }
  1761. // pidlFolder - fully qualified pidl to the folder to open
  1762. // cidl/apidl - array of items in that folder to select
  1763. //
  1764. // if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's
  1765. // folder is opened and it is selected.
  1766. //
  1767. // dwFlags - optional flags, pass 0 for now
  1768. SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags)
  1769. {
  1770. HRESULT hr;
  1771. if (0 == cidl)
  1772. {
  1773. // overload the 0 item case to mean pidlFolder is the full pidl to the item
  1774. LPITEMIDLIST pidlTemp;
  1775. hr = SHILClone(pidlFolder, &pidlTemp);
  1776. if (SUCCEEDED(hr))
  1777. {
  1778. ILRemoveLastID(pidlTemp); // strip to the folder
  1779. LPCITEMIDLIST pidl = ILFindLastID(pidlFolder);
  1780. hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse
  1781. ILFree(pidlTemp);
  1782. }
  1783. }
  1784. else
  1785. {
  1786. IShellFolderViewDual *psfv;
  1787. hr = OpenFolderAndGetView(pidlFolder, &psfv);
  1788. if (SUCCEEDED(hr))
  1789. {
  1790. DWORD dwSelFlags = SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE;
  1791. for (UINT i = 0; i < cidl; i++)
  1792. {
  1793. hr = SelectPidlInSFV(psfv, apidl[i], dwSelFlags);
  1794. dwSelFlags = SVSI_SELECT; // second items append to sel
  1795. }
  1796. psfv->Release();
  1797. }
  1798. }
  1799. return hr;
  1800. }
  1801. #else
  1802. HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, REFIID riid, void **ppv)
  1803. {
  1804. *ppv = NULL;
  1805. IWebBrowserApp *pauto;
  1806. HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto);
  1807. if (SUCCEEDED(hr))
  1808. {
  1809. HWND hwnd;
  1810. if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd)))
  1811. {
  1812. // Make sure we make this the active window
  1813. SetForegroundWindow(hwnd);
  1814. ShowWindow(hwnd, SW_SHOWNORMAL);
  1815. }
  1816. IShellBrowser* psb;
  1817. hr = IUnknown_QueryService(pauto, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  1818. if (SUCCEEDED(hr))
  1819. {
  1820. IShellView* psv;
  1821. hr = psb->QueryActiveShellView(&psv);
  1822. if (SUCCEEDED(hr))
  1823. {
  1824. hr = psv->QueryInterface(riid, ppv);
  1825. psv->Release();
  1826. }
  1827. psb->Release();
  1828. }
  1829. pauto->Release();
  1830. }
  1831. return hr;
  1832. }
  1833. // pidlFolder - fully qualified pidl to the folder to open
  1834. // cidl/apidl - array of items in that folder to select
  1835. //
  1836. // if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's
  1837. // folder is opened and it is selected.
  1838. //
  1839. // dwFlags - optional flags, pass 0 for now
  1840. SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags)
  1841. {
  1842. HRESULT hr;
  1843. if (0 == cidl)
  1844. {
  1845. // overload the 0 item case to mean pidlFolder is the full pidl to the item
  1846. LPITEMIDLIST pidlTemp;
  1847. hr = SHILClone(pidlFolder, &pidlTemp);
  1848. if (SUCCEEDED(hr))
  1849. {
  1850. ILRemoveLastID(pidlTemp); // strip to the folder
  1851. LPCITEMIDLIST pidl = ILFindLastID(pidlFolder);
  1852. hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse
  1853. ILFree(pidlTemp);
  1854. }
  1855. }
  1856. else
  1857. {
  1858. IFolderView *pfv;
  1859. hr = OpenFolderAndGetView(pidlFolder, IID_PPV_ARG(IFolderView, &pfv));
  1860. if (SUCCEEDED(hr))
  1861. {
  1862. pfv->SelectAndPositionItems(1, apidl, NULL, SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
  1863. if (cidl > 1)
  1864. pfv->SelectAndPositionItems(cidl - 1, apidl + 1, NULL, SVSI_SELECT);
  1865. pfv->Release();
  1866. }
  1867. }
  1868. return hr;
  1869. }
  1870. #endif
  1871. SHSTDAPI SHCreateShellItem(LPCITEMIDLIST pidlParent, IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi)
  1872. {
  1873. *ppsi = NULL;
  1874. IShellItem *psi;
  1875. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellItem, NULL, IID_PPV_ARG(IShellItem, &psi));
  1876. if (SUCCEEDED(hr))
  1877. {
  1878. if (pidlParent || psfParent)
  1879. {
  1880. IParentAndItem *pinit;
  1881. ASSERT(pidl);
  1882. hr = psi->QueryInterface(IID_PPV_ARG(IParentAndItem, &pinit));
  1883. if (SUCCEEDED(hr))
  1884. {
  1885. hr = pinit->SetParentAndItem(pidlParent, psfParent, pidl);
  1886. pinit->Release();
  1887. }
  1888. }
  1889. else
  1890. {
  1891. IPersistIDList *pinit;
  1892. hr = psi->QueryInterface(IID_PPV_ARG(IPersistIDList, &pinit));
  1893. if (SUCCEEDED(hr))
  1894. {
  1895. hr = pinit->SetIDList(pidl);
  1896. pinit->Release();
  1897. }
  1898. }
  1899. if (SUCCEEDED(hr))
  1900. *ppsi = psi;
  1901. else
  1902. psi->Release();
  1903. }
  1904. return hr;
  1905. }
  1906. STDAPI SHCreateShellItemFromParent(IShellItem *psiParent, LPCWSTR pszName, IShellItem **ppsi)
  1907. {
  1908. *ppsi = NULL;
  1909. IShellFolder *psf;
  1910. HRESULT hr = psiParent->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder, &psf));
  1911. if (SUCCEEDED(hr))
  1912. {
  1913. LPITEMIDLIST pidl;
  1914. hr = SHGetIDListFromUnk(psiParent, &pidl);
  1915. if (SUCCEEDED(hr))
  1916. {
  1917. ULONG cchEaten;
  1918. LPITEMIDLIST pidlChild;
  1919. hr = psf->ParseDisplayName(NULL, NULL, (LPWSTR)pszName, &cchEaten, &pidlChild, NULL);
  1920. if (SUCCEEDED(hr))
  1921. {
  1922. hr = SHCreateShellItem(pidl, psf, pidlChild, ppsi);
  1923. ILFree(pidlChild);
  1924. }
  1925. ILFree(pidl);
  1926. }
  1927. psf->Release();
  1928. }
  1929. return hr;
  1930. }
  1931. SHSTDAPI SHSetLocalizedName(LPWSTR pszPath, LPCWSTR pszResModule, int idsRes)
  1932. {
  1933. IShellFolder *psfDesktop;
  1934. HRESULT hrInit = SHCoInitialize();
  1935. HRESULT hr = hrInit;
  1936. if (SUCCEEDED(hrInit))
  1937. {
  1938. hr = SHGetDesktopFolder(&psfDesktop);
  1939. if (SUCCEEDED(hr))
  1940. {
  1941. LPITEMIDLIST pidl;
  1942. hr = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL);
  1943. if (SUCCEEDED(hr))
  1944. {
  1945. LPCITEMIDLIST pidlChild;
  1946. IShellFolder *psf;
  1947. hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
  1948. if (SUCCEEDED(hr))
  1949. {
  1950. // WARNING - this is a stack sensitive function - ZekeL 29-Jan-2001
  1951. // since this function is called by winlogon/userenv
  1952. // we need to be sensitive to the stack limitations of those callers
  1953. // the shortname will be no larger than the long name
  1954. DWORD cchShort = lstrlenW(pszResModule) + 1;
  1955. WCHAR *pszShort = (WCHAR *)alloca(CbFromCchW(cchShort));
  1956. DWORD cch = GetShortPathName(pszResModule, pszShort, cchShort);
  1957. if (cch)
  1958. {
  1959. pszResModule = pszShort;
  1960. }
  1961. else
  1962. {
  1963. // GSPN() fails when the module passed in is a relative path
  1964. cch = cchShort;
  1965. }
  1966. cch += 14; // 11 for id + ',' + '@' + null
  1967. WCHAR *pszName = (WCHAR *)alloca(CbFromCchW(cch));
  1968. wnsprintfW(pszName, cch, L"@%s,%d", pszResModule, (idsRes * -1));
  1969. hr = psf->SetNameOf(NULL, pidlChild, pszName, SHGDN_NORMAL, NULL);
  1970. psf->Release();
  1971. }
  1972. SHFree(pidl);
  1973. }
  1974. psfDesktop->Release();
  1975. }
  1976. }
  1977. SHCoUninitialize(hrInit);
  1978. return hr;
  1979. }
  1980. // ShellHookProc was mistakenly exported in the original NT SHELL32.DLL when
  1981. // it didn't need to be (hookproc's, like wndproc's don't need to be exported
  1982. // in the 32-bit world). In order to maintain loadability of a app
  1983. // which might have linked to it, we stub it here. If some app ended up really
  1984. // using it, then we'll look into a specific fix for that app.
  1985. STDAPI_(LONG) ShellHookProc(int code, WPARAM wParam, LPARAM lParam)
  1986. {
  1987. return 0;
  1988. }
  1989. // RegisterShellHook - wrapper around RegisterShellHookWindow()/DeregisterShellHookWindow()
  1990. // the GetTaskmanWindow() stuff is legacy that I don't think is really needed
  1991. HWND g_hwndTaskMan = NULL;
  1992. STDAPI_(BOOL) RegisterShellHook(HWND hwnd, BOOL fInstall)
  1993. {
  1994. BOOL fOk = TRUE;
  1995. switch (fInstall)
  1996. {
  1997. case 0:
  1998. // un-installation of shell hooks
  1999. g_hwndTaskMan = GetTaskmanWindow();
  2000. if (hwnd == g_hwndTaskMan)
  2001. {
  2002. SetTaskmanWindow(NULL);
  2003. }
  2004. DeregisterShellHookWindow(hwnd);
  2005. return TRUE;
  2006. case 3:
  2007. // explorer.exe Tray uses this
  2008. if (g_hwndTaskMan != NULL)
  2009. {
  2010. SetTaskmanWindow(NULL);
  2011. g_hwndTaskMan = NULL;
  2012. }
  2013. fOk = SetTaskmanWindow(hwnd);
  2014. if (fOk)
  2015. {
  2016. g_hwndTaskMan = hwnd;
  2017. }
  2018. RegisterShellHookWindow(hwnd); // install
  2019. break;
  2020. }
  2021. return TRUE;
  2022. }
  2023. EXTERN_C DWORD g_dwThreadBindCtx;
  2024. class CThreadBindCtx : public IBindCtx
  2025. {
  2026. public:
  2027. CThreadBindCtx(IBindCtx *pbc) : _cRef(1) { _pbc = pbc; _pbc->AddRef(); }
  2028. ~CThreadBindCtx();
  2029. // *** IUnknown methods ***
  2030. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  2031. STDMETHODIMP_(ULONG) AddRef(void);
  2032. STDMETHODIMP_(ULONG) Release(void);
  2033. // *** IBindCtx methods ***
  2034. STDMETHODIMP RegisterObjectBound(IUnknown *punk)
  2035. { return _pbc->RegisterObjectBound(punk); }
  2036. STDMETHODIMP RevokeObjectBound(IUnknown *punk)
  2037. { return _pbc->RevokeObjectBound(punk); }
  2038. STDMETHODIMP ReleaseBoundObjects(void)
  2039. { return _pbc->ReleaseBoundObjects(); }
  2040. STDMETHODIMP SetBindOptions(BIND_OPTS *pbindopts)
  2041. { return _pbc->SetBindOptions(pbindopts); }
  2042. STDMETHODIMP GetBindOptions(BIND_OPTS *pbindopts)
  2043. { return _pbc->GetBindOptions(pbindopts); }
  2044. STDMETHODIMP GetRunningObjectTable(IRunningObjectTable **pprot)
  2045. { return _pbc->GetRunningObjectTable(pprot); }
  2046. STDMETHODIMP RegisterObjectParam(LPOLESTR pszKey, IUnknown *punk)
  2047. { return _pbc->RegisterObjectParam(pszKey, punk); }
  2048. STDMETHODIMP GetObjectParam(LPOLESTR pszKey, IUnknown **ppunk)
  2049. { return _pbc->GetObjectParam(pszKey, ppunk); }
  2050. STDMETHODIMP EnumObjectParam(IEnumString **ppenum)
  2051. { return _pbc->EnumObjectParam(ppenum); }
  2052. STDMETHODIMP RevokeObjectParam(LPOLESTR pszKey)
  2053. { return _pbc->RevokeObjectParam(pszKey); }
  2054. private:
  2055. LONG _cRef;
  2056. IBindCtx * _pbc;
  2057. };
  2058. CThreadBindCtx::~CThreadBindCtx()
  2059. {
  2060. ATOMICRELEASE(_pbc);
  2061. }
  2062. HRESULT CThreadBindCtx::QueryInterface(REFIID riid, void **ppvObj)
  2063. {
  2064. static const QITAB qit[] = {
  2065. QITABENT(CThreadBindCtx, IBindCtx),
  2066. { 0 },
  2067. };
  2068. return QISearch(this, qit, riid, ppvObj);
  2069. }
  2070. ULONG CThreadBindCtx::AddRef()
  2071. {
  2072. return InterlockedIncrement(&_cRef);
  2073. }
  2074. ULONG CThreadBindCtx::Release()
  2075. {
  2076. if (InterlockedDecrement(&_cRef))
  2077. return _cRef;
  2078. delete this;
  2079. // clear ourselves out
  2080. TlsSetValue(g_dwThreadBindCtx, NULL);
  2081. return 0;
  2082. }
  2083. STDAPI TBCGetBindCtx(BOOL fCreate, IBindCtx **ppbc)
  2084. {
  2085. HRESULT hr = E_UNEXPECTED;
  2086. *ppbc = NULL;
  2087. if ((DWORD) -1 != g_dwThreadBindCtx)
  2088. {
  2089. CThreadBindCtx *ptbc = (CThreadBindCtx *)TlsGetValue(g_dwThreadBindCtx);
  2090. if (ptbc)
  2091. {
  2092. ptbc->AddRef();
  2093. *ppbc = SAFECAST(ptbc, IBindCtx *);
  2094. hr = S_OK;
  2095. }
  2096. else if (fCreate)
  2097. {
  2098. IBindCtx *pbcInner;
  2099. hr = CreateBindCtx(0, &pbcInner);
  2100. if (SUCCEEDED(hr))
  2101. {
  2102. hr = E_OUTOFMEMORY;
  2103. ptbc = new CThreadBindCtx(pbcInner);
  2104. if (ptbc)
  2105. {
  2106. if (TlsSetValue(g_dwThreadBindCtx, ptbc))
  2107. {
  2108. *ppbc = SAFECAST(ptbc, IBindCtx *);
  2109. hr = S_OK;
  2110. }
  2111. else
  2112. delete ptbc;
  2113. }
  2114. pbcInner->Release();
  2115. }
  2116. }
  2117. }
  2118. return hr;
  2119. }
  2120. STDAPI TBCRegisterObjectParam(LPCOLESTR pszKey, IUnknown *punk, IBindCtx **ppbcLifetime)
  2121. {
  2122. IBindCtx *pbc;
  2123. HRESULT hr = TBCGetBindCtx(TRUE, &pbc);
  2124. if (SUCCEEDED(hr))
  2125. {
  2126. hr = BindCtx_RegisterObjectParam(pbc, pszKey, punk, ppbcLifetime);
  2127. pbc->Release();
  2128. }
  2129. else
  2130. *ppbcLifetime = 0;
  2131. return hr;
  2132. }
  2133. STDAPI TBCGetObjectParam(LPCOLESTR pszKey, REFIID riid, void **ppv)
  2134. {
  2135. IBindCtx *pbc;
  2136. HRESULT hr = TBCGetBindCtx(FALSE, &pbc);
  2137. if (SUCCEEDED(hr))
  2138. {
  2139. IUnknown *punk;
  2140. hr = pbc->GetObjectParam((LPOLESTR)pszKey, &punk);
  2141. if (SUCCEEDED(hr) )
  2142. {
  2143. if (ppv)
  2144. hr = punk->QueryInterface(riid, ppv);
  2145. punk->Release();
  2146. }
  2147. pbc->Release();
  2148. }
  2149. return hr;
  2150. }
  2151. #define TBCENVOBJECT L"ThreadEnvironmentVariables"
  2152. STDAPI TBCGetEnvironmentVariable(LPCWSTR pszVar, LPWSTR pszValue, DWORD cchValue)
  2153. {
  2154. IPropertyBag *pbag;
  2155. HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag));
  2156. if (SUCCEEDED(hr))
  2157. {
  2158. hr = SHPropertyBag_ReadStr(pbag, pszVar, pszValue, cchValue);
  2159. pbag->Release();
  2160. }
  2161. return hr;
  2162. }
  2163. STDAPI TBCSetEnvironmentVariable(LPCWSTR pszVar, LPCWSTR pszValue, IBindCtx **ppbcLifetime)
  2164. {
  2165. *ppbcLifetime = 0;
  2166. IPropertyBag *pbag;
  2167. HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag));
  2168. if (FAILED(hr))
  2169. hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &pbag));
  2170. if (SUCCEEDED(hr))
  2171. {
  2172. hr = SHPropertyBag_WriteStr(pbag, pszVar, pszValue);
  2173. if (SUCCEEDED(hr))
  2174. hr = TBCRegisterObjectParam(TBCENVOBJECT, pbag, ppbcLifetime);
  2175. pbag->Release();
  2176. }
  2177. return hr;
  2178. }
  2179. // create a stock IExtractIcon handler for a thing that is file like. this is typically
  2180. // used by name space extensiosn that display things that are like files in the
  2181. // file system. that is the extension, file attributes decrive all that is needed
  2182. // for a simple icon extractor
  2183. STDAPI SHCreateFileExtractIconW(LPCWSTR pszFile, DWORD dwFileAttributes, REFIID riid, void **ppv)
  2184. {
  2185. *ppv = NULL;
  2186. HRESULT hr = E_FAIL;
  2187. SHFILEINFO sfi = {0};
  2188. if (SHGetFileInfo(pszFile, dwFileAttributes, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES))
  2189. {
  2190. hr = SHCreateDefExtIcon(TEXT("*"), sfi.iIcon, sfi.iIcon, GIL_PERCLASS | GIL_NOTFILENAME, -1, riid, ppv);
  2191. DestroyIcon(sfi.hIcon);
  2192. }
  2193. return hr;
  2194. }