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.

7524 lines
225 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "ole2dup.h"
  4. #include "copy.h"
  5. #include <regstr.h>
  6. #include <comcat.h>
  7. #include <intshcut.h>
  8. #include "_security.h"
  9. #include "ovrlaymn.h"
  10. #include "filefldr.h"
  11. #include "drives.h"
  12. #include "netview.h"
  13. #include <filetype.h>
  14. #include "shitemid.h"
  15. #include "infotip.h"
  16. #include "recdocs.h"
  17. #include <idhidden.h>
  18. #include "datautil.h"
  19. #include "deskfldr.h"
  20. #include "prop.h" // COLUMN_INFO
  21. #include <oledb.h> // IFilter stuff
  22. #include <query.h>
  23. #include <ntquery.h>
  24. #include <filterr.h>
  25. #include <ciintf.h>
  26. #include "folder.h"
  27. #include "ids.h"
  28. #include "category.h"
  29. #include "stgenum.h"
  30. #include "clsobj.h"
  31. #include "stgutil.h"
  32. #include "sfstorage.h"
  33. #include "mtpt.h"
  34. #include "defcm.h"
  35. STDAPI CFolderInfoTip_CreateInstance(IUnknown *punkOutter, LPCTSTR pszFolder, REFIID riid, void **ppv);
  36. #define SHCF_IS_BROWSABLE (SHCF_IS_SHELLEXT | SHCF_IS_DOCOBJECT)
  37. #define CSIDL_NORMAL ((UINT)-2) // has to not be -1
  38. // File-scope pointer to a ShellIconOverlayManager
  39. // Callers access this pointer through GetIconOverlayManager().
  40. static IShellIconOverlayManager * g_psiom = NULL;
  41. // #define FULL_DEBUG
  42. TCHAR const c_szCLSIDSlash[] = TEXT("CLSID\\");
  43. TCHAR const c_szShellOpenCmd[] = TEXT("shell\\open\\command");
  44. TCHAR g_szFolderTypeName[32] = TEXT(""); // "Folder"
  45. TCHAR g_szFileTypeName[32] = TEXT(""); // "File"
  46. TCHAR g_szFileTemplate[32] = TEXT(""); // "ext File"
  47. enum
  48. {
  49. FS_ICOL_NAME = 0,
  50. FS_ICOL_SIZE,
  51. FS_ICOL_TYPE,
  52. FS_ICOL_WRITETIME,
  53. FS_ICOL_CREATETIME,
  54. FS_ICOL_ACCESSTIME,
  55. FS_ICOL_ATTRIB,
  56. FS_ICOL_CSC_STATUS,
  57. };
  58. const COLUMN_INFO c_fs_cols[] =
  59. {
  60. DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL),
  61. DEFINE_COL_SIZE_ENTRY(SCID_SIZE, IDS_SIZE_COL),
  62. DEFINE_COL_STR_ENTRY(SCID_TYPE, 20, IDS_TYPE_COL),
  63. DEFINE_COL_DATE_ENTRY(SCID_WRITETIME, IDS_MODIFIED_COL),
  64. // these are off by default (don't have SHCOLSTATE_ONBYDEFAULT) set
  65. DEFINE_COL_ENTRY(SCID_CREATETIME, VT_DATE, LVCFMT_LEFT, 20, SHCOLSTATE_TYPE_DATE, IDS_EXCOL_CREATE),
  66. DEFINE_COL_ENTRY(SCID_ACCESSTIME, VT_DATE, LVCFMT_LEFT, 20, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_SECONDARYUI, IDS_EXCOL_ACCESSTIME),
  67. DEFINE_COL_ENTRY(SCID_ATTRIBUTES, VT_LPWSTR, LVCFMT_LEFT, 10, SHCOLSTATE_TYPE_STR, IDS_ATTRIB_COL),
  68. DEFINE_COL_STR_DLG_ENTRY(SCID_CSC_STATUS, 10, IDS_CSC_STATUS),
  69. };
  70. //
  71. // List of file attribute bit values. The order (with respect
  72. // to meaning) must match that of the characters in g_szAttributeChars[].
  73. //
  74. const DWORD g_adwAttributeBits[] =
  75. {
  76. FILE_ATTRIBUTE_READONLY,
  77. FILE_ATTRIBUTE_HIDDEN,
  78. FILE_ATTRIBUTE_SYSTEM,
  79. FILE_ATTRIBUTE_ARCHIVE,
  80. FILE_ATTRIBUTE_COMPRESSED,
  81. FILE_ATTRIBUTE_ENCRYPTED,
  82. FILE_ATTRIBUTE_OFFLINE
  83. };
  84. //
  85. // Buffer for characters that represent attributes in Details View attributes
  86. // column. Must provide room for 1 character for each bit a NUL. The current 5
  87. // represent Read-only, Archive, Compressed, Hidden and System in that order.
  88. // This can't be const because we overwrite it using LoadString.
  89. //
  90. TCHAR g_szAttributeChars[ARRAYSIZE(g_adwAttributeBits) + 1] = { 0 } ;
  91. // order here is important, first one found will terminate the search
  92. const int c_csidlSpecial[] = {
  93. CSIDL_STARTMENU | TEST_SUBFOLDER,
  94. CSIDL_COMMON_STARTMENU | TEST_SUBFOLDER,
  95. CSIDL_RECENT,
  96. CSIDL_WINDOWS,
  97. CSIDL_SYSTEM,
  98. CSIDL_PERSONAL,
  99. CSIDL_FONTS
  100. };
  101. BOOL CFSFolder::_IsCSIDL(UINT csidl)
  102. {
  103. BOOL bRet = (_csidl == csidl);
  104. if (!bRet)
  105. {
  106. TCHAR szPath[MAX_PATH];
  107. _GetPath(szPath);
  108. bRet = PathIsEqualOrSubFolder(MAKEINTRESOURCE(csidl), szPath);
  109. if (bRet)
  110. _csidl = csidl;
  111. }
  112. return bRet;
  113. }
  114. UINT CFSFolder::_GetCSIDL()
  115. {
  116. // Cache the special folder ID, if it is not cached yet.
  117. if (_csidl == -1)
  118. {
  119. TCHAR szPath[MAX_PATH];
  120. _GetPath(szPath);
  121. // Always cache the real Csidl.
  122. _csidl = GetSpecialFolderID(szPath, c_csidlSpecial, ARRAYSIZE(c_csidlSpecial));
  123. if (_csidl == -1)
  124. {
  125. _csidl = CSIDL_NORMAL; // default
  126. }
  127. }
  128. return _csidl;
  129. }
  130. STDAPI_(LPCIDFOLDER) CFSFolder::_IsValidID(LPCITEMIDLIST pidl)
  131. {
  132. if (pidl && pidl->mkid.cb && (((LPCIDFOLDER)pidl)->bFlags & SHID_GROUPMASK) == SHID_FS)
  133. return (LPCIDFOLDER)pidl;
  134. return NULL;
  135. }
  136. // folder.{guid} or file.{guid}
  137. // system | readonly folder with desktop.ini and CLSID={guid} in the desktop.ini
  138. // file.ext where ext corresponds to a shell extension (such as .cab/.zip)
  139. // see _MarkAsJunction
  140. inline BOOL CFSFolder::_IsJunction(LPCIDFOLDER pidf)
  141. {
  142. return pidf->bFlags & SHID_JUNCTION;
  143. }
  144. inline BYTE CFSFolder::_GetType(LPCIDFOLDER pidf)
  145. {
  146. return pidf->bFlags & SHID_FS_TYPEMASK;
  147. }
  148. // this tests for old simple pidls that use SHID_FS
  149. // typically this only happens with persisted pidls in upgrade scenarios (shortcuts in the start menu)
  150. inline BOOL CFSFolder::_IsSimpleID(LPCIDFOLDER pidf)
  151. {
  152. return _GetType(pidf) == SHID_FS;
  153. }
  154. inline LPIDFOLDER CFSFolder::_FindLastID(LPCIDFOLDER pidf)
  155. {
  156. return (LPIDFOLDER)ILFindLastID((LPITEMIDLIST)pidf);
  157. }
  158. inline LPIDFOLDER CFSFolder::_Next(LPCIDFOLDER pidf)
  159. {
  160. return (LPIDFOLDER)_ILNext((LPITEMIDLIST)pidf);
  161. }
  162. // special marking for "All Users" items on the desktop (this is a hack to support the desktop
  163. // folder delegating to the approprate shell folder and is not generally useful)
  164. BOOL CFSFolder::_IsCommonItem(LPCITEMIDLIST pidl)
  165. {
  166. if (pidl && pidl->mkid.cb && (((LPCIDFOLDER)pidl)->bFlags & (SHID_GROUPMASK | SHID_FS_COMMONITEM)) == SHID_FS_COMMONITEM)
  167. return TRUE;
  168. return FALSE;
  169. }
  170. // a win32 file (might be a shell extension .cab/.zip that behaves like a folder)
  171. BOOL CFSFolder::_IsFile(LPCIDFOLDER pidf)
  172. {
  173. BOOL bRet = _GetType(pidf) == SHID_FS_FILE || _GetType(pidf) == SHID_FS_FILEUNICODE;
  174. // if it's a file, it shouldn't be a folder.
  175. // if it's not a file, usually it's a folder -- except if the type is SHID_FS,
  176. // that's okay too because it's a simple pidl in a .lnk from a downlevel shell.
  177. ASSERT(bRet ? !_IsFolder(pidf) : (_IsFolder(pidf) || _IsSimpleID(pidf)));
  178. return bRet;
  179. }
  180. // it is a win32 file system folder (maybe a junction, maybe not)
  181. BOOL CFSFolder::_IsFolder(LPCIDFOLDER pidf)
  182. {
  183. BOOL bRet = _GetType(pidf) == SHID_FS_DIRECTORY || _GetType(pidf) == SHID_FS_DIRUNICODE;
  184. ASSERT(bRet ? (pidf->wAttrs & FILE_ATTRIBUTE_DIRECTORY) : !(pidf->wAttrs & FILE_ATTRIBUTE_DIRECTORY));
  185. return bRet;
  186. }
  187. // is it a file system folder that is not a junction
  188. BOOL CFSFolder::_IsFileFolder(LPCIDFOLDER pidf)
  189. {
  190. return _IsFolder(pidf) && !_IsJunction(pidf);
  191. }
  192. // non junction, but has the system or readonly bit (regular folder marked special for us)
  193. BOOL CFSFolder::_IsSystemFolder(LPCIDFOLDER pidf)
  194. {
  195. return _IsFileFolder(pidf) && (pidf->wAttrs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY));
  196. }
  197. // this is a heuristic to determine if the IDList was created
  198. // normally or with a simple bind context (null size/mod date)
  199. BOOL CFSFolder::_IsReal(LPCIDFOLDER pidf)
  200. {
  201. return pidf->dwSize || pidf->dateModified ? TRUE : FALSE;
  202. }
  203. DWORD CFSFolder::_GetUID(LPCIDFOLDER pidf)
  204. {
  205. return pidf->dwSize + ((DWORD)pidf->dateModified << 8) + ((DWORD)pidf->timeModified << 12);
  206. }
  207. void CFSFolder::_GetSize(LPCITEMIDLIST pidlParent, LPCIDFOLDER pidf, ULONGLONG *pcbSize)
  208. {
  209. ULONGLONG cbSize = pidf->dwSize;
  210. if (cbSize != 0xFFFFFFFF)
  211. *pcbSize = cbSize;
  212. else if (pidlParent == NULL)
  213. *pcbSize = 0;
  214. else
  215. {
  216. HANDLE hfind;
  217. WIN32_FIND_DATA wfd = {0};
  218. TCHAR szPath[MAX_PATH];
  219. // Get the real size by asking the file system
  220. SHGetPathFromIDList(pidlParent, szPath);
  221. _AppendItemToPath(szPath, pidf);
  222. if (SHFindFirstFileRetry(NULL, NULL, szPath, &wfd, &hfind, SHPPFW_NONE) != S_OK)
  223. {
  224. *pcbSize = 0;
  225. }
  226. else
  227. {
  228. FindClose(hfind);
  229. ULARGE_INTEGER uli;
  230. uli.LowPart = wfd.nFileSizeLow;
  231. uli.HighPart = wfd.nFileSizeHigh;
  232. *pcbSize = uli.QuadPart;
  233. }
  234. }
  235. }
  236. ULONGLONG CFSFolder::_Size(LPCIDFOLDER pidf)
  237. {
  238. ULONGLONG cbSize;
  239. _GetSize(_pidl, pidf, &cbSize);
  240. return cbSize;
  241. }
  242. LPWSTR CFSFolder::_CopyName(LPCIDFOLDER pidf, LPWSTR pszName, UINT cchName)
  243. {
  244. CFileSysItem fsi(pidf);
  245. return (LPWSTR) fsi.MayCopyFSName(TRUE, pszName, cchName);
  246. }
  247. BOOL CFSFolder::_ShowExtension(LPCIDFOLDER pidf)
  248. {
  249. CFileSysItemString fsi(pidf);
  250. return fsi.ShowExtension(_DefaultShowExt());
  251. }
  252. BOOL CFSFolder::_DefaultShowExt()
  253. {
  254. if (_tbDefShowExt == TRIBIT_UNDEFINED)
  255. {
  256. SHELLSTATE ss;
  257. SHGetSetSettings(&ss, SSF_SHOWEXTENSIONS, FALSE);
  258. _tbDefShowExt = ss.fShowExtensions ? TRIBIT_TRUE : TRIBIT_FALSE;
  259. }
  260. return _tbDefShowExt == TRIBIT_TRUE;
  261. }
  262. BOOL CFileSysItemString::ShowExtension(BOOL fDefaultShowExt)
  263. {
  264. DWORD dwFlags = ClassFlags(FALSE);
  265. if (dwFlags & SHCF_NEVER_SHOW_EXT)
  266. return FALSE;
  267. if (fDefaultShowExt)
  268. return TRUE;
  269. return dwFlags & (SHCF_ALWAYS_SHOW_EXT | SHCF_UNKNOWN);
  270. }
  271. //
  272. // get the type name from the registry, if the name is blank make
  273. // up a default.
  274. //
  275. // directory ==> "Folder"
  276. // foo ==> "File"
  277. // foo.xyz ==> "XYZ File"
  278. //
  279. void SHGetTypeName(LPCTSTR pszFile, HKEY hkey, BOOL fFolder, LPTSTR pszName, int cchNameMax)
  280. {
  281. LONG cb = cchNameMax * sizeof(TCHAR);
  282. if (hkey == NULL || SHRegQueryValue(hkey, NULL, pszName, &cb) != ERROR_SUCCESS || pszName[0] == 0)
  283. {
  284. if (fFolder)
  285. {
  286. // NOTE the registry doesn't have a name for Folder
  287. // because old apps would think it was a file type.
  288. lstrcpy(pszName, g_szFolderTypeName);
  289. }
  290. else
  291. {
  292. LPTSTR pszExt = PathFindExtension(pszFile);
  293. ASSERT(pszExt);
  294. if (*pszExt == 0)
  295. {
  296. // Probably don't need the cchmax here, but...
  297. lstrcpyn(pszName, g_szFileTypeName, cchNameMax);
  298. }
  299. else
  300. {
  301. TCHAR szExt[_MAX_EXT];
  302. int cchMaxExtCopy = (int)min((cchNameMax - lstrlen(g_szFileTemplate)), ARRAYSIZE(szExt));
  303. // Compose '<ext> File' (or what ever the template defines we do)
  304. lstrcpyn(szExt, pszExt + 1, cchMaxExtCopy);
  305. CharUpperNoDBCS(szExt);
  306. wsprintf(pszName, g_szFileTemplate, szExt);
  307. }
  308. }
  309. }
  310. }
  311. //
  312. // return a pointer to the type name for the given PIDL
  313. // the pointer is only valid while in a critical section
  314. //
  315. LPCTSTR CFSFolder::_GetTypeName(LPCIDFOLDER pidf)
  316. {
  317. CFileSysItemString fsi(pidf);
  318. ASSERTCRITICAL
  319. LPCTSTR pszClassName = LookupFileClassName(fsi.Class());
  320. if (pszClassName == NULL)
  321. {
  322. WCHAR sz[80];
  323. IQueryAssociations *pqa;
  324. HRESULT hr = fsi.AssocCreate(NULL, FALSE, IID_PPV_ARG(IQueryAssociations, &pqa));
  325. if (SUCCEEDED(hr))
  326. {
  327. DWORD cch = ARRAYSIZE(sz);
  328. hr = pqa->GetString(0, ASSOCSTR_FRIENDLYDOCNAME, NULL, sz, &cch);
  329. if (SUCCEEDED(hr))
  330. {
  331. pszClassName = AddFileClassName(fsi.Class(), sz);
  332. }
  333. pqa->Release();
  334. }
  335. }
  336. return pszClassName;
  337. }
  338. //
  339. // return the type name for the given PIDL
  340. //
  341. HRESULT CFSFolder::_GetTypeNameBuf(LPCIDFOLDER pidf, LPTSTR pszName, int cchNameMax)
  342. {
  343. HRESULT hr = S_OK;
  344. ENTERCRITICAL;
  345. LPCTSTR pszSource = _GetTypeName(pidf);
  346. // pszSource will be NULL if the file does not have an extension.
  347. if (!pszSource)
  348. {
  349. pszSource = TEXT(""); // Terminate Buffer
  350. hr = E_FAIL;
  351. }
  352. StrCpyN(pszName, pszSource, cchNameMax);
  353. LEAVECRITICAL;
  354. return hr;
  355. }
  356. //
  357. // Build a text string containing characters that represent attributes of a file.
  358. // The attribute characters are assigned as follows:
  359. // (R)eadonly, (H)idden, (S)ystem, (A)rchive, (H)idden.
  360. //
  361. void BuildAttributeString(DWORD dwAttributes, LPTSTR pszString, UINT nChars)
  362. {
  363. // Make sure we have attribute chars to build this string out of
  364. if (!g_szAttributeChars[0])
  365. LoadString(HINST_THISDLL, IDS_ATTRIB_CHARS, g_szAttributeChars, ARRAYSIZE(g_szAttributeChars));
  366. // Make sure buffer is big enough to hold worst-case attributes
  367. ASSERT(nChars >= ARRAYSIZE(g_adwAttributeBits) + 1);
  368. for (int i = 0; i < ARRAYSIZE(g_adwAttributeBits); i++)
  369. {
  370. if (dwAttributes & g_adwAttributeBits[i])
  371. *pszString++ = g_szAttributeChars[i];
  372. }
  373. *pszString = 0; // null terminate
  374. }
  375. // BryanSt: This doesn't work with FRAGMENTs. We should return the path
  376. // without the Fragment for backward compatibility and then callers that care,
  377. // can later determine that and take care of it.
  378. //
  379. // in/out:
  380. // pszPath path to append pidf names to
  381. // in:
  382. // pidf relative pidl fragment
  383. HRESULT CFSFolder::_AppendItemToPath(LPTSTR pszPath, LPCIDFOLDER pidf)
  384. {
  385. HRESULT hr = S_OK;
  386. LPTSTR pszPathCur = pszPath + lstrlen(pszPath);
  387. // e want to do this, but we stil have broken code in SHGetPathFromIDList
  388. // ASSERT(_FindJunctionNext(pidf) == NULL); // no extra goo please
  389. for (; SUCCEEDED(hr) && !ILIsEmpty((LPITEMIDLIST)pidf); pidf = _Next(pidf))
  390. {
  391. CFileSysItemString fsi(pidf);
  392. int cchName = lstrlen(fsi.FSName()); // store the length of szName, to avoid calculating it twice
  393. // mil 142338: handle bogus pidls that have multiple "C:"s in them
  394. // due to bad shortcut creation.
  395. if ((cchName == 2) && (fsi.FSName()[1] == TEXT(':')))
  396. {
  397. pszPathCur = pszPath;
  398. }
  399. else
  400. {
  401. // ASSERT(lstrlen(pszPath)+lstrlen(szName)+2 <= MAX_PATH);
  402. if (((pszPathCur - pszPath) + cchName + 2) > MAX_PATH)
  403. {
  404. hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); // FormatMessage = "The file name is too long"
  405. break;
  406. }
  407. LPTSTR pszTmp = CharPrev(pszPath, pszPathCur);
  408. if (*pszTmp != TEXT('\\'))
  409. *(pszPathCur++) = TEXT('\\');
  410. }
  411. // don't need lstrncpy cause we verified size above
  412. lstrcpy(pszPathCur, fsi.FSName());
  413. pszPathCur += cchName;
  414. }
  415. if (FAILED(hr))
  416. *pszPath = 0;
  417. return hr;
  418. }
  419. // get the file system folder path for this
  420. //
  421. // HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) is returned if we are a tracking
  422. // folder that does not (yet) have a valid target.
  423. HRESULT CFSFolder::_GetPath(LPTSTR pszPath)
  424. {
  425. HRESULT hr = E_FAIL;
  426. if (_csidlTrack >= 0)
  427. {
  428. hr = SHGetFolderPath(NULL, _csidlTrack | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, pszPath);
  429. if (hr == S_FALSE || FAILED(hr))
  430. hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  431. }
  432. else if (_pszPath)
  433. {
  434. lstrcpyn(pszPath, _pszPath, MAX_PATH);
  435. hr = S_OK;
  436. }
  437. else
  438. {
  439. if (_pidlTarget &&
  440. SUCCEEDED(SHGetNameAndFlags(_pidlTarget, SHGDN_FORPARSING, pszPath, MAX_PATH, NULL)))
  441. {
  442. _pszPath = StrDup(pszPath);
  443. hr = S_OK;
  444. }
  445. else if (SUCCEEDED(SHGetNameAndFlags(_pidl, SHGDN_FORPARSING, pszPath, MAX_PATH, NULL)))
  446. {
  447. _pszPath = StrDup(pszPath);
  448. hr = S_OK;
  449. }
  450. }
  451. if (hr==S_OK && !(*pszPath))
  452. hr= E_FAIL; // old behavior was to fail if pszPath was empty
  453. return hr;
  454. }
  455. // Will fail (return FALSE) if not a mount point
  456. BOOL CFSFolder::_GetMountingPointInfo(LPCIDFOLDER pidf, LPTSTR pszMountPoint, DWORD cchMountPoint)
  457. {
  458. BOOL bRet = FALSE;
  459. // Is this a reparse point?
  460. if (FILE_ATTRIBUTE_REPARSE_POINT & pidf->wAttrs)
  461. {
  462. TCHAR szLocalMountPoint[MAX_PATH];
  463. if (SUCCEEDED(_GetPathForItem(pidf, szLocalMountPoint)))
  464. {
  465. int iDrive = PathGetDriveNumber(szLocalMountPoint);
  466. if (-1 != iDrive)
  467. {
  468. TCHAR szDrive[4];
  469. if (DRIVE_REMOTE != GetDriveType(PathBuildRoot(szDrive, iDrive)))
  470. {
  471. TCHAR szVolumeName[50]; //50 according to doc
  472. PathAddBackslash(szLocalMountPoint);
  473. // Check if it is a mounting point
  474. if (GetVolumeNameForVolumeMountPoint(szLocalMountPoint, szVolumeName,
  475. ARRAYSIZE(szVolumeName)))
  476. {
  477. bRet = TRUE;
  478. if (pszMountPoint && cchMountPoint)
  479. lstrcpyn(pszMountPoint, szLocalMountPoint, cchMountPoint);
  480. }
  481. }
  482. }
  483. }
  484. }
  485. return bRet;
  486. }
  487. // in:
  488. // pidf may be NULL, or multi level item to append to path for this folder
  489. // out:
  490. // pszPath MAX_PATH buffer to receive the fully qualified file path for the item
  491. //
  492. HRESULT CFSFolder::_GetPathForItem(LPCIDFOLDER pidf, LPWSTR pszPath)
  493. {
  494. if (SUCCEEDED(_GetPath(pszPath)))
  495. {
  496. if (pidf)
  497. {
  498. return _AppendItemToPath(pszPath, pidf);
  499. }
  500. return S_OK;
  501. }
  502. return E_FAIL;
  503. }
  504. HRESULT CFSFolder::_GetPathForItems(LPCIDFOLDER pidfParent, LPCIDFOLDER pidfLast, LPTSTR pszPath)
  505. {
  506. HRESULT hr = _GetPathForItem(pidfParent ? pidfParent : pidfLast, pszPath);
  507. if (SUCCEEDED(hr) && pidfParent)
  508. hr = _AppendItemToPath(pszPath, pidfLast);
  509. return hr;
  510. }
  511. BOOL _GetIniPath(BOOL fCreate, LPCTSTR pszFolder, LPCTSTR pszProvider, LPTSTR pszPath)
  512. {
  513. BOOL fExists = FALSE;
  514. PathCombine(pszPath, pszFolder, c_szDesktopIni);
  515. // CHECK for PathFileExists BEFORE calling to GetPrivateProfileString
  516. // because if the file isn't there (which is the majority of cases)
  517. // GetPrivateProfileString hits the disk twice looking for the file
  518. if (pszProvider && *pszProvider)
  519. {
  520. union {
  521. NETRESOURCE nr;
  522. TCHAR buf[512];
  523. } nrb;
  524. LPTSTR lpSystem;
  525. DWORD dwRes, dwSize = sizeof(nrb);
  526. nrb.nr.dwType = RESOURCETYPE_ANY;
  527. nrb.nr.lpRemoteName = pszPath;
  528. nrb.nr.lpProvider = (LPTSTR)pszProvider; // const -> non const
  529. dwRes = WNetGetResourceInformation(&nrb.nr, &nrb, &dwSize, &lpSystem);
  530. fExists = (dwRes == WN_SUCCESS) || (dwRes == WN_MORE_DATA);
  531. }
  532. else
  533. {
  534. fExists = PathFileExists(pszPath);
  535. }
  536. if (fCreate && !fExists)
  537. {
  538. // we need to touch this file first
  539. HANDLE h = CreateFile(pszPath, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, NULL);
  540. if (INVALID_HANDLE_VALUE != h)
  541. {
  542. PathMakeSystemFolder(pszFolder);
  543. fExists = TRUE;
  544. CloseHandle(h);
  545. }
  546. }
  547. return fExists;
  548. }
  549. STDAPI_(BOOL) SetFolderString(BOOL fCreate, LPCTSTR pszFolder, LPCTSTR pszProvider, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszData)
  550. {
  551. TCHAR szPath[MAX_PATH];
  552. if (_GetIniPath(fCreate, pszFolder, pszProvider, szPath))
  553. {
  554. return SHSetIniStringUTF7(pszSection, pszKey, pszData, szPath);
  555. }
  556. return FALSE;
  557. }
  558. //
  559. // This function retrieves the private profile strings from the desktop.ini file and
  560. // return it through pszOut
  561. //
  562. // This function uses SHGetIniStringUTF7 to get the string, so it is valid
  563. // to use SZ_CANBEUNICODE on the key name.
  564. BOOL GetFolderStringEx(LPCTSTR pszFolder, LPCTSTR pszProvider, LPCTSTR pszSection, LPCTSTR pszKey, LPTSTR pszOut, int cch)
  565. {
  566. BOOL fRet = FALSE;
  567. TCHAR szPath[MAX_PATH];
  568. if (_GetIniPath(FALSE, pszFolder, pszProvider, szPath))
  569. {
  570. TCHAR szTemp[INFOTIPSIZE];
  571. fRet = SHGetIniStringUTF7(pszSection, pszKey, szTemp, ARRAYSIZE(szTemp), szPath);
  572. if (fRet)
  573. {
  574. SHExpandEnvironmentStrings(szTemp, pszOut, cch); // This could be a path, so expand the env vars in it
  575. }
  576. }
  577. return fRet;
  578. }
  579. int GetFolderInt(LPCTSTR pszFolder, LPCTSTR pszProvider, LPCTSTR pszSection, LPCTSTR pszKey, int iDefault)
  580. {
  581. BOOL fRet = FALSE;
  582. TCHAR szPath[MAX_PATH];
  583. if (_GetIniPath(FALSE, pszFolder, pszProvider, szPath))
  584. {
  585. return GetPrivateProfileInt(pszSection, pszKey, iDefault, szPath);
  586. }
  587. return iDefault;
  588. }
  589. STDAPI_(BOOL) GetFolderString(LPCTSTR pszFolder, LPCTSTR pszProvider, LPTSTR pszOut, int cch, LPCTSTR pszKey)
  590. {
  591. return GetFolderStringEx(pszFolder, pszProvider, STRINI_CLASSINFO, pszKey, pszOut, cch);
  592. }
  593. // This function retrieves the specifice GUID from desktop.ini file.
  594. // replace this with property bag access on the folder
  595. STDAPI_(BOOL) GetFolderGUID(LPCTSTR pszFolder, LPCTSTR pszProvider, CLSID* pclsid, LPCTSTR pszKey)
  596. {
  597. TCHAR szCLSID[40];
  598. if (GetFolderString(pszFolder, pszProvider, szCLSID, ARRAYSIZE(szCLSID), pszKey))
  599. {
  600. return SUCCEEDED(SHCLSIDFromString(szCLSID, pclsid));
  601. }
  602. return FALSE;
  603. }
  604. //
  605. // This function retrieves the correct CLSID from desktop.ini file.
  606. //
  607. BOOL _GetFolderCLSID(LPCTSTR pszFolder, LPCTSTR pszProvider, CLSID* pclsid)
  608. {
  609. BOOL bFound = FALSE;
  610. WCHAR szPath[MAX_PATH];
  611. if (_GetIniPath(FALSE, pszFolder, pszProvider, szPath))
  612. {
  613. DWORD dwChars;
  614. WCHAR szSectionValue[1024];
  615. dwChars = GetPrivateProfileSection(STRINI_CLASSINFO, szSectionValue, sizeof(szSectionValue), szPath);
  616. if (dwChars != (sizeof(szSectionValue) - 2) && (dwChars != 0))
  617. {
  618. static WCHAR *const c_rgpsz[] = {TEXT("CLSID2"),
  619. TEXT("CLSID"),
  620. TEXT("UICLSID")};
  621. int iFoundIndex = ARRAYSIZE(c_rgpsz);
  622. // We look for CLSID2, CLSID, then UICLSID, since there could be multiple kes in this section.
  623. // CLSID2 makes folders work on Win95 if the CLSID does not exist on the machine
  624. for (WCHAR *pNextKeyPointer = szSectionValue; *pNextKeyPointer; pNextKeyPointer += lstrlen(pNextKeyPointer) + 1)
  625. {
  626. PWCHAR pBuffer = pNextKeyPointer;
  627. PWCHAR pEqual = StrChrW(pBuffer, L'=');
  628. if (pEqual && (*(pEqual+1) != L'\0'))
  629. {
  630. *pEqual = L'\0';
  631. for (int i = 0; i < ARRAYSIZE(c_rgpsz); i++)
  632. {
  633. if (StrCmpIC(c_rgpsz[i], pBuffer) == 0)
  634. {
  635. CLSID clsid;
  636. if ((iFoundIndex < i) && bFound)
  637. {
  638. break;
  639. }
  640. pBuffer += lstrlen(pBuffer) + 1;
  641. if (SUCCEEDED(SHCLSIDFromString(pBuffer, &clsid)))
  642. {
  643. if (i == ARRAYSIZE(c_rgpsz) - 1)
  644. {
  645. // hack for "Temporary Internet Files"
  646. if (clsid == CLSID_CacheFolder)
  647. {
  648. *pclsid = CLSID_CacheFolder2;
  649. bFound = TRUE;
  650. }
  651. }
  652. else
  653. {
  654. *pclsid = clsid;
  655. bFound = TRUE;
  656. }
  657. iFoundIndex = i;
  658. }
  659. break;
  660. }
  661. } // end of for
  662. } // end of if
  663. } //end of for
  664. }
  665. }
  666. return bFound;
  667. }
  668. LPTSTR PathFindCLSIDExtension(LPCTSTR pszFile, CLSID *pclsid)
  669. {
  670. LPCTSTR pszExt = PathFindExtension(pszFile);
  671. ASSERT(pszExt);
  672. if (*pszExt == TEXT('.') && *(pszExt + 1) == TEXT('{') /* '}' */)
  673. {
  674. CLSID clsid;
  675. if (pclsid == NULL)
  676. pclsid = &clsid;
  677. if (SUCCEEDED(SHCLSIDFromString(pszExt + 1, pclsid)))
  678. return (LPTSTR)pszExt; // const -> non const
  679. }
  680. return NULL;
  681. }
  682. //
  683. // This function retrieves the CLSID from a filename
  684. // file.{GUID}
  685. //
  686. BOOL _GetFileCLSID(LPCTSTR pszFile, CLSID* pclsid)
  687. {
  688. return PathFindCLSIDExtension(pszFile, pclsid) != NULL;
  689. }
  690. // test pidf for properties that make make it a junction, mark it as a junction
  691. // as needed, see _IsJunction usage
  692. BOOL _ClsidExists(REFGUID clsid)
  693. {
  694. HKEY hk;
  695. if (SUCCEEDED(SHRegGetCLSIDKey(clsid, NULL, FALSE, FALSE, &hk)))
  696. {
  697. RegCloseKey(hk);
  698. return TRUE;
  699. }
  700. return FALSE;
  701. }
  702. LPIDFOLDER CFSFolder::_MarkAsJunction(LPCIDFOLDER pidfSimpleParent, LPIDFOLDER pidf, LPCTSTR pszName)
  703. {
  704. CLSID clsid;
  705. BOOL fJunction = FALSE;
  706. // check for a junction point, junctions are either
  707. // Folder.{guid} or File.{guid} both fall into this case
  708. if (_GetFileCLSID(pszName, &clsid))
  709. {
  710. fJunction = TRUE;
  711. }
  712. else if (_IsSystemFolder(pidf))
  713. {
  714. // system (read only or system bit) look for the desktop.ini in a folder
  715. TCHAR szPath[MAX_PATH];
  716. if (SUCCEEDED(_GetPathForItems(pidfSimpleParent, pidf, szPath)))
  717. {
  718. // CLSID2 makes folders work on Win95 if the CLSID does not exist on the machine
  719. if (_GetFolderCLSID(szPath, _pszNetProvider, &clsid))
  720. {
  721. fJunction = TRUE;
  722. }
  723. }
  724. }
  725. if (fJunction && _ClsidExists(clsid))
  726. {
  727. pidf->bFlags |= SHID_JUNCTION;
  728. pidf = (LPIDFOLDER) ILAppendHiddenClsid((LPITEMIDLIST)pidf, IDLHID_JUNCTION, &clsid);
  729. }
  730. return pidf;
  731. }
  732. BOOL CFSFolder::_GetJunctionClsid(LPCIDFOLDER pidf, CLSID *pclsid)
  733. {
  734. CFileSysItemString fsi(pidf);
  735. return fsi.GetJunctionClsid(pclsid, TRUE);
  736. }
  737. BOOL CFileSysItemString::GetJunctionClsid(CLSID *pclsid, BOOL fShellExtOk)
  738. {
  739. BOOL bRet = FALSE;
  740. *pclsid = CLSID_NULL;
  741. if (CFSFolder::_IsJunction(_pidf))
  742. {
  743. // if this is a junction point that was created with a hidden CLSID
  744. // then it should be stored with IDLHID_JUNCTION
  745. if (ILGetHiddenClsid((LPCITEMIDLIST)_pidf, IDLHID_JUNCTION, pclsid))
  746. bRet = TRUE;
  747. else
  748. {
  749. // it might be an oldstyle JUNCTION point that was persisted out or a ROOT_REGITEM
  750. if (SIL_GetType((LPITEMIDLIST)_pidf) == SHID_ROOT_REGITEM)
  751. {
  752. const UNALIGNED CLSID *pc = (UNALIGNED CLSID *)(((BYTE *)_pidf) + _pidf->cb - sizeof(CLSID));
  753. *pclsid = *pc;
  754. bRet = TRUE;
  755. }
  756. }
  757. }
  758. else if (fShellExtOk)
  759. {
  760. if (ClassFlags(FALSE) & SHCF_IS_SHELLEXT)
  761. {
  762. IAssociationArray *paa;
  763. // must pass NULL for CFSFolder to avoid recursion
  764. if (SUCCEEDED(AssocCreate(NULL, FALSE, IID_PPV_ARG(IAssociationArray, &paa))))
  765. {
  766. CSmartCoTaskMem<WCHAR> spsz;
  767. if (SUCCEEDED(paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQS_CLSID, NULL, &spsz)))
  768. {
  769. bRet = GUIDFromString(spsz, pclsid);
  770. }
  771. paa->Release();
  772. }
  773. }
  774. }
  775. else if (CFSFolder::_IsFolder(_pidf))
  776. {
  777. // directory.{guid} is always of Class() {guid}
  778. bRet = _GetFileCLSID(FSName(), pclsid);
  779. }
  780. return bRet;
  781. }
  782. //
  783. // returns a unique name for a class, dont use this function to get
  784. // the ProgID for a class call SHGetClassKey() for that
  785. //
  786. // Returns: class name in pszClass
  787. //
  788. // foo.ext ".ext"
  789. // foo "."
  790. // (empty) "Folder"
  791. // directory "Directory"
  792. // junction "CLSID\{clsid}"
  793. //
  794. BOOL CFSFolder::_GetClass(LPCIDFOLDER pidf, LPTSTR pszClass, UINT cch)
  795. {
  796. CFileSysItemString fsi(pidf);
  797. StrCpyN(pszClass, fsi.Class(), cch);
  798. return TRUE;
  799. }
  800. LPCWSTR CFileSysItemString::_Class()
  801. {
  802. if (_pidf->cb == 0) // ILIsEmpty()
  803. {
  804. // the desktop. Always use the "Folder" class.
  805. _pszClass = c_szFolderClass;
  806. }
  807. // else if (ILGetHiddenString(IDLHID_TREATASCLASS))
  808. else
  809. {
  810. CLSID clsid;
  811. if (GetJunctionClsid(&clsid, FALSE))
  812. {
  813. // This is a junction point, get the CLSID from it.
  814. CSmartCoTaskMem<OLECHAR> spsz;
  815. if (SUCCEEDED(ProgIDFromCLSID(clsid, &spsz)))
  816. {
  817. StrCpyN(_sz, spsz, ARRAYSIZE(_sz));
  818. }
  819. else
  820. SHStringFromGUID(clsid, _sz , ARRAYSIZE(_sz));
  821. _fsin = FSINAME_CLASS;
  822. }
  823. else if (CFSFolder::_IsFolder(_pidf))
  824. {
  825. // This is a directory. Always use the "Directory" class.
  826. // This can also be a Drive id.
  827. _pszClass = TEXT("Directory");
  828. }
  829. else
  830. {
  831. // This is a file. Get the class based on the extension.
  832. LPCWSTR pszFile = FSName();
  833. LPCWSTR pszExt = PathFindExtension(pszFile);
  834. ASSERT(pszExt);
  835. ASSERT(!(_pidf->wAttrs & FILE_ATTRIBUTE_DIRECTORY));
  836. if (*pszExt == 0)
  837. {
  838. if (_pidf->wAttrs & FILE_ATTRIBUTE_SYSTEM)
  839. _pszClass = TEXT(".sys");
  840. else
  841. _pszClass = TEXT(".");
  842. }
  843. else if (pszFile == _sz)
  844. {
  845. // we need the buffer to be setup correctly
  846. MoveMemory(_sz, pszExt, CbFromCchW(lstrlen(pszExt) + 1));
  847. _fsin = FSINAME_CLASS;
  848. }
  849. else
  850. _pszClass = pszExt;
  851. }
  852. }
  853. ASSERT(_pszClass || *_sz);
  854. return _pszClass ? _pszClass : _sz;
  855. }
  856. LPCWSTR CFileSysItemString::Class()
  857. {
  858. if (!_pszClass)
  859. {
  860. if (!(_fsin & FSINAME_CLASS))
  861. {
  862. return _Class();
  863. }
  864. else
  865. {
  866. return _sz;
  867. }
  868. }
  869. return _pszClass;
  870. }
  871. CFSAssocEnumData::CFSAssocEnumData(BOOL fIsUnknown, CFSFolder *pfs, LPCIDFOLDER pidf) : _fIsUnknown(fIsUnknown)
  872. {
  873. _fIsSystemFolder = pfs->_IsSystemFolder(pidf);
  874. pfs->_GetPathForItem(pidf, _szPath);
  875. if (_fIsUnknown)
  876. _fIsUnknown = !(FILE_ATTRIBUTE_OFFLINE & pidf->wAttrs);
  877. else
  878. {
  879. if (CFSFolder::_IsFileFolder(pidf))
  880. _pidl = ILCombine(pfs->_GetIDList(), (LPCITEMIDLIST)pidf);
  881. }
  882. }
  883. LPCWSTR _GetDirectoryClass(LPCWSTR pszPath, LPCITEMIDLIST pidl, BOOL fIsSystemFolder);
  884. BOOL CFSAssocEnumData::_Next(IAssociationElement **ppae)
  885. {
  886. HRESULT hr = E_FAIL;
  887. if (_fIsUnknown)
  888. {
  889. CLSID clsid;
  890. hr = GetClassFile(_szPath, &clsid);
  891. if (SUCCEEDED(hr))
  892. {
  893. CSmartCoTaskMem<OLECHAR> spszProgid;
  894. hr = ProgIDFromCLSID(clsid, &spszProgid);
  895. if (SUCCEEDED(hr))
  896. {
  897. hr = AssocElemCreateForClass(&CLSID_AssocProgidElement, spszProgid, ppae);
  898. }
  899. if (FAILED(hr))
  900. {
  901. WCHAR sz[GUIDSTR_MAX];
  902. SHStringFromGUIDW(clsid, sz, ARRAYSIZE(sz));
  903. hr = AssocElemCreateForClass(&CLSID_AssocClsidElement, sz, ppae);
  904. }
  905. }
  906. if (FAILED(hr))
  907. {
  908. hr = AssocElemCreateForClass(&CLSID_AssocShellElement, L"Unknown", ppae);
  909. }
  910. _fIsUnknown = FALSE;
  911. }
  912. if (FAILED(hr) && _pidl)
  913. {
  914. PCWSTR psz = _GetDirectoryClass(_szPath, _pidl, _fIsSystemFolder);
  915. if (psz)
  916. hr = AssocElemCreateForClass(&CLSID_AssocSystemElement, psz, ppae);
  917. ILFree(_pidl);
  918. _pidl = NULL;
  919. }
  920. return SUCCEEDED(hr);
  921. }
  922. class CFSAssocEnumExtra : public CEnumAssociationElements
  923. {
  924. public:
  925. protected:
  926. BOOL _Next(IAssociationElement **ppae);
  927. protected:
  928. };
  929. BOOL CFSAssocEnumExtra::_Next(IAssociationElement **ppae)
  930. {
  931. if (_cNext == 0)
  932. {
  933. // corel wp suite 7 relies on the fact that send to menu is hard coded
  934. // not an extension so do not insert it (and the similar items)
  935. if (!(SHGetAppCompatFlags(ACF_CONTEXTMENU) & ACF_CONTEXTMENU))
  936. {
  937. AssocElemCreateForClass(&CLSID_AssocShellElement, L"AllFilesystemObjects", ppae);
  938. }
  939. }
  940. return *ppae != NULL;
  941. }
  942. HRESULT CFileSysItemString::AssocCreate(CFSFolder *pfs, BOOL fForCtxMenu, REFIID riid, void **ppv)
  943. {
  944. // WARNING - the pfs keeps us from recursing.
  945. *ppv = NULL;
  946. IAssociationArrayInitialize *paai;
  947. HRESULT hr = ::AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IAssociationArrayInitialize, &paai));
  948. if (SUCCEEDED(hr))
  949. {
  950. // the base class for directory's is always Folder
  951. ASSOCELEM_MASK base;
  952. if (CFSFolder::_IsFolder(_pidf))
  953. base = ASSOCELEM_BASEIS_FOLDER;
  954. else
  955. {
  956. // for files it is always *
  957. base = ASSOCELEM_BASEIS_STAR;
  958. if (pfs)
  959. {
  960. CLSID clsid;
  961. if (GetJunctionClsid(&clsid, TRUE))
  962. {
  963. // but if this file is also a folder (like .zip and .cab)
  964. // then we should also use Folder
  965. if (SHGetAttributesFromCLSID2(&clsid, 0, SFGAO_FOLDER) & SFGAO_FOLDER)
  966. base |= ASSOCELEM_BASEIS_FOLDER;
  967. }
  968. }
  969. }
  970. hr = paai->InitClassElements(base, Class());
  971. if (SUCCEEDED(hr) && pfs)
  972. {
  973. BOOL fIsLink = fForCtxMenu && (_ClassFlags(paai, FALSE) & SHCF_IS_LINK);
  974. if (fIsLink)
  975. {
  976. // we dont like to do everything for LINK, but
  977. // maybe we should be adding BASEIS_STAR?
  978. paai->FilterElements(ASSOCELEM_DEFAULT | ASSOCELEM_EXTRA);
  979. }
  980. IEnumAssociationElements *penum = new CFSAssocEnumExtra();
  981. if (penum)
  982. {
  983. paai->InsertElements(ASSOCELEM_EXTRA, penum);
  984. penum->Release();
  985. }
  986. if (!fIsLink)
  987. {
  988. penum = new CFSAssocEnumData(hr == S_FALSE, pfs, _pidf);
  989. if (penum)
  990. {
  991. paai->InsertElements(ASSOCELEM_DATA | ASSOCELEMF_INCLUDE_SLOW, penum);
  992. penum->Release();
  993. }
  994. }
  995. }
  996. if (SUCCEEDED(hr))
  997. hr = paai->QueryInterface(riid, ppv);
  998. paai->Release();
  999. }
  1000. return hr;
  1001. }
  1002. HRESULT CFSFolder::_AssocCreate(LPCIDFOLDER pidf, REFIID riid, void **ppv)
  1003. {
  1004. CFileSysItemString fsi(pidf);
  1005. return fsi.AssocCreate(this, FALSE, riid, ppv);
  1006. }
  1007. //
  1008. // Description: This simulates the ComponentCategoryManager
  1009. // call which checks to see if a CLSID is a member of a CATID.
  1010. //
  1011. STDAPI_(BOOL) IsMemberOfCategory(IAssociationArray *paa, REFCATID rcatid)
  1012. {
  1013. BOOL fRet = FALSE;
  1014. CSmartCoTaskMem<WCHAR> spsz;
  1015. if (SUCCEEDED(paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQS_CLSID, NULL, &spsz)))
  1016. {
  1017. TCHAR szKey[GUIDSTR_MAX * 4], szCATID[GUIDSTR_MAX];
  1018. // Construct the registry key that detects if
  1019. // a CLSID is a member of a CATID.
  1020. SHStringFromGUID(rcatid, szCATID, ARRAYSIZE(szCATID));
  1021. wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("CLSID\\%s\\Implemented Categories\\%s"), spsz, szCATID);
  1022. // See if it's there.
  1023. fRet = SHRegQueryValue(HKEY_CLASSES_ROOT, szKey, NULL, NULL) == ERROR_SUCCESS;
  1024. }
  1025. return fRet;
  1026. }
  1027. // get flags for a file class.
  1028. //
  1029. // given a FS PIDL returns a DWORD of flags, or 0 for error
  1030. //
  1031. // SHCF_ICON_INDEX this is this sys image index for per class
  1032. // SHCF_ICON_PERINSTANCE icons are per instance (one per file)
  1033. // SHCF_ICON_DOCICON icon is in shell\open\command (simulate doc icon)
  1034. //
  1035. // SHCF_HAS_ICONHANDLER set if class has a IExtractIcon handler
  1036. //
  1037. // SHCF_UNKNOWN set if extenstion is not registered
  1038. //
  1039. // SHCF_IS_LINK set if class is a link
  1040. // SHCF_ALWAYS_SHOW_EXT always show the extension
  1041. // SHCF_NEVER_SHOW_EXT never show the extension
  1042. //
  1043. DWORD CFSFolder::_GetClassFlags(LPCIDFOLDER pidf)
  1044. {
  1045. CFileSysItemString fsi(pidf);
  1046. return fsi.ClassFlags(FALSE);
  1047. }
  1048. void CFileSysItemString::_QueryIconIndex(IAssociationArray *paa)
  1049. {
  1050. // check for the default icon under HKCU for this file extension.
  1051. // null out the icon index
  1052. _dwClass &= ~SHCF_ICON_INDEX;
  1053. PWSTR pszIcon;
  1054. HRESULT hr = E_FAIL;
  1055. if (paa)
  1056. {
  1057. // check for icon in ProgID
  1058. // Then, check if the default icon is specified in OLE-style.
  1059. hr = paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQS_DEFAULTICON, NULL, &pszIcon);
  1060. if (SUCCEEDED(hr))
  1061. {
  1062. // hijack these icons
  1063. // office XP has really ugly icons for images
  1064. // and ours are so beautiful...office wont mind
  1065. static const struct
  1066. {
  1067. PCWSTR pszUgly;
  1068. PCWSTR pszPretty;
  1069. } s_hijack[] =
  1070. {
  1071. { L"PEicons.exe,1", L"shimgvw.dll,2" }, // PNG
  1072. { L"PEicons.exe,4", L"shimgvw.dll,2" }, // GIF
  1073. { L"PEicons.exe,5", L"shimgvw.dll,3" }, // JPEG
  1074. { L"MSPVIEW.EXE,1", L"shimgvw.dll,4" }, // TIF
  1075. { L"wordicon.exe,8", L"moricons.dll,-109"},
  1076. { L"xlicons.exe,13", L"moricons.dll,-110"},
  1077. { L"accicons.exe,57", L"moricons.dll,-111"},
  1078. { L"pptico.exe,6", L"moricons.dll,-112"},
  1079. { L"fpicon.exe,2", L"moricons.dll,-113"},
  1080. };
  1081. PCWSTR pszName = PathFindFileName(pszIcon);
  1082. for (int i = 0; i < ARRAYSIZE(s_hijack); i++)
  1083. {
  1084. if (0 == StrCmpIW(pszName, s_hijack[i].pszUgly))
  1085. {
  1086. // replace this ugly chicken
  1087. CoTaskMemFree(pszIcon);
  1088. hr = SHStrDupW(s_hijack[i].pszPretty, &pszIcon);
  1089. break;
  1090. }
  1091. }
  1092. }
  1093. else if (!CFSFolder::_IsFolder(_pidf))
  1094. {
  1095. hr = paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQVS_APPLICATION_PATH, NULL, &pszIcon);
  1096. if (SUCCEEDED(hr))
  1097. _dwClass |= SHCF_ICON_DOCICON;
  1098. }
  1099. }
  1100. // Check if this is a per-instance icon
  1101. if (SUCCEEDED(hr) && (lstrcmp(pszIcon, TEXT("%1")) == 0 ||
  1102. lstrcmp(pszIcon, TEXT("\"%1\"")) == 0))
  1103. {
  1104. _dwClass &= ~SHCF_ICON_DOCICON;
  1105. _dwClass |= SHCF_ICON_PERINSTANCE;
  1106. }
  1107. else
  1108. {
  1109. int iIcon, iImage;
  1110. if (SUCCEEDED(hr))
  1111. {
  1112. iIcon = PathParseIconLocation(pszIcon);
  1113. iImage = Shell_GetCachedImageIndex(pszIcon, iIcon, _dwClass & SHCF_ICON_DOCICON ? GIL_SIMULATEDOC : 0);
  1114. if (iImage == -1)
  1115. {
  1116. iIcon = _dwClass & SHCF_ICON_DOCICON ? II_DOCUMENT : II_DOCNOASSOC;
  1117. iImage = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0);
  1118. }
  1119. }
  1120. else
  1121. {
  1122. iIcon = CFSFolder::_IsFolder(_pidf) ? II_FOLDER : II_DOCNOASSOC;
  1123. iImage = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0);
  1124. _dwClass |= SHCF_ICON_DOCICON; // make _dwClass non-zero
  1125. }
  1126. // Shell_GetCachedImageIndex can return -1 for failure cases. We
  1127. // dont want to or -1 in, so check to make sure the index is valid.
  1128. if ((iImage & ~SHCF_ICON_INDEX) == 0)
  1129. {
  1130. // no higher bits set so its ok to or the index in
  1131. _dwClass |= iImage;
  1132. }
  1133. }
  1134. if (SUCCEEDED(hr))
  1135. CoTaskMemFree(pszIcon);
  1136. }
  1137. #define ASSOCELEM_GETBITS (ASSOCELEM_USER | ASSOCELEM_DEFAULT | ASSOCELEM_SYSTEM)
  1138. BOOL _IsKnown(IAssociationArray *paa)
  1139. {
  1140. BOOL fRet = FALSE;
  1141. CComPtr<IEnumAssociationElements> spenum;
  1142. if (paa && SUCCEEDED(paa->EnumElements(ASSOCELEM_GETBITS, &spenum)))
  1143. {
  1144. CComPtr<IAssociationElement> spae;
  1145. ULONG c;
  1146. fRet = S_OK == spenum->Next(1, &spae, &c);
  1147. }
  1148. return fRet;
  1149. }
  1150. void CFileSysItemString::_QueryClassFlags(IAssociationArray *paa)
  1151. {
  1152. // always hide extension for .{guid} junction points:
  1153. // unless ShowSuperHidden() is on. since this means the user wants to see system stuff
  1154. if (!ShowSuperHidden() && _GetFileCLSID(FSName(), NULL))
  1155. _dwClass = SHCF_NEVER_SHOW_EXT;
  1156. else if (CFSFolder::_IsFolder(_pidf))
  1157. _dwClass = SHCF_ALWAYS_SHOW_EXT;
  1158. else
  1159. _dwClass = 0;
  1160. if (_IsKnown(paa))
  1161. {
  1162. // see what handlers exist
  1163. if (SUCCEEDED(paa->QueryExists(ASSOCELEM_GETBITS, AQNS_SHELLEX_HANDLER, TEXT("IconHandler"))))
  1164. _dwClass |= SHCF_HAS_ICONHANDLER;
  1165. // check for browsability
  1166. if (!(SHGetAppCompatFlags(ACF_DOCOBJECT) & ACF_DOCOBJECT))
  1167. {
  1168. if (SUCCEEDED(paa->QueryExists(ASSOCELEM_GETBITS, AQN_NAMED_VALUE, TEXT("DocObject")))
  1169. || SUCCEEDED(paa->QueryExists(ASSOCELEM_GETBITS, AQN_NAMED_VALUE, TEXT("BrowseInPlace"))))
  1170. _dwClass |= SHCF_IS_DOCOBJECT;
  1171. }
  1172. if (IsMemberOfCategory(paa, CATID_BrowsableShellExt))
  1173. _dwClass |= SHCF_IS_SHELLEXT;
  1174. // get attributes
  1175. if (SUCCEEDED(paa->QueryExists(ASSOCELEM_GETBITS, AQN_NAMED_VALUE, TEXT("IsShortcut"))))
  1176. _dwClass |= SHCF_IS_LINK;
  1177. if (SUCCEEDED(paa->QueryExists(ASSOCELEM_GETBITS, AQN_NAMED_VALUE, TEXT("AlwaysShowExt"))))
  1178. _dwClass |= SHCF_ALWAYS_SHOW_EXT;
  1179. if (SUCCEEDED(paa->QueryExists(ASSOCELEM_GETBITS, AQN_NAMED_VALUE, TEXT("NeverShowExt"))))
  1180. _dwClass |= SHCF_NEVER_SHOW_EXT;
  1181. // figure out what type of icon this type of file uses.
  1182. if (_dwClass & SHCF_HAS_ICONHANDLER)
  1183. {
  1184. _dwClass |= SHCF_ICON_PERINSTANCE;
  1185. }
  1186. }
  1187. else
  1188. {
  1189. // unknown type - pick defaults and get out.
  1190. _dwClass |= SHCF_UNKNOWN | SHCF_ALWAYS_SHOW_EXT;
  1191. }
  1192. }
  1193. CFSFolder * GetFSFolderFromShellFolder(IShellFolder *psf)
  1194. {
  1195. CFSFolder *pfs = NULL;
  1196. if (psf)
  1197. psf->QueryInterface(IID_INeedRealCFSFolder, (void **)&pfs);
  1198. return pfs;
  1199. }
  1200. PERCEIVED GetPerceivedType(IShellFolder *psf, LPCITEMIDLIST pidl)
  1201. {
  1202. PERCEIVED gen = GEN_UNKNOWN;
  1203. CFSFolder *pfsf = GetFSFolderFromShellFolder(psf);
  1204. if (pfsf)
  1205. {
  1206. LPCIDFOLDER pidf = CFSFolder_IsValidID(pidl);
  1207. if (pidf)
  1208. {
  1209. CFileSysItemString fsi(pidf);
  1210. gen = fsi.PerceivedType();
  1211. }
  1212. }
  1213. return gen;
  1214. }
  1215. const struct {
  1216. PERCEIVED gen;
  1217. LPCWSTR psz;
  1218. } c_rgPerceivedTypes[] = {
  1219. {GEN_TEXT, L"text"},
  1220. {GEN_IMAGE, L"image"},
  1221. {GEN_AUDIO, L"audio"},
  1222. {GEN_VIDEO, L"video"},
  1223. {GEN_COMPRESSED, L"compressed"},
  1224. };
  1225. PERCEIVED CFileSysItemString::PerceivedType()
  1226. {
  1227. // look up the file type in the cache.
  1228. PERCEIVED gen = LookupFilePerceivedType(Class());
  1229. if (gen == GEN_UNKNOWN)
  1230. {
  1231. WCHAR sz[40];
  1232. DWORD cb = sizeof(sz);
  1233. if (NOERROR == SHGetValueW(HKEY_CLASSES_ROOT, Class(), L"PerceivedType", NULL, sz, &cb))
  1234. {
  1235. gen = GEN_CUSTOM;
  1236. for (int i = 0; i < ARRAYSIZE(c_rgPerceivedTypes); i++)
  1237. {
  1238. if (0 == StrCmpC(c_rgPerceivedTypes[i].psz, sz))
  1239. {
  1240. gen = c_rgPerceivedTypes[i].gen;
  1241. break;
  1242. }
  1243. }
  1244. }
  1245. else if (CFSFolder::_IsFolder(_pidf))
  1246. {
  1247. gen = GEN_FOLDER;
  1248. }
  1249. else
  1250. {
  1251. gen = GEN_UNSPECIFIED;
  1252. }
  1253. AddFilePerceivedType(Class(), gen);
  1254. }
  1255. return gen;
  1256. }
  1257. BOOL _IsImageExt(PCWSTR psz);
  1258. BOOL CFileSysItemString::IsShimgvwImage()
  1259. {
  1260. return _IsImageExt(Class());
  1261. }
  1262. DWORD CFileSysItemString::_ClassFlags(IUnknown *punkAssoc, BOOL fNeedsIconBits)
  1263. {
  1264. // look up the file type in the cache.
  1265. if (!_dwClass)
  1266. _dwClass = LookupFileClass(Class());
  1267. if (_dwClass)
  1268. {
  1269. if (!fNeedsIconBits || (_dwClass & SHCF_ICON_INDEX) != SHCF_ICON_INDEX)
  1270. return _dwClass;
  1271. }
  1272. IAssociationArray *paa;
  1273. HRESULT hr;
  1274. if (punkAssoc)
  1275. hr = punkAssoc->QueryInterface(IID_PPV_ARG(IAssociationArray, &paa));
  1276. else
  1277. hr = AssocCreate(NULL, FALSE, IID_PPV_ARG(IAssociationArray, &paa));
  1278. if (!_dwClass)
  1279. _QueryClassFlags(paa);
  1280. if (fNeedsIconBits && !(_dwClass & SHCF_ICON_PERINSTANCE))
  1281. _QueryIconIndex(paa);
  1282. else
  1283. {
  1284. // set it to be not init'd
  1285. _dwClass |= SHCF_ICON_INDEX;
  1286. }
  1287. if (SUCCEEDED(hr))
  1288. {
  1289. paa->Release();
  1290. if (0 == _dwClass)
  1291. {
  1292. // If we hit this, the extension for this file type is incorrectly installed
  1293. // and it will cause double clicking on such files to open the "Open With..."
  1294. // file associatins dialog.
  1295. //
  1296. // IF YOU HIT THIS:
  1297. // 1. Find the file type by checking szClass.
  1298. // 2. Contact the person that installed that file type and have them fix
  1299. // the install to have an icon and an associated program.
  1300. TraceMsg(TF_WARNING, "_GetClassFlags() has detected an improperly registered class: '%s'", Class());
  1301. }
  1302. }
  1303. AddFileClass(Class(), _dwClass);
  1304. return _dwClass;
  1305. }
  1306. //
  1307. // this function checks for flags in desktop.ini
  1308. //
  1309. #define GFF_DEFAULT_TO_FS 0x0001 // the shell-xtension permits FS as the default where it cannot load
  1310. #define GFF_ICON_FOR_ALL_FOLDERS 0x0002 // use the icon specified in the desktop.ini for all sub folders
  1311. BOOL CFSFolder::_GetFolderFlags(LPCIDFOLDER pidf, UINT *prgfFlags)
  1312. {
  1313. TCHAR szPath[MAX_PATH];
  1314. *prgfFlags = 0;
  1315. if (FAILED(_GetPathForItem(pidf, szPath)))
  1316. return FALSE;
  1317. if (PathAppend(szPath, c_szDesktopIni))
  1318. {
  1319. if (GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("DefaultToFS"), 1, szPath))
  1320. {
  1321. *prgfFlags |= GFF_DEFAULT_TO_FS;
  1322. }
  1323. #if 0
  1324. if (GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("SubFoldersUseIcon"), 1, szPath))
  1325. {
  1326. *prgfFlags |= GFF_ICON_FOR_ALL_FOLDERS;
  1327. }
  1328. #endif
  1329. }
  1330. return TRUE;
  1331. }
  1332. //
  1333. // This funtion retrieves the ICONPATh from desktop.ini file.
  1334. // It takes a pidl as an input.
  1335. // NOTE: There is code in SHDOCVW--ReadIconLocation that does almost the same thing
  1336. // only that code looks in .URL files instead of desktop.ini
  1337. BOOL CFSFolder::_GetFolderIconPath(LPCIDFOLDER pidf, LPTSTR pszIcon, int cchMax, UINT *pIndex)
  1338. {
  1339. TCHAR szPath[MAX_PATH], szIcon[MAX_PATH];
  1340. BOOL fSuccess = FALSE;
  1341. UINT iIndex;
  1342. if (pszIcon == NULL)
  1343. {
  1344. pszIcon = szIcon;
  1345. cchMax = ARRAYSIZE(szPath);
  1346. }
  1347. if (pIndex == NULL)
  1348. pIndex = &iIndex;
  1349. *pIndex = _GetDefaultFolderIcon(); // Default the index to II_FOLDER (default folder icon)
  1350. if (SUCCEEDED(_GetPathForItem(pidf, szPath)))
  1351. {
  1352. if (GetFolderString(szPath, _pszNetProvider, pszIcon, cchMax, SZ_CANBEUNICODE TEXT("IconFile")))
  1353. {
  1354. // Fix the relative path
  1355. PathCombine(pszIcon, szPath, pszIcon);
  1356. fSuccess = PathFileExistsAndAttributes(pszIcon, NULL);
  1357. if (fSuccess)
  1358. {
  1359. TCHAR szIndex[16];
  1360. if (GetFolderString(szPath, _pszNetProvider, szIndex, ARRAYSIZE(szIndex), TEXT("IconIndex")))
  1361. {
  1362. StrToIntEx(szIndex, 0, (int *)pIndex);
  1363. }
  1364. }
  1365. }
  1366. }
  1367. return fSuccess;
  1368. }
  1369. // IDList factory
  1370. CFileSysItem::CFileSysItem(LPCIDFOLDER pidf)
  1371. : _pidf(pidf), _pidp((PCIDPERSONALIZED)-1)
  1372. {
  1373. _pidfx = (PCIDFOLDEREX) ILFindHiddenIDOn((LPCITEMIDLIST)pidf, IDLHID_IDFOLDEREX, FALSE);
  1374. if (_pidfx && _pidfx->hid.wVersion < IDFX_V1)
  1375. _pidfx = NULL;
  1376. }
  1377. BOOL CFileSysItem::_IsPersonalized()
  1378. {
  1379. if (_pidp == (PCIDPERSONALIZED) -1)
  1380. {
  1381. _pidp = (PCIDPERSONALIZED) ILFindHiddenIDOn((LPCITEMIDLIST)_pidf, IDLHID_PERSONALIZED, FALSE);
  1382. if (_pidp && 0 >= (signed short) _pidp->hid.wVersion)
  1383. _pidp = NULL;
  1384. }
  1385. return _pidp != NULL;
  1386. }
  1387. CFileSysItemString::CFileSysItemString(LPCIDFOLDER pidf)
  1388. : CFileSysItem(pidf), _pszFSName(NULL), _pszUIName(NULL), _pszClass(NULL), _dwClass(0), _fsin(FSINAME_NONE)
  1389. {
  1390. *_sz = 0;
  1391. }
  1392. LPCWSTR CFileSysItemString::FSName()
  1393. {
  1394. if (!_pszFSName)
  1395. {
  1396. if (!(_fsin & FSINAME_FS))
  1397. {
  1398. LPCWSTR psz = MayCopyFSName(FALSE, _sz, ARRAYSIZE(_sz));
  1399. if (psz == _sz)
  1400. _fsin = FSINAME_FS;
  1401. else
  1402. _pszFSName = psz;
  1403. }
  1404. }
  1405. return _pszFSName ? _pszFSName : _sz;
  1406. }
  1407. LPCWSTR CFileSysItem::MayCopyFSName(BOOL fMustCopy, LPWSTR psz, DWORD cch)
  1408. {
  1409. if (_pidfx)
  1410. {
  1411. LPNWSTR pnsz = UASTROFFW(_pidfx, _pidfx->offNameW);
  1412. // return back a pointer inside the pidfx
  1413. // if we can...
  1414. if (fMustCopy || ((INT_PTR)pnsz & 1))
  1415. {
  1416. ualstrcpynW(psz, pnsz, cch);
  1417. }
  1418. else
  1419. psz = (LPWSTR) pnsz;
  1420. }
  1421. else
  1422. {
  1423. if ((CFSFolder::_GetType(_pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE)
  1424. {
  1425. ualstrcpynW(psz, (LPCWSTR)_pidf->cFileName, cch);
  1426. }
  1427. else
  1428. {
  1429. MultiByteToWideChar(CP_ACP, 0, _pidf->cFileName, -1, psz, cch);
  1430. }
  1431. }
  1432. return psz;
  1433. }
  1434. LPCSTR CFileSysItemString::AltName()
  1435. {
  1436. UINT cbName;
  1437. if (_pidfx)
  1438. {
  1439. // we put the altname in cFileName
  1440. cbName = 0;
  1441. }
  1442. else if ((CFSFolder::_GetType(_pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE)
  1443. {
  1444. cbName = (ualstrlenW((LPCWSTR)_pidf->cFileName) + 1) * sizeof(WCHAR);
  1445. }
  1446. else
  1447. {
  1448. cbName = lstrlenA(_pidf->cFileName) + 1;
  1449. }
  1450. return _pidf->cFileName + cbName;
  1451. }
  1452. LPCWSTR CFileSysItemString::UIName(CFSFolder *pfs)
  1453. {
  1454. if (!_pszUIName)
  1455. {
  1456. if (!(_fsin & FSINAME_UI))
  1457. {
  1458. if (!_pidfx || !_LoadResource(pfs))
  1459. {
  1460. if (!ShowExtension(pfs->_DefaultShowExt()))
  1461. {
  1462. // we need to have a buffer
  1463. if (!(_fsin & FSINAME_FS))
  1464. MayCopyFSName(TRUE, _sz, ARRAYSIZE(_sz));
  1465. PathRemoveExtension(_sz);
  1466. // lose the FSINAME_FS bit
  1467. _fsin = FSINAME_UI;
  1468. }
  1469. else
  1470. {
  1471. // the FSName and the UIName are the same
  1472. if (_sz == FSName())
  1473. {
  1474. // the FSName and the UIName are the same
  1475. // pidl is unaligned so the buffer gets double work
  1476. _fsin = FSINAME_FSUI;
  1477. }
  1478. else
  1479. {
  1480. // and we are aligned so we can use the same name
  1481. // directories are often this way.
  1482. _pszUIName = _pszFSName;
  1483. }
  1484. }
  1485. }
  1486. }
  1487. }
  1488. return _pszUIName ? _pszUIName : _sz;
  1489. }
  1490. UINT UnicodeToAscii(LPCWSTR pwsz, LPSTR psz, UINT cch)
  1491. {
  1492. // always at least copy the NULL
  1493. UINT cchRet = 1;
  1494. while (cch-- && (*psz++ = (CHAR) *pwsz++))
  1495. {
  1496. ASSERT(!(*pwsz & 0xff00));
  1497. cchRet++;
  1498. }
  1499. if (0 > (INT)cch)
  1500. psz[cchRet - 1] = 0;
  1501. return cchRet;
  1502. }
  1503. UINT AsciiToUnicode(LPCSTR psz, LPWSTR pwsz, UINT cch)
  1504. {
  1505. UINT cchRet = 1;
  1506. while (cch-- && (*pwsz++ = (WCHAR) *psz++))
  1507. {
  1508. cchRet++;
  1509. }
  1510. if (0 > (INT)cch)
  1511. pwsz[cchRet - 1] = 0;
  1512. return cchRet;
  1513. }
  1514. BOOL CFileSysItemString::_ResourceName(LPWSTR psz, DWORD cch, BOOL fIsMine)
  1515. {
  1516. BOOL fRet = FALSE;
  1517. if (_IsPersonalized())
  1518. {
  1519. int ids = _GetPersonalizedRes((int)_pidp->hid.wVersion, fIsMine);
  1520. if (ids != -1)
  1521. {
  1522. wnsprintf(psz, cch, L"@shell32.dll,-%d", ids);
  1523. fRet = TRUE;
  1524. }
  1525. }
  1526. else if (_pidfx && _pidfx->offResourceA)
  1527. {
  1528. SHAnsiToUnicode(UASTROFFA(_pidfx, _pidfx->offResourceA), psz, cch);
  1529. fRet = TRUE;
  1530. }
  1531. return fRet;
  1532. }
  1533. LPCWSTR CFileSysItemString::ResourceName()
  1534. {
  1535. if (!(_fsin & FSINAME_RESOURCE))
  1536. {
  1537. if (!_ResourceName(_sz, ARRAYSIZE(_sz), FALSE))
  1538. *_sz = 0;
  1539. }
  1540. _fsin = FSINAME_RESOURCE;
  1541. return _sz;
  1542. }
  1543. HRESULT CFileSysItemString::GetFindDataSimple(WIN32_FIND_DATAW *pfd)
  1544. {
  1545. ZeroMemory(pfd, sizeof(*pfd));
  1546. // Note that COFSFolder doesn't provide any times _but_ COFSFolder
  1547. DosDateTimeToFileTime(_pidf->dateModified, _pidf->timeModified, &pfd->ftLastWriteTime);
  1548. pfd->dwFileAttributes = _pidf->wAttrs;
  1549. pfd->nFileSizeLow = _pidf->dwSize;
  1550. StrCpyN(pfd->cFileName, FSName(), ARRAYSIZE(pfd->cFileName));
  1551. SHAnsiToUnicode(AltName(), pfd->cAlternateFileName, ARRAYSIZE(pfd->cAlternateFileName));
  1552. if (_pidfx)
  1553. {
  1554. DosDateTimeToFileTime(_pidfx->dsCreate.wDate, _pidfx->dsCreate.wTime, &pfd->ftCreationTime);
  1555. DosDateTimeToFileTime(_pidfx->dsAccess.wDate, _pidfx->dsAccess.wTime, &pfd->ftLastAccessTime);
  1556. }
  1557. return S_OK;
  1558. }
  1559. HRESULT CFileSysItemString::GetFindData(WIN32_FIND_DATAW *pfd)
  1560. {
  1561. HRESULT hr;
  1562. // if its a simple ID, there's no data in it
  1563. if (CFSFolder::_IsReal(_pidf))
  1564. {
  1565. hr = GetFindDataSimple(pfd);
  1566. }
  1567. else
  1568. {
  1569. ZeroMemory(pfd, sizeof(*pfd));
  1570. hr = E_INVALIDARG;
  1571. }
  1572. return hr;
  1573. }
  1574. typedef struct
  1575. {
  1576. int csidl;
  1577. int idsMine;
  1578. int idsTheirs;
  1579. } PERSONALIZEDNAME;
  1580. int CFileSysItemString::_GetPersonalizedRes(int csidl, BOOL fIsMine)
  1581. {
  1582. static const PERSONALIZEDNAME s_pnames[] =
  1583. {
  1584. { CSIDL_PERSONAL, -1, IDS_LOCALGDN_FLD_THEIRDOCUMENTS},
  1585. { CSIDL_MYPICTURES, IDS_LOCALGDN_FLD_MYPICTURES, IDS_LOCALGDN_FLD_THEIRPICTURES},
  1586. { CSIDL_MYMUSIC, IDS_LOCALGDN_FLD_MYMUSIC, IDS_LOCALGDN_FLD_THEIRMUSIC},
  1587. { CSIDL_MYVIDEO, IDS_LOCALGDN_FLD_MYVIDEOS, IDS_LOCALGDN_FLD_THEIRVIDEOS},
  1588. };
  1589. for (int i = 0; i < ARRAYSIZE(s_pnames); i++)
  1590. {
  1591. if (s_pnames[i].csidl == csidl)
  1592. {
  1593. return fIsMine ? s_pnames[i].idsMine : s_pnames[i].idsTheirs;
  1594. }
  1595. }
  1596. AssertMsg(FALSE, TEXT("Personalized Resource not in table"));
  1597. return -1;
  1598. }
  1599. TRIBIT CFileSysItem::_IsMine(CFSFolder *pfs)
  1600. {
  1601. TRIBIT tb = TRIBIT_UNDEFINED;
  1602. if (_IsPersonalized())
  1603. {
  1604. WCHAR szPath[MAX_PATH];
  1605. if (SUCCEEDED(SHGetFolderPath(NULL, (int)_pidp->hid.wVersion | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath)))
  1606. {
  1607. WCHAR szThis[MAX_PATH];
  1608. if (SUCCEEDED(pfs->_GetPathForItem(_pidf, szThis)))
  1609. {
  1610. // if they match then its ours
  1611. // if they dont then it still personalized (theirs)
  1612. if (0 == StrCmpI(szThis, szPath))
  1613. tb = TRIBIT_TRUE;
  1614. else
  1615. {
  1616. tb = TRIBIT_FALSE;
  1617. }
  1618. }
  1619. }
  1620. }
  1621. return tb;
  1622. }
  1623. void CFileSysItemString::_FormatTheirs(LPCWSTR pszFormat)
  1624. {
  1625. WCHAR szOwner[UNLEN];
  1626. ualstrcpynW(szOwner, _pidp->szUserName, ARRAYSIZE(szOwner));
  1627. if (!IsOS(OS_DOMAINMEMBER))
  1628. {
  1629. // maybe we should do caching here???
  1630. // pfs->GetUserName(szOwner, szOwner, ARRAYSIZE(szOwner));
  1631. USER_INFO_10 *pui;
  1632. if (NERR_Success == NetUserGetInfo(NULL, szOwner, 10, (LPBYTE*)&pui))
  1633. {
  1634. LPTSTR pszName = (*pui->usri10_full_name) ? pui->usri10_full_name: pui->usri10_name;
  1635. if (*pszName)
  1636. {
  1637. StrCpyN(szOwner, pszName, ARRAYSIZE(szOwner));
  1638. }
  1639. NetApiBufferFree(pui);
  1640. }
  1641. }
  1642. wnsprintf(_sz, ARRAYSIZE(_sz), pszFormat, szOwner);
  1643. }
  1644. BOOL CFileSysItemString::_LoadResource(CFSFolder *pfs)
  1645. {
  1646. WCHAR szResource[MAX_PATH];
  1647. BOOL fRet = FALSE;
  1648. TRIBIT tbIsMine = _IsMine(pfs);
  1649. if (_ResourceName(szResource, ARRAYSIZE(szResource), tbIsMine == TRIBIT_TRUE))
  1650. {
  1651. DWORD cb = sizeof(_sz);
  1652. // first check the registry for overrides
  1653. if (S_OK == SKGetValueW(SHELLKEY_HKCU_SHELL, L"LocalizedResourceName", szResource, NULL, _sz, &cb)
  1654. && *_sz)
  1655. {
  1656. fRet = TRUE;
  1657. }
  1658. else if (szResource[0] == TEXT('@'))
  1659. {
  1660. // it does caching for us
  1661. fRet = SUCCEEDED(SHLoadIndirectString(szResource, _sz, ARRAYSIZE(_sz), NULL));
  1662. // If the call fails, this means that the
  1663. // localized string belongs to a DLL that has been uninstalled.
  1664. // Just return the failure code so we act as if the MUI string
  1665. // isn't there. (Don't show the user "@DLLNAME.DLL,-5" as the
  1666. // name!)
  1667. if (fRet && tbIsMine == TRIBIT_FALSE)
  1668. {
  1669. // reuse szResource as the format string
  1670. StrCpyN(szResource, _sz, ARRAYSIZE(szResource));
  1671. _FormatTheirs(szResource);
  1672. }
  1673. }
  1674. }
  1675. if (fRet)
  1676. _fsin = FSINAME_UI;
  1677. ASSERT(!_fsin || *_sz);
  1678. return fRet;
  1679. }
  1680. BOOL CFileSysItem::CantRename(CFSFolder *pfs)
  1681. {
  1682. // BOOL fRest = SHRestricted(REST_NORENAMELOCALIZED);
  1683. if (_IsPersonalized())
  1684. {
  1685. if (!_IsMine(pfs))
  1686. return TRUE;
  1687. // return fRest;
  1688. }
  1689. else if (_pidfx && _pidfx->offResourceA)
  1690. {
  1691. // return fRest;
  1692. }
  1693. return FALSE;
  1694. }
  1695. UINT _CopyResource(LPWSTR pszSrc, LPSTR pszRes, UINT cchRes)
  1696. {
  1697. ASSERT(*pszSrc == L'@');
  1698. LPWSTR pszS32 = StrStrIW(pszSrc, L"shell32.dll");
  1699. if (pszS32)
  1700. {
  1701. *(--pszS32) = L'@';
  1702. pszSrc = pszS32;
  1703. }
  1704. return SHUnicodeToAnsi(pszSrc, pszRes, cchRes);
  1705. }
  1706. UINT CFSFolder::_GetItemExStrings(LPCIDFOLDER pidfSimpleParent, const WIN32_FIND_DATA *pfd, EXSTRINGS *pxs)
  1707. {
  1708. UINT cbRet = 0;
  1709. TCHAR szTemp[MAX_PATH];
  1710. if ((pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  1711. && (pfd->dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
  1712. {
  1713. if (SUCCEEDED(_GetPathForItem(pidfSimpleParent, szTemp)))
  1714. {
  1715. PathAppend(szTemp, pfd->cFileName);
  1716. if (GetFolderStringEx(szTemp, _pszNetProvider, L"DeleteOnCopy", SZ_CANBEUNICODE TEXT("Owner"), pxs->idp.szUserName, ARRAYSIZE(pxs->idp.szUserName)))
  1717. {
  1718. pxs->idp.hid.cb = sizeof(pxs->idp.hid) + CbFromCchW(lstrlenW(pxs->idp.szUserName) + 1);
  1719. pxs->idp.hid.id = IDLHID_PERSONALIZED;
  1720. WCHAR szFile[MAX_PATH];
  1721. if (GetFolderStringEx(szTemp, _pszNetProvider, L"DeleteOnCopy", SZ_CANBEUNICODE TEXT("PersonalizedName"), szFile, ARRAYSIZE(szFile)))
  1722. {
  1723. if (0 == StrCmpI(pfd->cFileName, szFile))
  1724. pxs->idp.hid.wVersion = (WORD) GetFolderInt(szTemp, _pszNetProvider, L"DeleteOnCopy", TEXT("Personalized"), -1);
  1725. }
  1726. }
  1727. else if (GetFolderString(szTemp, _pszNetProvider, szTemp, ARRAYSIZE(szTemp), TEXT("LocalizedResourceName")))
  1728. {
  1729. pxs->cbResource = _CopyResource(szTemp, pxs->szResource, ARRAYSIZE(pxs->szResource));
  1730. cbRet += pxs->cbResource;
  1731. }
  1732. }
  1733. }
  1734. else if (!pidfSimpleParent && _IsSelfSystemFolder())
  1735. {
  1736. if (_HasLocalizedFileNames() && SUCCEEDED(_GetPath(szTemp)))
  1737. {
  1738. if (GetFolderStringEx(szTemp, _pszNetProvider, TEXT("LocalizedFileNames"), pfd->cFileName, szTemp, ARRAYSIZE(szTemp)))
  1739. {
  1740. pxs->cbResource = _CopyResource(szTemp, pxs->szResource, ARRAYSIZE(pxs->szResource));
  1741. cbRet += pxs->cbResource;
  1742. }
  1743. }
  1744. }
  1745. return cbRet;
  1746. }
  1747. BOOL _PrepIDFName(const WIN32_FIND_DATA *pfd, LPSTR psz, DWORD cch, const void **ppvName, UINT *pcbName)
  1748. {
  1749. // the normal case:
  1750. // the altname should only not be filled in
  1751. // in the case of the name being a shortname (ASCII)
  1752. LPCWSTR pwsz = *pfd->cAlternateFileName && !(SHGetAppCompatFlags(ACF_FORCELFNIDLIST) & ACF_FORCELFNIDLIST)
  1753. ? pfd->cAlternateFileName : pfd->cFileName;
  1754. if (DoesStringRoundTrip(pwsz, psz, cch))
  1755. {
  1756. *pcbName = lstrlenA(psz) + 1;
  1757. *ppvName = psz;
  1758. }
  1759. else
  1760. {
  1761. *pcbName = CbFromCchW(lstrlenW(pwsz) + 1);
  1762. *ppvName = pfd->cFileName;
  1763. }
  1764. return *ppvName != psz;
  1765. }
  1766. HRESULT CFSFolder::_CreateIDList(const WIN32_FIND_DATA *pfd, LPCIDFOLDER pidfSimpleParent, LPITEMIDLIST *ppidl)
  1767. {
  1768. // for the idf
  1769. CHAR szNameIDF[MAX_PATH];
  1770. UINT cbNameIDF;
  1771. const void *pvNameIDF;
  1772. BOOL fNeedsUnicode = _PrepIDFName(pfd, szNameIDF, ARRAYSIZE(szNameIDF), &pvNameIDF, &cbNameIDF);
  1773. UINT cbIDF = FIELD_OFFSET(IDFOLDER, cFileName) + cbNameIDF;
  1774. ASSERT(*((char *)pvNameIDF));
  1775. // for the idfx
  1776. UINT cbNameIDFX = CbFromCchW(lstrlenW(pfd->cFileName) + 1);
  1777. EXSTRINGS xs = {0};
  1778. UINT cbIDFX = sizeof(IDFOLDEREX) + cbNameIDFX + _GetItemExStrings(pidfSimpleParent, pfd, &xs);
  1779. // try to align these babies
  1780. cbIDF = ROUNDUP(cbIDF, 2);
  1781. cbIDFX = ROUNDUP(cbIDFX, 2);
  1782. // ILCreateWithHidden() fills in the cb values
  1783. LPIDFOLDER pidf = (LPIDFOLDER)ILCreateWithHidden(cbIDF, cbIDFX);
  1784. if (pidf)
  1785. {
  1786. // initialize the idf
  1787. // tag files > 4G so we can do a full find first when we need to know the real size
  1788. pidf->dwSize = pfd->nFileSizeHigh ? 0xFFFFFFFF : pfd->nFileSizeLow;
  1789. pidf->wAttrs = (WORD)pfd->dwFileAttributes;
  1790. // Since the idf entry is not aligned, we cannot just send the address
  1791. // of one of its members blindly into FileTimeToDosDateTime.
  1792. WORD date, time;
  1793. if (FileTimeToDosDateTime(&pfd->ftLastWriteTime, &date, &time))
  1794. {
  1795. *((UNALIGNED WORD *)&pidf->dateModified) = date;
  1796. *((UNALIGNED WORD *)&pidf->timeModified) = time;
  1797. }
  1798. // copy the short name
  1799. memcpy(pidf->cFileName, pvNameIDF, cbNameIDF);
  1800. // setup bFlags
  1801. pidf->bFlags = pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? SHID_FS_DIRECTORY : SHID_FS_FILE;
  1802. if (CSIDL_COMMON_DESKTOPDIRECTORY == _csidlTrack)
  1803. pidf->bFlags |= SHID_FS_COMMONITEM;
  1804. if (fNeedsUnicode)
  1805. pidf->bFlags |= SHID_FS_UNICODE;
  1806. // now initialize the hidden idfx
  1807. PIDFOLDEREX pidfx = (PIDFOLDEREX) _ILSkip((LPITEMIDLIST)pidf, cbIDF);
  1808. pidfx->hid.id = IDLHID_IDFOLDEREX;
  1809. pidfx->hid.wVersion = IDFX_CV;
  1810. if (FileTimeToDosDateTime(&pfd->ftCreationTime, &date, &time))
  1811. {
  1812. pidfx->dsCreate.wDate = date;
  1813. pidfx->dsCreate.wTime = time;
  1814. }
  1815. if (FileTimeToDosDateTime(&pfd->ftLastAccessTime, &date, &time))
  1816. {
  1817. pidfx->dsAccess.wDate = date;
  1818. pidfx->dsAccess.wTime = time;
  1819. }
  1820. // append the strings
  1821. pidfx->offNameW = (USHORT) sizeof(IDFOLDEREX);
  1822. ualstrcpyW(UASTROFFW(pidfx, pidfx->offNameW), pfd->cFileName);
  1823. USHORT offNext = (USHORT) sizeof(IDFOLDEREX) + cbNameIDFX;
  1824. if (xs.cbResource)
  1825. {
  1826. pidfx->offResourceA = offNext;
  1827. ualstrcpyA(UASTROFFA(pidfx, pidfx->offResourceA), xs.szResource);
  1828. // offNext += (USHORT) xs.cbResource; if we have more offsets...
  1829. }
  1830. pidf = _MarkAsJunction(pidfSimpleParent, pidf, pfd->cFileName);
  1831. if (pidf && xs.idp.hid.cb)
  1832. pidf = (LPIDFOLDER) ILAppendHiddenID((LPITEMIDLIST)pidf, &xs.idp.hid);
  1833. }
  1834. *ppidl = (LPITEMIDLIST)pidf;
  1835. return *ppidl != NULL ? S_OK : E_OUTOFMEMORY;
  1836. }
  1837. BOOL _ValidPathSegment(LPCTSTR pszSegment)
  1838. {
  1839. if (*pszSegment && !PathIsDotOrDotDot(pszSegment))
  1840. {
  1841. for (LPCTSTR psz = pszSegment; *psz; psz = CharNext(psz))
  1842. {
  1843. if (!PathIsValidChar(*psz, PIVC_LFN_NAME))
  1844. return FALSE;
  1845. }
  1846. return TRUE;
  1847. }
  1848. return FALSE;
  1849. }
  1850. // used to parse up file path like strings:
  1851. // "folder\folder\file.txt"
  1852. // "file.txt"
  1853. //
  1854. // in/out:
  1855. // *ppszIn in: pointer to start of the buffer,
  1856. // output: advanced to next location, NULL on last segment
  1857. // out:
  1858. // *pszSegment NULL if nothing left
  1859. //
  1860. // returns:
  1861. // S_OK got a segment
  1862. // S_FALSE loop done, *pszSegment emtpy
  1863. // E_INVALIDARG invalid input "", "\foo", "\\foo", "foo\\bar", "?<>*" chars in seg
  1864. HRESULT _NextSegment(LPCWSTR *ppszIn, LPTSTR pszSegment, UINT cchSegment, BOOL bValidate)
  1865. {
  1866. HRESULT hr;
  1867. *pszSegment = 0;
  1868. if (*ppszIn)
  1869. {
  1870. // WARNING! Do not use StrPBrkW(*ppszIn, L"\\/"), because
  1871. // Trident passes fully-qualified URLs to
  1872. // SHGetFileInfo(USEFILEATTRIBUTES) and relies on the fact that
  1873. // we won't choke on the embedded "//" in "http://".
  1874. LPWSTR pszSlash = StrChrW(*ppszIn, L'\\');
  1875. if (pszSlash)
  1876. {
  1877. if (pszSlash > *ppszIn) // make sure well formed (no dbl slashes)
  1878. {
  1879. OleStrToStrN(pszSegment, cchSegment, *ppszIn, (int)(pszSlash - *ppszIn));
  1880. // make sure that there is another segment to return
  1881. if (!*(++pszSlash))
  1882. pszSlash = NULL;
  1883. hr = S_OK;
  1884. }
  1885. else
  1886. {
  1887. pszSlash = NULL;
  1888. hr = E_INVALIDARG; // bad input
  1889. }
  1890. }
  1891. else
  1892. {
  1893. SHUnicodeToTChar(*ppszIn, pszSegment, cchSegment);
  1894. hr = S_OK;
  1895. }
  1896. *ppszIn = pszSlash;
  1897. if (hr == S_OK && bValidate && !_ValidPathSegment(pszSegment))
  1898. {
  1899. *pszSegment = 0;
  1900. hr = E_INVALIDARG;
  1901. }
  1902. }
  1903. else
  1904. hr = S_FALSE; // done with loop
  1905. return hr;
  1906. }
  1907. // this makes a fake wfd and then uses the normal
  1908. // FillIDFolder as if it were a real found path.
  1909. HRESULT CFSFolder::_ParseSimple(LPCWSTR pszPath, const WIN32_FIND_DATA *pfdLast, LPITEMIDLIST *ppidl)
  1910. {
  1911. WIN32_FIND_DATA wfd = {0};
  1912. HRESULT hr = S_OK;
  1913. *ppidl = NULL;
  1914. ASSERT(*pszPath);
  1915. while (SUCCEEDED(hr) && (S_OK == (hr = _NextSegment((LPCWSTR *)&pszPath, wfd.cFileName, ARRAYSIZE(wfd.cFileName), FALSE))))
  1916. {
  1917. LPITEMIDLIST pidl;
  1918. if (pszPath)
  1919. {
  1920. // internal componets must be folders
  1921. wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  1922. }
  1923. else
  1924. {
  1925. // last segment takes the find data from that passed in
  1926. // copy everything except the cFileName field
  1927. memcpy(&wfd, pfdLast, FIELD_OFFSET(WIN32_FIND_DATA, cFileName));
  1928. lstrcpyn(wfd.cAlternateFileName, pfdLast->cAlternateFileName, ARRAYSIZE(wfd.cAlternateFileName));
  1929. }
  1930. hr = _CreateIDList(&wfd, (LPCIDFOLDER)*ppidl, &pidl);
  1931. if (SUCCEEDED(hr))
  1932. hr = SHILAppend(pidl, ppidl);
  1933. }
  1934. if (FAILED(hr))
  1935. {
  1936. if (*ppidl)
  1937. {
  1938. ILFree(*ppidl);
  1939. *ppidl = NULL;
  1940. }
  1941. }
  1942. else
  1943. hr = S_OK; // pin all success to S_OK
  1944. return hr;
  1945. }
  1946. BOOL IsAllWhiteSpace(LPCTSTR pszString)
  1947. {
  1948. while (*pszString)
  1949. {
  1950. if ((TEXT(' ') == *pszString) ||
  1951. (TEXT('\t') == *pszString))
  1952. {
  1953. pszString++; // keep walking the string
  1954. }
  1955. else
  1956. {
  1957. return FALSE; // something other than a space or tab, done
  1958. }
  1959. }
  1960. return TRUE; // made it through the loop, just spaces or tabs in this string
  1961. }
  1962. HRESULT _CheckPortName(LPCTSTR pszName)
  1963. {
  1964. if (PathIsInvalid(pszName))
  1965. return HRESULT_FROM_WIN32(ERROR_BAD_DEVICE);
  1966. else
  1967. return S_OK;
  1968. }
  1969. class CFindFirstWithTimeout
  1970. {
  1971. public:
  1972. CFindFirstWithTimeout(LPCTSTR pszPath, DWORD dwTicksToAllow);
  1973. HRESULT FindFirstWithTimeout(WIN32_FIND_DATA *pfd);
  1974. ULONG AddRef();
  1975. ULONG Release();
  1976. private:
  1977. static DWORD WINAPI _FindFistThreadProc(void *pv);
  1978. LONG _cRef;
  1979. DWORD _dwTicksToAllow;
  1980. TCHAR _szPath[MAX_PATH];
  1981. WIN32_FIND_DATA _fd;
  1982. };
  1983. CFindFirstWithTimeout::CFindFirstWithTimeout(LPCTSTR pszPath, DWORD dwTicksToAllow) : _cRef(1), _dwTicksToAllow(dwTicksToAllow)
  1984. {
  1985. lstrcpyn(_szPath, pszPath, ARRAYSIZE(_szPath));
  1986. }
  1987. ULONG CFindFirstWithTimeout::AddRef()
  1988. {
  1989. return InterlockedIncrement(&_cRef);
  1990. }
  1991. ULONG CFindFirstWithTimeout::Release()
  1992. {
  1993. if (InterlockedDecrement(&_cRef))
  1994. return _cRef;
  1995. delete this;
  1996. return 0;
  1997. }
  1998. DWORD CFindFirstWithTimeout::_FindFistThreadProc(void *pv)
  1999. {
  2000. CFindFirstWithTimeout *pffwt = (CFindFirstWithTimeout *)pv;
  2001. HRESULT hr = SHFindFirstFileRetry(NULL, NULL, pffwt->_szPath, &pffwt->_fd, NULL, SHPPFW_NONE);
  2002. pffwt->Release();
  2003. return hr; // retrieved via GetExitCodeThread()
  2004. }
  2005. HRESULT CFindFirstWithTimeout::FindFirstWithTimeout(WIN32_FIND_DATA *pfd)
  2006. {
  2007. HRESULT hr;
  2008. AddRef(); // ref for the thread
  2009. DWORD dwID;
  2010. HANDLE hThread = CreateThread(NULL, 0, _FindFistThreadProc, this, 0, &dwID);
  2011. if (hThread)
  2012. {
  2013. // assume timeout...
  2014. hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); // timeout return value
  2015. if (WAIT_OBJECT_0 == WaitForSingleObject(hThread, _dwTicksToAllow))
  2016. {
  2017. // thread finished with an HRESULT for us
  2018. DWORD dw;
  2019. if (GetExitCodeThread(hThread, &dw))
  2020. {
  2021. *pfd = _fd;
  2022. hr = dw; // HRESULT returned by _FindFistThreadProc
  2023. }
  2024. }
  2025. CloseHandle(hThread);
  2026. }
  2027. else
  2028. {
  2029. hr = E_OUTOFMEMORY;
  2030. Release(); // thread create failed, remove that ref
  2031. }
  2032. return hr;
  2033. }
  2034. HRESULT SHFindFirstFileWithTimeout(LPCTSTR pszPath, DWORD dwTicksToAllow, WIN32_FIND_DATA *pfd)
  2035. {
  2036. HRESULT hr;
  2037. CFindFirstWithTimeout *pffwt = new CFindFirstWithTimeout(pszPath, dwTicksToAllow);
  2038. if (pffwt)
  2039. {
  2040. hr = pffwt->FindFirstWithTimeout(pfd);
  2041. pffwt->Release();
  2042. }
  2043. else
  2044. {
  2045. hr = E_OUTOFMEMORY;
  2046. }
  2047. return hr;
  2048. }
  2049. HRESULT CFSFolder::_FindDataFromName(LPCTSTR pszName, DWORD dwAttribs, IBindCtx *pbc, WIN32_FIND_DATA **ppfd)
  2050. {
  2051. *ppfd = NULL;
  2052. HRESULT hr = _CheckPortName(pszName);
  2053. if (SUCCEEDED(hr))
  2054. {
  2055. hr = SHLocalAlloc(sizeof(**ppfd), ppfd);
  2056. if (SUCCEEDED(hr))
  2057. {
  2058. if (-1 == dwAttribs)
  2059. {
  2060. TCHAR szPath[MAX_PATH];
  2061. hr = _GetPath(szPath);
  2062. if (SUCCEEDED(hr))
  2063. {
  2064. PathAppend(szPath, pszName);
  2065. DWORD dwTicksToAllow;
  2066. if (SUCCEEDED(BindCtx_GetTimeoutDelta(pbc, &dwTicksToAllow)) && PathIsNetworkPath(szPath))
  2067. {
  2068. hr = SHFindFirstFileWithTimeout(szPath, dwTicksToAllow, *ppfd);
  2069. }
  2070. else
  2071. {
  2072. hr = SHFindFirstFileRetry(NULL, NULL, szPath, *ppfd, NULL, SHPPFW_NONE);
  2073. }
  2074. }
  2075. }
  2076. else
  2077. {
  2078. // make a simple one up
  2079. StrCpyN((*ppfd)->cFileName, pszName, ARRAYSIZE((*ppfd)->cFileName));
  2080. (*ppfd)->dwFileAttributes = dwAttribs;
  2081. }
  2082. if (FAILED(hr))
  2083. {
  2084. LocalFree(*ppfd);
  2085. *ppfd = NULL;
  2086. }
  2087. }
  2088. }
  2089. ASSERT(SUCCEEDED(hr) ? NULL != *ppfd : NULL == *ppfd);
  2090. return hr;
  2091. }
  2092. //
  2093. // This function returns a relative pidl for the specified file/folder
  2094. //
  2095. HRESULT CFSFolder::_CreateIDListFromName(LPCTSTR pszName, DWORD dwAttribs, IBindCtx *pbc, LPITEMIDLIST *ppidl)
  2096. {
  2097. WIN32_FIND_DATA *pfd;
  2098. HRESULT hr = _FindDataFromName(pszName, dwAttribs, pbc, &pfd);
  2099. if (SUCCEEDED(hr))
  2100. {
  2101. hr = _CreateIDList(pfd, NULL, ppidl);
  2102. LocalFree(pfd);
  2103. }
  2104. else
  2105. *ppidl = NULL;
  2106. return hr;
  2107. }
  2108. // used to detect if a name is a folder. this is used in the case that the
  2109. // security for this folders parent is set so you can't enum it's contents
  2110. BOOL CFSFolder::_CanSeeInThere(LPCTSTR pszName)
  2111. {
  2112. TCHAR szPath[MAX_PATH];
  2113. if (SUCCEEDED(_GetPath(szPath)))
  2114. {
  2115. HANDLE hfind;
  2116. WIN32_FIND_DATA fd;
  2117. PathAppend(szPath, pszName);
  2118. PathAppend(szPath, TEXT("*.*"));
  2119. hfind = FindFirstFile(szPath, &fd);
  2120. if (hfind != INVALID_HANDLE_VALUE)
  2121. FindClose(hfind);
  2122. return hfind != INVALID_HANDLE_VALUE;
  2123. }
  2124. return FALSE;
  2125. }
  2126. HRESULT CFSFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
  2127. {
  2128. static const QITAB qit[] = {
  2129. QITABENTMULTI(CFSFolder, IShellFolder, IShellFolder2),
  2130. QITABENT(CFSFolder, IShellFolder2),
  2131. QITABENT(CFSFolder, IShellIconOverlay),
  2132. QITABENT(CFSFolder, IShellIcon),
  2133. QITABENTMULTI(CFSFolder, IPersist, IPersistFolder3),
  2134. QITABENTMULTI(CFSFolder, IPersistFolder, IPersistFolder3),
  2135. QITABENTMULTI(CFSFolder, IPersistFolder2, IPersistFolder3),
  2136. QITABENT(CFSFolder, IPersistFolder3),
  2137. QITABENT(CFSFolder, IStorage),
  2138. QITABENT(CFSFolder, IPropertySetStorage),
  2139. QITABENT(CFSFolder, IItemNameLimits),
  2140. QITABENT(CFSFolder, IContextMenuCB),
  2141. QITABENT(CFSFolder, ISetFolderEnumRestriction),
  2142. QITABENT(CFSFolder, IOleCommandTarget),
  2143. { 0 },
  2144. };
  2145. HRESULT hr = QISearch(this, qit, riid, ppv);
  2146. if (FAILED(hr))
  2147. {
  2148. if (IsEqualIID(IID_INeedRealCFSFolder, riid))
  2149. {
  2150. *ppv = this; // not ref counted
  2151. hr = S_OK;
  2152. }
  2153. else if (IsEqualIID(riid, IID_IPersistFreeThreadedObject))
  2154. {
  2155. if (_GetInner() == _GetOuter()) // not aggregated
  2156. {
  2157. hr = QueryInterface(IID_IPersist, ppv);
  2158. }
  2159. else
  2160. {
  2161. hr = E_NOINTERFACE;
  2162. }
  2163. }
  2164. }
  2165. return hr;
  2166. }
  2167. // briefcase and file system folder call to reset data
  2168. HRESULT CFSFolder::_Reset()
  2169. {
  2170. _DestroyColHandlers();
  2171. if (_pidl)
  2172. {
  2173. ILFree(_pidl);
  2174. _pidl = NULL;
  2175. }
  2176. if (_pidlTarget)
  2177. {
  2178. ILFree(_pidlTarget);
  2179. _pidlTarget = NULL;
  2180. }
  2181. if (_pszPath)
  2182. {
  2183. LocalFree(_pszPath);
  2184. _pszPath = NULL;
  2185. }
  2186. if (_pszNetProvider)
  2187. {
  2188. LocalFree(_pszNetProvider);
  2189. _pszNetProvider = NULL;
  2190. }
  2191. _csidl = -1;
  2192. _dwAttributes = -1;
  2193. _csidlTrack = -1;
  2194. ATOMICRELEASE(_pstg);
  2195. return S_OK;
  2196. }
  2197. #define INVALID_PATHSPEED (-100)
  2198. CFSFolder::CFSFolder(IUnknown *punkOuter) : CAggregatedUnknown(punkOuter)
  2199. {
  2200. _csidl = -1;
  2201. _iFolderIcon = -1;
  2202. _dwAttributes = -1;
  2203. _csidlTrack = -1;
  2204. _nFolderType = FVCBFT_DOCUMENTS;
  2205. _bSlowPath = INVALID_PATHSPEED; // some non-common value
  2206. // Note: BOOL is not bool
  2207. _tbOfflineCSC = TRIBIT_UNDEFINED;
  2208. DllAddRef();
  2209. }
  2210. CFSFolder::~CFSFolder()
  2211. {
  2212. _Reset();
  2213. DllRelease();
  2214. }
  2215. // we need to fail relative type paths since we use PathCombine
  2216. // and we don't want that and the Win32 APIs to give us relative path behavior
  2217. // ShellExecute() depends on this so it falls back and resolves the relative paths itself
  2218. HRESULT CFSFolder::ParseDisplayName(HWND hwnd, IBindCtx *pbc, WCHAR *pszName, ULONG *pchEaten,
  2219. LPITEMIDLIST *ppidl, DWORD *pdwAttributes)
  2220. {
  2221. HRESULT hr;
  2222. WIN32_FIND_DATA *pfd;
  2223. if (!ppidl)
  2224. return E_INVALIDARG;
  2225. *ppidl = NULL; // assume error
  2226. if (pszName == NULL)
  2227. return E_INVALIDARG;
  2228. if (S_OK == SHIsFileSysBindCtx(pbc, &pfd))
  2229. {
  2230. hr = _ParseSimple(pszName, pfd, ppidl);
  2231. if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
  2232. {
  2233. // while strictly not a legit thing to do here, we
  2234. // pass the last IDList because 1) this is a simple IDList
  2235. // 2) we hope that callers don't ask for bits that
  2236. // require a full path to be valid inside the impl of
  2237. // ::GetAttributesOf()
  2238. LPCITEMIDLIST pidlLast = ILFindLastID(*ppidl);
  2239. GetAttributesOf(1, &pidlLast, pdwAttributes);
  2240. }
  2241. LocalFree(pfd);
  2242. }
  2243. else
  2244. {
  2245. DWORD cchNext = lstrlen(pszName) + 1;
  2246. WCHAR *pszNext = (WCHAR *)alloca(CbFromCchW(cchNext));
  2247. hr = _NextSegment((LPCWSTR *)&pszName, pszNext, cchNext, TRUE);
  2248. if (SUCCEEDED(hr))
  2249. {
  2250. hr = _CreateIDListFromName(pszNext, -1, pbc, ppidl);
  2251. if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
  2252. {
  2253. // security "List folder contents" may be disabled for
  2254. // this items parent. so see if this is really there
  2255. if (pszName || _CanSeeInThere(pszNext))
  2256. {
  2257. hr = _CreateIDListFromName(pszNext, FILE_ATTRIBUTE_DIRECTORY, pbc, ppidl);
  2258. }
  2259. }
  2260. else if (((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))) &&
  2261. (pszName == NULL) &&
  2262. (BindCtx_GetMode(pbc, 0) & STGM_CREATE) &&
  2263. !_fDontForceCreate)
  2264. {
  2265. // create a pidl to something that doesnt exist.
  2266. hr = _CreateIDListFromName(pszNext, FILE_ATTRIBUTE_NORMAL, pbc, ppidl);
  2267. }
  2268. if (SUCCEEDED(hr))
  2269. {
  2270. if (pszName) // more stuff to parse?
  2271. {
  2272. IShellFolder *psfFolder;
  2273. hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psfFolder));
  2274. if (SUCCEEDED(hr))
  2275. {
  2276. ULONG chEaten;
  2277. LPITEMIDLIST pidlNext;
  2278. hr = psfFolder->ParseDisplayName(hwnd, pbc,
  2279. pszName, &chEaten, &pidlNext, pdwAttributes);
  2280. if (SUCCEEDED(hr))
  2281. {
  2282. hr = SHILAppend(pidlNext, ppidl);
  2283. }
  2284. psfFolder->Release();
  2285. }
  2286. }
  2287. else
  2288. {
  2289. if (pdwAttributes && *pdwAttributes)
  2290. GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes);
  2291. }
  2292. }
  2293. }
  2294. }
  2295. if (FAILED(hr) && *ppidl)
  2296. {
  2297. // This is needed if psfFolder->ParseDisplayName() or BindToObject()
  2298. // fails because the pidl is already allocated.
  2299. ILFree(*ppidl);
  2300. *ppidl = NULL;
  2301. }
  2302. ASSERT(SUCCEEDED(hr) ? (*ppidl != NULL) : (*ppidl == NULL));
  2303. // display this only as a warning, this can get hit during mergfldr or IStorage::Create probes
  2304. if (FAILED(hr))
  2305. TraceMsg(TF_WARNING, "CFSFolder::ParseDisplayName(), hr:%x %ls", hr, pszName);
  2306. return hr;
  2307. }
  2308. STDAPI InitFileFolderClassNames(void)
  2309. {
  2310. if (g_szFileTemplate[0] == 0) // test last one to avoid race
  2311. {
  2312. LoadString(HINST_THISDLL, IDS_FOLDERTYPENAME, g_szFolderTypeName, ARRAYSIZE(g_szFolderTypeName));
  2313. LoadString(HINST_THISDLL, IDS_FILETYPENAME, g_szFileTypeName, ARRAYSIZE(g_szFileTypeName));
  2314. LoadString(HINST_THISDLL, IDS_EXTTYPETEMPLATE, g_szFileTemplate, ARRAYSIZE(g_szFileTemplate));
  2315. }
  2316. return S_OK;
  2317. }
  2318. HRESULT CFSFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
  2319. {
  2320. InitFileFolderClassNames();
  2321. grfFlags |= _dwEnumRequired;
  2322. grfFlags &= ~_dwEnumForbidden;
  2323. return CFSFolder_CreateEnum(this, hwnd, grfFlags, ppenum);
  2324. }
  2325. HRESULT CFSFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  2326. {
  2327. // MIL 117282 - Enroute Imaging QuickStitch depends on pre-Jan'97 behavior of us
  2328. // *not* nulling ppv out on !_IsValidID(pidl). (They pass in a perfectly valid
  2329. // IShellFolder* interfacing asking for IID_IShellFolder on the empty PIDL.)
  2330. //
  2331. if (!(SHGetAppCompatFlags(ACF_WIN95BINDTOOBJECT) & ACF_WIN95BINDTOOBJECT))
  2332. *ppv = NULL;
  2333. HRESULT hr;
  2334. LPCIDFOLDER pidf = _IsValidID(pidl);
  2335. if (pidf)
  2336. {
  2337. LPCITEMIDLIST pidlRight;
  2338. LPIDFOLDER pidfBind;
  2339. hr = _GetJunctionForBind(pidf, &pidfBind, &pidlRight);
  2340. if (SUCCEEDED(hr))
  2341. {
  2342. if (hr == S_OK)
  2343. {
  2344. IShellFolder *psfJunction;
  2345. hr = _Bind(pbc, pidfBind, IID_PPV_ARG(IShellFolder, &psfJunction));
  2346. if (SUCCEEDED(hr))
  2347. {
  2348. // now bind to the stuff below the junction point
  2349. hr = psfJunction->BindToObject(pidlRight, pbc, riid, ppv);
  2350. psfJunction->Release();
  2351. }
  2352. ILFree((LPITEMIDLIST)pidfBind);
  2353. }
  2354. else
  2355. {
  2356. ASSERT(pidfBind == NULL);
  2357. hr = _Bind(pbc, pidf, riid, ppv);
  2358. }
  2359. }
  2360. }
  2361. else
  2362. {
  2363. hr = E_INVALIDARG;
  2364. TraceMsg(TF_WARNING, "CFSFolder::BindToObject(), hr:%x bad PIDL %s", hr, DumpPidl(pidl));
  2365. }
  2366. return hr;
  2367. }
  2368. HRESULT CFSFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  2369. {
  2370. return BindToObject(pidl, pbc, riid, ppv);
  2371. }
  2372. HRESULT CFSFolder::_CheckDriveRestriction(HWND hwnd, REFIID riid)
  2373. {
  2374. HRESULT hr = S_OK;
  2375. DWORD dwRest = SHRestricted(REST_NOVIEWONDRIVE);
  2376. if (dwRest)
  2377. {
  2378. TCHAR szPath[MAX_PATH];
  2379. hr = _GetPath(szPath);
  2380. if (SUCCEEDED(hr))
  2381. {
  2382. int iDrive = PathGetDriveNumber(szPath);
  2383. if (iDrive != -1)
  2384. {
  2385. // is the drive restricted
  2386. if (dwRest & (1 << iDrive))
  2387. {
  2388. // don't show the error message on droptarget -- just fail silently
  2389. if (hwnd && !IsEqualIID(riid, IID_IDropTarget))
  2390. {
  2391. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_RESTRICTIONS),
  2392. MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE), MB_OK | MB_ICONSTOP);
  2393. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // user saw the error
  2394. }
  2395. else
  2396. hr = E_ACCESSDENIED;
  2397. }
  2398. }
  2399. }
  2400. }
  2401. return hr;
  2402. }
  2403. HRESULT CFSFolder::_CreateUIHandler(REFIID riid, void **ppv)
  2404. {
  2405. HRESULT hr;
  2406. // Cache the view CLSID if not cached.
  2407. if (!_fCachedCLSID)
  2408. {
  2409. if (_IsSelfSystemFolder())
  2410. {
  2411. TCHAR szPath[MAX_PATH];
  2412. if (SUCCEEDED(_GetPath(szPath)))
  2413. _fHasCLSID = GetFolderGUID(szPath, _pszNetProvider, &_clsidView, TEXT("UICLSID"));
  2414. _fCachedCLSID = TRUE;
  2415. }
  2416. }
  2417. // Use the handler if it exists
  2418. if (_fHasCLSID)
  2419. {
  2420. IPersistFolder *ppf;
  2421. hr = SHExtCoCreateInstance(NULL, &_clsidView, NULL, IID_PPV_ARG(IPersistFolder, &ppf));
  2422. if (SUCCEEDED(hr))
  2423. {
  2424. hr = ppf->Initialize(_pidl);
  2425. if (FAILED(hr) && _pidlTarget)
  2426. {
  2427. // It may have failed because the _pidl is an alias (not a file folder). if so try
  2428. // again with _pidlTarget (that will be a file system folder)
  2429. // this was required for the Fonts FolderShortcut in the ControlPanel (stephstm)
  2430. hr = ppf->Initialize(_pidlTarget);
  2431. }
  2432. if (SUCCEEDED(hr))
  2433. hr = ppf->QueryInterface(riid, ppv);
  2434. ppf->Release();
  2435. }
  2436. }
  2437. else
  2438. hr = E_FAIL; // no handler
  2439. return hr;
  2440. }
  2441. HRESULT CFSFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  2442. {
  2443. HRESULT hr;
  2444. *ppv = NULL;
  2445. if (IsEqualIID(riid, IID_IShellView) ||
  2446. IsEqualIID(riid, IID_IDropTarget))
  2447. {
  2448. hr = _CheckDriveRestriction(hwnd, riid);
  2449. if (SUCCEEDED(hr))
  2450. {
  2451. hr = _CreateUIHandler(riid, ppv);
  2452. if (FAILED(hr))
  2453. {
  2454. if (IsEqualIID(riid, IID_IDropTarget))
  2455. {
  2456. hr = CFSDropTarget_CreateInstance(this, hwnd, (IDropTarget **)ppv);
  2457. }
  2458. else
  2459. {
  2460. SFV_CREATE csfv = { sizeof(csfv), 0 };
  2461. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &csfv.pshf));
  2462. if (SUCCEEDED(hr))
  2463. {
  2464. CFSFolderCallback_Create(this, &csfv.psfvcb);
  2465. hr = SHCreateShellFolderView(&csfv, (IShellView **)ppv);
  2466. if (csfv.psfvcb)
  2467. csfv.psfvcb->Release();
  2468. csfv.pshf->Release();
  2469. }
  2470. }
  2471. }
  2472. }
  2473. }
  2474. else if (IsEqualIID(riid, IID_IContextMenu))
  2475. {
  2476. // do background menu.
  2477. IShellFolder *psfToPass; // May be an Aggregate...
  2478. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfToPass));
  2479. if (SUCCEEDED(hr))
  2480. {
  2481. LPCITEMIDLIST pidlMenuTarget = (_pidlTarget ? _pidlTarget : _pidl);
  2482. HKEY hkNoFiles;
  2483. RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory\\Background"), &hkNoFiles);
  2484. IContextMenuCB *pcmcb = new CDefBackgroundMenuCB(pidlMenuTarget);
  2485. if (pcmcb)
  2486. {
  2487. hr = CDefFolderMenu_Create2Ex(pidlMenuTarget, hwnd, 0, NULL, psfToPass, pcmcb,
  2488. 1, &hkNoFiles, (IContextMenu **)ppv);
  2489. pcmcb->Release();
  2490. }
  2491. psfToPass->Release();
  2492. if (hkNoFiles) // CDefFolderMenu_Create can handle NULL ok
  2493. RegCloseKey(hkNoFiles);
  2494. }
  2495. }
  2496. else if (IsEqualIID(riid, IID_ICategoryProvider))
  2497. {
  2498. HKEY hk = NULL;
  2499. RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory\\shellex\\Category"), &hk);
  2500. hr = CCategoryProvider_Create(NULL, NULL, hk, NULL, this, riid, ppv);
  2501. if (hk)
  2502. RegCloseKey(hk);
  2503. }
  2504. else
  2505. {
  2506. ASSERT(*ppv == NULL);
  2507. hr = E_NOINTERFACE;
  2508. }
  2509. return hr;
  2510. }
  2511. #define LOGICALXOR(a, b) (((a) && !(b)) || (!(a) && (b)))
  2512. HRESULT CFSFolder::_CompareNames(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2, BOOL fCaseSensitive, BOOL fCanonical)
  2513. {
  2514. CFileSysItemString fsi1(pidf1), fsi2(pidf2);
  2515. int iRet = StrCmpICW(fsi1.FSName(), fsi2.FSName());
  2516. if (iRet)
  2517. {
  2518. //
  2519. // additional check for identity using the 8.3 or AltName()
  2520. // if we are then the identity compare is better based off
  2521. // the AltName() which should be the same regardless of
  2522. // platform or CP.
  2523. //
  2524. if (LOGICALXOR(fsi1.IsLegacy(), fsi2.IsLegacy()))
  2525. {
  2526. if (lstrcmpiA(fsi1.AltName(), fsi2.AltName()) == 0)
  2527. iRet = 0;
  2528. }
  2529. if (iRet && !fCanonical)
  2530. {
  2531. // they are definitely not the same item
  2532. // Sort it based on the primary (long) name -- ignore case.
  2533. int iUI = StrCmpLogicalRestricted(fsi1.UIName(this), fsi2.UIName(this));
  2534. // if they are the same we might want case sensitive instead
  2535. if (iUI == 0 && fCaseSensitive)
  2536. {
  2537. iUI = ustrcmp(fsi1.UIName(this), fsi2.UIName(this));
  2538. }
  2539. if (iUI)
  2540. iRet = iUI;
  2541. }
  2542. }
  2543. return ResultFromShort((short)iRet);
  2544. }
  2545. HRESULT CFSFolder::_CompareFileTypes(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  2546. {
  2547. short result;
  2548. ENTERCRITICAL;
  2549. LPCTSTR psz1 = _GetTypeName(pidf1);
  2550. LPCTSTR psz2 = _GetTypeName(pidf2);
  2551. if (psz1 != psz2)
  2552. result = (short) ustrcmpi(psz1, psz2);
  2553. else
  2554. result = 0;
  2555. LEAVECRITICAL;
  2556. return ResultFromShort(result);
  2557. }
  2558. HRESULT CFSFolder::_CompareModifiedDate(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  2559. {
  2560. if ((DWORD)MAKELONG(pidf1->timeModified, pidf1->dateModified) <
  2561. (DWORD)MAKELONG(pidf2->timeModified, pidf2->dateModified))
  2562. {
  2563. return ResultFromShort(-1);
  2564. }
  2565. if ((DWORD)MAKELONG(pidf1->timeModified, pidf1->dateModified) >
  2566. (DWORD)MAKELONG(pidf2->timeModified, pidf2->dateModified))
  2567. {
  2568. return ResultFromShort(1);
  2569. }
  2570. return ResultFromShort(0);
  2571. }
  2572. HRESULT CFSFolder::_CompareCreateTime(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  2573. {
  2574. WIN32_FIND_DATAW wfd1, wfd2;
  2575. if (SUCCEEDED(_FindDataFromIDFolder(pidf1, &wfd1, FALSE)) && SUCCEEDED(_FindDataFromIDFolder(pidf2, &wfd2, FALSE)))
  2576. {
  2577. return ResultFromShort(CompareFileTime(&wfd1.ftCreationTime, &wfd2.ftCreationTime));
  2578. }
  2579. return ResultFromShort(0);
  2580. }
  2581. HRESULT CFSFolder::_CompareAccessTime(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  2582. {
  2583. WIN32_FIND_DATAW wfd1, wfd2;
  2584. if (SUCCEEDED(_FindDataFromIDFolder(pidf1, &wfd1, FALSE)) && SUCCEEDED(_FindDataFromIDFolder(pidf2, &wfd2, FALSE)))
  2585. {
  2586. return ResultFromShort(CompareFileTime(&wfd1.ftLastAccessTime, &wfd2.ftLastAccessTime));
  2587. }
  2588. return ResultFromShort(0);
  2589. }
  2590. HRESULT CFSFolder::_CompareAttribs(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  2591. {
  2592. const DWORD mask = FILE_ATTRIBUTE_READONLY |
  2593. FILE_ATTRIBUTE_HIDDEN |
  2594. FILE_ATTRIBUTE_SYSTEM |
  2595. FILE_ATTRIBUTE_ARCHIVE |
  2596. FILE_ATTRIBUTE_COMPRESSED|
  2597. FILE_ATTRIBUTE_ENCRYPTED |
  2598. FILE_ATTRIBUTE_OFFLINE;
  2599. // Calculate value of desired bits in attribute DWORD.
  2600. DWORD dwValueA = pidf1->wAttrs & mask;
  2601. DWORD dwValueB = pidf2->wAttrs & mask;
  2602. if (dwValueA != dwValueB)
  2603. {
  2604. // If the values are not equal,
  2605. // sort alphabetically based on string representation.
  2606. TCHAR szTempA[ARRAYSIZE(g_adwAttributeBits) + 1];
  2607. TCHAR szTempB[ARRAYSIZE(g_adwAttributeBits) + 1];
  2608. // Create attribute string for objects A and B.
  2609. BuildAttributeString(pidf1->wAttrs, szTempA, ARRAYSIZE(szTempA));
  2610. BuildAttributeString(pidf2->wAttrs, szTempB, ARRAYSIZE(szTempB));
  2611. // Compare attribute strings and determine difference.
  2612. int diff = ustrcmp(szTempA, szTempB);
  2613. if (diff > 0)
  2614. return ResultFromShort(1);
  2615. if (diff < 0)
  2616. return ResultFromShort(-1);
  2617. }
  2618. return ResultFromShort(0);
  2619. }
  2620. HRESULT CFSFolder::_CompareFolderness(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  2621. {
  2622. if (_IsReal(pidf1) && _IsReal(pidf2))
  2623. {
  2624. // Always put the folders first
  2625. if (_IsFolder(pidf1))
  2626. {
  2627. if (!_IsFolder(pidf2))
  2628. return ResultFromShort(-1);
  2629. }
  2630. else if (_IsFolder(pidf2))
  2631. return ResultFromShort(1);
  2632. }
  2633. return ResultFromShort(0); // same
  2634. }
  2635. HRESULT CFSFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2636. {
  2637. HRESULT hr;
  2638. LPCIDFOLDER pidf1 = _IsValidID(pidl1);
  2639. LPCIDFOLDER pidf2 = _IsValidID(pidl2);
  2640. if (!pidf1 || !pidf2)
  2641. {
  2642. // ASSERT(0); // we hit this often... who is the bad guy?
  2643. return E_INVALIDARG;
  2644. }
  2645. hr = _CompareFolderness(pidf1, pidf2);
  2646. if (hr != ResultFromShort(0))
  2647. return hr;
  2648. // SHCIDS_ALLFIELDS means to compare absolutely, ie: even if only filetimes
  2649. // are different, we rule file pidls to be different
  2650. int iColumn = ((DWORD)lParam & SHCIDS_COLUMNMASK);
  2651. switch (iColumn)
  2652. {
  2653. case FS_ICOL_SIZE:
  2654. {
  2655. ULONGLONG ull1 = _Size(pidf1);
  2656. ULONGLONG ull2 = _Size(pidf2);
  2657. if (ull1 < ull2)
  2658. return ResultFromShort(-1);
  2659. if (ull1 > ull2)
  2660. return ResultFromShort(1);
  2661. }
  2662. goto DoDefault;
  2663. case FS_ICOL_TYPE:
  2664. hr = _CompareFileTypes(pidf1, pidf2);
  2665. if (!hr)
  2666. goto DoDefault;
  2667. break;
  2668. case FS_ICOL_WRITETIME:
  2669. hr = _CompareModifiedDate(pidf1, pidf2);
  2670. if (!hr)
  2671. goto DoDefault;
  2672. break;
  2673. case FS_ICOL_NAME:
  2674. hr = _CompareNames(pidf1, pidf2, TRUE, BOOLIFY((SHCIDS_CANONICALONLY & lParam)));
  2675. if (hr == ResultFromShort(0))
  2676. {
  2677. // pidl1 is not simple
  2678. hr = ILCompareRelIDs(this, pidl1, pidl2, lParam);
  2679. goto DoDefaultModification;
  2680. }
  2681. break;
  2682. case FS_ICOL_CREATETIME:
  2683. hr = _CompareCreateTime(pidf1, pidf2);
  2684. if (!hr)
  2685. goto DoDefault;
  2686. break;
  2687. case FS_ICOL_ACCESSTIME:
  2688. hr = _CompareAccessTime(pidf1, pidf2);
  2689. if (!hr)
  2690. goto DoDefault;
  2691. break;
  2692. case FS_ICOL_ATTRIB:
  2693. hr = _CompareAttribs(pidf1, pidf2);
  2694. if (hr)
  2695. return hr;
  2696. goto DoDefault;
  2697. default:
  2698. iColumn -= ARRAYSIZE(c_fs_cols);
  2699. // 99/03/24 #295631 vtan: If not one of the standard columns then
  2700. // it's probably an extended column. Make a check for dates.
  2701. // 99/05/18 #341468 vtan: But also fail if it is an extended column
  2702. // because this implementation of IShellFolder::CompareIDs only
  2703. // understands basic file system columns and extended date columns.
  2704. if (iColumn >= 0)
  2705. {
  2706. hr = _CompareExtendedProp(iColumn, pidf1, pidf2);
  2707. if (hr)
  2708. return hr;
  2709. }
  2710. DoDefault:
  2711. hr = _CompareNames(pidf1, pidf2, FALSE, BOOLIFY((SHCIDS_CANONICALONLY & lParam)));
  2712. }
  2713. DoDefaultModification:
  2714. // If they were equal so far, but the caller wants SHCIDS_ALLFIELDS,
  2715. // then look closer.
  2716. if ((S_OK == hr) && (lParam & SHCIDS_ALLFIELDS))
  2717. {
  2718. // Must sort by modified date to pick up any file changes!
  2719. hr = _CompareModifiedDate(pidf1, pidf2);
  2720. if (!hr)
  2721. hr = _CompareAttribs(pidf1, pidf2);
  2722. }
  2723. return hr;
  2724. }
  2725. // test to see if this folder object is a net folder
  2726. BOOL CFSFolder::_IsNetPath()
  2727. {
  2728. BOOL fRemote = FALSE; // assume no
  2729. TCHAR szPath[MAX_PATH];
  2730. if (SUCCEEDED(_GetPath(szPath)))
  2731. {
  2732. fRemote = PathIsRemote(szPath);
  2733. }
  2734. return fRemote;
  2735. }
  2736. BOOL _CanRenameFolder(LPCTSTR pszFolder)
  2737. {
  2738. static const UINT c_aiNoRenameFolders[] = {
  2739. CSIDL_WINDOWS,
  2740. CSIDL_SYSTEM,
  2741. CSIDL_PROGRAM_FILES,
  2742. CSIDL_FONTS,
  2743. };
  2744. return !PathIsOneOf(pszFolder, c_aiNoRenameFolders, ARRAYSIZE(c_aiNoRenameFolders));
  2745. }
  2746. STDAPI_(LPCIDFOLDER) CFSFolder::_IsValidIDHack(LPCITEMIDLIST pidl)
  2747. {
  2748. if (!(ACF_NOVALIDATEFSIDS & SHGetAppCompatFlags(ACF_NOVALIDATEFSIDS)))
  2749. {
  2750. return _IsValidID(pidl);
  2751. }
  2752. else if (pidl)
  2753. {
  2754. // old behavior was that we didnt validate, we just
  2755. // looked for the last id and casted it
  2756. return (LPCIDFOLDER)ILFindLastID(pidl);
  2757. }
  2758. return NULL;
  2759. }
  2760. #define SFGAO_NOT_RECENT (SFGAO_CANRENAME | SFGAO_CANLINK)
  2761. #define SFGAO_REQ_MASK (SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_DROPTARGET | SFGAO_LINK | SFGAO_STREAM | SFGAO_STORAGEANCESTOR | SFGAO_STORAGE | SFGAO_READONLY)
  2762. HRESULT CFSFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
  2763. {
  2764. LPCIDFOLDER pidf = cidl ? _IsValidIDHack(apidl[0]) : NULL;
  2765. ULONG rgfOut = SFGAO_CANDELETE | SFGAO_CANMOVE | SFGAO_CANCOPY | SFGAO_HASPROPSHEET
  2766. | SFGAO_FILESYSTEM | SFGAO_DROPTARGET | SFGAO_CANRENAME | SFGAO_CANLINK;
  2767. ASSERT(cidl ? apidl[0] == ILFindLastID(apidl[0]) : TRUE); // should be single level IDs only
  2768. ASSERT(cidl ? BOOLFROMPTR(pidf) : TRUE); // should always be FS PIDLs
  2769. // the RECENT folder doesnt like items in it renamed or linked to.
  2770. if ((*prgfInOut & (SFGAO_NOT_RECENT)) && _IsCSIDL(CSIDL_RECENT))
  2771. {
  2772. rgfOut &= ~SFGAO_NOT_RECENT;
  2773. }
  2774. if (cidl == 1 && pidf)
  2775. {
  2776. CFileSysItemString fsi(pidf);
  2777. TCHAR szPath[MAX_PATH];
  2778. if (*prgfInOut & (SFGAO_VALIDATE | SFGAO_CANRENAME | SFGAO_REMOVABLE | SFGAO_SHARE))
  2779. {
  2780. HRESULT hr = _GetPathForItem(pidf, szPath);
  2781. if (FAILED(hr))
  2782. return hr;
  2783. }
  2784. else
  2785. {
  2786. // just in case -- if somebody else needs the path they should add to the check above
  2787. szPath[0] = 0;
  2788. }
  2789. if (*prgfInOut & SFGAO_VALIDATE)
  2790. {
  2791. DWORD dwAttribs;
  2792. if (!PathFileExistsAndAttributes(szPath, &dwAttribs))
  2793. return E_FAIL;
  2794. // Tell the extended columns to update when someone request validation of a pidl
  2795. // This allows a client of the shell folder who uses extended columns without a
  2796. // view to force an update on stale information (i.e. Start Menu with InfoTips)
  2797. // - lamadio 6.11.99
  2798. _bUpdateExtendedCols = TRUE;
  2799. // hackhack. if they pass in validate, we party into it and update
  2800. // the attribs
  2801. if (!IsBadWritePtr((void *)&pidf->wAttrs, sizeof(pidf->wAttrs)))
  2802. ((LPIDFOLDER)pidf)->wAttrs = (WORD)dwAttribs;
  2803. }
  2804. if (*prgfInOut & SFGAO_COMPRESSED)
  2805. {
  2806. if (pidf->wAttrs & FILE_ATTRIBUTE_COMPRESSED)
  2807. {
  2808. rgfOut |= SFGAO_COMPRESSED;
  2809. }
  2810. }
  2811. if (*prgfInOut & SFGAO_ENCRYPTED)
  2812. {
  2813. if (pidf->wAttrs & FILE_ATTRIBUTE_ENCRYPTED)
  2814. {
  2815. rgfOut |= SFGAO_ENCRYPTED;
  2816. }
  2817. }
  2818. if (*prgfInOut & SFGAO_READONLY)
  2819. {
  2820. if ((pidf->wAttrs & FILE_ATTRIBUTE_READONLY) && !(pidf->wAttrs & FILE_ATTRIBUTE_DIRECTORY))
  2821. {
  2822. rgfOut |= SFGAO_READONLY;
  2823. }
  2824. }
  2825. if (*prgfInOut & SFGAO_HIDDEN)
  2826. {
  2827. if (pidf->wAttrs & FILE_ATTRIBUTE_HIDDEN)
  2828. {
  2829. rgfOut |= SFGAO_HIDDEN;
  2830. }
  2831. }
  2832. if (*prgfInOut & SFGAO_NONENUMERATED)
  2833. {
  2834. if (IsSuperHidden(pidf->wAttrs))
  2835. {
  2836. // mark superhidden as nonenumerated, IsSuperHidden checks current settings
  2837. rgfOut |= SFGAO_NONENUMERATED;
  2838. }
  2839. else if (pidf->wAttrs & FILE_ATTRIBUTE_HIDDEN)
  2840. {
  2841. // mark normal hidden as nonenumerated if necessary
  2842. SHELLSTATE ss;
  2843. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
  2844. if (!ss.fShowAllObjects)
  2845. {
  2846. rgfOut |= SFGAO_NONENUMERATED;
  2847. }
  2848. }
  2849. }
  2850. if (_IsFolder(pidf))
  2851. {
  2852. rgfOut |= SFGAO_FOLDER | SFGAO_STORAGE | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR;
  2853. if ((*prgfInOut & SFGAO_CANRENAME) && (fsi.CantRename(this) || !_CanRenameFolder(szPath)))
  2854. rgfOut &= ~SFGAO_CANRENAME;
  2855. if ((*prgfInOut & SFGAO_REMOVABLE) && PathIsRemovable(szPath))
  2856. {
  2857. rgfOut |= SFGAO_REMOVABLE;
  2858. }
  2859. if ((*prgfInOut & SFGAO_SHARE) && IsShared(szPath, FALSE))
  2860. {
  2861. rgfOut |= SFGAO_SHARE;
  2862. }
  2863. }
  2864. else
  2865. {
  2866. rgfOut |= SFGAO_STREAM;
  2867. }
  2868. if (*prgfInOut & SFGAO_LINK)
  2869. {
  2870. DWORD dwFlags = fsi.ClassFlags(FALSE);
  2871. if (dwFlags & SHCF_IS_LINK)
  2872. {
  2873. rgfOut |= SFGAO_LINK;
  2874. }
  2875. }
  2876. CLSID clsid;
  2877. if (fsi.GetJunctionClsid(&clsid, TRUE))
  2878. {
  2879. // NOTE: here we are always including SFGAO_FILESYSTEM. this was not the original
  2880. // shell behavior. but since these things will succeeded on SHGetPathFromIDList()
  2881. // it is the right thing to do. to filter out SFGAO_FOLDER things that might
  2882. // have files in them use SFGAO_FILESYSANCESTOR.
  2883. //
  2884. // clear out the things we want the extension to be able to optionally have
  2885. rgfOut &= ~(SFGAO_DROPTARGET | SFGAO_STORAGE | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR);
  2886. // let folder shortcuts yank the folder bit too for bad apps.
  2887. if (IsEqualGUID(clsid, CLSID_FolderShortcut) &&
  2888. (SHGetAppCompatFlags(ACF_STRIPFOLDERBIT) & ACF_STRIPFOLDERBIT))
  2889. {
  2890. rgfOut &= ~SFGAO_FOLDER;
  2891. }
  2892. // and let him add some bits in
  2893. rgfOut |= SHGetAttributesFromCLSID2(&clsid, SFGAO_HASSUBFOLDER, SFGAO_REQ_MASK) & SFGAO_REQ_MASK;
  2894. // Mill #123708
  2895. // prevent zips, cabs and other files with SFGAO_FOLDER set
  2896. // from being treated like folders inside bad file open dialogs.
  2897. if (!(pidf->wAttrs & FILE_ATTRIBUTE_DIRECTORY) &&
  2898. (SHGetAppCompatFlags (ACF_STRIPFOLDERBIT) & ACF_STRIPFOLDERBIT))
  2899. {
  2900. rgfOut &= ~SFGAO_FOLDER;
  2901. }
  2902. // Check if this folder needs File System Ancestor bit
  2903. if ((rgfOut & SFGAO_FOLDER) && !(rgfOut & SFGAO_FILESYSANCESTOR)
  2904. && SHGetObjectCompatFlags(NULL, &clsid) & OBJCOMPATF_NEEDSFILESYSANCESTOR)
  2905. {
  2906. rgfOut |= SFGAO_FILESYSANCESTOR;
  2907. }
  2908. }
  2909. // it can only have subfolders if we've first found it's a folder
  2910. if ((rgfOut & SFGAO_FOLDER) && (*prgfInOut & SFGAO_HASSUBFOLDER))
  2911. {
  2912. if (pidf->wAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
  2913. {
  2914. rgfOut |= SFGAO_HASSUBFOLDER; // DFS junction, local mount point, assume sub folders
  2915. }
  2916. else if (_IsNetPath())
  2917. {
  2918. // it would be nice to not assume this. this messes up
  2919. // home net cases where we get the "+" wrong
  2920. rgfOut |= SFGAO_HASSUBFOLDER; // assume yes because these are slow
  2921. }
  2922. else if (!(rgfOut & SFGAO_HASSUBFOLDER))
  2923. {
  2924. IShellFolder *psf;
  2925. if (SUCCEEDED(_Bind(NULL, pidf, IID_PPV_ARG(IShellFolder, &psf))))
  2926. {
  2927. IEnumIDList *peunk;
  2928. if (S_OK == psf->EnumObjects(NULL, SHCONTF_FOLDERS, &peunk))
  2929. {
  2930. LPITEMIDLIST pidlT;
  2931. if (peunk->Next(1, &pidlT, NULL) == S_OK)
  2932. {
  2933. rgfOut |= SFGAO_HASSUBFOLDER;
  2934. SHFree(pidlT);
  2935. }
  2936. peunk->Release();
  2937. }
  2938. psf->Release();
  2939. }
  2940. }
  2941. }
  2942. if (*prgfInOut & SFGAO_GHOSTED)
  2943. {
  2944. if (pidf->wAttrs & FILE_ATTRIBUTE_HIDDEN)
  2945. rgfOut |= SFGAO_GHOSTED;
  2946. }
  2947. if ((*prgfInOut & SFGAO_BROWSABLE) &&
  2948. (_IsFile(pidf)) &&
  2949. (fsi.ClassFlags(FALSE) & SHCF_IS_BROWSABLE))
  2950. {
  2951. rgfOut |= SFGAO_BROWSABLE;
  2952. }
  2953. }
  2954. *prgfInOut = rgfOut;
  2955. return S_OK;
  2956. }
  2957. // load handler for an item based on the handler type:
  2958. // DropHandler, IconHandler, etc.
  2959. // in:
  2960. // pidf type of this object specifies the type of handler - can be multilevel
  2961. // pszHandlerType handler type name "DropTarget", may be NULL
  2962. // riid interface to talk on
  2963. // out:
  2964. // ppv output object
  2965. //
  2966. HRESULT CFSFolder::_LoadHandler(LPCIDFOLDER pidf, DWORD grfMode, LPCTSTR pszHandlerType, REFIID riid, void **ppv)
  2967. {
  2968. HRESULT hr = E_FAIL;
  2969. TCHAR szIID[40];
  2970. ASSERT(_FindJunctionNext(pidf) == NULL); // no extra non file sys goo please
  2971. *ppv = NULL;
  2972. // empty handler type, use the stringized IID as the handler name
  2973. if (NULL == pszHandlerType)
  2974. {
  2975. szIID[0] = 0;
  2976. SHStringFromGUID(riid, szIID, ARRAYSIZE(szIID));
  2977. pszHandlerType = szIID;
  2978. }
  2979. CFileSysItemString fsi(_FindLastID(pidf));
  2980. IAssociationArray *paa;
  2981. hr = fsi.AssocCreate(this, FALSE, IID_PPV_ARG(IAssociationArray, &paa));
  2982. if (SUCCEEDED(hr))
  2983. {
  2984. CSmartCoTaskMem<WCHAR> spszClsid;
  2985. hr = paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQNS_SHELLEX_HANDLER, pszHandlerType, &spszClsid);
  2986. if (SUCCEEDED(hr))
  2987. {
  2988. hr = _HandlerCreateInstance(pidf, spszClsid, grfMode, riid, ppv);
  2989. }
  2990. paa->Release();
  2991. }
  2992. return hr;
  2993. }
  2994. HRESULT CFSFolder::_HandlerCreateInstance(LPCIDFOLDER pidf, PCWSTR pszClsid, DWORD grfMode, REFIID riid, void **ppv)
  2995. {
  2996. IPersistFile *ppf;
  2997. HRESULT hr = SHExtCoCreateInstance(pszClsid, NULL, NULL, IID_PPV_ARG(IPersistFile, &ppf));
  2998. if (SUCCEEDED(hr))
  2999. {
  3000. WCHAR wszPath[MAX_PATH];
  3001. hr = _GetPathForItem(pidf, wszPath);
  3002. if (SUCCEEDED(hr))
  3003. {
  3004. hr = ppf->Load(wszPath, grfMode);
  3005. if (SUCCEEDED(hr))
  3006. {
  3007. hr = ppf->QueryInterface(riid, ppv);
  3008. }
  3009. }
  3010. ppf->Release();
  3011. }
  3012. return hr;
  3013. }
  3014. HRESULT CFSFolder::_CreateShimgvwExtractor(LPCIDFOLDER pidf, REFIID riid, void **ppv)
  3015. {
  3016. HRESULT hr = E_FAIL;
  3017. CFileSysItemString fsi(pidf);
  3018. if (fsi.IsShimgvwImage())
  3019. {
  3020. // cocreate CLSID_GdiThumbnailExtractor implemented in shimgvw.dll
  3021. hr = _HandlerCreateInstance(pidf, L"{3F30C968-480A-4C6C-862D-EFC0897BB84B}", STGM_READ, riid, ppv);
  3022. }
  3023. return hr;
  3024. }
  3025. int CFSFolder::_GetDefaultFolderIcon()
  3026. {
  3027. int iIcon = II_FOLDER;
  3028. UINT csidlFolder = _GetCSIDL();
  3029. // We're removing the icon distinction between per user and common folders.
  3030. switch (csidlFolder)
  3031. {
  3032. case CSIDL_STARTMENU:
  3033. case CSIDL_COMMON_STARTMENU:
  3034. case CSIDL_PROGRAMS:
  3035. case CSIDL_COMMON_PROGRAMS:
  3036. iIcon = II_STSPROGS;
  3037. break;
  3038. }
  3039. return iIcon;
  3040. }
  3041. DWORD CFSFolder::_Attributes()
  3042. {
  3043. if (_dwAttributes == -1)
  3044. {
  3045. TCHAR szPath[MAX_PATH];
  3046. if (SUCCEEDED(_GetPath(szPath)))
  3047. _dwAttributes = GetFileAttributes(szPath);
  3048. if (_dwAttributes == -1)
  3049. _dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // assume this on failure
  3050. }
  3051. return _dwAttributes;
  3052. }
  3053. // non junction, but has the system or readonly bit (regular folder marked special for us)
  3054. BOOL CFSFolder::_IsSelfSystemFolder()
  3055. {
  3056. return (_Attributes() & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY));
  3057. }
  3058. // Determine if there is a LocalizedFileName section in our desktop.ini file
  3059. BOOL CFSFolder::_HasLocalizedFileNames()
  3060. {
  3061. if (_tbHasLocalizedFileNamesSection == TRIBIT_UNDEFINED)
  3062. {
  3063. TCHAR szPath[MAX_PATH];
  3064. TCHAR szName[MAX_PATH];
  3065. TCHAR szBuf[4];
  3066. _GetPath(szPath);
  3067. if (_GetIniPath(FALSE, szPath, _pszNetProvider, szName) &&
  3068. GetPrivateProfileSection(TEXT("LocalizedFileNames"), szBuf, sizeof(szBuf)/sizeof(TCHAR), szName) > 0)
  3069. _tbHasLocalizedFileNamesSection = TRIBIT_TRUE;
  3070. else
  3071. _tbHasLocalizedFileNamesSection = TRIBIT_FALSE;
  3072. }
  3073. return (_tbHasLocalizedFileNamesSection == TRIBIT_TRUE);
  3074. }
  3075. // This function creates a default IExtractIcon object for either
  3076. // a file or a junction point. We should not supposed to call this function
  3077. // for a non-junction point directory (we don't want to hit the disk!).
  3078. HRESULT CFSFolder::_CreateDefExtIcon(LPCIDFOLDER pidf, REFIID riid, void **ppxicon)
  3079. {
  3080. HRESULT hr = E_OUTOFMEMORY;
  3081. DWORD dwFlags;
  3082. // WARNING: don't replace this if-statement with _IsFolder(pidf))!!!
  3083. // otherwise all junctions (like briefcase) will get the Folder icon.
  3084. //
  3085. if (_IsFileFolder(pidf))
  3086. {
  3087. UINT iIcon = _GetDefaultFolderIcon();
  3088. UINT iIconOpen = II_FOLDEROPEN;
  3089. TCHAR szPath[MAX_PATH], szModule[MAX_PATH];
  3090. szModule[0] = 0;
  3091. if (_GetMountingPointInfo(pidf, szPath, ARRAYSIZE(szPath)))
  3092. {
  3093. // We want same icon for open and close moun point (kind of drive)
  3094. iIconOpen = iIcon = GetMountedVolumeIcon(szPath, szModule, ARRAYSIZE(szModule));
  3095. }
  3096. else if (_IsSystemFolder(pidf))
  3097. {
  3098. if (_GetFolderIconPath(pidf, szPath, ARRAYSIZE(szPath), &iIcon))
  3099. {
  3100. return SHCreateDefExtIcon(szPath, iIcon, iIcon, GIL_PERINSTANCE, II_FOLDER, riid, ppxicon);
  3101. }
  3102. }
  3103. return SHCreateDefExtIcon(szModule, iIcon, iIconOpen, GIL_PERCLASS, II_FOLDER, riid, ppxicon);
  3104. }
  3105. // not a folder, get IExtractIcon and extract it.
  3106. // (might be a ds folder)
  3107. CFileSysItemString fsi(pidf);
  3108. dwFlags = fsi.ClassFlags(TRUE);
  3109. if (dwFlags & SHCF_ICON_PERINSTANCE)
  3110. {
  3111. if (dwFlags & SHCF_HAS_ICONHANDLER)
  3112. {
  3113. IUnknown *punk;
  3114. hr = _LoadHandler(pidf, STGM_READ, TEXT("IconHandler"), IID_PPV_ARG(IUnknown, &punk));
  3115. if (SUCCEEDED(hr))
  3116. {
  3117. hr = punk->QueryInterface(riid, ppxicon);
  3118. punk->Release();
  3119. }
  3120. else
  3121. {
  3122. *ppxicon = NULL;
  3123. }
  3124. }
  3125. else
  3126. {
  3127. DWORD uid = _GetUID(pidf);
  3128. TCHAR szPath[MAX_PATH];
  3129. hr = _GetPathForItem(pidf, szPath);
  3130. if (SUCCEEDED(hr))
  3131. {
  3132. hr = SHCreateDefExtIcon(szPath, uid, uid, GIL_PERINSTANCE | GIL_NOTFILENAME, -1, riid, ppxicon);
  3133. }
  3134. }
  3135. }
  3136. else
  3137. {
  3138. UINT iIcon = (dwFlags & SHCF_ICON_INDEX);
  3139. if (II_FOLDER == iIcon)
  3140. {
  3141. iIcon = _GetDefaultFolderIcon();
  3142. }
  3143. hr = SHCreateDefExtIcon(c_szStar, iIcon, iIcon, GIL_PERCLASS | GIL_NOTFILENAME, -1, riid, ppxicon);
  3144. }
  3145. return hr;
  3146. }
  3147. DWORD CALLBACK CFSFolder::_PropertiesThread(void *pv)
  3148. {
  3149. PROPSTUFF * pps = (PROPSTUFF *)pv;
  3150. STGMEDIUM medium;
  3151. ULONG_PTR dwCookie = 0;
  3152. ActivateActCtx(NULL, &dwCookie);
  3153. LPIDA pida = DataObj_GetHIDA(pps->pdtobj, &medium);
  3154. if (pida)
  3155. {
  3156. LPITEMIDLIST pidl = IDA_ILClone(pida, 0);
  3157. if (pidl)
  3158. {
  3159. TCHAR szPath[MAX_PATH];
  3160. LPTSTR pszCaption;
  3161. HKEY rgKeys[MAX_ASSOC_KEYS] = {0};
  3162. DWORD cKeys = SHGetAssocKeysForIDList(pidl, rgKeys, ARRAYSIZE(rgKeys));
  3163. // REVIEW: psb?
  3164. pszCaption = SHGetCaption(medium.hGlobal);
  3165. SHOpenPropSheet(pszCaption, rgKeys, cKeys,
  3166. &CLSID_ShellFileDefExt, pps->pdtobj, NULL, pps->pStartPage);
  3167. if (pszCaption)
  3168. SHFree(pszCaption);
  3169. SHRegCloseKeys(rgKeys, cKeys);
  3170. if (SHGetPathFromIDList(pidl, szPath))
  3171. {
  3172. if (lstrcmpi(PathFindExtension(szPath), TEXT(".pif")) == 0)
  3173. {
  3174. DebugMsg(TF_FSTREE, TEXT("cSHCNRF_pt: DOS properties done, generating event."));
  3175. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, pidl, NULL);
  3176. }
  3177. }
  3178. ILFree(pidl);
  3179. }
  3180. HIDA_ReleaseStgMedium(pida, &medium);
  3181. }
  3182. return 0;
  3183. }
  3184. //
  3185. // Display a property sheet for a set of files.
  3186. // The data object supplied must provide the "Shell IDList Array"
  3187. // clipboard format.
  3188. // The dwFlags argument is provided for future expansion. It is
  3189. // currently unused.
  3190. //
  3191. STDAPI SHMultiFileProperties(IDataObject *pdtobj, DWORD dwFlags)
  3192. {
  3193. return SHLaunchPropSheet(CFSFolder::_PropertiesThread, pdtobj, 0, NULL, NULL);
  3194. }
  3195. HMENU FindMenuBySubMenuID(HMENU hmenu, UINT id, LPINT pIndex)
  3196. {
  3197. HMENU hmenuReturn = NULL;
  3198. MENUITEMINFO mii;
  3199. mii.cbSize = sizeof(mii);
  3200. mii.fMask = MIIM_ID;
  3201. mii.cch = 0; // just in case...
  3202. for (int cMax = GetMenuItemCount(hmenu) - 1 ; cMax >= 0 ; cMax--)
  3203. {
  3204. HMENU hmenuSub = GetSubMenu(hmenu, cMax);
  3205. if (hmenuSub && GetMenuItemInfo(hmenuSub, 0, TRUE, &mii))
  3206. {
  3207. if (mii.wID == id)
  3208. {
  3209. // found it!
  3210. hmenuReturn = hmenuSub;
  3211. break;
  3212. }
  3213. }
  3214. }
  3215. if (hmenuReturn && pIndex)
  3216. *pIndex = cMax;
  3217. return hmenuReturn;
  3218. }
  3219. void DeleteMenuBySubMenuID(HMENU hmenu, UINT id)
  3220. {
  3221. int i;
  3222. if (FindMenuBySubMenuID(hmenu, id, &i))
  3223. {
  3224. DeleteMenu(hmenu, i, MF_BYPOSITION);
  3225. }
  3226. }
  3227. // fMask is from CMIC_MASK_*
  3228. STDAPI CFSFolder_CreateLinks(HWND hwnd, IShellFolder *psf, IDataObject *pdtobj, LPCTSTR pszDir, DWORD fMask)
  3229. {
  3230. LPITEMIDLIST pidl;
  3231. HRESULT hr = SHGetIDListFromUnk(psf, &pidl);
  3232. if (SUCCEEDED(hr))
  3233. {
  3234. TCHAR szPath[MAX_PATH];
  3235. if (SHGetPathFromIDList(pidl, szPath))
  3236. {
  3237. UINT fCreateLinkFlags;
  3238. int cItems = DataObj_GetHIDACount(pdtobj);
  3239. LPITEMIDLIST *ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, sizeof(LPITEMIDLIST) * cItems);
  3240. // passing ppidl == NULL is correct in failure case
  3241. if ((pszDir == NULL) || (lstrcmpi(pszDir, szPath) == 0))
  3242. {
  3243. // create the link in the current folder
  3244. fCreateLinkFlags = SHCL_USETEMPLATE;
  3245. }
  3246. else
  3247. {
  3248. // this is a sys menu, ask to create on desktop
  3249. fCreateLinkFlags = SHCL_USETEMPLATE | SHCL_USEDESKTOP;
  3250. if (!(fMask & CMIC_MASK_FLAG_NO_UI))
  3251. {
  3252. fCreateLinkFlags |= SHCL_CONFIRM;
  3253. }
  3254. }
  3255. hr = SHCreateLinks(hwnd, szPath, pdtobj, fCreateLinkFlags, ppidl);
  3256. if (ppidl)
  3257. {
  3258. // select those objects;
  3259. HWND hwndSelect = ShellFolderViewWindow(hwnd);
  3260. // select the new links, but on the first one deselect all other selected things
  3261. for (int i = 0; i < cItems; i++)
  3262. {
  3263. if (ppidl[i])
  3264. {
  3265. SendMessage(hwndSelect, SVM_SELECTITEM,
  3266. i == 0 ? SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED :
  3267. SVSI_SELECT,
  3268. (LPARAM)ILFindLastID(ppidl[i]));
  3269. ILFree(ppidl[i]);
  3270. }
  3271. }
  3272. LocalFree((HLOCAL)ppidl);
  3273. }
  3274. }
  3275. else
  3276. {
  3277. hr = E_FAIL;
  3278. }
  3279. ILFree(pidl);
  3280. }
  3281. return hr;
  3282. }
  3283. // Parameter to the "Delete" thread.
  3284. //
  3285. typedef struct {
  3286. IDataObject *pDataObj; // null on entry to thread proc
  3287. IStream *pstmDataObj; // marshalled data object
  3288. HWND hwndOwner;
  3289. UINT uFlags;
  3290. UINT fOptions;
  3291. } FSDELTHREADPARAM;
  3292. void FreeFSDELThreadParam(FSDELTHREADPARAM * pfsthp)
  3293. {
  3294. ATOMICRELEASE(pfsthp->pDataObj);
  3295. ATOMICRELEASE(pfsthp->pstmDataObj);
  3296. LocalFree(pfsthp);
  3297. }
  3298. DWORD CALLBACK FileDeleteThreadProc(void *pv)
  3299. {
  3300. FSDELTHREADPARAM *pfsthp = (FSDELTHREADPARAM *)pv;
  3301. CoGetInterfaceAndReleaseStream(pfsthp->pstmDataObj, IID_PPV_ARG(IDataObject, &pfsthp->pDataObj));
  3302. pfsthp->pstmDataObj = NULL;
  3303. if (pfsthp->pDataObj)
  3304. DeleteFilesInDataObject(pfsthp->hwndOwner, pfsthp->uFlags, pfsthp->pDataObj, pfsthp->fOptions);
  3305. FreeFSDELThreadParam(pfsthp);
  3306. return 0;
  3307. }
  3308. //
  3309. // IContextMenuCB
  3310. // right click context menu for items handler
  3311. //
  3312. // Returns:
  3313. // S_OK, if successfully processed.
  3314. // S_FALSE, if default code should be used.
  3315. //
  3316. STDMETHODIMP CFSFolder::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3317. {
  3318. HRESULT hr = S_OK;
  3319. switch (uMsg)
  3320. {
  3321. case DFM_MERGECONTEXTMENU:
  3322. if (!(wParam & CMF_VERBSONLY))
  3323. {
  3324. LPQCMINFO pqcm = (LPQCMINFO)lParam;
  3325. // corel relies on the hard coded send to menu so we give them one
  3326. BOOL bCorelSuite7Hack = (SHGetAppCompatFlags(ACF_CONTEXTMENU) & ACF_CONTEXTMENU);
  3327. if (bCorelSuite7Hack)
  3328. {
  3329. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_FSVIEW_ITEM_COREL7_HACK, 0, pqcm);
  3330. }
  3331. }
  3332. break;
  3333. case DFM_GETHELPTEXT:
  3334. LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
  3335. break;
  3336. case DFM_GETHELPTEXTW:
  3337. LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
  3338. break;
  3339. case DFM_INVOKECOMMANDEX:
  3340. {
  3341. DFMICS *pdfmics = (DFMICS *)lParam;
  3342. switch (wParam)
  3343. {
  3344. case DFM_CMD_DELETE:
  3345. // try not to do delete on the UI thread
  3346. // with System Restore it may be slow
  3347. //
  3348. // NOTE: we need to test to make sure this is acceptable as the data
  3349. // object may have come from a data object extension, for example a
  3350. // scrap file. but that is a very rare case (DataObj_CanGoAsync() will almost always
  3351. // return true).
  3352. hr = E_FAIL;
  3353. if ((pdfmics->fMask & CMIC_MASK_ASYNCOK) && DataObj_CanGoAsync(pdtobj))
  3354. {
  3355. FSDELTHREADPARAM *pfsthp;
  3356. hr = SHLocalAlloc(sizeof(*pfsthp), &pfsthp);
  3357. if (SUCCEEDED(hr))
  3358. {
  3359. hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdtobj, &pfsthp->pstmDataObj);
  3360. if (SUCCEEDED(hr))
  3361. {
  3362. pfsthp->hwndOwner = hwnd;
  3363. pfsthp->uFlags = pdfmics->fMask;
  3364. // dont allow undo in the recent folder.
  3365. pfsthp->fOptions = _IsCSIDL(CSIDL_RECENT) ? SD_NOUNDO : 0;
  3366. // create another thread to avoid blocking the source thread.
  3367. if (!SHCreateThread(FileDeleteThreadProc, pfsthp, CTF_COINIT, NULL))
  3368. {
  3369. hr = E_FAIL;
  3370. }
  3371. }
  3372. if (FAILED(hr))
  3373. {
  3374. FreeFSDELThreadParam(pfsthp); // cleanup
  3375. }
  3376. }
  3377. }
  3378. if (S_OK != hr)
  3379. {
  3380. // could not go async, do it sync here
  3381. // dont allow undo in the recent folder.
  3382. hr = DeleteFilesInDataObject(hwnd, pdfmics->fMask, pdtobj,
  3383. _IsCSIDL(CSIDL_RECENT) ? SD_NOUNDO : 0);
  3384. }
  3385. break;
  3386. case DFM_CMD_LINK:
  3387. hr = CFSFolder_CreateLinks(hwnd, psf, pdtobj, (LPCTSTR)pdfmics->lParam, pdfmics->fMask);
  3388. break;
  3389. case DFM_CMD_PROPERTIES:
  3390. hr = SHLaunchPropSheet(_PropertiesThread, pdtobj, (LPCTSTR)pdfmics->lParam, NULL, _pidl);
  3391. break;
  3392. default:
  3393. // This is common menu items, use the default code.
  3394. hr = S_FALSE;
  3395. break;
  3396. }
  3397. }
  3398. break;
  3399. default:
  3400. hr = E_NOTIMPL;
  3401. break;
  3402. }
  3403. return hr;
  3404. }
  3405. HRESULT CFSFolder::_CreateContextMenu(HWND hwnd, LPCIDFOLDER pidf, LPCITEMIDLIST *apidl, UINT cidl, IContextMenu **ppcm)
  3406. {
  3407. // we need a key for each
  3408. // 1. UserCustomized
  3409. // 2. default Progid
  3410. // 3. SFA\.ext
  3411. // 4. SFA\PerceivedType
  3412. // 5. * or Folder
  3413. // 6. AllFileSystemObjects
  3414. // (?? 7. maybe pszProvider ??)
  3415. IAssociationArray *paa;
  3416. CFileSysItemString fsi(pidf);
  3417. fsi.AssocCreate(this, TRUE, IID_PPV_ARG(IAssociationArray, &paa));
  3418. IShellFolder *psfToPass; // May be an Aggregate...
  3419. HRESULT hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfToPass));
  3420. if (SUCCEEDED(hr))
  3421. {
  3422. DEFCONTEXTMENU dcm = {
  3423. hwnd,
  3424. SAFECAST(this, IContextMenuCB *),
  3425. _pidl,
  3426. psfToPass,
  3427. cidl,
  3428. apidl,
  3429. paa,
  3430. 0,
  3431. NULL};
  3432. hr = CreateDefaultContextMenu(&dcm, ppcm);
  3433. psfToPass->Release();
  3434. }
  3435. if (paa)
  3436. paa->Release();
  3437. return hr;
  3438. }
  3439. HRESULT CFileFolderIconManager_Create(IShellFolder *psf, LPCITEMIDLIST pidl, REFIID riid, void **ppv);
  3440. HRESULT CFSFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  3441. REFIID riid, UINT *prgfInOut, void **ppv)
  3442. {
  3443. HRESULT hr = E_INVALIDARG;
  3444. LPCIDFOLDER pidf = cidl ? _IsValidID(apidl[0]) : NULL;
  3445. *ppv = NULL;
  3446. if (pidf &&
  3447. (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)))
  3448. {
  3449. hr = _CreateDefExtIcon(pidf, riid, ppv);
  3450. }
  3451. else if (IsEqualIID(riid, IID_IContextMenu) && pidf)
  3452. {
  3453. hr = _CreateContextMenu(hwnd, pidf, apidl, cidl, (IContextMenu **)ppv);
  3454. }
  3455. else if (IsEqualIID(riid, IID_IDataObject) && cidl)
  3456. {
  3457. IDataObject *pdtInner = NULL;
  3458. if ((cidl == 1) && pidf)
  3459. {
  3460. _LoadHandler(pidf, STGM_READ, TEXT("DataHandler"), IID_PPV_ARG(IDataObject, &pdtInner));
  3461. }
  3462. hr = SHCreateFileDataObject(_pidl, cidl, apidl, pdtInner, (IDataObject **)ppv);
  3463. if (pdtInner)
  3464. pdtInner->Release();
  3465. }
  3466. else if (IsEqualIID(riid, IID_IDropTarget) && pidf)
  3467. {
  3468. CLSID clsid;
  3469. if (_IsFolder(pidf) || (_GetJunctionClsid(pidf, &clsid) && !SHQueryShellFolderValue(&clsid, L"UseDropHandler")))
  3470. {
  3471. IShellFolder *psfT;
  3472. hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfT));
  3473. if (SUCCEEDED(hr))
  3474. {
  3475. hr = psfT->CreateViewObject(hwnd, riid, ppv);
  3476. psfT->Release();
  3477. }
  3478. }
  3479. else
  3480. {
  3481. // old code supported absolute PIDLs here. that was bogus...
  3482. ASSERT(ILIsEmpty(apidl[0]) || (ILFindLastID(apidl[0]) == apidl[0]));
  3483. ASSERT(_IsFile(pidf) || _IsSimpleID(pidf));
  3484. hr = _LoadHandler(pidf, STGM_READ, TEXT("DropHandler"), riid, ppv);
  3485. }
  3486. }
  3487. else if (IsEqualIID(riid, IID_ICustomIconManager) && pidf)
  3488. {
  3489. if (_IsFileFolder(pidf))
  3490. {
  3491. TCHAR szItemPath[MAX_PATH];
  3492. szItemPath[0] = NULL;
  3493. hr = _GetPath(szItemPath);
  3494. if (SUCCEEDED(hr))
  3495. {
  3496. // No support in ICustomIconManager for remote shares.
  3497. if (PathIsNetworkPath(szItemPath))
  3498. hr = E_NOTIMPL;
  3499. else
  3500. {
  3501. hr = CFileFolderIconManager_Create(this, (LPCITEMIDLIST)pidf, riid, ppv);
  3502. }
  3503. }
  3504. }
  3505. else
  3506. {
  3507. hr = E_NOTIMPL;
  3508. }
  3509. }
  3510. else if (pidf)
  3511. {
  3512. // too many people bogusly register extractors that
  3513. // dont work as well as ours for images
  3514. // we hard code our list of supported types.
  3515. if (IsEqualIID(riid, IID_IExtractImage))
  3516. {
  3517. hr = _CreateShimgvwExtractor(pidf, riid, ppv);
  3518. }
  3519. if (FAILED(hr))
  3520. hr = _LoadHandler(pidf, STGM_READ, NULL, riid, ppv);
  3521. if (FAILED(hr))
  3522. {
  3523. if (IsEqualIID(riid, IID_IQueryInfo))
  3524. {
  3525. hr = _GetToolTipForItem(pidf, riid, ppv);
  3526. }
  3527. else if (IsEqualIID(riid, IID_IQueryAssociations)
  3528. || IsEqualIID(riid, IID_IAssociationArray))
  3529. {
  3530. hr = _AssocCreate(pidf, riid, ppv);
  3531. }
  3532. else if ((IsEqualIID(riid, IID_IExtractImage) ||
  3533. IsEqualIID(riid, IID_IExtractLogo)) && _IsFileFolder(pidf))
  3534. {
  3535. // default handler type, use the IID_ as the key to open for the handler
  3536. // if it is an image extractor, then check to see if it is a per-folder logo...
  3537. hr = CFolderExtractImage_Create(this, (LPCITEMIDLIST)pidf, riid, ppv);
  3538. }
  3539. }
  3540. }
  3541. return hr;
  3542. }
  3543. HRESULT CFSFolder::GetDefaultSearchGUID(GUID *pGuid)
  3544. {
  3545. return E_NOTIMPL;
  3546. }
  3547. HRESULT CFSFolder::EnumSearches(IEnumExtraSearch **ppenum)
  3548. {
  3549. *ppenum = NULL;
  3550. return E_NOTIMPL;
  3551. }
  3552. LPCIDFOLDER CFSFolder::_FindJunction(LPCIDFOLDER pidf)
  3553. {
  3554. for (; pidf->cb; pidf = _Next(pidf))
  3555. {
  3556. if (_IsJunction(pidf))
  3557. return pidf; // true junction (folder.{guid} folder\desktop.ini)
  3558. if (_IsFile(pidf))
  3559. {
  3560. DWORD dwFlags = _GetClassFlags(pidf);
  3561. if (dwFlags & (SHCF_IS_BROWSABLE | SHCF_IS_SHELLEXT))
  3562. return pidf; // browsable file (.HTM)
  3563. }
  3564. }
  3565. return NULL;
  3566. }
  3567. // return IDLIST of item just past the junction point (if there is one)
  3568. // if there's no next pointer, return NULL.
  3569. LPCITEMIDLIST CFSFolder::_FindJunctionNext(LPCIDFOLDER pidf)
  3570. {
  3571. pidf = _FindJunction(pidf);
  3572. if (pidf)
  3573. {
  3574. // cast here represents the fact that this data is opaque
  3575. LPCITEMIDLIST pidl = (LPCITEMIDLIST)_Next(pidf);
  3576. if (!ILIsEmpty(pidl))
  3577. return pidl; // first item past junction
  3578. }
  3579. return NULL;
  3580. }
  3581. void CFSFolder::_UpdateItem(LPCIDFOLDER pidf)
  3582. {
  3583. LPITEMIDLIST pidlAbs = ILCombine(_pidl, (LPCITEMIDLIST)pidf);
  3584. if (pidlAbs)
  3585. {
  3586. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_FLUSH | SHCNF_IDLIST, pidlAbs, NULL);
  3587. ILFree(pidlAbs);
  3588. }
  3589. }
  3590. HRESULT CFSFolder::_SetLocalizedDisplayName(LPCIDFOLDER pidf, LPCWSTR pszName)
  3591. {
  3592. HRESULT hr = E_FAIL;
  3593. WCHAR sz[MAX_PATH];
  3594. CFileSysItemString fsi(pidf);
  3595. if (*pszName == TEXT('@') && SUCCEEDED(SHLoadIndirectString(pszName, sz, ARRAYSIZE(sz), NULL)))
  3596. {
  3597. TCHAR szPath[MAX_PATH];
  3598. //
  3599. // this is a localized resource.
  3600. // save this off as the items UI name.
  3601. //
  3602. if (_IsFolder(pidf))
  3603. {
  3604. if (SUCCEEDED(_GetPathForItem(pidf, szPath))
  3605. && SetFolderString(TRUE, szPath, _pszNetProvider, STRINI_CLASSINFO, TEXT("LocalizedResourceName"), pszName))
  3606. {
  3607. // we need to insure the bits are set for MUI on upgraded users
  3608. // PathMakeSystemFolder(szPath);
  3609. hr = S_OK;
  3610. }
  3611. }
  3612. else
  3613. {
  3614. _GetPath(szPath);
  3615. if (SetFolderString(TRUE, szPath, _pszNetProvider, TEXT("LocalizedFileNames"), fsi.FSName(), pszName))
  3616. hr = S_OK;
  3617. }
  3618. }
  3619. else
  3620. {
  3621. if (fsi.HasResourceName())
  3622. {
  3623. if (*pszName)
  3624. {
  3625. DWORD cb = CbFromCch(lstrlen(pszName)+1);
  3626. // set the registry overrides
  3627. if (S_OK == SKSetValueW(SHELLKEY_HKCU_SHELL, L"LocalizedResourceName", fsi.ResourceName(), REG_SZ, pszName, cb))
  3628. {
  3629. hr = S_OK;
  3630. }
  3631. }
  3632. else
  3633. {
  3634. SKDeleteValue(SHELLKEY_HKCU_SHELL, L"LocalizedResourceName", fsi.ResourceName());
  3635. hr = S_OK;
  3636. }
  3637. }
  3638. }
  3639. if (SUCCEEDED(hr))
  3640. _UpdateItem(pidf);
  3641. return hr;
  3642. }
  3643. HRESULT CFSFolder::_NormalGetDisplayNameOf(LPCIDFOLDER pidf, STRRET *pStrRet)
  3644. {
  3645. //
  3646. // WARNING - Some apps (e.g., Norton Uninstall Deluxe)
  3647. // don't handle STRRET_WSTR properly. NT4's shell32
  3648. // returned STRRET_WSTR only if it had no choice, so these apps
  3649. // seemed to run just fine on NT as long as you never had any
  3650. // UNICODE filenames. We must preserve the NT4 behavior or
  3651. // these buggy apps start blowing chunks.
  3652. //
  3653. // if this is still important, we will apphack these guys
  3654. CFileSysItemString fsi(pidf);
  3655. if (SHGetAppCompatFlags(ACF_ANSIDISPLAYNAMES) & ACF_ANSIDISPLAYNAMES)
  3656. {
  3657. pStrRet->uType = STRRET_CSTR;
  3658. SHUnicodeToAnsi(fsi.UIName(this), pStrRet->cStr, ARRAYSIZE(pStrRet->cStr));
  3659. return S_OK;
  3660. }
  3661. return StringToStrRet(fsi.UIName(this), pStrRet);
  3662. }
  3663. HRESULT CFSFolder::_NormalDisplayName(LPCIDFOLDER pidf, LPTSTR psz, UINT cch)
  3664. {
  3665. CFileSysItemString fsi(pidf);
  3666. StrCpyN(psz, fsi.UIName(this), cch);
  3667. return S_OK;
  3668. }
  3669. HRESULT CFSFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET pStrRet)
  3670. {
  3671. HRESULT hr = S_FALSE;
  3672. LPCIDFOLDER pidf = _IsValidID(pidl);
  3673. if (pidf)
  3674. {
  3675. TCHAR szPath[MAX_PATH];
  3676. LPCITEMIDLIST pidlNext = _ILNext(pidl);
  3677. if (dwFlags & SHGDN_FORPARSING)
  3678. {
  3679. if (dwFlags & SHGDN_INFOLDER)
  3680. {
  3681. _CopyName(pidf, szPath, ARRAYSIZE(szPath));
  3682. if (dwFlags & SHGDN_FORADDRESSBAR)
  3683. {
  3684. LPTSTR pszExt = PathFindCLSIDExtension(szPath, NULL);
  3685. if (pszExt)
  3686. *pszExt = 0;
  3687. }
  3688. if (ILIsEmpty(pidlNext)) // single level idlist
  3689. hr = StringToStrRet(szPath, pStrRet);
  3690. else
  3691. hr = ILGetRelDisplayName(this, pStrRet, pidl, szPath, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_BACKSLASH), dwFlags);
  3692. }
  3693. else
  3694. {
  3695. LPIDFOLDER pidfBind;
  3696. LPCITEMIDLIST pidlRight;
  3697. hr = _GetJunctionForBind(pidf, &pidfBind, &pidlRight);
  3698. if (SUCCEEDED(hr))
  3699. {
  3700. if (hr == S_OK)
  3701. {
  3702. IShellFolder *psfJctn;
  3703. hr = _Bind(NULL, pidfBind, IID_PPV_ARG(IShellFolder, &psfJctn));
  3704. if (SUCCEEDED(hr))
  3705. {
  3706. hr = psfJctn->GetDisplayNameOf(pidlRight, dwFlags, pStrRet);
  3707. psfJctn->Release();
  3708. }
  3709. ILFree((LPITEMIDLIST)pidfBind);
  3710. }
  3711. else
  3712. {
  3713. hr = _GetPathForItem(pidf, szPath);
  3714. if (SUCCEEDED(hr))
  3715. {
  3716. if (dwFlags & SHGDN_FORADDRESSBAR)
  3717. {
  3718. LPTSTR pszExt = PathFindCLSIDExtension(szPath, NULL);
  3719. if (pszExt)
  3720. *pszExt = 0;
  3721. }
  3722. hr = StringToStrRet(szPath, pStrRet);
  3723. }
  3724. }
  3725. }
  3726. }
  3727. }
  3728. else if (_IsCSIDL(CSIDL_RECENT) &&
  3729. SUCCEEDED(RecentDocs_GetDisplayName((LPCITEMIDLIST)pidf, szPath, SIZECHARS(szPath))))
  3730. {
  3731. LPITEMIDLIST pidlRecent;
  3732. WIN32_FIND_DATA wfd = {0};
  3733. StrCpyN(wfd.cFileName, szPath, SIZECHARS(wfd.cFileName));
  3734. if (SUCCEEDED(_CreateIDList(&wfd, NULL, &pidlRecent)))
  3735. {
  3736. hr = _NormalGetDisplayNameOf((LPCIDFOLDER)pidlRecent, pStrRet);
  3737. ILFree(pidlRecent);
  3738. }
  3739. }
  3740. else
  3741. {
  3742. ASSERT(ILIsEmpty(pidlNext)); // this variation should be single level
  3743. hr = _NormalGetDisplayNameOf(pidf, pStrRet);
  3744. }
  3745. }
  3746. else
  3747. {
  3748. if (IsSelf(1, &pidl) &&
  3749. ((dwFlags & (SHGDN_FORADDRESSBAR | SHGDN_INFOLDER | SHGDN_FORPARSING)) == SHGDN_FORPARSING))
  3750. {
  3751. TCHAR szPath[MAX_PATH];
  3752. hr = _GetPath(szPath);
  3753. if (SUCCEEDED(hr))
  3754. hr = StringToStrRet(szPath, pStrRet);
  3755. }
  3756. else
  3757. {
  3758. hr = E_INVALIDARG;
  3759. TraceMsg(TF_WARNING, "CFSFolder::GetDisplayNameOf() failing on PIDL %s", DumpPidl(pidl));
  3760. }
  3761. }
  3762. return hr;
  3763. }
  3764. void DoSmartQuotes(LPTSTR pszName)
  3765. {
  3766. LPTSTR pszFirst = StrChr(pszName, TEXT('"'));
  3767. if (pszFirst)
  3768. {
  3769. LPTSTR pszSecond = StrChr(pszFirst + 1, TEXT('"'));
  3770. if (pszSecond)
  3771. {
  3772. if (NULL == StrChr(pszSecond + 1, TEXT('"')))
  3773. {
  3774. *pszFirst = 0x201C; // left double quotation
  3775. *pszSecond = 0x201D; // right double quotation
  3776. }
  3777. }
  3778. }
  3779. }
  3780. HRESULT _PrepareNameForRename(LPTSTR pszName)
  3781. {
  3782. if (*pszName)
  3783. {
  3784. HRESULT hr = _CheckPortName(pszName);
  3785. if (SUCCEEDED(hr))
  3786. {
  3787. DoSmartQuotes(pszName);
  3788. }
  3789. return hr;
  3790. }
  3791. // avoid a bogus error msg with blank name (treat as user cancel)
  3792. return HRESULT_FROM_WIN32(ERROR_CANCELLED);
  3793. }
  3794. HRESULT CFSFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
  3795. DWORD dwFlags, LPITEMIDLIST *ppidl)
  3796. {
  3797. HRESULT hr = E_INVALIDARG;
  3798. if (ppidl)
  3799. *ppidl = NULL;
  3800. LPCIDFOLDER pidf = _IsValidID(pidl);
  3801. if (pidf)
  3802. {
  3803. CFileSysItemString fsi(pidf);
  3804. TCHAR szNewName[MAX_PATH];
  3805. SHUnicodeToTChar(pszName, szNewName, ARRAYSIZE(szNewName));
  3806. PathRemoveBlanks(szNewName); // leading and trailing blanks
  3807. if (dwFlags == SHGDN_NORMAL || dwFlags == SHGDN_INFOLDER)
  3808. {
  3809. hr = _SetLocalizedDisplayName(pidf, pszName);
  3810. if (SUCCEEDED(hr))
  3811. {
  3812. // Return the new pidl if ppidl is specified.
  3813. if (ppidl)
  3814. return _CreateIDListFromName(fsi.FSName(), -1, NULL, ppidl);
  3815. }
  3816. else if (*pszName == TEXT('@') && PathParseIconLocation(szNewName + 1))
  3817. {
  3818. // this is a localized string (eg "@C:\WINNT\System32\shell32.dll,-3")
  3819. // so do not go on and try to call SHRenameFileEx
  3820. return hr;
  3821. }
  3822. }
  3823. if (FAILED(hr))
  3824. {
  3825. hr = _PrepareNameForRename(szNewName);
  3826. if (SUCCEEDED(hr))
  3827. {
  3828. TCHAR szDir[MAX_PATH], szOldName[MAX_PATH];
  3829. _CopyName(pidf, szOldName, ARRAYSIZE(szOldName));
  3830. // If the extension is hidden
  3831. if (!(dwFlags & SHGDN_FORPARSING) && !fsi.ShowExtension(_DefaultShowExt()))
  3832. {
  3833. // copy it from the old name
  3834. StrCatBuff(szNewName, PathFindExtension(szOldName), ARRAYSIZE(szNewName));
  3835. }
  3836. hr = _GetPath(szDir);
  3837. if (SUCCEEDED(hr))
  3838. {
  3839. UINT cchDirLen = lstrlen(szDir);
  3840. // There are cases where the old name exceeded the maximum path, which
  3841. // would give a bogus error message. To avoid this we should check for
  3842. // this case and see if using the short name for the file might get
  3843. // around this...
  3844. //
  3845. if (cchDirLen + lstrlen(szOldName) + 2 > MAX_PATH)
  3846. {
  3847. if (cchDirLen + lstrlenA(fsi.AltName()) + 2 <= MAX_PATH)
  3848. SHAnsiToTChar(fsi.AltName(), szOldName, ARRAYSIZE(szOldName));
  3849. }
  3850. // do a binary compare, locale insenstive compare to avoid mappings of
  3851. // single chars into multiple and the reverse. specifically german
  3852. // sharp-S and "ss"
  3853. if (StrCmpC(szOldName, szNewName) == 0)
  3854. {
  3855. // when the before and after strings are identical we're okay with that.
  3856. // SHRenameFileEx would return -1 in that case -- we check here to save
  3857. // some stack.
  3858. hr = S_OK;
  3859. }
  3860. else
  3861. {
  3862. // We need to impl ::SetSite() and pass it to SHRenameFile
  3863. // to go modal if we display UI.
  3864. int iRes = SHRenameFileEx(hwnd, NULL, szDir, szOldName, szNewName);
  3865. hr = HRESULT_FROM_WIN32(iRes);
  3866. }
  3867. if (SUCCEEDED(hr) && ppidl)
  3868. {
  3869. // Return the new pidl if ppidl is specified.
  3870. hr = _CreateIDListFromName(szNewName, -1, NULL, ppidl);
  3871. }
  3872. }
  3873. }
  3874. }
  3875. }
  3876. return hr;
  3877. }
  3878. HRESULT CFSFolder::_FindDataFromIDFolder(LPCIDFOLDER pidf, WIN32_FIND_DATAW *pfd, BOOL fAllowSimplePid)
  3879. {
  3880. HRESULT hr;
  3881. CFileSysItemString fsi(pidf);
  3882. if (!fAllowSimplePid)
  3883. {
  3884. hr = fsi.GetFindData(pfd);
  3885. }
  3886. else
  3887. {
  3888. hr = fsi.GetFindDataSimple(pfd);
  3889. }
  3890. return hr;
  3891. }
  3892. /***
  3893. To avoid registry explosion, each pidl is passed to each handler.
  3894. HKCR\Folder\ColumnHandlers
  3895. <clsid>
  3896. "" = "Docfile handler"
  3897. <clsid>
  3898. "" = "Imagefile handler"
  3899. ***/
  3900. void CFSFolder::_DestroyColHandlers()
  3901. {
  3902. if (_hdsaColHandlers)
  3903. {
  3904. for (int i = 0; i < DSA_GetItemCount(_hdsaColHandlers); i++)
  3905. {
  3906. COLUMNLISTENTRY *pcle = (COLUMNLISTENTRY *)DSA_GetItemPtr(_hdsaColHandlers, i);
  3907. if (pcle->pcp)
  3908. pcle->pcp->Release();
  3909. }
  3910. DSA_Destroy(_hdsaColHandlers);
  3911. _hdsaColHandlers = NULL;
  3912. }
  3913. }
  3914. // returns the n'th handler for a given column
  3915. BOOL CFSFolder::_FindColHandler(UINT iCol, UINT iN, COLUMNLISTENTRY *pcle)
  3916. {
  3917. for (int i = 0; i < DSA_GetItemCount(_hdsaColHandlers); i++)
  3918. {
  3919. COLUMNLISTENTRY *pcleWalk = (COLUMNLISTENTRY *)DSA_GetItemPtr(_hdsaColHandlers, i);
  3920. if (pcleWalk->iColumnId == iCol)
  3921. {
  3922. if (iN-- == 0)
  3923. {
  3924. *pcle = *pcleWalk;
  3925. return TRUE;
  3926. }
  3927. }
  3928. }
  3929. return FALSE;
  3930. }
  3931. HRESULT CFSFolder::_LoadColumnHandlers()
  3932. {
  3933. // Have we been here?
  3934. if (NULL != _hdsaColHandlers)
  3935. return S_OK; // nothing to do.
  3936. ASSERT(0 == _dwColCount);
  3937. SHCOLUMNINIT shci = {0};
  3938. // retrieve folder path for provider init
  3939. HRESULT hr = _GetPathForItem(NULL, shci.wszFolder);
  3940. if (SUCCEEDED(hr))
  3941. {
  3942. _hdsaColHandlers = DSA_Create(sizeof(COLUMNLISTENTRY), 5);
  3943. if (_hdsaColHandlers)
  3944. {
  3945. int iUniqueColumnCount = 0;
  3946. HKEY hkCH;
  3947. // Enumerate HKCR\Folder\Shellex\ColumnProviders
  3948. // note: this really should have been "Directory", not "Folder"
  3949. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Folder\\shellex\\ColumnHandlers"), &hkCH))
  3950. {
  3951. TCHAR szHandlerCLSID[GUIDSTR_MAX];
  3952. int iHandler = 0;
  3953. while (ERROR_SUCCESS == RegEnumKey(hkCH, iHandler++, szHandlerCLSID, ARRAYSIZE(szHandlerCLSID)))
  3954. {
  3955. CLSID clsid;
  3956. IColumnProvider *pcp;
  3957. if (SUCCEEDED(SHCLSIDFromString(szHandlerCLSID, &clsid)) &&
  3958. SUCCEEDED(SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IColumnProvider, &pcp))))
  3959. {
  3960. if (SUCCEEDED(pcp->Initialize(&shci)))
  3961. {
  3962. int iCol = 0;
  3963. COLUMNLISTENTRY cle;
  3964. cle.pcp = pcp;
  3965. while (S_OK == pcp->GetColumnInfo(iCol++, &cle.shci))
  3966. {
  3967. cle.pcp->AddRef();
  3968. cle.iColumnId = iUniqueColumnCount++;
  3969. // Check if there's already a handler for this column ID,
  3970. for (int i = 0; i < DSA_GetItemCount(_hdsaColHandlers); i++)
  3971. {
  3972. COLUMNLISTENTRY *pcleLoop = (COLUMNLISTENTRY *)DSA_GetItemPtr(_hdsaColHandlers, i);
  3973. if (IsEqualSCID(pcleLoop->shci.scid, cle.shci.scid))
  3974. {
  3975. cle.iColumnId = pcleLoop->iColumnId; // set the iColumnId to the same as the first one
  3976. iUniqueColumnCount--; // so our count stays right
  3977. break;
  3978. }
  3979. }
  3980. DSA_AppendItem(_hdsaColHandlers, &cle);
  3981. }
  3982. }
  3983. pcp->Release();
  3984. }
  3985. }
  3986. RegCloseKey(hkCH);
  3987. }
  3988. // Sanity check
  3989. if (!DSA_GetItemCount(_hdsaColHandlers))
  3990. {
  3991. // DSA_Destroy(*phdsa);
  3992. ASSERT(iUniqueColumnCount==0);
  3993. iUniqueColumnCount = 0;
  3994. }
  3995. _dwColCount = (DWORD)iUniqueColumnCount;
  3996. }
  3997. else
  3998. {
  3999. hr = E_OUTOFMEMORY;
  4000. }
  4001. }
  4002. return hr;
  4003. }
  4004. // Initializes a SHCOLUMNDATA block.
  4005. HRESULT CFSFolder::_InitColData(LPCIDFOLDER pidf, SHCOLUMNDATA* pscd)
  4006. {
  4007. ZeroMemory(pscd, sizeof(*pscd));
  4008. HRESULT hr = _GetPathForItem(pidf, pscd->wszFile);
  4009. if (SUCCEEDED(hr))
  4010. {
  4011. pscd->pwszExt = PathFindExtensionW(pscd->wszFile);
  4012. pscd->dwFileAttributes = pidf->wAttrs;
  4013. if (FILE_ATTRIBUTE_OFFLINE & pscd->dwFileAttributes)
  4014. hr = E_FAIL;
  4015. else if (_bUpdateExtendedCols)
  4016. {
  4017. // set the dwFlags member to tell the col handler to
  4018. // not take data from it's cache
  4019. pscd->dwFlags = SHCDF_UPDATEITEM;
  4020. _bUpdateExtendedCols = FALSE; // only do this once!
  4021. }
  4022. }
  4023. return hr;
  4024. }
  4025. // Note:
  4026. // Setting _tbOfflineCSC = TRIBIT_UNDEFINED will retest the connection (good for a refresh).
  4027. // Setting _tbOfflineCSC = { other } will use a little cache hooey for perf.
  4028. //
  4029. // Return:
  4030. // TRUE pidl is offline
  4031. // FALSE otherwise
  4032. //
  4033. BOOL CFSFolder::_IsOfflineCSC(LPCIDFOLDER pidf)
  4034. {
  4035. TCHAR szPath[MAX_PATH];
  4036. // Update local cached answer for _pidl (folder).
  4037. if (_tbOfflineCSC == TRIBIT_UNDEFINED)
  4038. {
  4039. if (SUCCEEDED(_GetPath(szPath)) && _IsOfflineCSC(szPath))
  4040. _tbOfflineCSC = TRIBIT_TRUE;
  4041. else
  4042. _tbOfflineCSC = TRIBIT_FALSE;
  4043. }
  4044. ASSERT(_tbOfflineCSC != TRIBIT_UNDEFINED);
  4045. // Calculate answer for pidl.
  4046. BOOL bIsOffline;
  4047. if (_tbOfflineCSC == TRIBIT_TRUE)
  4048. bIsOffline = TRUE;
  4049. else
  4050. {
  4051. bIsOffline = _IsFolder(pidf) && SUCCEEDED(_GetPathForItem(pidf, szPath)) && _IsOfflineCSC(szPath);
  4052. }
  4053. return bIsOffline;
  4054. }
  4055. // Make sure we have a UNC \\server\share path. Do this before checking
  4056. // whether CSC is enabled, to avoid loading CSCDLL.DLL unless absolutely
  4057. // necessary.
  4058. BOOL CFSFolder::_IsOfflineCSC(LPCTSTR pszPath)
  4059. {
  4060. BOOL bUNC = FALSE;
  4061. TCHAR szUNC[MAX_PATH];
  4062. szUNC[0] = 0;
  4063. if (PathIsUNC(pszPath))
  4064. {
  4065. StrCpyN(szUNC, pszPath, ARRAYSIZE(szUNC));
  4066. }
  4067. else if (pszPath[1] == TEXT(':'))
  4068. {
  4069. TCHAR szLocalName[3] = { pszPath[0], pszPath[1], TEXT('\0') };
  4070. // Call GetDriveType() before WNetGetConnection(), to
  4071. // avoid loading MPR.DLL unless absolutely necessary.
  4072. if (DRIVE_REMOTE == GetDriveType(szLocalName))
  4073. {
  4074. // ignore return, szUNC filled in on success
  4075. DWORD cch = ARRAYSIZE(szUNC);
  4076. WNetGetConnection(szLocalName, szUNC, &cch);
  4077. }
  4078. }
  4079. return szUNC[0] &&
  4080. PathStripToRoot(szUNC) &&
  4081. (GetOfflineShareStatus(szUNC) == OFS_OFFLINE);
  4082. }
  4083. HRESULT CFSFolder::_ExtendedColumn(LPCIDFOLDER pidf, UINT iColumn, SHELLDETAILS *pDetails)
  4084. {
  4085. HRESULT hr = _LoadColumnHandlers();
  4086. if (SUCCEEDED(hr))
  4087. {
  4088. if (iColumn < _dwColCount)
  4089. {
  4090. if (NULL == pidf)
  4091. {
  4092. COLUMNLISTENTRY cle;
  4093. if (_FindColHandler(iColumn, 0, &cle))
  4094. {
  4095. pDetails->fmt = cle.shci.fmt;
  4096. pDetails->cxChar = cle.shci.cChars;
  4097. hr = StringToStrRet(cle.shci.wszTitle, &pDetails->str);
  4098. }
  4099. else
  4100. {
  4101. hr = E_NOTIMPL;
  4102. }
  4103. }
  4104. else
  4105. {
  4106. SHCOLUMNDATA shcd;
  4107. hr = _InitColData(pidf, &shcd);
  4108. if (SUCCEEDED(hr))
  4109. {
  4110. hr = E_FAIL; // loop below will try to reset this
  4111. // loop through all the column providers, breaking when one succeeds
  4112. COLUMNLISTENTRY cle;
  4113. for (int iTry = 0; _FindColHandler(iColumn, iTry, &cle); iTry++)
  4114. {
  4115. VARIANT var = {0};
  4116. hr = cle.pcp->GetItemData(&cle.shci.scid, &shcd, &var);
  4117. if (SUCCEEDED(hr))
  4118. {
  4119. if (S_OK == hr)
  4120. {
  4121. PROPERTYUI_FORMAT_FLAGS puiff = PUIFFDF_DEFAULT;
  4122. if (pDetails->fmt == LVCFMT_RIGHT_TO_LEFT)
  4123. {
  4124. puiff = PUIFFDF_RIGHTTOLEFT;
  4125. }
  4126. TCHAR szTemp[MAX_PATH];
  4127. hr = SHFormatForDisplay(cle.shci.scid.fmtid,
  4128. cle.shci.scid.pid,
  4129. (PROPVARIANT*)&var,
  4130. puiff,
  4131. szTemp,
  4132. ARRAYSIZE(szTemp));
  4133. if (SUCCEEDED(hr))
  4134. {
  4135. hr = StringToStrRet(szTemp, &pDetails->str);
  4136. }
  4137. VariantClear(&var);
  4138. break;
  4139. }
  4140. VariantClear(&var);
  4141. }
  4142. }
  4143. // if we failed to find a value here return empty success so we don't
  4144. // endlessly pester all column handlers for this column/item.
  4145. if (S_OK != hr)
  4146. {
  4147. pDetails->str.uType = STRRET_CSTR;
  4148. pDetails->str.cStr[0] = 0;
  4149. hr = S_FALSE;
  4150. }
  4151. }
  4152. }
  4153. }
  4154. else
  4155. hr = E_NOTIMPL; // the bogus return value defview expects...
  4156. }
  4157. return hr;
  4158. }
  4159. HRESULT CFSFolder::_CompareExtendedProp(int iColumn, LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  4160. {
  4161. HRESULT hr = _LoadColumnHandlers();
  4162. if (SUCCEEDED(hr))
  4163. {
  4164. if ((DWORD)iColumn < _dwColCount)
  4165. {
  4166. COLUMNLISTENTRY cle;
  4167. if (_FindColHandler(iColumn, 0, &cle))
  4168. {
  4169. int iRet = CompareBySCID(this, &cle.shci.scid, (LPCITEMIDLIST)pidf1, (LPCITEMIDLIST)pidf2);
  4170. hr = ResultFromShort(iRet);
  4171. }
  4172. }
  4173. else
  4174. {
  4175. hr = E_FAIL;
  4176. }
  4177. }
  4178. return hr;
  4179. }
  4180. HRESULT CFSFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
  4181. {
  4182. LPCIDFOLDER pidf = _IsValidID(pidl);
  4183. pDetails->str.uType = STRRET_CSTR;
  4184. pDetails->str.cStr[0] = 0;
  4185. if (iColumn >= ARRAYSIZE(c_fs_cols))
  4186. {
  4187. return _ExtendedColumn(pidf, iColumn - ARRAYSIZE(c_fs_cols), pDetails);
  4188. }
  4189. if (!pidf)
  4190. {
  4191. return GetDetailsOfInfo(c_fs_cols, ARRAYSIZE(c_fs_cols), iColumn, pDetails);
  4192. }
  4193. TCHAR szTemp[MAX_PATH];
  4194. szTemp[0] = 0;
  4195. switch (iColumn)
  4196. {
  4197. case FS_ICOL_NAME:
  4198. _NormalDisplayName(pidf, szTemp, ARRAYSIZE(szTemp));
  4199. break;
  4200. case FS_ICOL_SIZE:
  4201. if (!_IsFolder(pidf))
  4202. {
  4203. ULONGLONG cbSize = _Size(pidf);
  4204. StrFormatKBSize(cbSize, szTemp, ARRAYSIZE(szTemp));
  4205. }
  4206. break;
  4207. case FS_ICOL_TYPE:
  4208. _GetTypeNameBuf(pidf, szTemp, ARRAYSIZE(szTemp));
  4209. break;
  4210. case FS_ICOL_WRITETIME:
  4211. DosTimeToDateTimeString(pidf->dateModified, pidf->timeModified, szTemp, ARRAYSIZE(szTemp), pDetails->fmt & LVCFMT_DIRECTION_MASK);
  4212. break;
  4213. case FS_ICOL_CREATETIME:
  4214. case FS_ICOL_ACCESSTIME:
  4215. {
  4216. WIN32_FIND_DATAW wfd;
  4217. if (SUCCEEDED(_FindDataFromIDFolder(pidf, &wfd, FALSE)))
  4218. {
  4219. DWORD dwFlags = FDTF_DEFAULT;
  4220. switch (pDetails->fmt)
  4221. {
  4222. case LVCFMT_LEFT_TO_RIGHT:
  4223. dwFlags |= FDTF_LTRDATE;
  4224. break;
  4225. case LVCFMT_RIGHT_TO_LEFT:
  4226. dwFlags |= FDTF_RTLDATE;
  4227. break;
  4228. }
  4229. FILETIME ft = (iColumn == FS_ICOL_CREATETIME) ? wfd.ftCreationTime : wfd.ftLastAccessTime;
  4230. SHFormatDateTime(&ft, &dwFlags, szTemp, ARRAYSIZE(szTemp));
  4231. }
  4232. }
  4233. break;
  4234. case FS_ICOL_ATTRIB:
  4235. BuildAttributeString(pidf->wAttrs, szTemp, ARRAYSIZE(szTemp));
  4236. break;
  4237. case FS_ICOL_CSC_STATUS:
  4238. LoadString(HINST_THISDLL, _IsOfflineCSC(pidf) ? IDS_CSC_STATUS_OFFLINE : IDS_CSC_STATUS_ONLINE, szTemp, ARRAYSIZE(szTemp));
  4239. break;
  4240. }
  4241. return StringToStrRet(szTemp, &pDetails->str);
  4242. }
  4243. HRESULT CFSFolder::_GetIntroText(LPCIDFOLDER pidf, WCHAR* pwszIntroText, UINT cchIntroText)
  4244. {
  4245. HRESULT hr = E_FAIL;
  4246. TCHAR szPath[MAX_PATH];
  4247. if (SUCCEEDED(_GetPathForItem(pidf, szPath)))
  4248. {
  4249. // Keep the order in csidlIntroText and IntroTextCSIDLFolders, the same
  4250. const int csidlIntroText[] = {
  4251. CSIDL_STARTMENU,
  4252. CSIDL_COMMON_DOCUMENTS,
  4253. CSIDL_COMMON_PICTURES,
  4254. CSIDL_COMMON_MUSIC
  4255. };
  4256. UINT csidl = GetSpecialFolderID(szPath, csidlIntroText, ARRAYSIZE(csidlIntroText));
  4257. if (csidl != -1)
  4258. {
  4259. // Keep the order in csidlIntroText and IntroTextCSIDLFolders, the same
  4260. static struct
  4261. {
  4262. UINT csidl;
  4263. UINT resid;
  4264. } IntroTextCSIDLFolders[] = { {CSIDL_STARTMENU, IDS_INTRO_STARTMENU},
  4265. {CSIDL_COMMON_DOCUMENTS, IDS_INTRO_SHAREDDOCS},
  4266. {CSIDL_COMMON_PICTURES, IDS_INTRO_SHAREDPICTURES},
  4267. {CSIDL_COMMON_MUSIC, IDS_INTRO_SHAREDMUSIC} };
  4268. UINT residIntroText = 0;
  4269. for (int i = 0; i < ARRAYSIZE(IntroTextCSIDLFolders); i++)
  4270. {
  4271. if (IntroTextCSIDLFolders[i].csidl == csidl)
  4272. {
  4273. residIntroText = IntroTextCSIDLFolders[i].resid;
  4274. break;
  4275. }
  4276. }
  4277. if (residIntroText)
  4278. {
  4279. if (LoadString(HINST_THISDLL, residIntroText, pwszIntroText, cchIntroText))
  4280. {
  4281. hr = S_OK;
  4282. }
  4283. }
  4284. }
  4285. }
  4286. return hr;
  4287. }
  4288. DEFINE_SCID(SCID_HTMLINFOTIPFILE, PSGUID_MISC, PID_HTMLINFOTIPFILE);
  4289. BOOL GetShellClassInfoHTMLInfoTipFile(LPCTSTR pszPath, LPTSTR pszBuffer, DWORD cchBuffer)
  4290. {
  4291. TCHAR szHTMLInfoTipFile[MAX_PATH];
  4292. BOOL fRet = GetShellClassInfo(pszPath, TEXT("HTMLInfoTipFile"),
  4293. szHTMLInfoTipFile, ARRAYSIZE(szHTMLInfoTipFile));
  4294. if (fRet)
  4295. {
  4296. LPTSTR psz = szHTMLInfoTipFile;
  4297. if (StrCmpNI(TEXT("file://"), psz, 7) == 0) // ARRAYSIZE(TEXT("file://"))
  4298. {
  4299. psz += 7; // ARRAYSIZE(TEXT("file://"))
  4300. }
  4301. PathCombine(psz, pszPath, psz);
  4302. lstrcpyn(pszBuffer, psz, cchBuffer);
  4303. }
  4304. return fRet;
  4305. }
  4306. // These next functions are for the shell OM script support
  4307. HRESULT CFSFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  4308. {
  4309. BOOL fFound;
  4310. HRESULT hr = AssocGetDetailsOfSCID(this, pidl, pscid, pv, &fFound);
  4311. LPCIDFOLDER pidf = _IsValidID(pidl);
  4312. if (FAILED(hr) && !fFound && pidf)
  4313. {
  4314. if (IsEqualSCID(*pscid, SCID_FINDDATA))
  4315. {
  4316. WIN32_FIND_DATAW wfd;
  4317. hr = _FindDataFromIDFolder(pidf, &wfd, TRUE);
  4318. if (SUCCEEDED(hr))
  4319. {
  4320. hr = InitVariantFromBuffer(pv, &wfd, sizeof(wfd));
  4321. }
  4322. }
  4323. else if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID))
  4324. {
  4325. SHDESCRIPTIONID did = {0};
  4326. switch (((SIL_GetType(pidl) & SHID_TYPEMASK) & ~(SHID_FS_UNICODE | SHID_FS_COMMONITEM)) | SHID_FS)
  4327. {
  4328. case SHID_FS_FILE: did.dwDescriptionId = SHDID_FS_FILE; break;
  4329. case SHID_FS_DIRECTORY: did.dwDescriptionId = SHDID_FS_DIRECTORY; break;
  4330. default: did.dwDescriptionId = SHDID_FS_OTHER; break;
  4331. }
  4332. _GetJunctionClsid(pidf, &did.clsid);
  4333. hr = InitVariantFromBuffer(pv, &did, sizeof(did));
  4334. }
  4335. else if (IsEqualSCID(*pscid, SCID_FolderIntroText))
  4336. {
  4337. WCHAR wszIntroText[INFOTIPSIZE];
  4338. hr = _GetIntroText(pidf, wszIntroText, ARRAYSIZE(wszIntroText));
  4339. if (SUCCEEDED(hr))
  4340. {
  4341. hr = InitVariantFromStr(pv, wszIntroText);
  4342. }
  4343. }
  4344. else if (IsEqualSCID(*pscid, SCID_SIZE))
  4345. {
  4346. TCHAR szMountPoint[MAX_PATH];
  4347. // In case we fail
  4348. pv->ullVal = 0;
  4349. pv->vt = VT_UI8;
  4350. if (_GetMountingPointInfo(pidf, szMountPoint, ARRAYSIZE(szMountPoint)))
  4351. {
  4352. ULARGE_INTEGER uliFreeToCaller, uliTotal, uliTotalFree;
  4353. if (SHGetDiskFreeSpaceExW(szMountPoint, &uliFreeToCaller, &uliTotal,
  4354. &uliTotalFree))
  4355. {
  4356. pv->ullVal = uliTotal.QuadPart;
  4357. }
  4358. }
  4359. else
  4360. {
  4361. pv->ullVal = _Size(pidf); // note, size for folder is 0
  4362. pv->vt = VT_UI8;
  4363. }
  4364. hr = S_OK;
  4365. }
  4366. else if (IsEqualSCID(*pscid, SCID_FREESPACE))
  4367. {
  4368. TCHAR szMountPoint[MAX_PATH];
  4369. if (_GetMountingPointInfo(pidf, szMountPoint, ARRAYSIZE(szMountPoint)))
  4370. {
  4371. ULARGE_INTEGER uliFreeToCaller, uliTotal, uliTotalFree;
  4372. if (SHGetDiskFreeSpaceExW(szMountPoint, &uliFreeToCaller, &uliTotal, &uliTotalFree))
  4373. {
  4374. pv->ullVal = uliFreeToCaller.QuadPart;
  4375. pv->vt = VT_UI8;
  4376. hr = S_OK;
  4377. }
  4378. }
  4379. }
  4380. else if (IsEqualSCID(*pscid, SCID_WRITETIME) ||
  4381. IsEqualSCID(*pscid, SCID_CREATETIME) ||
  4382. IsEqualSCID(*pscid, SCID_ACCESSTIME))
  4383. {
  4384. WIN32_FIND_DATAW wfd;
  4385. hr = _FindDataFromIDFolder(pidf, &wfd, FALSE);
  4386. if (SUCCEEDED(hr))
  4387. {
  4388. FILETIME ft;
  4389. if (pscid->pid == PID_STG_WRITETIME)
  4390. {
  4391. ft = wfd.ftLastWriteTime;
  4392. }
  4393. else if (pscid->pid == PID_STG_CREATETIME)
  4394. {
  4395. ft = wfd.ftCreationTime;
  4396. }
  4397. else
  4398. {
  4399. ft = wfd.ftLastAccessTime;
  4400. }
  4401. hr = InitVariantFromFileTime(&ft, pv);
  4402. }
  4403. }
  4404. else if (IsEqualSCID(*pscid, SCID_DIRECTORY))
  4405. {
  4406. TCHAR szTemp[MAX_PATH];
  4407. hr = _GetPath(szTemp);
  4408. if (SUCCEEDED(hr))
  4409. {
  4410. hr = InitVariantFromStr(pv, szTemp);
  4411. }
  4412. }
  4413. else if (IsEqualSCID(*pscid, SCID_ATTRIBUTES_DESCRIPTION))
  4414. {
  4415. hr = _GetAttributesDescription(pidf, pv);
  4416. }
  4417. else if (IsEqualSCID(*pscid, SCID_LINKTARGET))
  4418. {
  4419. hr = _GetLinkTarget(pidl, pv);
  4420. }
  4421. else if (IsEqualSCID(*pscid, SCID_CSC_STATUS))
  4422. {
  4423. hr = _GetCSCStatus(pidf, pv);
  4424. }
  4425. else if ((IsEqualSCID(*pscid, SCID_Comment) || IsEqualSCID(*pscid, SCID_HTMLINFOTIPFILE)) &&
  4426. _IsSystemFolder(pidf))
  4427. {
  4428. TCHAR szPath[MAX_PATH];
  4429. hr = _GetPathForItem(pidf, szPath);
  4430. if (SUCCEEDED(hr))
  4431. {
  4432. TCHAR szText[MAX_PATH];
  4433. if (IsEqualSCID(*pscid, SCID_Comment))
  4434. GetShellClassInfoInfoTip(szPath, szText, ARRAYSIZE(szText));
  4435. else
  4436. GetShellClassInfoHTMLInfoTipFile(szPath, szText, ARRAYSIZE(szText));
  4437. hr = InitVariantFromStr(pv, szText);
  4438. }
  4439. }
  4440. else if (IsEqualSCID(*pscid, SCID_COMPUTERNAME))
  4441. {
  4442. hr = _GetComputerName(pidf, pv);
  4443. }
  4444. else if (IsEqualSCID(*pscid, SCID_NETWORKLOCATION))
  4445. {
  4446. hr = _GetNetworkLocation(pidf, pv);
  4447. }
  4448. else // if (Column Handler)
  4449. {
  4450. int iCol = FindSCID(c_fs_cols, ARRAYSIZE(c_fs_cols), pscid);
  4451. if (iCol >= 0)
  4452. {
  4453. SHELLDETAILS sd;
  4454. hr = GetDetailsOf(pidl, iCol, &sd);
  4455. if (SUCCEEDED(hr))
  4456. {
  4457. hr = InitVariantFromStrRet(&sd.str, pidl, pv);
  4458. }
  4459. }
  4460. else
  4461. {
  4462. hr = _LoadColumnHandlers();
  4463. if (SUCCEEDED(hr))
  4464. {
  4465. hr = E_FAIL;
  4466. for (int i = 0; i < DSA_GetItemCount(_hdsaColHandlers); i++)
  4467. {
  4468. COLUMNLISTENTRY *pcle = (COLUMNLISTENTRY *)DSA_GetItemPtr(_hdsaColHandlers, i);
  4469. if (IsEqualSCID(*pscid, pcle->shci.scid))
  4470. {
  4471. SHCOLUMNDATA shcd;
  4472. hr = _InitColData(pidf, &shcd);
  4473. if (SUCCEEDED(hr))
  4474. {
  4475. hr = pcle->pcp->GetItemData(pscid, &shcd, pv);
  4476. if (S_OK == hr)
  4477. break;
  4478. else if (SUCCEEDED(hr))
  4479. VariantClear(pv);
  4480. }
  4481. else
  4482. break;
  4483. }
  4484. }
  4485. }
  4486. }
  4487. }
  4488. }
  4489. return hr;
  4490. }
  4491. // GetDetailsEx() helper.
  4492. HRESULT CFSFolder::_GetAttributesDescription(LPCIDFOLDER pidf, VARIANT *pv)
  4493. {
  4494. static WCHAR szR[32] = {0}; // read-only
  4495. static WCHAR szH[32] = {0}; // hidden
  4496. static WCHAR szS[32] = {0}; // system
  4497. static WCHAR szC[32] = {0}; // compressed
  4498. static WCHAR szE[32] = {0}; // encrypted
  4499. static WCHAR szO[32] = {0}; // offline
  4500. WCHAR szAttributes[256] = {0};
  4501. size_t cchAttributes = ARRAYSIZE(szAttributes);
  4502. BOOL bIsFolder = _IsFolder(pidf);
  4503. //
  4504. // Initialize cached values once 'n only once.
  4505. //
  4506. if (!szR[0])
  4507. {
  4508. ASSERT(!szH[0] && !szS[0] && !szC[0] && !szE[0] && !szO[0]);
  4509. LoadString(HINST_THISDLL, IDS_ATTRIBUTE_READONLY, szR, ARRAYSIZE(szR));
  4510. LoadString(HINST_THISDLL, IDS_ATTRIBUTE_HIDDEN, szH, ARRAYSIZE(szH));
  4511. LoadString(HINST_THISDLL, IDS_ATTRIBUTE_SYSTEM, szS, ARRAYSIZE(szS));
  4512. LoadString(HINST_THISDLL, IDS_ATTRIBUTE_COMPRESSED, szC, ARRAYSIZE(szC));
  4513. LoadString(HINST_THISDLL, IDS_ATTRIBUTE_ENCRYPTED, szE, ARRAYSIZE(szE));
  4514. LoadString(HINST_THISDLL, IDS_ATTRIBUTE_OFFLINE, szO, ARRAYSIZE(szO));
  4515. }
  4516. else
  4517. {
  4518. ASSERT(szH[0] && szS[0] && szC[0] && szE[0] && szO[0]);
  4519. }
  4520. //
  4521. // Create attribute description string.
  4522. //
  4523. // read-only
  4524. if ((pidf->wAttrs & FILE_ATTRIBUTE_READONLY) && !bIsFolder)
  4525. _GetAttributesDescriptionBuilder(szAttributes, cchAttributes, szR);
  4526. // hidden
  4527. if (pidf->wAttrs & FILE_ATTRIBUTE_HIDDEN)
  4528. _GetAttributesDescriptionBuilder(szAttributes, cchAttributes, szH);
  4529. // system
  4530. if ((pidf->wAttrs & FILE_ATTRIBUTE_SYSTEM) && !bIsFolder)
  4531. _GetAttributesDescriptionBuilder(szAttributes, cchAttributes, szS);
  4532. // archive
  4533. // By design, archive is not exposed as an attribute description. It is
  4534. // used by "backup applications" and in general is a loose convention no
  4535. // one really cares about (chrisg). The decision to hide archive stems
  4536. // from a desire to keep the Details pane free of useless gargabe. Note
  4537. // that in Windows 2000, archive was not exposed through the web view.
  4538. // compressed
  4539. if (pidf->wAttrs & FILE_ATTRIBUTE_COMPRESSED)
  4540. _GetAttributesDescriptionBuilder(szAttributes, cchAttributes, szC);
  4541. // encrypted
  4542. if (pidf->wAttrs & FILE_ATTRIBUTE_ENCRYPTED)
  4543. _GetAttributesDescriptionBuilder(szAttributes, cchAttributes, szE);
  4544. // offline
  4545. if (pidf->wAttrs & FILE_ATTRIBUTE_OFFLINE)
  4546. _GetAttributesDescriptionBuilder(szAttributes, cchAttributes, szO);
  4547. return InitVariantFromStr(pv, szAttributes);
  4548. }
  4549. HRESULT CFSFolder::_GetAttributesDescriptionBuilder(LPWSTR szAttributes, size_t cchAttributes, LPWSTR szAttribute)
  4550. {
  4551. static WCHAR szDelimiter[4] = {0};
  4552. // Initialize cached delimiter once 'n only once.
  4553. if (!szDelimiter[0])
  4554. {
  4555. LoadString(HINST_THISDLL, IDS_COMMASPACE, szDelimiter, ARRAYSIZE(szDelimiter));
  4556. }
  4557. // Build attribute description.
  4558. if (!szAttributes[0])
  4559. {
  4560. StrNCpy(szAttributes, szAttribute, cchAttributes);
  4561. }
  4562. else
  4563. {
  4564. StrCatBuff(szAttributes, szDelimiter, cchAttributes);
  4565. StrCatBuff(szAttributes, szAttribute, cchAttributes);
  4566. }
  4567. return S_OK;
  4568. }
  4569. // GetDetailsEx() helper.
  4570. HRESULT CFSFolder::_GetLinkTarget(LPCITEMIDLIST pidl, VARIANT *pv)
  4571. {
  4572. IShellLink *psl;
  4573. HRESULT hr = GetUIObjectOf(NULL, 1, &pidl, IID_PPV_ARG_NULL(IShellLink, &psl));
  4574. if (SUCCEEDED(hr))
  4575. {
  4576. LPITEMIDLIST pidlTarget;
  4577. hr = psl->GetIDList(&pidlTarget);
  4578. if (SUCCEEDED(hr))
  4579. {
  4580. WCHAR szPath[MAX_PATH];
  4581. hr = SHGetNameAndFlags(pidlTarget, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL);
  4582. if (SUCCEEDED(hr))
  4583. hr = InitVariantFromStr(pv, szPath);
  4584. ILFree(pidlTarget);
  4585. }
  4586. psl->Release();
  4587. }
  4588. return hr;
  4589. }
  4590. // GetDetailsEx() helper.
  4591. HRESULT CFSFolder::_GetNetworkLocation(LPCIDFOLDER pidf, VARIANT *pv)
  4592. {
  4593. LPCITEMIDLIST pidl = (LPCITEMIDLIST)pidf;
  4594. IShellLink *psl;
  4595. HRESULT hr = GetUIObjectOf(NULL, 1, &pidl, IID_PPV_ARG_NULL(IShellLink, &psl));
  4596. if (SUCCEEDED(hr))
  4597. {
  4598. LPITEMIDLIST pidlTarget;
  4599. hr = psl->GetIDList(&pidlTarget);
  4600. if (SUCCEEDED(hr))
  4601. {
  4602. TCHAR szPath[MAX_PATH];
  4603. hr = SHGetNameAndFlags(pidlTarget, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL);
  4604. if (SUCCEEDED(hr))
  4605. {
  4606. DWORD dwZone;
  4607. hr = GetZoneFromUrl(szPath, NULL, &dwZone);
  4608. if (SUCCEEDED(hr))
  4609. {
  4610. TCHAR szBuffer[MAX_PATH];
  4611. switch (dwZone)
  4612. {
  4613. case URLZONE_LOCAL_MACHINE:
  4614. case URLZONE_INTRANET:
  4615. LoadString(g_hinst, IDS_NETLOC_LOCALNETWORK, szBuffer, ARRAYSIZE(szBuffer));
  4616. hr = InitVariantFromStr(pv, szBuffer);
  4617. break;
  4618. case URLZONE_INTERNET:
  4619. LoadString(g_hinst, IDS_NETLOC_INTERNET, szBuffer, ARRAYSIZE(szBuffer));
  4620. hr = InitVariantFromStr(pv, szBuffer);
  4621. break;
  4622. default:
  4623. hr = S_FALSE;
  4624. break;
  4625. }
  4626. }
  4627. }
  4628. ILFree(pidlTarget);
  4629. }
  4630. psl->Release();
  4631. }
  4632. return hr;
  4633. }
  4634. // GetDetailsEx() helper.
  4635. HRESULT CFSFolder::_GetComputerName(LPCIDFOLDER pidf, VARIANT *pv)
  4636. {
  4637. LPCITEMIDLIST pidl = (LPCITEMIDLIST)pidf;
  4638. IShellLink *psl;
  4639. HRESULT hr = GetUIObjectOf(NULL, 1, &pidl, IID_PPV_ARG_NULL(IShellLink, &psl));
  4640. if (SUCCEEDED(hr))
  4641. {
  4642. LPITEMIDLIST pidlTarget;
  4643. hr = psl->GetIDList(&pidlTarget);
  4644. if (SUCCEEDED(hr))
  4645. {
  4646. WCHAR szPath[MAX_PATH];
  4647. if (SUCCEEDED(SHGetNameAndFlags(pidlTarget, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL)))
  4648. {
  4649. if (PathIsURL(szPath))
  4650. {
  4651. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH + 1];
  4652. URL_COMPONENTS urlComps = {0};
  4653. urlComps.dwStructSize = sizeof(urlComps);
  4654. urlComps.lpszHostName = szServer;
  4655. urlComps.dwHostNameLength = ARRAYSIZE(szServer);
  4656. BOOL fResult = InternetCrackUrl(szPath, 0, ICU_DECODE, &urlComps);
  4657. if (fResult)
  4658. {
  4659. hr = InitVariantFromStr(pv, szServer);
  4660. }
  4661. else
  4662. {
  4663. hr = E_FAIL;
  4664. }
  4665. }
  4666. else if (PathIsUNC(szPath))
  4667. {
  4668. hr = _GetComputerName_FromPath(szPath, pv);
  4669. }
  4670. else
  4671. {
  4672. hr = E_FAIL;
  4673. }
  4674. }
  4675. else
  4676. {
  4677. hr = E_FAIL;
  4678. }
  4679. ILFree(pidlTarget);
  4680. }
  4681. psl->Release();
  4682. }
  4683. else
  4684. {
  4685. TCHAR szPath[MAX_PATH];
  4686. hr = _GetPath(szPath);
  4687. if (SUCCEEDED(hr))
  4688. {
  4689. hr = _GetComputerName_FromPath(szPath, pv);
  4690. }
  4691. }
  4692. if (FAILED(hr))
  4693. {
  4694. WCHAR sz[MAX_PATH];
  4695. LoadString(HINST_THISDLL, IDS_UNKNOWNGROUP, sz, ARRAYSIZE(sz));
  4696. hr = InitVariantFromStr(pv, sz);
  4697. }
  4698. return hr;
  4699. }
  4700. HRESULT CFSFolder::_GetComputerName_FromPath(LPCWSTR pszPath, VARIANT *pv)
  4701. {
  4702. HRESULT hr;
  4703. TCHAR szPath[MAX_PATH];
  4704. lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath));
  4705. PathStripToRoot(szPath);
  4706. if (PathIsUNC(szPath))
  4707. {
  4708. hr = _GetComputerName_FromUNC(szPath, pv);
  4709. }
  4710. else
  4711. {
  4712. CMountPoint* pMtPt = CMountPoint::GetMountPoint(szPath, FALSE);
  4713. if (pMtPt)
  4714. {
  4715. if (pMtPt->IsRemote())
  4716. {
  4717. WCHAR szRemotePath[MAX_PATH];
  4718. hr = pMtPt->GetRemotePath(szRemotePath, ARRAYSIZE(szRemotePath));
  4719. if (SUCCEEDED(hr))
  4720. {
  4721. hr = _GetComputerName_FromPath(szRemotePath, pv);
  4722. }
  4723. }
  4724. else
  4725. {
  4726. WCHAR sz[MAX_PATH];
  4727. LoadString(HINST_THISDLL, IDS_THISCOMPUTERGROUP, sz, ARRAYSIZE(sz));
  4728. hr = InitVariantFromStr(pv, sz);
  4729. }
  4730. pMtPt->Release();
  4731. }
  4732. else
  4733. {
  4734. hr = E_OUTOFMEMORY;
  4735. }
  4736. }
  4737. return hr;
  4738. }
  4739. HRESULT CFSFolder::_GetComputerName_FromUNC(LPWSTR pszPath, VARIANT *pv)
  4740. {
  4741. // strip to "\\server"
  4742. LPWSTR psz = pszPath;
  4743. while (*psz && *psz==L'\\')
  4744. psz++;
  4745. while (*psz && *psz!=L'\\')
  4746. psz++;
  4747. *psz = NULL;
  4748. LPITEMIDLIST pidl;
  4749. HRESULT hr = SHParseDisplayName(pszPath, NULL, &pidl, 0, NULL);
  4750. if (SUCCEEDED(hr))
  4751. {
  4752. WCHAR szName[MAX_PATH];
  4753. hr = SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, szName, ARRAYSIZE(szName), NULL);
  4754. if (SUCCEEDED(hr))
  4755. {
  4756. hr = InitVariantFromStr(pv, szName);
  4757. }
  4758. ILFree(pidl);
  4759. }
  4760. return hr;
  4761. }
  4762. // GetDetailsEx() helper.
  4763. HRESULT CFSFolder::_GetCSCStatus(LPCIDFOLDER pidf, VARIANT *pv)
  4764. {
  4765. HRESULT hr;
  4766. // Note:
  4767. // Only display the status in the Details task pane if it is "Offline".
  4768. if (_IsOfflineCSC(pidf))
  4769. {
  4770. WCHAR wszStatus[MAX_PATH];
  4771. if (LoadString(HINST_THISDLL, IDS_CSC_STATUS_OFFLINE, wszStatus, ARRAYSIZE(wszStatus)))
  4772. hr = InitVariantFromStr(pv, wszStatus);
  4773. else
  4774. hr = HRESULT_FROM_WIN32(GetLastError());
  4775. }
  4776. else
  4777. {
  4778. VariantInit(pv);
  4779. pv->vt = VT_NULL;
  4780. hr = S_OK;
  4781. }
  4782. return hr;
  4783. }
  4784. HRESULT CFSFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  4785. {
  4786. return E_NOTIMPL;
  4787. }
  4788. #define FVCBFT_MUSICFOLDER(ft) (FVCBFT_MUSIC == ft || FVCBFT_MYMUSIC == ft || FVCBFT_MUSICARTIST == ft || FVCBFT_MUSICALBUM == ft)
  4789. BOOL CFSFolder::_ShouldNotShowColumn(UINT iColumn)
  4790. {
  4791. BOOL fRet = FALSE; // show by default
  4792. if (FVCBFT_MUSICFOLDER(_nFolderType))
  4793. {
  4794. fRet = (iColumn == FS_ICOL_WRITETIME); // hide this one in the music case
  4795. }
  4796. return fRet;
  4797. }
  4798. BOOL CFSFolder::_ShouldShowExtendedColumn(const SHCOLUMNID* pscid)
  4799. {
  4800. BOOL fRet;
  4801. switch(_nFolderType)
  4802. {
  4803. case FVCBFT_PICTURES:
  4804. case FVCBFT_MYPICTURES:
  4805. case FVCBFT_PHOTOALBUM:
  4806. fRet = (IsEqualSCID(*pscid, SCID_WhenTaken) || IsEqualSCID(*pscid, SCID_ImageDimensions));
  4807. break;
  4808. case FVCBFT_MUSIC:
  4809. case FVCBFT_MYMUSIC:
  4810. case FVCBFT_MUSICARTIST:
  4811. case FVCBFT_MUSICALBUM:
  4812. fRet = (IsEqualSCID(*pscid, SCID_MUSIC_Artist) || IsEqualSCID(*pscid, SCID_MUSIC_Year) ||
  4813. IsEqualSCID(*pscid, SCID_MUSIC_Album) || IsEqualSCID(*pscid, SCID_MUSIC_Track) ||
  4814. IsEqualSCID(*pscid, SCID_AUDIO_Duration));
  4815. break;
  4816. case FVCBFT_VIDEOS:
  4817. case FVCBFT_MYVIDEOS:
  4818. case FVCBFT_VIDEOALBUM:
  4819. fRet = (IsEqualSCID(*pscid, SCID_AUDIO_Duration) || IsEqualSCID(*pscid, SCID_ImageDimensions));
  4820. break;
  4821. default:
  4822. fRet = FALSE;
  4823. break;
  4824. }
  4825. return fRet;
  4826. }
  4827. HRESULT CFSFolder::GetDefaultColumnState(UINT iColumn, DWORD *pdwState)
  4828. {
  4829. HRESULT hr = S_OK;
  4830. *pdwState = 0;
  4831. if (iColumn < ARRAYSIZE(c_fs_cols))
  4832. {
  4833. *pdwState = c_fs_cols[iColumn].csFlags;
  4834. if (_ShouldNotShowColumn(iColumn))
  4835. {
  4836. *pdwState &= ~SHCOLSTATE_ONBYDEFAULT; // flip stuff off
  4837. }
  4838. }
  4839. else
  4840. {
  4841. iColumn -= ARRAYSIZE(c_fs_cols);
  4842. hr = _LoadColumnHandlers();
  4843. if (SUCCEEDED(hr))
  4844. {
  4845. hr = E_INVALIDARG;
  4846. if (iColumn < _dwColCount)
  4847. {
  4848. COLUMNLISTENTRY cle;
  4849. if (_FindColHandler(iColumn, 0, &cle))
  4850. {
  4851. *pdwState |= (cle.shci.csFlags | SHCOLSTATE_EXTENDED | SHCOLSTATE_SLOW);
  4852. if (_ShouldShowExtendedColumn(&cle.shci.scid))
  4853. {
  4854. *pdwState |= SHCOLSTATE_ONBYDEFAULT;
  4855. }
  4856. else
  4857. {
  4858. *pdwState &= ~SHCOLSTATE_ONBYDEFAULT; // strip this one
  4859. }
  4860. hr = S_OK;
  4861. }
  4862. }
  4863. }
  4864. }
  4865. return hr;
  4866. }
  4867. HRESULT CFSFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  4868. {
  4869. HRESULT hr = MapColumnToSCIDImpl(c_fs_cols, ARRAYSIZE(c_fs_cols), iColumn, pscid);
  4870. if (hr != S_OK)
  4871. {
  4872. COLUMNLISTENTRY cle;
  4873. if (SUCCEEDED(_LoadColumnHandlers()))
  4874. {
  4875. iColumn -= ARRAYSIZE(c_fs_cols);
  4876. if (_FindColHandler(iColumn, 0, &cle))
  4877. {
  4878. *pscid = cle.shci.scid;
  4879. hr = S_OK;
  4880. }
  4881. }
  4882. }
  4883. return hr;
  4884. }
  4885. HRESULT CFSFolder::_MapSCIDToColumn(const SHCOLUMNID* pscid, UINT* puCol)
  4886. {
  4887. HRESULT hr;
  4888. int iCol = FindSCID(c_fs_cols, ARRAYSIZE(c_fs_cols), pscid);
  4889. if (iCol >= 0)
  4890. {
  4891. *puCol = iCol;
  4892. hr = S_OK;
  4893. }
  4894. else
  4895. {
  4896. hr = _LoadColumnHandlers();
  4897. if (SUCCEEDED(hr))
  4898. {
  4899. hr = E_FAIL;
  4900. for (int i = 0; i < DSA_GetItemCount(_hdsaColHandlers); i++)
  4901. {
  4902. COLUMNLISTENTRY *pcle = (COLUMNLISTENTRY *)DSA_GetItemPtr(_hdsaColHandlers, i);
  4903. if (IsEqualSCID(*pscid, pcle->shci.scid))
  4904. {
  4905. *puCol = pcle->iColumnId;
  4906. hr = S_OK;
  4907. break;
  4908. }
  4909. }
  4910. }
  4911. }
  4912. return hr;
  4913. }
  4914. //
  4915. // N ways to get a clasid for an item
  4916. //
  4917. BOOL CFSFolder::_GetBindCLSID(IBindCtx *pbc, LPCIDFOLDER pidf, CLSID *pclsid)
  4918. {
  4919. CFileSysItemString fsi(pidf);
  4920. DWORD dwClassFlags = fsi.ClassFlags(FALSE);
  4921. if (dwClassFlags & SHCF_IS_DOCOBJECT)
  4922. {
  4923. *pclsid = CLSID_CDocObjectFolder;
  4924. }
  4925. else if (fsi.GetJunctionClsid(pclsid, TRUE))
  4926. {
  4927. // *pclsid has the value
  4928. // HACK: CLSID_Briefcase is use to identify the briefcase
  4929. // but it's InProcServer is syncui.dll. we need to map that CLSID
  4930. // to the object implemented in shell32 (CLSID_BriefcaseFolder)
  4931. // ZEKELTODO - why isnt this a COM "TreatAs"?
  4932. if (IsEqualCLSID(*pclsid, CLSID_Briefcase))
  4933. *pclsid = CLSID_BriefcaseFolder;
  4934. }
  4935. else if (!IsEqualCLSID(CLSID_NULL, _clsidBind))
  4936. {
  4937. *pclsid = _clsidBind; // briefcase forces all children this way
  4938. }
  4939. else
  4940. {
  4941. return FALSE; // do normal binding
  4942. }
  4943. // TRUE -> special binding, FALSE -> normal file system binding
  4944. return !SHSkipJunctionBinding(pbc, pclsid);
  4945. }
  4946. // initalize shell folder handlers
  4947. // in:
  4948. // pidf multi level file system pidl
  4949. //
  4950. // in/out:
  4951. // *ppunk
  4952. //
  4953. // note: on failure this frees *ppunk
  4954. HRESULT CFSFolder::_InitFolder(IBindCtx *pbc, LPCIDFOLDER pidf, IUnknown **ppunk)
  4955. {
  4956. ASSERT(_FindJunctionNext(pidf) == NULL); // no extra goo please
  4957. LPITEMIDLIST pidlInit;
  4958. HRESULT hr = SHILCombine(_pidl, (LPITEMIDLIST)pidf, &pidlInit);
  4959. if (SUCCEEDED(hr))
  4960. {
  4961. IPersistFolder3 *ppf3;
  4962. hr = (*ppunk)->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3));
  4963. if (SUCCEEDED(hr))
  4964. {
  4965. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  4966. if (_csidlTrack >= 0)
  4967. {
  4968. // SHGetSpecialFolderlocation will return error if the target
  4969. // doesn't exist (which is good, since that means there's
  4970. // nothing to bind to).
  4971. LPITEMIDLIST pidl;
  4972. hr = SHGetSpecialFolderLocation(NULL, _csidlTrack, &pidl);
  4973. if (SUCCEEDED(hr))
  4974. {
  4975. hr = SHILCombine(pidl, (LPITEMIDLIST)pidf, &pfti.pidlTargetFolder);
  4976. ILFree(pidl);
  4977. }
  4978. }
  4979. else if (_pidlTarget)
  4980. hr = SHILCombine(_pidlTarget, (LPITEMIDLIST)pidf, &pfti.pidlTargetFolder);
  4981. if (SUCCEEDED(hr))
  4982. {
  4983. hr = _GetPathForItem(pidf, pfti.szTargetParsingName);
  4984. if (SUCCEEDED(hr))
  4985. {
  4986. if (_pszNetProvider)
  4987. SHTCharToUnicode(_pszNetProvider, pfti.szNetworkProvider, ARRAYSIZE(pfti.szNetworkProvider));
  4988. pfti.dwAttributes = _FindLastID(pidf)->wAttrs;
  4989. pfti.csidl = -1;
  4990. hr = ppf3->InitializeEx(pbc, pidlInit, &pfti);
  4991. }
  4992. ILFree(pfti.pidlTargetFolder);
  4993. }
  4994. ppf3->Release();
  4995. }
  4996. else
  4997. {
  4998. IPersistFolder *ppf;
  4999. hr = (*ppunk)->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf));
  5000. if (SUCCEEDED(hr))
  5001. {
  5002. hr = ppf->Initialize(pidlInit);
  5003. ppf->Release();
  5004. if (hr == E_NOTIMPL) // map E_NOTIMPL into success, the folder does not care
  5005. hr = S_OK;
  5006. }
  5007. }
  5008. ILFree(pidlInit);
  5009. }
  5010. if (FAILED(hr))
  5011. {
  5012. ((IUnknown *)*ppunk)->Release();
  5013. *ppunk = NULL;
  5014. }
  5015. return hr;
  5016. }
  5017. HRESULT CFSFolder::_InitStgFolder(LPCIDFOLDER pidf, LPCWSTR wszPath, DWORD grfMode, REFIID riid, void **ppv)
  5018. {
  5019. IStorage *pstg;
  5020. // pick up the storage from the pidl.
  5021. // TODO: make this work on stuff other than docfiles
  5022. HRESULT hr = StgGetStorageFromFile(wszPath, grfMode, &pstg);
  5023. if (SUCCEEDED(hr))
  5024. {
  5025. hr = CStgFolder_CreateInstance(NULL, riid, ppv);
  5026. if (SUCCEEDED(hr))
  5027. {
  5028. // we need to init CStgFolder with an IStorage that it
  5029. // wraps, so pass it in through the bind context.
  5030. IBindCtx *pbc;
  5031. hr = CreateBindCtx(0, &pbc);
  5032. if (SUCCEEDED(hr))
  5033. {
  5034. hr = pbc->RegisterObjectParam(STGSTR_STGTOBIND, pstg);
  5035. if (SUCCEEDED(hr))
  5036. hr = _InitFolder(pbc, pidf, (IUnknown **)ppv);
  5037. pbc->Release();
  5038. }
  5039. if (FAILED(hr))
  5040. ATOMICRELEASE(*((IUnknown **)ppv));
  5041. }
  5042. pstg->Release();
  5043. }
  5044. return hr;
  5045. }
  5046. CFSFolderPropertyBag::CFSFolderPropertyBag(CFSFolder *pFSFolder, DWORD grfMode) :
  5047. _cRef(1), _grfMode(grfMode), _pFSFolder(pFSFolder)
  5048. {
  5049. _pFSFolder->AddRef();
  5050. }
  5051. CFSFolderPropertyBag::~CFSFolderPropertyBag()
  5052. {
  5053. _pFSFolder->Release();
  5054. // Release all the property bags
  5055. for (int i = 0; i < ARRAYSIZE(_pPropertyBags); i++)
  5056. {
  5057. if (_pPropertyBags[i])
  5058. {
  5059. _pPropertyBags[i]->Release();
  5060. }
  5061. }
  5062. }
  5063. STDMETHODIMP CFSFolderPropertyBag::QueryInterface(REFIID riid, void **ppv)
  5064. {
  5065. static const QITAB qit[] = {
  5066. QITABENT(CFSFolderPropertyBag, IPropertyBag), // IID_IPropertyBag
  5067. { 0 },
  5068. };
  5069. return QISearch(this, qit, riid, ppv);
  5070. }
  5071. STDMETHODIMP_(ULONG) CFSFolderPropertyBag::AddRef()
  5072. {
  5073. return InterlockedIncrement(&_cRef);
  5074. }
  5075. STDMETHODIMP_(ULONG) CFSFolderPropertyBag::Release()
  5076. {
  5077. if (InterlockedDecrement(&_cRef))
  5078. return _cRef;
  5079. delete this;
  5080. return 0;
  5081. }
  5082. HRESULT CFSFolderPropertyBag::_Init(LPCIDFOLDER pidfLast)
  5083. {
  5084. TCHAR szFolderPath[MAX_PATH];
  5085. HRESULT hr = _pFSFolder->_GetPathForItem(pidfLast, szFolderPath);
  5086. if (SUCCEEDED(hr))
  5087. {
  5088. TCHAR szPath[MAX_PATH];
  5089. if (_GetIniPath((_grfMode == STGM_WRITE) || (_grfMode == STGM_READWRITE), szFolderPath, NULL, szPath))
  5090. {
  5091. // This is a customized folder (likely).
  5092. // Get an IPropertyBag on it's desktop.ini.
  5093. if (SUCCEEDED(SHCreatePropertyBagOnProfileSection(szPath, STRINI_CLASSINFO, _grfMode,
  5094. IID_PPV_ARG(IPropertyBag, &_pPropertyBags[INDEX_PROPERTYBAG_DESKTOPINI]))))
  5095. {
  5096. TCHAR szFolderType[128];
  5097. if (SUCCEEDED(SHPropertyBag_ReadStr(_pPropertyBags[INDEX_PROPERTYBAG_DESKTOPINI],
  5098. L"FolderType", szFolderType, ARRAYSIZE(szFolderType))))
  5099. {
  5100. TCHAR szRegPath[256];
  5101. StrCpyN(szRegPath, REGSTR_PATH_EXPLORER L"\\FolderClasses\\", ARRAYSIZE(szRegPath));
  5102. StrCatN(szRegPath, szFolderType, ARRAYSIZE(szRegPath));
  5103. SHCreatePropertyBagOnRegKey(HKEY_CURRENT_USER, szRegPath,
  5104. _grfMode, IID_PPV_ARG(IPropertyBag, &_pPropertyBags[INDEX_PROPERTYBAG_HKCU]));
  5105. SHCreatePropertyBagOnRegKey(HKEY_LOCAL_MACHINE, szRegPath,
  5106. _grfMode, IID_PPV_ARG(IPropertyBag, &_pPropertyBags[INDEX_PROPERTYBAG_HKLM]));
  5107. }
  5108. }
  5109. }
  5110. else
  5111. {
  5112. hr = E_FAIL;
  5113. }
  5114. }
  5115. return hr;
  5116. }
  5117. HRESULT CFSFolderPropertyBag::Read(LPCOLESTR pszPropName, VARIANT *pvar, IErrorLog *pErrorLog)
  5118. {
  5119. // We first try reading HKCU\RegKeySpecifiedInDesktopIniForTheFolder,
  5120. // then HKLM\RegKeySpecifiedInDesktopIniForTheFolder and finally
  5121. // the desktop.ini
  5122. HRESULT hr = E_FAIL;
  5123. for (int i = 0; FAILED(hr) && (i < ARRAYSIZE(_pPropertyBags)); i++)
  5124. {
  5125. if (_pPropertyBags[i])
  5126. {
  5127. hr = _pPropertyBags[i]->Read(pszPropName, pvar, pErrorLog);
  5128. }
  5129. }
  5130. return hr;
  5131. }
  5132. HRESULT CFSFolderPropertyBag::Write(LPCOLESTR pszPropName, VARIANT *pvar)
  5133. {
  5134. // We first try writing to HKCU\RegKeySpecifiedInDesktopIniForTheFolder,
  5135. // then to HKLM\RegKeySpecifiedInDesktopIniForTheFolder and finally
  5136. // to desktop.ini
  5137. HRESULT hr = E_FAIL;
  5138. for (int i = 0; FAILED(hr) && (i < ARRAYSIZE(_pPropertyBags)); i++)
  5139. {
  5140. if (_pPropertyBags[i])
  5141. {
  5142. hr = _pPropertyBags[i]->Write(pszPropName, pvar);
  5143. }
  5144. }
  5145. return hr;
  5146. }
  5147. // pidfLast can be NULL, if so create the bag on this folder
  5148. HRESULT CFSFolder::_CreateFolderPropertyBag(DWORD grfMode, LPCIDFOLDER pidfLast, REFIID riid, void **ppv)
  5149. {
  5150. *ppv = NULL;
  5151. HRESULT hr;
  5152. CFSFolderPropertyBag *pbag = new CFSFolderPropertyBag(this, grfMode);
  5153. if (pbag)
  5154. {
  5155. hr = pbag->_Init(pidfLast);
  5156. if (SUCCEEDED(hr))
  5157. {
  5158. hr = pbag->QueryInterface(riid, ppv);
  5159. }
  5160. pbag->Release();
  5161. }
  5162. else
  5163. {
  5164. hr = E_OUTOFMEMORY;
  5165. }
  5166. return hr;
  5167. }
  5168. //
  5169. // pidfLast and pszIniPath can be NULL.
  5170. // If not NULL, pidfLast is an IN param - specifies the relative pidl of a subfolder
  5171. // inside the CFSFolder object.
  5172. // If not NULL, pszIniPath is an OUT param (pointer to a buffer atleast MAX_PATH long)
  5173. // - receives the path to desktop.ini
  5174. //
  5175. BOOL CFSFolder::_CheckDefaultIni(LPCIDFOLDER pidfLast, LPTSTR pszIniPath)
  5176. {
  5177. BOOL fForceIni = FALSE;
  5178. TCHAR szPath[MAX_PATH];
  5179. if (!pszIniPath)
  5180. pszIniPath = szPath;
  5181. HRESULT hr = _GetPathForItem(pidfLast, pszIniPath);
  5182. if (SUCCEEDED(hr) && PathIsRoot(pszIniPath))
  5183. { // Desktop.ini has to be checked for the root folders
  5184. // even if the RO or SYSTEM bits are not set on them
  5185. fForceIni = TRUE;
  5186. }
  5187. else
  5188. {
  5189. UINT csidl;
  5190. if (!pidfLast)
  5191. {
  5192. csidl = _GetCSIDL(); // Get the cached value for the current folder
  5193. }
  5194. else
  5195. { // For subfolders, we don't have any cached values. So, compute.
  5196. _csidl = GetSpecialFolderID(pszIniPath, c_csidlSpecial, ARRAYSIZE(c_csidlSpecial));
  5197. }
  5198. switch (csidl)
  5199. { // Desktop.ini has to be checked for the following special folders
  5200. // even if the RO or SYSTEM bits are not set on them
  5201. case CSIDL_SYSTEM:
  5202. case CSIDL_WINDOWS:
  5203. case CSIDL_PERSONAL:
  5204. fForceIni = TRUE;
  5205. break;
  5206. }
  5207. }
  5208. if (!fForceIni)
  5209. { // Is the RO or SYSTEM bit set?
  5210. fForceIni = (_Attributes() & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM));
  5211. }
  5212. // Append desktop.ini to the path
  5213. if (SUCCEEDED(hr))
  5214. {
  5215. PathAppend(pszIniPath, c_szDesktopIni);
  5216. }
  5217. return fForceIni;
  5218. }
  5219. LPCTSTR CFSFolder::_BindHandlerName(REFIID riid)
  5220. {
  5221. LPCTSTR pszHandler = NULL;
  5222. if (IsEqualIID(riid, IID_IPropertySetStorage))
  5223. pszHandler = TEXT("PropertyHandler");
  5224. else if (IsEqualIID(riid, IID_IStorage))
  5225. pszHandler = TEXT("StorageHandler");
  5226. return pszHandler;
  5227. }
  5228. const CLSID CLSID_CTextIFilter = {
  5229. 0xc1243ca0,
  5230. 0xbf96,
  5231. 0x11cd,
  5232. { 0xb5, 0x79, 0x08, 0x00, 0x2b, 0x30, 0xbf, 0xeb }};
  5233. HRESULT LoadIFilterWithTextFallback(
  5234. WCHAR const *pwcsPath,
  5235. IUnknown *pUnkOuter,
  5236. void **ppIUnk)
  5237. {
  5238. HRESULT hr = LoadIFilter(pwcsPath, pUnkOuter, ppIUnk);
  5239. if (FAILED(hr))
  5240. {
  5241. DWORD dwFilterUnknown = 0;
  5242. DWORD cb = sizeof(dwFilterUnknown);
  5243. SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\ContentIndex"),
  5244. TEXT("FilterFilesWithUnknownExtensions"), NULL, &dwFilterUnknown, &cb);
  5245. if (dwFilterUnknown != 0)
  5246. {
  5247. IPersistFile *ppf;
  5248. hr = CoCreateInstance(CLSID_CTextIFilter, pUnkOuter, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPersistFile, &ppf));
  5249. if (SUCCEEDED(hr))
  5250. {
  5251. hr = ppf->Load(pwcsPath, STGM_READ);
  5252. if (SUCCEEDED(hr))
  5253. {
  5254. hr = ppf->QueryInterface(IID_IFilter, ppIUnk);
  5255. }
  5256. ppf->Release();
  5257. }
  5258. }
  5259. }
  5260. return hr;
  5261. }
  5262. // pidf - multi level file system only item
  5263. HRESULT CFSFolder::_Bind(LPBC pbc, LPCIDFOLDER pidf, REFIID riid, void **ppv)
  5264. {
  5265. ASSERT(_FindJunctionNext(pidf) == NULL); // no extra non file sys goo please
  5266. *ppv = NULL;
  5267. HRESULT hr;
  5268. CLSID clsid;
  5269. LPCIDFOLDER pidfLast = _FindLastID(pidf);
  5270. if (_GetBindCLSID(pbc, pidfLast, &clsid))
  5271. {
  5272. hr = SHExtCoCreateInstance(NULL, &clsid, NULL, riid, ppv);
  5273. if (SUCCEEDED(hr))
  5274. hr = _InitFolder(pbc, pidf, (IUnknown **)ppv);
  5275. if (FAILED(hr) && (E_NOINTERFACE != hr) && _IsFolder(pidfLast))
  5276. {
  5277. // the IShellFolder extension failed to load (might not be installed
  5278. // on this machine), so check if we should fall back to default to CFSFolder
  5279. UINT dwFlags;
  5280. if (_GetFolderFlags(pidf, &dwFlags) && (dwFlags & GFF_DEFAULT_TO_FS))
  5281. {
  5282. hr = CFSFolder_CreateInstance(NULL, riid, ppv);
  5283. if (SUCCEEDED(hr))
  5284. hr = _InitFolder(pbc, pidf, (IUnknown **)ppv);
  5285. }
  5286. }
  5287. }
  5288. else if (_IsFolder(pidfLast) || _IsSimpleID(pidfLast))
  5289. {
  5290. hr = CFSFolder_CreateInstance(NULL, riid, ppv);
  5291. if (SUCCEEDED(hr))
  5292. hr = _InitFolder(pbc, pidf, (IUnknown **)ppv);
  5293. }
  5294. else
  5295. hr = E_FAIL;
  5296. if (FAILED(hr))
  5297. {
  5298. // this handler has a string version
  5299. DWORD grfMode = BindCtx_GetMode(pbc, STGM_READ | STGM_SHARE_DENY_WRITE);
  5300. LPCTSTR pszHandler = _BindHandlerName(riid);
  5301. hr = _LoadHandler(pidf, grfMode, pszHandler, riid, ppv);
  5302. if (FAILED(hr))
  5303. {
  5304. WCHAR wszPath[MAX_PATH];
  5305. if (SUCCEEDED(_GetPathForItem(pidf, wszPath)))
  5306. {
  5307. if (IsEqualIID(riid, IID_IStream) && _IsFile(pidfLast))
  5308. {
  5309. hr = SHCreateStreamOnFileEx(wszPath, grfMode, FILE_ATTRIBUTE_NORMAL, FALSE, NULL, (IStream **)ppv);
  5310. }
  5311. else if (IsEqualIID(riid, IID_IPropertyBag) && _IsFolder(pidfLast))
  5312. {
  5313. hr = _CreateFolderPropertyBag(grfMode, pidf, riid, ppv);
  5314. }
  5315. else if (IsEqualIID(riid, IID_IPropertySetStorage))
  5316. {
  5317. // this is questionable at best. the caller
  5318. // should be filtering offline files, not this code.
  5319. // legacy support, I don't think anyone depends on this
  5320. // avoid offline files...
  5321. if (FILE_ATTRIBUTE_OFFLINE & pidf->wAttrs)
  5322. hr = STG_E_INVALIDFUNCTION;
  5323. else
  5324. {
  5325. hr = StgOpenStorageEx(wszPath, grfMode, STGFMT_ANY, 0, NULL, NULL, riid, ppv);
  5326. }
  5327. }
  5328. else if (IsEqualIID(riid, IID_IStorage))
  5329. {
  5330. hr = _InitStgFolder(pidf, wszPath, grfMode, riid, ppv);
  5331. }
  5332. else if (IsEqualIID(riid, IID_IMoniker))
  5333. {
  5334. hr = CreateFileMoniker(wszPath, (IMoniker **)ppv);
  5335. }
  5336. else if (IsEqualIID(riid, IID_IFilter))
  5337. {
  5338. hr = LoadIFilterWithTextFallback(wszPath, NULL, ppv);
  5339. }
  5340. }
  5341. }
  5342. }
  5343. ASSERT((SUCCEEDED(hr) && *ppv) || (FAILED(hr) && (NULL == *ppv))); // Assert hr is consistent w/out param.
  5344. return hr;
  5345. }
  5346. // returns:
  5347. // *ppidfBind - multi level file system pidl part (must free this on S_OK return)
  5348. // *ppidlRight - non file system part of pidl, continue bind down to this
  5349. //
  5350. // S_OK
  5351. // *ppidfBind needs to be freed
  5352. // S_FALSE
  5353. // pidf is a multi level file system only, bind to him
  5354. // FAILED() out of meory errors
  5355. HRESULT CFSFolder::_GetJunctionForBind(LPCIDFOLDER pidf, LPIDFOLDER *ppidfBind, LPCITEMIDLIST *ppidlRight)
  5356. {
  5357. *ppidfBind = NULL;
  5358. *ppidlRight = _FindJunctionNext(pidf);
  5359. if (*ppidlRight)
  5360. {
  5361. *ppidfBind = (LPIDFOLDER)ILClone((LPITEMIDLIST)pidf);
  5362. if (*ppidfBind)
  5363. {
  5364. // remove the part below the junction point
  5365. _ILSkip(*ppidfBind, (ULONG)((ULONG_PTR)*ppidlRight - (ULONG_PTR)pidf))->mkid.cb = 0;
  5366. return S_OK;
  5367. }
  5368. return E_OUTOFMEMORY;
  5369. }
  5370. return S_FALSE; // nothing interesting
  5371. }
  5372. HRESULT CFSFolder::GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex)
  5373. {
  5374. LPCIDFOLDER pidf = _IsValidID(pidl);
  5375. if (pidf)
  5376. {
  5377. CFileSysItemString fsi(pidf);
  5378. DWORD dwFlags;
  5379. int iIcon = -1;
  5380. // WARNING: don't include junctions (_IsFileFolder(pidf))
  5381. // so junctions like briefcase get their own cusotm icon.
  5382. //
  5383. if (_IsFileFolder(pidf))
  5384. {
  5385. TCHAR szMountPoint[MAX_PATH];
  5386. TCHAR szModule[MAX_PATH];
  5387. iIcon = II_FOLDER;
  5388. if (_GetMountingPointInfo(pidf, szMountPoint, ARRAYSIZE(szMountPoint)))
  5389. {
  5390. iIcon = GetMountedVolumeIcon(szMountPoint, szModule, ARRAYSIZE(szModule));
  5391. *piIndex = Shell_GetCachedImageIndex(szModule[0] ? szModule : c_szShell32Dll, iIcon, 0);
  5392. return S_OK;
  5393. }
  5394. else
  5395. {
  5396. if (!_IsSystemFolder(pidf) && (_GetCSIDL() == CSIDL_NORMAL))
  5397. {
  5398. if (flags & GIL_OPENICON)
  5399. iIcon = II_FOLDEROPEN;
  5400. else
  5401. iIcon = II_FOLDER;
  5402. *piIndex = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0);
  5403. return S_OK;
  5404. }
  5405. iIcon = II_FOLDER;
  5406. dwFlags = SHCF_ICON_PERINSTANCE;
  5407. }
  5408. }
  5409. else
  5410. dwFlags = fsi.ClassFlags(TRUE);
  5411. // the icon is per-instance, try to look it up
  5412. if (dwFlags & SHCF_ICON_PERINSTANCE)
  5413. {
  5414. TCHAR szFullPath[MAX_PATH];
  5415. DWORD uid = _GetUID(pidf); // get a unique identifier for this file.
  5416. if (uid == 0)
  5417. return S_FALSE;
  5418. if (FAILED(_GetPathForItem(pidf, szFullPath)))
  5419. {
  5420. // fall back to the relative name if we can't get the full path
  5421. lstrcpyn(szFullPath, fsi.FSName(), ARRAYSIZE(szFullPath));
  5422. }
  5423. *piIndex = LookupIconIndex(szFullPath, uid, flags | GIL_NOTFILENAME);
  5424. if (*piIndex != -1)
  5425. return S_OK;
  5426. // async extract (GIL_ASYNC) support
  5427. //
  5428. // we cant find the icon in the icon cache, we need to do real work
  5429. // to get the icon. if the caller specified GIL_ASYNC
  5430. // dont do the work, return E_PENDING forcing the caller to call
  5431. // back later to get the real icon.
  5432. //
  5433. // when returing E_PENDING we must fill in a default icon index
  5434. if (flags & GIL_ASYNC)
  5435. {
  5436. // come up with a default icon and return E_PENDING
  5437. if (_IsFolder(pidf))
  5438. iIcon = II_FOLDER;
  5439. else if (!(dwFlags & SHCF_HAS_ICONHANDLER) && PathIsExe(fsi.FSName()))
  5440. iIcon = II_APPLICATION;
  5441. else
  5442. iIcon = II_DOCNOASSOC;
  5443. *piIndex = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0);
  5444. TraceMsg(TF_IMAGE, "Shell_GetCachedImageIndex(%d) returned = %d", iIcon, *piIndex);
  5445. return E_PENDING; // we will be called back later for the real one
  5446. }
  5447. // If this is a folder, see if this folder has Per-Instance folder icon
  5448. // we do this here because it's too expensive to open a desktop.ini
  5449. // file and see what's in there. Most of the cases we will just hit
  5450. // the above cases
  5451. if (_IsSystemFolder(pidf))
  5452. {
  5453. if (!_GetFolderIconPath(pidf, NULL, 0, NULL))
  5454. {
  5455. // Note: the iIcon value has already been computed at the start of this funciton
  5456. ASSERT(iIcon != -1);
  5457. *piIndex = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0);
  5458. return S_OK;
  5459. }
  5460. }
  5461. //
  5462. // look up icon using IExtractIcon, this will load handler iff needed
  5463. // by calling ::GetUIObjectOf
  5464. //
  5465. IShellFolder *psf;
  5466. HRESULT hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  5467. if (SUCCEEDED(hr))
  5468. {
  5469. hr = SHGetIconFromPIDL(psf, NULL, (LPCITEMIDLIST)pidf, flags, piIndex);
  5470. psf->Release();
  5471. }
  5472. //
  5473. // remember this perinstance icon in the cache so we dont
  5474. // need to load the handler again.
  5475. //
  5476. // SHGetIconFromPIDL will always return a valid image index
  5477. // (it may default to a standard one) but it will fail
  5478. // if the file cant be accessed or some other sort of error.
  5479. // we dont want to cache in this case.
  5480. //
  5481. if (*piIndex != -1 && SUCCEEDED(hr) && (dwFlags & SHCF_HAS_ICONHANDLER))
  5482. {
  5483. int iIndexRetry;
  5484. ENTERCRITICAL;
  5485. //
  5486. // Inside the critical section, make sure the icon isn't already
  5487. // loaded, and if its not, then add it.
  5488. //
  5489. iIndexRetry = LookupIconIndex(szFullPath, uid, flags | GIL_NOTFILENAME);
  5490. if (iIndexRetry == -1)
  5491. {
  5492. AddToIconTable(szFullPath, uid, flags | GIL_NOTFILENAME, *piIndex);
  5493. }
  5494. LEAVECRITICAL;
  5495. }
  5496. return *piIndex == -1 ? S_FALSE : S_OK;
  5497. }
  5498. // icon is per-class dwFlags has the image index
  5499. *piIndex = (dwFlags & SHCF_ICON_INDEX);
  5500. return S_OK;
  5501. }
  5502. else
  5503. {
  5504. ASSERT(ILIsEmpty(pidl) || SIL_GetType(pidl) == SHID_ROOT_REGITEM); // regitems gives us these
  5505. return S_FALSE;
  5506. }
  5507. }
  5508. HANDLE g_hOverlayMgrCounter = NULL; // Global count of Overlay Manager changes.
  5509. int g_lOverlayMgrPerProcessCount = 0; // Per process count of Overlay Manager changes.
  5510. //
  5511. // Use this function to obtain address of the singleton icon overlay manager.
  5512. // If the function succeeds, caller is responsible for calling Release() through
  5513. // the returned interface pointer.
  5514. // The function ensures that the manager is initialized and up to date.
  5515. //
  5516. STDAPI GetIconOverlayManager(IShellIconOverlayManager **ppsiom)
  5517. {
  5518. HRESULT hr = E_FAIL;
  5519. if (IconOverlayManagerInit())
  5520. {
  5521. //
  5522. // Is a critsec for g_psiom required here you ask?
  5523. //
  5524. // No. The first call to IconOverlayInit in any process creates
  5525. // the overlay manager object and initializes g_psiom. This creation
  5526. // contributes 1 to the object's ref count. Subsequent calls to
  5527. // GetIconOverlayManager add to the ref count and the caller is
  5528. // responsible for decrementing the count through Release().
  5529. // The original ref count of 1 is not removed until
  5530. // IconOverlayManagerTerminate is called which happens only
  5531. // during PROCESS_DETACH. Therefore, the manager referenced by g_psiom
  5532. // in this code block will always be valid and a critsec is not
  5533. // required.
  5534. //
  5535. //
  5536. // ID for the global overlay manager counter.
  5537. //
  5538. static const GUID GUID_Counter = { /* 090851a5-eb96-11d2-8be4-00c04fa31a66 */
  5539. 0x090851a5,
  5540. 0xeb96,
  5541. 0x11d2,
  5542. {0x8b, 0xe4, 0x00, 0xc0, 0x4f, 0xa3, 0x1a, 0x66}
  5543. };
  5544. g_psiom->AddRef();
  5545. HANDLE hCounter = SHGetCachedGlobalCounter(&g_hOverlayMgrCounter, &GUID_Counter);
  5546. long lGlobalCount = SHGlobalCounterGetValue(hCounter);
  5547. if (lGlobalCount != g_lOverlayMgrPerProcessCount)
  5548. {
  5549. //
  5550. // Per-process counter is out of sync with the global counter.
  5551. // This means someone called SHLoadNonloadedIconOverlayIdentifiers
  5552. // so we must load any non-loaded identifiers from the registry.
  5553. //
  5554. g_psiom->LoadNonloadedOverlayIdentifiers();
  5555. g_lOverlayMgrPerProcessCount = lGlobalCount;
  5556. }
  5557. *ppsiom = g_psiom;
  5558. hr = S_OK;
  5559. }
  5560. return hr;
  5561. }
  5562. BOOL IconOverlayManagerInit()
  5563. {
  5564. if (!g_psiom)
  5565. {
  5566. IShellIconOverlayManager* psiom;
  5567. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_CFSIconOverlayManager, NULL, IID_PPV_ARG(IShellIconOverlayManager, &psiom))))
  5568. {
  5569. if (SHInterlockedCompareExchange((void **)&g_psiom, psiom, 0))
  5570. psiom->Release();
  5571. }
  5572. }
  5573. return BOOLFROMPTR(g_psiom);
  5574. }
  5575. void IconOverlayManagerTerminate()
  5576. {
  5577. ASSERTDLLENTRY; // does not require a critical section
  5578. IShellIconOverlayManager *psiom = (IShellIconOverlayManager *)InterlockedExchangePointer((void **)&g_psiom, 0);
  5579. if (psiom)
  5580. psiom->Release();
  5581. if (NULL != g_hOverlayMgrCounter)
  5582. {
  5583. CloseHandle(g_hOverlayMgrCounter);
  5584. g_hOverlayMgrCounter = NULL;
  5585. }
  5586. }
  5587. STDAPI SHLoadNonloadedIconOverlayIdentifiers(void)
  5588. {
  5589. //
  5590. // This will cause the next call GetIconOverlayManager() call in each process
  5591. // to load any non-loaded icon overlay identifiers.
  5592. //
  5593. if (g_hOverlayMgrCounter)
  5594. SHGlobalCounterIncrement(g_hOverlayMgrCounter);
  5595. return S_OK;
  5596. }
  5597. HRESULT CFSFolder::_GetOverlayInfo(LPCITEMIDLIST pidl, int * pIndex, DWORD dwFlags)
  5598. {
  5599. HRESULT hr = E_FAIL;
  5600. LPCIDFOLDER pidf = _IsValidID(pidl);
  5601. *pIndex = 0;
  5602. if (!pidf)
  5603. {
  5604. ASSERT(SIL_GetType(pidl) != SHID_ROOT_REGITEM); // CRegFolder should have handled it
  5605. return S_FALSE;
  5606. }
  5607. ASSERT(pidl == ILFindLastID(pidl));
  5608. if (IconOverlayManagerInit())
  5609. {
  5610. int iReservedID = -1;
  5611. WCHAR wszPath[MAX_PATH];
  5612. hr = _GetPathForItem(pidf, wszPath);
  5613. if (SUCCEEDED(hr))
  5614. {
  5615. IShellIconOverlayManager *psiom;
  5616. // The order of the "if" statements here is significant
  5617. if (_IsFile(pidf) && (_GetClassFlags(pidf) & SHCF_IS_LINK))
  5618. iReservedID = SIOM_RESERVED_LINK;
  5619. else
  5620. {
  5621. USES_CONVERSION;
  5622. LPCTSTR szPath = W2CT(wszPath);
  5623. if (_IsFolder(pidf) && (IsShared(szPath, FALSE)))
  5624. iReservedID = SIOM_RESERVED_SHARED;
  5625. else if (FILE_ATTRIBUTE_OFFLINE & pidf->wAttrs)
  5626. iReservedID = SIOM_RESERVED_SLOWFILE;
  5627. }
  5628. hr = GetIconOverlayManager(&psiom);
  5629. if (SUCCEEDED(hr))
  5630. {
  5631. if (iReservedID != -1)
  5632. hr = psiom->GetReservedOverlayInfo(wszPath, pidf->wAttrs, pIndex, dwFlags, iReservedID);
  5633. else
  5634. hr = psiom->GetFileOverlayInfo(wszPath, pidf->wAttrs, pIndex, dwFlags);
  5635. psiom->Release();
  5636. }
  5637. }
  5638. }
  5639. return hr;
  5640. }
  5641. HRESULT CFSFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex)
  5642. {
  5643. HRESULT hr = E_INVALIDARG;
  5644. ASSERT(pIndex);
  5645. if (pIndex)
  5646. hr = (*pIndex == OI_ASYNC) ? E_PENDING :
  5647. _GetOverlayInfo(pidl, pIndex, SIOM_OVERLAYINDEX);
  5648. return hr;
  5649. }
  5650. HRESULT CFSFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIconIndex)
  5651. {
  5652. return _GetOverlayInfo(pidl, pIconIndex, SIOM_ICONINDEX);
  5653. }
  5654. // CFSFolder : IPersist, IPersistFolder, IPersistFolder2, IPersistFolderAlias Members
  5655. HRESULT CFSFolder::GetClassID(CLSID *pclsid)
  5656. {
  5657. if (!IsEqualCLSID(_clsidBind, CLSID_NULL))
  5658. {
  5659. *pclsid = _clsidBind;
  5660. }
  5661. else
  5662. {
  5663. *pclsid = CLSID_ShellFSFolder;
  5664. }
  5665. return S_OK;
  5666. }
  5667. HRESULT CFSFolder::Initialize(LPCITEMIDLIST pidl)
  5668. {
  5669. _Reset();
  5670. return SHILClone(pidl, &_pidl);
  5671. }
  5672. HRESULT CFSFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  5673. {
  5674. return GetCurFolderImpl(_pidl, ppidl);
  5675. }
  5676. LPTSTR StrDupUnicode(const WCHAR *pwsz)
  5677. {
  5678. if (*pwsz)
  5679. {
  5680. USES_CONVERSION;
  5681. return StrDup(W2CT(pwsz));
  5682. }
  5683. return NULL;
  5684. }
  5685. HRESULT CFSFolder::_SetStgMode(DWORD grfFlags)
  5686. {
  5687. HRESULT hr = S_OK;
  5688. if (grfFlags & STGM_TRANSACTED)
  5689. hr = E_INVALIDARG;
  5690. if (SUCCEEDED(hr))
  5691. _grfFlags = grfFlags;
  5692. return hr;
  5693. }
  5694. HRESULT CFSFolder::InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot,
  5695. const PERSIST_FOLDER_TARGET_INFO *pfti)
  5696. {
  5697. HRESULT hr = Initialize(pidlRoot);
  5698. if (SUCCEEDED(hr))
  5699. {
  5700. if (pfti)
  5701. {
  5702. _dwAttributes = pfti->dwAttributes;
  5703. if (pfti->pidlTargetFolder ||
  5704. pfti->szTargetParsingName[0] ||
  5705. (pfti->csidl != -1))
  5706. {
  5707. if ((pfti->csidl != -1) && (pfti->csidl & CSIDL_FLAG_PFTI_TRACKTARGET))
  5708. {
  5709. // For tracking target, all other fields must be null.
  5710. if (!pfti->pidlTargetFolder &&
  5711. !pfti->szTargetParsingName[0] &&
  5712. !pfti->szNetworkProvider[0])
  5713. {
  5714. _csidlTrack = pfti->csidl & (~CSIDL_FLAG_MASK | CSIDL_FLAG_CREATE);
  5715. }
  5716. else
  5717. {
  5718. hr = E_INVALIDARG;
  5719. }
  5720. }
  5721. else
  5722. {
  5723. _pidlTarget = ILClone(pfti->pidlTargetFolder); // on NULL returns NULL
  5724. _pszPath = StrDupUnicode(pfti->szTargetParsingName);
  5725. _pszNetProvider = StrDupUnicode(pfti->szNetworkProvider);
  5726. if (pfti->csidl != -1)
  5727. _csidl = pfti->csidl & (~CSIDL_FLAG_MASK | CSIDL_FLAG_CREATE);
  5728. }
  5729. }
  5730. }
  5731. if (SUCCEEDED(hr))
  5732. {
  5733. hr = _SetStgMode(BindCtx_GetMode(pbc, STGM_READ | STGM_SHARE_DENY_WRITE));
  5734. }
  5735. if (SUCCEEDED(hr) && pbc)
  5736. {
  5737. _fDontForceCreate = BindCtx_ContainsObject(pbc, STR_DONT_FORCE_CREATE);
  5738. }
  5739. }
  5740. return hr;
  5741. }
  5742. HRESULT CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *pfti)
  5743. {
  5744. HRESULT hr = S_OK;
  5745. ZeroMemory(pfti, sizeof(*pfti));
  5746. _GetPathForItem(NULL, pfti->szTargetParsingName);
  5747. if (_pidlTarget)
  5748. hr = SHILClone(_pidlTarget, &pfti->pidlTargetFolder);
  5749. if (_pszNetProvider)
  5750. SHTCharToUnicode(_pszNetProvider, pfti->szNetworkProvider, ARRAYSIZE(pfti->szNetworkProvider));
  5751. pfti->dwAttributes = _dwAttributes;
  5752. if (_csidlTrack >= 0)
  5753. pfti->csidl = _csidlTrack | CSIDL_FLAG_PFTI_TRACKTARGET;
  5754. else
  5755. pfti->csidl = _GetCSIDL();
  5756. return hr;
  5757. }
  5758. STDAPI CFSFolder_CreateFolder(IUnknown *punkOuter, LPBC pbc, LPCITEMIDLIST pidl,
  5759. const PERSIST_FOLDER_TARGET_INFO *pfti, REFIID riid, void **ppv)
  5760. {
  5761. *ppv = NULL;
  5762. HRESULT hr;
  5763. CFSFolder *pfolder = new CFSFolder(punkOuter);
  5764. if (pfolder)
  5765. {
  5766. hr = pfolder->InitializeEx(pbc, pidl, pfti);
  5767. if (SUCCEEDED(hr))
  5768. hr = pfolder->_GetInner()->QueryInterface(riid, ppv);
  5769. pfolder->_GetInner()->Release();
  5770. }
  5771. else
  5772. hr = E_OUTOFMEMORY;
  5773. return hr;
  5774. }
  5775. // COM object creation entry point for CLSID_ShellFSFolder
  5776. STDAPI CFSFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  5777. {
  5778. return CFSFolder_CreateFolder(punkOuter, NULL, &c_idlDesktop, NULL, riid, ppv);
  5779. }
  5780. BOOL CFSFolder::_IsSlowPath()
  5781. {
  5782. if (_bSlowPath == INVALID_PATHSPEED)
  5783. {
  5784. TCHAR szPath[MAX_PATH];
  5785. _GetPath(szPath);
  5786. _bSlowPath = PathIsSlow(szPath, _Attributes()) ? TRUE : FALSE;
  5787. }
  5788. return _bSlowPath;
  5789. }
  5790. HRESULT CFSFolder::_GetToolTipForItem(LPCIDFOLDER pidf, REFIID riid, void **ppv)
  5791. {
  5792. IQueryAssociations *pqa;
  5793. HRESULT hr = _AssocCreate(pidf, IID_PPV_ARG(IQueryAssociations, &pqa));
  5794. if (SUCCEEDED(hr))
  5795. {
  5796. WCHAR szText[INFOTIPSIZE];
  5797. // If we are looking at a folder over a slow connection,
  5798. // show only quickly accessible properties
  5799. ASSOCSTR assocstr = _IsSlowPath() ? ASSOCSTR_QUICKTIP : ASSOCSTR_INFOTIP;
  5800. hr = pqa->GetString(0, assocstr, NULL, szText, (DWORD *)MAKEINTRESOURCE(SIZECHARS(szText)));
  5801. if (SUCCEEDED(hr))
  5802. {
  5803. hr = CreateInfoTipFromItem(SAFECAST(this, IShellFolder2 *), (LPCITEMIDLIST)pidf, szText, riid, ppv);
  5804. if (SUCCEEDED(hr) && _IsFileFolder(pidf))
  5805. {
  5806. IUnknown *punk = (IUnknown *)*ppv;
  5807. *ppv = NULL;
  5808. WCHAR szPath[MAX_PATH];
  5809. hr = _GetPathForItem(pidf, szPath);
  5810. if (SUCCEEDED(hr))
  5811. hr = CFolderInfoTip_CreateInstance(punk, szPath, riid, ppv);
  5812. punk->Release();
  5813. }
  5814. }
  5815. pqa->Release();
  5816. }
  5817. return hr;
  5818. }
  5819. //
  5820. // Call the shell file operation code to delete recursively the given directory,
  5821. // don't show any UI.
  5822. //
  5823. HRESULT CFSFolder::_Delete(LPCWSTR pszFile)
  5824. {
  5825. SHFILEOPSTRUCT fos = { 0 };
  5826. TCHAR szFile[MAX_PATH + 1];
  5827. SHUnicodeToTChar(pszFile, szFile, MAX_PATH);
  5828. // szFile is a double-zero terminated list of files.
  5829. // we can't just zero-init the szFile string to start with,
  5830. // since in debug SHUnicodeToTChar will bonk the uncopied part
  5831. // of the string with noise.
  5832. szFile[lstrlen(szFile) + 1] = 0;
  5833. fos.wFunc = FO_DELETE;
  5834. fos.pFrom = szFile;
  5835. fos.fFlags = FOF_NOCONFIRMATION | FOF_SILENT;
  5836. return SHFileOperation(&fos) ? E_FAIL : S_OK;
  5837. }
  5838. //
  5839. // Do a path combine thunking accordingly
  5840. //
  5841. HRESULT CFSFolder::_GetFullPath(LPCWSTR pszRelPath, LPWSTR pszFull)
  5842. {
  5843. WCHAR szPath[MAX_PATH];
  5844. _GetPathForItem(NULL, szPath);
  5845. PathCombineW(pszFull, szPath, pszRelPath);
  5846. return S_OK; // for now
  5847. }
  5848. HRESULT _FileExists(LPCWSTR pszPath, DWORD *pdwAttribs)
  5849. {
  5850. return PathFileExistsAndAttributesW(pszPath, pdwAttribs) ? S_OK : STG_E_FILENOTFOUND;
  5851. }
  5852. // IStorage
  5853. STDMETHODIMP CFSFolder::CreateStream(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStream **ppstm)
  5854. {
  5855. HRESULT hr = _OpenCreateStream(pwcsName, grfMode, ppstm, TRUE);
  5856. if (SUCCEEDED(hr))
  5857. {
  5858. WCHAR szFullPath[MAX_PATH];
  5859. _GetFullPath(pwcsName, szFullPath);
  5860. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szFullPath, NULL);
  5861. }
  5862. return hr;
  5863. }
  5864. STDMETHODIMP CFSFolder::OpenStream(LPCWSTR pwcsName, void *res1, DWORD grfMode, DWORD res2, IStream **ppstm)
  5865. {
  5866. return _OpenCreateStream(pwcsName, grfMode, ppstm, FALSE);
  5867. }
  5868. HRESULT CFSFolder::_OpenCreateStream(LPCWSTR pwcsName, DWORD grfMode, IStream **ppstm, BOOL fCreate)
  5869. {
  5870. *ppstm = NULL;
  5871. if (!pwcsName)
  5872. return STG_E_INVALIDPARAMETER;
  5873. WCHAR szFullPath[MAX_PATH];
  5874. _GetFullPath(pwcsName, szFullPath);
  5875. HRESULT hr = SHCreateStreamOnFileEx(szFullPath, grfMode, FILE_ATTRIBUTE_NORMAL, fCreate, NULL, ppstm);
  5876. return MapWin32ErrorToSTG(hr);
  5877. }
  5878. STDMETHODIMP CFSFolder::CreateStorage(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStorage **ppstg)
  5879. {
  5880. return _OpenCreateStorage(pwcsName, grfMode, ppstg, TRUE);
  5881. }
  5882. STDMETHODIMP CFSFolder::OpenStorage(LPCWSTR pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD res, IStorage **ppstg)
  5883. {
  5884. return _OpenCreateStorage(pwcsName, grfMode, ppstg, FALSE);
  5885. }
  5886. HRESULT CFSFolder::_OpenCreateStorage(LPCWSTR pwcsName, DWORD grfMode, IStorage **ppstg, BOOL fCreate)
  5887. {
  5888. *ppstg = NULL;
  5889. if (!pwcsName)
  5890. return STG_E_INVALIDPARAMETER;
  5891. if (grfMode &
  5892. ~(STGM_READ |
  5893. STGM_WRITE |
  5894. STGM_READWRITE |
  5895. STGM_SHARE_DENY_NONE |
  5896. STGM_SHARE_DENY_READ |
  5897. STGM_SHARE_DENY_WRITE |
  5898. STGM_SHARE_EXCLUSIVE |
  5899. STGM_CREATE ))
  5900. {
  5901. return STG_E_INVALIDPARAMETER;
  5902. }
  5903. // if the storage doesn't exist then lets create it, then drop into the
  5904. // open storage to do the right thing.
  5905. WCHAR szFullPath[MAX_PATH];
  5906. _GetFullPath(pwcsName, szFullPath);
  5907. DWORD dwAttributes;
  5908. HRESULT hr = _FileExists(szFullPath, &dwAttributes);
  5909. if (SUCCEEDED(hr))
  5910. {
  5911. if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
  5912. {
  5913. if (fCreate)
  5914. {
  5915. // an object exists, we must fail grfMode == STGM_FAILIFTHERE, or
  5916. // the object that exists is not a directory.
  5917. //
  5918. // if the STGM_CREATE flag is set and the object exists we will
  5919. // delete the existing storage.
  5920. // Check to make sure only one existence flag is specified
  5921. // FAILIFTHERE is zero so it can't be checked
  5922. if (STGM_FAILIFTHERE == (grfMode & (STGM_CREATE | STGM_CONVERT)))
  5923. hr = STG_E_FILEALREADYEXISTS;
  5924. else if (grfMode & STGM_CREATE)
  5925. {
  5926. // If they have not passed STGM_FAILIFTHERE, we'll replace an existing
  5927. // folder even if its readonly or system. Its up to the caller to make
  5928. // such filesystem-dependant checks first if they want to prevent that,
  5929. // as there's no way to pass information about whether we should or not
  5930. // down into CreateStorage
  5931. if (dwAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))
  5932. SetFileAttributes(szFullPath, dwAttributes & ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY));
  5933. hr = _Delete(szFullPath);
  5934. //
  5935. // I don't trust the result from SHFileOperation, so I consider success
  5936. // to be iff the directory is -gone-
  5937. //
  5938. if (FAILED(_FileExists(szFullPath, &dwAttributes)))
  5939. {
  5940. DWORD err = SHCreateDirectoryExW(NULL, szFullPath, NULL);
  5941. hr = HRESULT_FROM_WIN32(err);
  5942. }
  5943. else
  5944. {
  5945. // We couldn't remove the existing directory, so return an error,
  5946. // using what _Delete() said or, it if didn't return an error, E_FAIL
  5947. return (FAILED(hr) ? hr : E_FAIL);
  5948. }
  5949. }
  5950. else
  5951. hr = STG_E_INVALIDPARAMETER;
  5952. }
  5953. }
  5954. else
  5955. hr = E_FAIL; // a file, not a folder!
  5956. }
  5957. else
  5958. {
  5959. // the object doesn't exist, and they have not set the STGM_CREATE, nor
  5960. // is this a ::CreateStorage call.
  5961. hr = STG_E_FILENOTFOUND;
  5962. if (fCreate)
  5963. {
  5964. DWORD err = SHCreateDirectoryExW(NULL, szFullPath, NULL);
  5965. hr = HRESULT_FROM_WIN32(err);
  5966. }
  5967. }
  5968. // create a directory (we assume this will always succeed)
  5969. if (SUCCEEDED(hr))
  5970. {
  5971. LPITEMIDLIST pidl;
  5972. hr = ParseDisplayName(NULL, NULL, (LPWSTR)pwcsName, NULL, &pidl, NULL); // const -> non const
  5973. if (SUCCEEDED(hr))
  5974. {
  5975. hr = BindToObject(pidl, NULL, IID_PPV_ARG(IStorage, ppstg));
  5976. ILFree(pidl);
  5977. }
  5978. }
  5979. return hr;
  5980. }
  5981. STDMETHODIMP CFSFolder::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest)
  5982. {
  5983. return E_NOTIMPL;
  5984. }
  5985. // CFSFolder::MoveElementTo
  5986. //
  5987. // Copies or moves a source file (stream) to a destination storage. The stream
  5988. // itself, in this case our filestream object, does the actual work of moving
  5989. // the data around.
  5990. STDMETHODIMP CFSFolder::MoveElementTo(LPCWSTR pwcsName, IStorage *pstgDest, LPCWSTR pwcsNewName, DWORD grfFlags)
  5991. {
  5992. return StgMoveElementTo(SAFECAST(this, IShellFolder *), SAFECAST(this, IStorage *), pwcsName, pstgDest, pwcsNewName, grfFlags);
  5993. }
  5994. STDMETHODIMP CFSFolder::Commit(DWORD grfCommitFlags)
  5995. {
  5996. return S_OK; // changes are commited as we go, so return S_OK;
  5997. }
  5998. STDMETHODIMP CFSFolder::Revert()
  5999. {
  6000. return E_NOTIMPL; // changes are commited as we go, so cannot implement this.
  6001. }
  6002. STDMETHODIMP CFSFolder::EnumElements(DWORD res1, void *res2, DWORD res3, IEnumSTATSTG **ppenum)
  6003. {
  6004. HRESULT hr;
  6005. CFSFolderEnumSTATSTG *penum = new CFSFolderEnumSTATSTG(this);
  6006. if (penum)
  6007. {
  6008. *ppenum = (IEnumSTATSTG *) penum;
  6009. hr = S_OK;
  6010. }
  6011. else
  6012. {
  6013. *ppenum = NULL;
  6014. hr = E_OUTOFMEMORY;
  6015. }
  6016. return hr;
  6017. }
  6018. STDMETHODIMP CFSFolder::DestroyElement(LPCWSTR pwcsName)
  6019. {
  6020. if (!pwcsName)
  6021. return STG_E_INVALIDPARAMETER;
  6022. WCHAR szFullPath[MAX_PATH];
  6023. _GetFullPath(pwcsName, szFullPath);
  6024. return _Delete(szFullPath);
  6025. }
  6026. STDMETHODIMP CFSFolder::RenameElement(LPCWSTR pwcsOldName, LPCWSTR pwcsNewName)
  6027. {
  6028. if (!pwcsOldName || !pwcsNewName)
  6029. return STG_E_INVALIDPARAMETER;
  6030. WCHAR szOldPath[MAX_PATH];
  6031. _GetFullPath(pwcsOldName, szOldPath);
  6032. HRESULT hr = _FileExists(szOldPath, NULL);
  6033. if (SUCCEEDED(hr))
  6034. {
  6035. WCHAR szNewPath[MAX_PATH];
  6036. _GetFullPath(pwcsNewName, szNewPath);
  6037. hr = _FileExists(szNewPath, NULL);
  6038. if (FAILED(hr))
  6039. {
  6040. if (MoveFileW(szOldPath, szNewPath))
  6041. hr = S_OK;
  6042. else
  6043. hr = E_FAIL;
  6044. }
  6045. else
  6046. hr = STG_E_FILEALREADYEXISTS;
  6047. }
  6048. return hr;
  6049. }
  6050. STDMETHODIMP CFSFolder::SetElementTimes(LPCWSTR pwcsName, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime)
  6051. {
  6052. if (!pwcsName)
  6053. return STG_E_INVALIDPARAMETER;
  6054. WCHAR szFullPath[MAX_PATH];
  6055. _GetFullPath(pwcsName, szFullPath);
  6056. HRESULT hr = S_OK;
  6057. HANDLE hFile = CreateFileW(szFullPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  6058. if (INVALID_HANDLE_VALUE != hFile)
  6059. {
  6060. if (!SetFileTime(hFile, pctime, patime, pmtime))
  6061. hr = HRESULT_FROM_WIN32(GetLastError());
  6062. CloseHandle(hFile);
  6063. }
  6064. else
  6065. {
  6066. hr = STG_E_FILENOTFOUND;
  6067. }
  6068. return hr;
  6069. }
  6070. STDMETHODIMP CFSFolder::SetClass(REFCLSID clsid)
  6071. {
  6072. return E_NOTIMPL;
  6073. }
  6074. STDMETHODIMP CFSFolder::SetStateBits(DWORD grfStateBits, DWORD grfMask)
  6075. {
  6076. return E_NOTIMPL;
  6077. }
  6078. STDMETHODIMP CFSFolder::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  6079. {
  6080. HRESULT hr = E_FAIL;
  6081. ZeroMemory(pstatstg, sizeof(STATSTG)); // per COM conventions
  6082. TCHAR szPath[MAX_PATH];
  6083. _GetPath(szPath);
  6084. HANDLE hFile = CreateFile(szPath, FILE_READ_ATTRIBUTES,
  6085. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  6086. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
  6087. if (hFile != INVALID_HANDLE_VALUE)
  6088. {
  6089. BY_HANDLE_FILE_INFORMATION bhfi;
  6090. if (GetFileInformationByHandle(hFile, &bhfi))
  6091. {
  6092. ASSERT(bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  6093. pstatstg->type = STGTY_STORAGE;
  6094. pstatstg->mtime = bhfi.ftLastWriteTime;
  6095. pstatstg->ctime = bhfi.ftCreationTime;
  6096. pstatstg->atime = bhfi.ftLastAccessTime;
  6097. pstatstg->cbSize.HighPart = bhfi.nFileSizeHigh;
  6098. pstatstg->cbSize.LowPart = bhfi.nFileSizeLow;
  6099. pstatstg->grfMode = _grfFlags;
  6100. pstatstg->reserved = bhfi.dwFileAttributes;
  6101. hr = S_OK;
  6102. if (!(grfStatFlag & STATFLAG_NONAME))
  6103. {
  6104. hr = SHStrDup(PathFindFileName(szPath), &pstatstg->pwcsName);
  6105. }
  6106. }
  6107. CloseHandle(hFile);
  6108. }
  6109. else
  6110. hr = HRESULT_FROM_WIN32(GetLastError());
  6111. return hr;
  6112. }
  6113. // ITransferDest
  6114. STDMETHODIMP CFSFolder::Advise(ITransferAdviseSink *pAdvise, DWORD *pdwCookie)
  6115. {
  6116. if (_pAdvise)
  6117. return E_FAIL;
  6118. _pAdvise = pAdvise;
  6119. _pAdvise->AddRef();
  6120. if (pdwCookie)
  6121. *pdwCookie = 1;
  6122. return S_OK;
  6123. }
  6124. STDMETHODIMP CFSFolder::Unadvise(DWORD dwCookie)
  6125. {
  6126. if (1 != dwCookie)
  6127. return E_INVALIDARG;
  6128. if (_pAdvise)
  6129. {
  6130. ATOMICRELEASE(_pAdvise);
  6131. return S_OK;
  6132. }
  6133. return S_FALSE;
  6134. }
  6135. HRESULT CFSFolder::_GetStatStgFromItemName(LPCTSTR szName, STATSTG * pstat)
  6136. {
  6137. WIN32_FIND_DATA fd;
  6138. HRESULT hr = STG_E_FILENOTFOUND;
  6139. HANDLE hFile = FindFirstFile(szName, &fd);
  6140. if (hFile != INVALID_HANDLE_VALUE)
  6141. {
  6142. hr = StatStgFromFindData(&fd, STATFLAG_DEFAULT, pstat);
  6143. FindClose(hFile);
  6144. }
  6145. return hr;
  6146. }
  6147. STDMETHODIMP CFSFolder::OpenElement(const WCHAR *pwcsName, STGXMODE grfMode, DWORD *pdwType, REFIID riid, void **ppunk)
  6148. {
  6149. return E_NOTIMPL;
  6150. }
  6151. STDMETHODIMP CFSFolder::CreateElement(const WCHAR *pwcsName, IShellItem *psiTemplate, STGXMODE grfMode, DWORD dwType, REFIID riid, void **ppunk)
  6152. {
  6153. return E_NOTIMPL;
  6154. }
  6155. STDMETHODIMP CFSFolder::MoveElement(IShellItem *psiItem, WCHAR *pwcsNewName, STGXMOVE grfOptions)
  6156. {
  6157. return E_NOTIMPL;
  6158. }
  6159. STDMETHODIMP CFSFolder::DestroyElement(const WCHAR * pwcsName, STGXDESTROY grfOptions)
  6160. {
  6161. return E_NOTIMPL;
  6162. }
  6163. // Create a storage object for the specified path, returning a suitable
  6164. // IStorage (or error).
  6165. //
  6166. // In:
  6167. // pwszPath -> directory
  6168. // grfMode -> flags passed to IStorage::CreateStorage
  6169. // ppstg -> receieves the storage object
  6170. //
  6171. // Out:
  6172. // HRESULT
  6173. STDAPI SHCreateStorageOnDirectory(LPCWSTR pszPath, DWORD grfMode, IStorage **ppstg)
  6174. {
  6175. *ppstg = NULL;
  6176. TCHAR szPath[MAX_PATH];
  6177. SHUnicodeToTChar(pszPath, szPath, ARRAYSIZE(szPath));
  6178. LPITEMIDLIST pidl;
  6179. HRESULT hr = SHILCreateFromPath(szPath, &pidl, NULL);
  6180. if (SUCCEEDED(hr))
  6181. {
  6182. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IStorage, pidl, ppstg));
  6183. ILFree(pidl);
  6184. }
  6185. return hr;
  6186. }
  6187. STDAPI SHCreatePropStgOnFolder(LPCTSTR pszFolder, DWORD grfMode, IPropertySetStorage **ppss);
  6188. HRESULT CFSFolder::_LoadPropHandler()
  6189. {
  6190. HRESULT hr = S_OK;
  6191. if (_pstg)
  6192. {
  6193. hr = S_OK;
  6194. }
  6195. else
  6196. {
  6197. TCHAR szPath[MAX_PATH];
  6198. _GetPath(szPath);
  6199. hr = StgOpenStorageOnFolder(szPath, _grfFlags, IID_PPV_ARG(IPropertySetStorage, &_pstg));
  6200. // if (FAILED(hr))
  6201. // hr = SHCreatePropStgOnFolder(szPath, _grfFlags, &_pstg);
  6202. }
  6203. return hr;
  6204. }
  6205. STDMETHODIMP CFSFolder::Create(REFFMTID fmtid, const CLSID *pclsid, DWORD grfFlags,
  6206. DWORD grfMode, IPropertyStorage **pppropstg)
  6207. {
  6208. HRESULT hr = _LoadPropHandler();
  6209. if (SUCCEEDED(hr))
  6210. hr = _pstg->Create(fmtid, pclsid, grfFlags, grfMode, pppropstg);
  6211. return hr;
  6212. }
  6213. STDMETHODIMP CFSFolder::Open(REFFMTID fmtid, DWORD grfMode, IPropertyStorage **pppropstg)
  6214. {
  6215. HRESULT hr = _LoadPropHandler();
  6216. if (SUCCEEDED(hr))
  6217. hr = _pstg->Open(fmtid, grfMode, pppropstg);
  6218. return hr;
  6219. }
  6220. STDMETHODIMP CFSFolder::Delete(REFFMTID fmtid)
  6221. {
  6222. HRESULT hr = _LoadPropHandler();
  6223. if (SUCCEEDED(hr))
  6224. hr = _pstg->Delete(fmtid);
  6225. return hr;
  6226. }
  6227. STDMETHODIMP CFSFolder::Enum(IEnumSTATPROPSETSTG ** ppenum)
  6228. {
  6229. HRESULT hr = _LoadPropHandler();
  6230. if (SUCCEEDED(hr))
  6231. hr = _pstg->Enum(ppenum);
  6232. return hr;
  6233. }
  6234. // IItemNameLimits methods
  6235. #define INVALID_NAME_CHARS L"\\/:*?\"<>|"
  6236. STDMETHODIMP CFSFolder::GetValidCharacters(LPWSTR *ppwszValidChars, LPWSTR *ppwszInvalidChars)
  6237. {
  6238. *ppwszValidChars = NULL;
  6239. return SHStrDup(INVALID_NAME_CHARS, ppwszInvalidChars);
  6240. }
  6241. STDMETHODIMP CFSFolder::GetMaxLength(LPCWSTR pszName, int *piMaxNameLen)
  6242. {
  6243. TCHAR szPath[MAX_PATH];
  6244. BOOL fShowExtension = _DefaultShowExt();
  6245. LPITEMIDLIST pidl;
  6246. StrCpyN(szPath, pszName, ARRAYSIZE(szPath));
  6247. HRESULT hr = ParseDisplayName(NULL, NULL, szPath, NULL, &pidl, NULL);
  6248. if (SUCCEEDED(hr))
  6249. {
  6250. LPCIDFOLDER pidf = _IsValidID(pidl);
  6251. if (pidf)
  6252. {
  6253. fShowExtension = _ShowExtension(pidf);
  6254. }
  6255. ILFree(pidl);
  6256. }
  6257. hr = _GetPath(szPath);
  6258. if (SUCCEEDED(hr))
  6259. {
  6260. if (PathAppend(szPath, pszName))
  6261. hr = GetCCHMaxFromPath(szPath, (UINT *)piMaxNameLen, fShowExtension);
  6262. else
  6263. hr = E_FAIL;
  6264. }
  6265. return hr;
  6266. }
  6267. // ISetFolderEnumRestriction methods
  6268. STDMETHODIMP CFSFolder::SetEnumRestriction(DWORD dwRequired, DWORD dwForbidden)
  6269. {
  6270. _dwEnumRequired = dwRequired;
  6271. _dwEnumForbidden = dwForbidden;
  6272. return S_OK;
  6273. }
  6274. // IOleCommandTarget stuff
  6275. STDMETHODIMP CFSFolder::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  6276. {
  6277. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  6278. if (pguidCmdGroup == NULL)
  6279. {
  6280. for (UINT i = 0; i < cCmds; i++)
  6281. {
  6282. // ONLY say that we support the stuff we support in ::OnExec
  6283. switch (rgCmds[i].cmdID)
  6284. {
  6285. case OLECMDID_REFRESH:
  6286. rgCmds[i].cmdf = OLECMDF_ENABLED;
  6287. break;
  6288. default:
  6289. rgCmds[i].cmdf = 0;
  6290. break;
  6291. }
  6292. hr = S_OK;
  6293. }
  6294. }
  6295. return hr;
  6296. }
  6297. STDMETHODIMP CFSFolder::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  6298. {
  6299. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  6300. if (pguidCmdGroup == NULL)
  6301. {
  6302. switch (nCmdID)
  6303. {
  6304. case OLECMDID_REFRESH:
  6305. _dwAttributes = -1;
  6306. _bUpdateExtendedCols = TRUE;
  6307. _tbDefShowExt = TRIBIT_UNDEFINED;
  6308. _tbOfflineCSC = TRIBIT_UNDEFINED;
  6309. hr = S_OK;
  6310. break;
  6311. }
  6312. }
  6313. return hr;
  6314. }
  6315. // global hook in the SHChangeNotify() dispatcher. note we get all change notifies
  6316. // here so be careful!
  6317. STDAPI CFSFolder_IconEvent(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  6318. {
  6319. switch (lEvent)
  6320. {
  6321. case SHCNE_ASSOCCHANGED:
  6322. {
  6323. FlushFileClass(); // flush them all
  6324. HWND hwnd = GetDesktopWindow();
  6325. if (IsWindow(hwnd))
  6326. PostMessage(hwnd, DTM_SETUPAPPRAN, 0, 0);
  6327. }
  6328. break;
  6329. }
  6330. return S_OK;
  6331. }
  6332. //
  6333. // 317617 - Hacky update for the icon cache - ZekeL - 19-APR-2001
  6334. // this is for defview to invalidate icon indeces that are indirected
  6335. // specifically if you have a LNK file and its target changes icons
  6336. // (like a CD will), then the LNK is updated by defview processing the
  6337. // SHCNE_UPDATEIMAGE and noticing that one of its items also matches
  6338. // this image index.
  6339. //
  6340. // the righteous fix is to make SCN call into the fileicon cache
  6341. // and reverse lookup any entries that match the icon index and invalidate
  6342. // them. that way we wouldnt miss anything.
  6343. //
  6344. STDAPI_(void) CFSFolder_UpdateIcon(IShellFolder *psf, LPCITEMIDLIST pidl)
  6345. {
  6346. LPCIDFOLDER pidf = CFSFolder::_IsValidID(pidl);
  6347. if (pidf)
  6348. {
  6349. TCHAR szName[MAX_PATH];
  6350. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_FORPARSING, szName, ARRAYSIZE(szName))))
  6351. {
  6352. RemoveFromIconTable(szName);
  6353. }
  6354. }
  6355. }
  6356. // ugly wrappers for external clients, remove these as possible
  6357. STDAPI CFSFolder_CompareNames(LPCIDFOLDER pidf1, LPCIDFOLDER pidf2)
  6358. {
  6359. CFileSysItemString fsi1(pidf1), fsi2(pidf2);
  6360. return ResultFromShort((short)lstrcmpi(fsi1.FSName(), fsi2.FSName()));
  6361. }
  6362. STDAPI_(DWORD) CFSFolder_PropertiesThread(void *pv)
  6363. {
  6364. return CFSFolder::_PropertiesThread(pv);
  6365. }
  6366. STDAPI_(LPCIDFOLDER) CFSFolder_IsValidID(LPCITEMIDLIST pidl)
  6367. {
  6368. return CFSFolder::_IsValidID(pidl);
  6369. }
  6370. STDAPI_(BOOL) CFSFolder_IsCommonItem(LPCITEMIDLIST pidl)
  6371. {
  6372. return CFSFolder::_IsCommonItem(pidl);
  6373. }
  6374. CFSIconManager::CFSIconManager()
  6375. {
  6376. _wszPath[0] = NULL;
  6377. _cRef = 1;
  6378. }
  6379. HRESULT CFSIconManager::_Init(LPCITEMIDLIST pidl, IShellFolder *psf)
  6380. {
  6381. HRESULT hr = S_OK;
  6382. if ((psf == NULL) || (pidl == NULL))
  6383. hr = E_INVALIDARG;
  6384. if (SUCCEEDED(hr))
  6385. hr = DisplayNameOf(psf, pidl, SHGDN_FORPARSING, _wszPath, ARRAYSIZE(_wszPath));
  6386. return hr;
  6387. }
  6388. HRESULT CFSIconManager::QueryInterface(REFIID riid, void **ppv)
  6389. {
  6390. static const QITAB qit[] = {
  6391. QITABENT(CFSIconManager, ICustomIconManager),
  6392. { 0 },
  6393. };
  6394. return QISearch(this, qit, riid, ppv);
  6395. }
  6396. ULONG CFSIconManager::AddRef()
  6397. {
  6398. return InterlockedIncrement(&_cRef);
  6399. }
  6400. ULONG CFSIconManager::Release()
  6401. {
  6402. if (InterlockedDecrement(&_cRef))
  6403. return _cRef;
  6404. delete this;
  6405. return 0;
  6406. }
  6407. STDMETHODIMP CFSIconManager::GetDefaultIconHandle(HICON *phIcon)
  6408. {
  6409. HRESULT hr = S_OK;
  6410. if (phIcon == NULL)
  6411. hr = E_INVALIDARG;
  6412. if (SUCCEEDED(hr))
  6413. {
  6414. WCHAR szCustomizedIconPath[MAX_PATH];
  6415. int nCustomizedIconIndex;
  6416. *phIcon = NULL;
  6417. if (SUCCEEDED(hr = GetIcon(szCustomizedIconPath, ARRAYSIZE(szCustomizedIconPath), &nCustomizedIconIndex)))
  6418. {
  6419. _SetDefaultIconEx(FALSE);
  6420. }
  6421. SHFILEINFOW sfiw;
  6422. if (SHGetFileInfoW(_wszPath, 0, &sfiw, sizeof(sfiw), SHGFI_ICON | SHGFI_LARGEICON))
  6423. {
  6424. *phIcon = sfiw.hIcon;
  6425. hr = S_OK;
  6426. }
  6427. else
  6428. hr = E_FAIL;
  6429. if (szCustomizedIconPath[0] != NULL)
  6430. _SetIconEx(szCustomizedIconPath, nCustomizedIconIndex, FALSE);
  6431. }
  6432. return hr;
  6433. }
  6434. STDMETHODIMP CFSIconManager::SetIcon(LPCWSTR pwszIconPath, int iIcon)
  6435. {
  6436. return _SetIconEx(pwszIconPath, iIcon, TRUE);
  6437. }
  6438. STDMETHODIMP CFSIconManager::SetDefaultIcon()
  6439. {
  6440. return _SetDefaultIconEx(TRUE);
  6441. }
  6442. HRESULT CFileFolderIconManager_Create(IShellFolder *psf, LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  6443. {
  6444. HRESULT hr = E_FAIL;
  6445. *ppv = NULL;
  6446. CFileFolderIconManager *pffim = new CFileFolderIconManager;
  6447. if (pffim)
  6448. {
  6449. hr = pffim->_Init(pidl, psf);
  6450. if (SUCCEEDED(hr))
  6451. hr = pffim->QueryInterface(riid, ppv);
  6452. pffim->Release();
  6453. }
  6454. else
  6455. {
  6456. hr = E_OUTOFMEMORY;
  6457. }
  6458. return hr;
  6459. }
  6460. STDMETHODIMP CFileFolderIconManager::_SetIconEx(LPCWSTR pwszIconPath, int iIcon, BOOL fChangeNotify)
  6461. {
  6462. HRESULT hr = S_OK;
  6463. WCHAR wszExpandedIconPath[MAX_PATH];
  6464. if (SHExpandEnvironmentStrings(pwszIconPath, wszExpandedIconPath, ARRAYSIZE(wszExpandedIconPath)) == 0)
  6465. hr = E_FAIL;
  6466. if (SUCCEEDED(hr))
  6467. {
  6468. SHFOLDERCUSTOMSETTINGS fcs;
  6469. ZeroMemory(&fcs, sizeof(fcs));
  6470. fcs.dwSize = sizeof(fcs);
  6471. fcs.dwMask = FCSM_ICONFILE;
  6472. fcs.pszIconFile = (LPWSTR) wszExpandedIconPath;
  6473. fcs.cchIconFile = ARRAYSIZE(wszExpandedIconPath);
  6474. fcs.iIconIndex = iIcon;
  6475. hr = SHGetSetFolderCustomSettings(&fcs, _wszPath, FCS_FORCEWRITE);
  6476. if (SUCCEEDED(hr) && fChangeNotify)
  6477. {
  6478. /*
  6479. // Work Around - We need to pump a image change message for the folder icon change.
  6480. // The right way is the following. But for some reason, the shell views which
  6481. // display the folder, don't update there images. So as a work around, we pump a
  6482. // SHCNE_RENAMEFOLDER message. This works!.
  6483. SHFILEINFO sfi;
  6484. if (SHGetFileInfo(pfpsp->szPath, 0, &sfi, sizeof(sfi), SHGFI_ICONLOCATION))
  6485. {
  6486. int iIconIndex = Shell_GetCachedImageIndex(sfi.szDisplayName, sfi.iIcon, 0);
  6487. SHUpdateImage(PathFindFileName(sfi.szDisplayName), sfi.iIcon, 0, iIconIndex);
  6488. }
  6489. */
  6490. SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_PATH, _wszPath, _wszPath);
  6491. }
  6492. }
  6493. return hr;
  6494. }
  6495. STDMETHODIMP CFileFolderIconManager::_SetDefaultIconEx(BOOL fChangeNotify)
  6496. {
  6497. HRESULT hr = E_FAIL;
  6498. SHFOLDERCUSTOMSETTINGS fcs;
  6499. ZeroMemory(&fcs, sizeof(fcs));
  6500. fcs.dwSize = sizeof(fcs);
  6501. fcs.dwMask = FCSM_ICONFILE;
  6502. fcs.pszIconFile = NULL;
  6503. fcs.cchIconFile = 0;
  6504. fcs.iIconIndex = 0;
  6505. hr = SHGetSetFolderCustomSettings(&fcs, _wszPath, FCS_FORCEWRITE);
  6506. if (SUCCEEDED(hr) && fChangeNotify)
  6507. {
  6508. /*
  6509. // Work Around - We need to pump a image change message for the folder icon change.
  6510. // The right way is the following. But for some reason, the shell views which
  6511. // display the folder, don't update there images. So as a work around, we pump a
  6512. // SHCNE_RENAMEFOLDER message. This works!.
  6513. SHFILEINFO sfi;
  6514. if (SHGetFileInfo(pfpsp->szPath, 0, &sfi, sizeof(sfi), SHGFI_ICONLOCATION))
  6515. {
  6516. int iIconIndex = Shell_GetCachedImageIndex(sfi.szDisplayName, sfi.iIcon, 0);
  6517. SHUpdateImage(PathFindFileName(sfi.szDisplayName), sfi.iIcon, 0, iIconIndex);
  6518. }
  6519. */
  6520. SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_PATH, _wszPath, _wszPath);
  6521. }
  6522. return hr;
  6523. }
  6524. HRESULT CFileFolderIconManager::GetIcon(LPWSTR pszIconPath, int cchszIconPath, int *piIconIndex)
  6525. {
  6526. HRESULT hr = S_OK;
  6527. if ((pszIconPath == NULL) || (cchszIconPath < MAX_PATH) || (piIconIndex == NULL))
  6528. hr = E_INVALIDARG;
  6529. if (SUCCEEDED(hr))
  6530. {
  6531. SHFOLDERCUSTOMSETTINGS fcs;
  6532. ZeroMemory(&fcs, sizeof(fcs));
  6533. fcs.dwSize = sizeof(fcs);
  6534. fcs.dwMask = FCSM_ICONFILE;
  6535. fcs.pszIconFile = pszIconPath;
  6536. fcs.cchIconFile = cchszIconPath;
  6537. hr = SHGetSetFolderCustomSettings(&fcs, _wszPath, FCS_READ);
  6538. if (SUCCEEDED(hr))
  6539. {
  6540. *piIconIndex = fcs.iIconIndex;
  6541. }
  6542. }
  6543. return hr;
  6544. }