Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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