Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1005 lines
31 KiB

  1. #include "shellprv.h"
  2. #include "intshcut.h"
  3. #include "ids.h"
  4. #include <ntquery.h> // defines some values used for fmtid and pid
  5. #include <sddl.h> // For ConvertSidToStringSid()
  6. #include "prop.h" // SCID_ stuff
  7. #include "netview.h" // SHWNetGetConnection
  8. #include "clsobj.h"
  9. HRESULT ReadProperty(IPropertySetStorage *ppss, REFFMTID fmtid, PROPID pid, VARIANT *pVar)
  10. {
  11. VariantInit(pVar);
  12. IPropertyStorage *pps;
  13. HRESULT hr = ppss->Open(fmtid, STGM_READ | STGM_SHARE_EXCLUSIVE, &pps);
  14. if (SUCCEEDED(hr))
  15. {
  16. PROPSPEC PropSpec;
  17. PROPVARIANT PropVar = {0};
  18. PropSpec.ulKind = PRSPEC_PROPID;
  19. PropSpec.propid = pid;
  20. hr = SHPropStgReadMultiple( pps, 0, 1, &PropSpec, &PropVar );
  21. if (SUCCEEDED(hr))
  22. {
  23. hr = PropVariantToVariant(&PropVar, pVar);
  24. PropVariantClear(&PropVar);
  25. }
  26. pps->Release();
  27. }
  28. return hr;
  29. }
  30. BOOL IsSlowProperty(IPropertySetStorage *ppss, REFFMTID fmtid, PROPID pid)
  31. {
  32. IPropertyStorage *pps;
  33. BOOL bRet = FALSE;
  34. if (SUCCEEDED(ppss->Open(fmtid, STGM_READ | STGM_SHARE_EXCLUSIVE, &pps)))
  35. {
  36. IQueryPropertyFlags *pqsp;
  37. if (SUCCEEDED(pps->QueryInterface(IID_PPV_ARG(IQueryPropertyFlags, &pqsp))))
  38. {
  39. PROPSPEC PropSpec;
  40. PROPVARIANT PropVar = {0};
  41. PropSpec.ulKind = PRSPEC_PROPID;
  42. PropSpec.propid = pid;
  43. SHCOLSTATEF csFlags;
  44. if (SUCCEEDED(pqsp->GetFlags(&PropSpec, &csFlags)))
  45. {
  46. bRet = ((csFlags & SHCOLSTATE_SLOW) == SHCOLSTATE_SLOW);
  47. }
  48. // If the property isn't part of this property set, IsSlowProperty will return fairlure,
  49. // which we'll treat as a fast property.
  50. pqsp->Release();
  51. }
  52. pps->Release();
  53. }
  54. return bRet;
  55. }
  56. class CBaseColumnProvider : public IPersist, public IColumnProvider
  57. {
  58. // IUnknown methods
  59. public:
  60. STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
  61. {
  62. static const QITAB qit[] = {
  63. QITABENT(CBaseColumnProvider, IColumnProvider), // IID_IColumnProvider
  64. QITABENT(CBaseColumnProvider, IPersist), // IID_IPersist
  65. { 0 },
  66. };
  67. return QISearch(this, qit, riid, ppv);
  68. };
  69. STDMETHODIMP_(ULONG) AddRef()
  70. {
  71. return InterlockedIncrement(&_cRef);
  72. };
  73. STDMETHODIMP_(ULONG) Release()
  74. {
  75. if (InterlockedDecrement(&_cRef))
  76. return _cRef;
  77. delete this;
  78. return 0;
  79. };
  80. // IPersist
  81. STDMETHODIMP GetClassID(CLSID *pClassID) { *pClassID = *_pclsid; return S_OK; };
  82. // IColumnProvider
  83. STDMETHODIMP Initialize(LPCSHCOLUMNINIT psci) { return S_OK ; }
  84. STDMETHODIMP GetColumnInfo(DWORD dwIndex, LPSHCOLUMNINFO psci);
  85. CBaseColumnProvider(const CLSID *pclsid, const COLUMN_INFO rgColMap[], int iCount, const LPCWSTR rgExts[]) :
  86. _cRef(1), _pclsid(pclsid), _rgColumns(rgColMap), _iCount(iCount), _rgExts(rgExts)
  87. {
  88. DllAddRef();
  89. }
  90. protected:
  91. virtual ~CBaseColumnProvider()
  92. {
  93. DllRelease();
  94. }
  95. BOOL _IsHandled(LPCWSTR pszExt);
  96. int _iCount;
  97. const COLUMN_INFO *_rgColumns;
  98. private:
  99. long _cRef;
  100. const CLSID * _pclsid;
  101. const LPCWSTR *_rgExts;
  102. };
  103. // the index is an arbitrary zero based index used for enumeration
  104. STDMETHODIMP CBaseColumnProvider::GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO *psci)
  105. {
  106. ZeroMemory(psci, sizeof(*psci));
  107. if (dwIndex < (UINT) _iCount)
  108. {
  109. psci->scid = *_rgColumns[dwIndex].pscid;
  110. psci->cChars = _rgColumns[dwIndex].cChars;
  111. psci->vt = _rgColumns[dwIndex].vt;
  112. psci->fmt = _rgColumns[dwIndex].fmt;
  113. psci->csFlags = _rgColumns[dwIndex].csFlags;
  114. TCHAR szTemp[MAX_COLUMN_NAME_LEN];
  115. LoadString(HINST_THISDLL, _rgColumns[dwIndex].idTitle, szTemp, ARRAYSIZE(szTemp));
  116. SHTCharToUnicode(szTemp, psci->wszTitle, ARRAYSIZE(psci->wszTitle));
  117. return S_OK;
  118. }
  119. return S_FALSE;
  120. }
  121. // see if this file type is one we are interested in
  122. BOOL CBaseColumnProvider::_IsHandled(LPCWSTR pszExt)
  123. {
  124. if (_rgExts)
  125. {
  126. for (int i = 0; _rgExts[i]; i++)
  127. {
  128. if (0 == StrCmpIW(pszExt, _rgExts[i]))
  129. return TRUE;
  130. }
  131. return FALSE;
  132. }
  133. return TRUE;
  134. }
  135. // col handler that works over IPropertySetStorage handlers
  136. const COLUMN_INFO c_rgDocObjColumns[] =
  137. {
  138. DEFINE_COL_STR_ENTRY(SCID_Author, 20, IDS_EXCOL_AUTHOR),
  139. DEFINE_COL_STR_ENTRY(SCID_Title, 20, IDS_EXCOL_TITLE),
  140. DEFINE_COL_STR_DLG_ENTRY(SCID_Subject, 20, IDS_EXCOL_SUBJECT),
  141. DEFINE_COL_STR_DLG_ENTRY(SCID_Category, 20, IDS_EXCOL_CATEGORY),
  142. DEFINE_COL_INT_DLG_ENTRY(SCID_PageCount, 10, IDS_EXCOL_PAGECOUNT),
  143. DEFINE_COL_STR_ENTRY(SCID_Comment, 30, IDS_EXCOL_COMMENT),
  144. DEFINE_COL_STR_DLG_ENTRY(SCID_Copyright, 30, IDS_EXCOL_COPYRIGHT),
  145. DEFINE_COL_STR_ENTRY(SCID_MUSIC_Artist, 15, IDS_EXCOL_ARTIST),
  146. DEFINE_COL_STR_ENTRY(SCID_MUSIC_Album, 15, IDS_EXCOL_ALBUM),
  147. DEFINE_COL_STR_ENTRY(SCID_MUSIC_Year, 10, IDS_EXCOL_YEAR),
  148. DEFINE_COL_INT_ENTRY(SCID_MUSIC_Track, 5, IDS_EXCOL_TRACK),
  149. DEFINE_COL_STR_ENTRY(SCID_MUSIC_Genre, 20, IDS_EXCOL_GENRE),
  150. DEFINE_COL_STR_ENTRY(SCID_AUDIO_Duration, 15, IDS_EXCOL_DURATION),
  151. DEFINE_COL_STR_ENTRY(SCID_AUDIO_Bitrate, 15, IDS_EXCOL_BITRATE),
  152. DEFINE_COL_STR_ENTRY(SCID_DRM_Protected, 10, IDS_EXCOL_PROTECTED),
  153. DEFINE_COL_STR_ENTRY(SCID_CameraModel, 20, IDS_EXCOL_CAMERAMODEL),
  154. DEFINE_COL_STR_ENTRY(SCID_WhenTaken, 20, IDS_EXCOL_WHENTAKEN),
  155. DEFINE_COL_STR_ENTRY(SCID_ImageDimensions, 20, IDS_EXCOL_DIMENSIONS),
  156. DEFINE_COL_INT_HIDDEN_ENTRY(SCID_ImageCX),
  157. DEFINE_COL_INT_HIDDEN_ENTRY(SCID_ImageCY),
  158. DEFINE_COL_DATE_HIDDEN_ENTRY(SCID_DocCreated),
  159. };
  160. class CPropStgColumns : public CBaseColumnProvider
  161. {
  162. STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
  163. private:
  164. // help on initializing base classes: mk:@ivt:vclang/FB/DD/S44B5E.HTM
  165. CPropStgColumns() :
  166. CBaseColumnProvider(&CLSID_DocFileColumnProvider, c_rgDocObjColumns, ARRAYSIZE(c_rgDocObjColumns), NULL)
  167. {
  168. ASSERT(_wszLastFile[0] == 0);
  169. ASSERT(_bSlowPropertiesCached == FALSE);
  170. };
  171. ~CPropStgColumns()
  172. {
  173. _FreeCache();
  174. }
  175. // for the cache
  176. VARIANT _rgvCache[ARRAYSIZE(c_rgDocObjColumns)]; // zero'ing allocator will fill with VT_EMPTY
  177. BOOL _rgbSlow[ARRAYSIZE(c_rgDocObjColumns)]; // Store if each property is "slow".
  178. WCHAR _wszLastFile[MAX_PATH];
  179. HRESULT _hrCache;
  180. BOOL _bSlowPropertiesCached;
  181. #ifdef DEBUG
  182. int deb_dwTotal, deb_dwMiss;
  183. #endif
  184. void _FreeCache();
  185. friend HRESULT CDocFileColumns_CreateInstance(IUnknown *punk, REFIID riid, void **ppv);
  186. };
  187. void CPropStgColumns::_FreeCache()
  188. {
  189. for (int i = 0; i < ARRAYSIZE(_rgvCache); i++)
  190. VariantClear(&_rgvCache[i]);
  191. _hrCache = S_OK;
  192. }
  193. STDMETHODIMP CPropStgColumns::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData)
  194. {
  195. HRESULT hr;
  196. // VariantCopy requires input to be initialized, and we handle failure case
  197. VariantInit(pvarData);
  198. // is this even a property we support?
  199. for (int iProp = 0; iProp < _iCount; iProp++)
  200. {
  201. if (IsEqualSCID(*_rgColumns[iProp].pscid, *pscid))
  202. {
  203. goto found;
  204. }
  205. }
  206. // Unknown property
  207. return S_FALSE;
  208. found:
  209. #ifdef DEBUG
  210. deb_dwTotal++;
  211. #endif
  212. // Three cases here:
  213. // 1) We need to update the cache. Fetch the properties again (and only get fast props if we asked for a fast prop)
  214. // 2) We've only cached fast properties so far, and we asked for a slow property, so now we need to get slow props.
  215. // 3) The property we want is cached.
  216. if ((pscd->dwFlags & SHCDF_UPDATEITEM) || (StrCmpW(_wszLastFile, pscd->wszFile) != 0))
  217. {
  218. // 1) Cache is no good - item has been updated, or this is a different file.
  219. // SHCDF_UPDATEITEM flag is a hint
  220. // that the file for which we are getting data has changed since the last call. This flag
  221. // is only passed once per filename, not once per column per filename so update the entire
  222. // cache if this flag is set.
  223. // sanity check our caching. If the shell thread pool is > 1, we will thrash like mad, and should change this
  224. #ifdef DEBUG
  225. deb_dwMiss++;
  226. if ((deb_dwTotal > 3) && (deb_dwTotal / deb_dwMiss <= 3))
  227. TraceMsg(TF_DEFVIEW, "Column data caching is ineffective (%d misses for %d access)", deb_dwMiss, deb_dwTotal);
  228. #endif
  229. _FreeCache();
  230. StrCpyW(_wszLastFile, pscd->wszFile);
  231. IPropertySetStorage *ppss;
  232. hr = SHFileSysBindToStorage(pscd->wszFile, pscd->dwFileAttributes, STGM_READ | STGM_SHARE_DENY_WRITE, 0,
  233. IID_PPV_ARG(IPropertySetStorage, &ppss));
  234. _hrCache = hr;
  235. if (SUCCEEDED(hr))
  236. {
  237. // Did we ask for a slow property?
  238. BOOL bSlowProperty = IsSlowProperty(ppss, _rgColumns[iProp].pscid->fmtid, _rgColumns[iProp].pscid->pid);
  239. hr = E_INVALIDARG; // normally overwritten by hrT below
  240. for (int i = 0; i < _iCount; i++)
  241. {
  242. // For every property, take note if it is "slow"
  243. _rgbSlow[i] = IsSlowProperty(ppss, _rgColumns[i].pscid->fmtid, _rgColumns[i].pscid->pid);
  244. // Only retrieve a value right now if we asked for a slow property, or this is not a slow property.
  245. if (bSlowProperty || (!_rgbSlow[i]))
  246. {
  247. // it would be slightly more efficient, but more code, to set up the propid array to call ReadMultiple
  248. HRESULT hrT = ReadProperty(ppss, _rgColumns[i].pscid->fmtid, _rgColumns[i].pscid->pid, &_rgvCache[i]);
  249. if (i == iProp)
  250. {
  251. hr = (SUCCEEDED(hrT) ? VariantCopy(pvarData, &_rgvCache[i]) : hrT);
  252. }
  253. }
  254. }
  255. ppss->Release();
  256. _bSlowPropertiesCached = bSlowProperty;
  257. }
  258. }
  259. else if (_rgbSlow[iProp] && !_bSlowPropertiesCached)
  260. {
  261. // 2) We asked for a slow property, but slow properties haven't been cached yet.
  262. // Bind to the storage a second time. This is a perf hit, but should be
  263. // minor compared to getting slow properties.
  264. IPropertySetStorage *ppss;
  265. hr = SHFileSysBindToStorage(pscd->wszFile, pscd->dwFileAttributes, STGM_READ | STGM_SHARE_DENY_WRITE, 0,
  266. IID_PPV_ARG(IPropertySetStorage, &ppss));
  267. _hrCache = hr;
  268. if (SUCCEEDED(hr))
  269. {
  270. hr = E_INVALIDARG; // normally overwritten by hrT below
  271. for (int i = 0; i < _iCount; i++)
  272. {
  273. if (_rgbSlow[i]) // If it's slow, get it.
  274. {
  275. ASSERT(_rgvCache[i].vt == VT_EMPTY); // Because we haven't retrieved it yet.
  276. HRESULT hrT = ReadProperty(ppss, _rgColumns[i].pscid->fmtid, _rgColumns[i].pscid->pid, &_rgvCache[i]);
  277. if (i == iProp)
  278. {
  279. hr = (SUCCEEDED(hrT) ? VariantCopy(pvarData, &_rgvCache[i]) : hrT);
  280. }
  281. }
  282. }
  283. ppss->Release();
  284. _bSlowPropertiesCached = TRUE;
  285. }
  286. }
  287. else
  288. {
  289. // 3) It's not a slow property, or slow properties are already cached.
  290. ASSERT(!_rgbSlow[iProp] || _bSlowPropertiesCached);
  291. hr = S_FALSE; // assume we don't have it
  292. if (SUCCEEDED(_hrCache))
  293. {
  294. if (_rgvCache[iProp].vt != VT_EMPTY)
  295. {
  296. hr = VariantCopy(pvarData, &_rgvCache[iProp]);
  297. }
  298. }
  299. }
  300. return hr;
  301. }
  302. STDAPI CDocFileColumns_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
  303. {
  304. HRESULT hr;
  305. CPropStgColumns *pdocp = new CPropStgColumns;
  306. if (pdocp)
  307. {
  308. hr = pdocp->QueryInterface(riid, ppv);
  309. pdocp->Release();
  310. }
  311. else
  312. {
  313. *ppv = NULL;
  314. hr = E_OUTOFMEMORY;
  315. }
  316. return hr;
  317. }
  318. // Shortcut handler
  319. // W because pidl is always converted to widechar filename
  320. const LPCWSTR c_szURLExtensions[] = {
  321. L".URL",
  322. L".LNK",
  323. NULL
  324. };
  325. const COLUMN_INFO c_rgURLColumns[] =
  326. {
  327. DEFINE_COL_STR_ENTRY(SCID_Author, 20, IDS_EXCOL_AUTHOR),
  328. DEFINE_COL_STR_ENTRY(SCID_Title, 20, IDS_EXCOL_TITLE),
  329. DEFINE_COL_STR_ENTRY(SCID_Comment, 30, IDS_EXCOL_COMMENT),
  330. };
  331. class CLinkColumnProvider : public CBaseColumnProvider
  332. {
  333. STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
  334. private:
  335. // help on initializing base classes: mk:@ivt:vclang/FB/DD/S44B5E.HTM
  336. CLinkColumnProvider() : CBaseColumnProvider(&CLSID_LinkColumnProvider, c_rgURLColumns, ARRAYSIZE(c_rgURLColumns), c_szURLExtensions)
  337. {};
  338. // friends
  339. friend HRESULT CLinkColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv);
  340. };
  341. const struct
  342. {
  343. DWORD dwSummaryPid;
  344. DWORD dwURLPid;
  345. } c_URLMap[] = {
  346. { PIDSI_AUTHOR, PID_INTSITE_AUTHOR },
  347. { PIDSI_TITLE, PID_INTSITE_TITLE },
  348. { PIDSI_COMMENTS, PID_INTSITE_COMMENT },
  349. };
  350. DWORD _MapSummaryToSitePID(DWORD pid)
  351. {
  352. for (int i = 0; i < ARRAYSIZE(c_URLMap); i++)
  353. {
  354. if (c_URLMap[i].dwSummaryPid == pid)
  355. return c_URLMap[i].dwURLPid;
  356. }
  357. return -1;
  358. }
  359. STDMETHODIMP CLinkColumnProvider::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData)
  360. {
  361. HRESULT hr;
  362. USES_CONVERSION;
  363. const CLSID *pclsidLink = &CLSID_ShellLink;
  364. // Some of the code-paths below assume pvarData is initialized
  365. VariantInit(pvarData);
  366. // should we match against a list of known extensions, or always try to open?
  367. if (FILE_ATTRIBUTE_DIRECTORY & pscd->dwFileAttributes)
  368. {
  369. if (PathIsShortcut(W2CT(pscd->wszFile), pscd->dwFileAttributes))
  370. {
  371. pclsidLink = &CLSID_FolderShortcut; // we are dealing with a folder shortcut now
  372. }
  373. else
  374. {
  375. return S_FALSE;
  376. }
  377. }
  378. else
  379. {
  380. if (!_IsHandled(pscd->pwszExt))
  381. {
  382. return S_FALSE;
  383. }
  384. }
  385. if (StrCmpIW(pscd->pwszExt, L".URL") == 0)
  386. {
  387. //
  388. // its a .URL so lets handle it by creating the Internet Shortcut object, loading
  389. // the file and then reading the properties from it.
  390. //
  391. IPropertySetStorage *ppss;
  392. hr = LoadFromFile(CLSID_InternetShortcut, W2CT(pscd->wszFile), IID_PPV_ARG(IPropertySetStorage, &ppss));
  393. if (SUCCEEDED(hr))
  394. {
  395. UINT pid;
  396. GUID fmtid;
  397. if (IsEqualGUID(pscid->fmtid, FMTID_SummaryInformation))
  398. {
  399. fmtid = FMTID_InternetSite;
  400. pid = _MapSummaryToSitePID(pscid->pid);
  401. }
  402. else
  403. {
  404. fmtid = pscid->fmtid;
  405. pid = pscid->pid;
  406. }
  407. hr = ReadProperty(ppss, fmtid, pid, pvarData);
  408. ppss->Release();
  409. }
  410. }
  411. else
  412. {
  413. //
  414. // open the .LNK file, load it and then read the description for it. we then
  415. // return this a the comment for this object.
  416. //
  417. if (IsEqualSCID(*pscid, SCID_Comment))
  418. {
  419. IShellLink *psl;
  420. hr = LoadFromFile(*pclsidLink, W2CT(pscd->wszFile), IID_PPV_ARG(IShellLink, &psl));
  421. if (SUCCEEDED(hr))
  422. {
  423. TCHAR szBuffer[MAX_PATH];
  424. hr = psl->GetDescription(szBuffer, ARRAYSIZE(szBuffer));
  425. if (SUCCEEDED(hr) && szBuffer[0])
  426. {
  427. hr = InitVariantFromStr(pvarData, szBuffer);
  428. }
  429. else
  430. {
  431. IQueryInfo *pqi;
  432. if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IQueryInfo, &pqi))))
  433. {
  434. WCHAR *pwszTip;
  435. if (SUCCEEDED(pqi->GetInfoTip(0, &pwszTip)) && pwszTip)
  436. {
  437. hr = InitVariantFromStr(pvarData, W2CT(pwszTip));
  438. SHFree(pwszTip);
  439. }
  440. pqi->Release();
  441. }
  442. }
  443. psl->Release();
  444. }
  445. }
  446. else
  447. hr = S_FALSE;
  448. }
  449. return hr;
  450. }
  451. STDAPI CLinkColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
  452. {
  453. HRESULT hr;
  454. CLinkColumnProvider *pdocp = new CLinkColumnProvider;
  455. if (pdocp)
  456. {
  457. hr = pdocp->QueryInterface(riid, ppv);
  458. pdocp->Release();
  459. }
  460. else
  461. {
  462. *ppv = NULL;
  463. hr = E_OUTOFMEMORY;
  464. }
  465. return hr;
  466. }
  467. const COLUMN_INFO c_rgFileSysColumns[] =
  468. {
  469. DEFINE_COL_STR_ENTRY(SCID_OWNER, 20, IDS_EXCOL_OWNER),
  470. };
  471. class COwnerColumnProvider : public CBaseColumnProvider
  472. {
  473. STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
  474. private:
  475. COwnerColumnProvider() : CBaseColumnProvider(&CLSID_FileSysColumnProvider, c_rgFileSysColumns, ARRAYSIZE(c_rgFileSysColumns), NULL)
  476. {
  477. ASSERT(_wszLastFile[0] == 0);
  478. ASSERT(_psid==NULL && _pwszName==NULL && _psd==NULL);
  479. LoadString(HINST_THISDLL, IDS_BUILTIN_DOMAIN, _szBuiltin, ARRAYSIZE(_szBuiltin));
  480. };
  481. ~COwnerColumnProvider() { _CacheSidName(NULL, NULL, NULL); }
  482. WCHAR _wszLastFile[MAX_PATH];
  483. // Since we typically get pinged for files all in the same folder,
  484. // cache the "folder to server" mapping to avoid calling
  485. // WNetGetConnection five million times.
  486. //
  487. // Since files in the same directory tend to have the same owner,
  488. // we cache the SID/Name mapping.
  489. //
  490. // Column providers do not have to support multithreaded clients,
  491. // so we won't take any critical sections.
  492. //
  493. HRESULT _LookupOwnerName(LPCTSTR pszFile, VARIANT *pvar);
  494. void _CacheSidName(PSECURITY_DESCRIPTOR psd, void *psid, LPCWSTR pwszName);
  495. void *_psid;
  496. LPWSTR _pwszName;
  497. PSECURITY_DESCRIPTOR _psd; // _psid points into here
  498. int _iCachedDrive; // What drive letter is cached in _pszServer?
  499. LPTSTR _pszServer; // What server to use (NULL = local machine)
  500. TCHAR _szBuiltin[MAX_COMPUTERNAME_LENGTH + 1];
  501. friend HRESULT CFileSysColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv);
  502. };
  503. //
  504. // _CacheSidName takes ownership of the psd. (psid points into the psd)
  505. //
  506. void COwnerColumnProvider::_CacheSidName(PSECURITY_DESCRIPTOR psd, void *psid, LPCWSTR pwszName)
  507. {
  508. LocalFree(_psd);
  509. _psd = psd;
  510. _psid = psid;
  511. Str_SetPtrW(&_pwszName, pwszName);
  512. }
  513. //
  514. // Given a string of the form \\server\share\blah\blah, stomps the
  515. // inner backslash (if necessary) and returns a pointer to "server".
  516. //
  517. STDAPI_(LPTSTR) PathExtractServer(LPTSTR pszUNC)
  518. {
  519. if (PathIsUNC(pszUNC))
  520. {
  521. pszUNC += 2; // Skip over the two leading backslashes
  522. LPTSTR pszEnd = StrChr(pszUNC, TEXT('\\'));
  523. if (pszEnd)
  524. *pszEnd = TEXT('\0'); // nuke the backslash
  525. }
  526. else
  527. {
  528. pszUNC = NULL;
  529. }
  530. return pszUNC;
  531. }
  532. HRESULT COwnerColumnProvider::_LookupOwnerName(LPCTSTR pszFile, VARIANT *pvar)
  533. {
  534. pvar->vt = VT_BSTR;
  535. pvar->bstrVal = NULL;
  536. PSECURITY_DESCRIPTOR psd;
  537. void *psid;
  538. DWORD err = GetNamedSecurityInfo(const_cast<LPTSTR>(pszFile),
  539. SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION,
  540. &psid, NULL, NULL, NULL, &psd);
  541. if (err == ERROR_SUCCESS)
  542. {
  543. if (_psid && EqualSid(psid, _psid) && _pwszName)
  544. {
  545. pvar->bstrVal = SysAllocString(_pwszName);
  546. LocalFree(psd);
  547. err = ERROR_SUCCESS;
  548. }
  549. else
  550. {
  551. LPTSTR pszServer;
  552. TCHAR szServer[MAX_PATH];
  553. //
  554. // Now go figure out which server to resolve the SID against.
  555. //
  556. if (PathIsUNC(pszFile))
  557. {
  558. lstrcpyn(szServer, pszFile, ARRAYSIZE(szServer));
  559. pszServer = PathExtractServer(szServer);
  560. }
  561. else if (pszFile[0] == _iCachedDrive)
  562. {
  563. // Local drive letter already in cache -- use it
  564. pszServer = _pszServer;
  565. }
  566. else
  567. {
  568. // Local drive not cached -- cache it
  569. _iCachedDrive = pszFile[0];
  570. DWORD cch = ARRAYSIZE(szServer);
  571. if (SHWNetGetConnection(pszFile, szServer, &cch) == NO_ERROR)
  572. pszServer = PathExtractServer(szServer);
  573. else
  574. pszServer = NULL;
  575. Str_SetPtr(&_pszServer, pszServer);
  576. }
  577. TCHAR szName[MAX_PATH];
  578. DWORD cchName = ARRAYSIZE(szName);
  579. TCHAR szDomain[MAX_COMPUTERNAME_LENGTH + 1];
  580. DWORD cchDomain = ARRAYSIZE(szDomain);
  581. SID_NAME_USE snu;
  582. LPTSTR pszName;
  583. BOOL fFreeName = FALSE; // Do we need to LocalFree(pszName)?
  584. if (LookupAccountSid(pszServer, psid, szName, &cchName,
  585. szDomain, &cchDomain, &snu))
  586. {
  587. //
  588. // If the domain is the bogus "BUILTIN" or we don't have a domain
  589. // at all, then just use the name. Otherwise, use domain\userid.
  590. //
  591. if (!szDomain[0] || StrCmpC(szDomain, _szBuiltin) == 0)
  592. {
  593. pszName = szName;
  594. }
  595. else
  596. {
  597. // Borrow szServer as a scratch buffer
  598. wnsprintf(szServer, ARRAYSIZE(szServer), TEXT("%s\\%s"), szDomain, szName);
  599. pszName = szServer;
  600. }
  601. err = ERROR_SUCCESS;
  602. }
  603. else
  604. {
  605. err = GetLastError();
  606. // Couldn't map the SID to a name. Use the horrid raw version
  607. // if available.
  608. if (ConvertSidToStringSid(psid, &pszName))
  609. {
  610. fFreeName = TRUE;
  611. err = ERROR_SUCCESS;
  612. }
  613. else
  614. pszName = NULL;
  615. }
  616. // Even on error, cache the result so we don't keep trying over and over
  617. // on the same SID.
  618. _CacheSidName(psd, psid, pszName);
  619. pvar->bstrVal = SysAllocString(pszName);
  620. if (fFreeName)
  621. LocalFree(pszName);
  622. }
  623. }
  624. if (err == ERROR_SUCCESS && pvar->bstrVal == NULL)
  625. err = ERROR_OUTOFMEMORY;
  626. return HRESULT_FROM_WIN32(err);
  627. }
  628. STDMETHODIMP COwnerColumnProvider::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData)
  629. {
  630. HRESULT hr = S_FALSE; // return S_FALSE on failure
  631. VariantInit(pvarData);
  632. if (IsEqualSCID(SCID_OWNER, *pscid))
  633. {
  634. hr = _LookupOwnerName(pscd->wszFile, pvarData);
  635. }
  636. return hr;
  637. }
  638. STDAPI CFileSysColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
  639. {
  640. HRESULT hr;
  641. COwnerColumnProvider *pfcp = new COwnerColumnProvider;
  642. if (pfcp)
  643. {
  644. hr = pfcp->QueryInterface(riid, ppv);
  645. pfcp->Release();
  646. }
  647. else
  648. {
  649. *ppv = NULL;
  650. hr = E_OUTOFMEMORY;
  651. }
  652. return hr;
  653. }
  654. STDAPI SHReadProperty(IShellFolder *psf, LPCITEMIDLIST pidl, REFFMTID fmtid, PROPID pid, VARIANT *pvar)
  655. {
  656. IPropertySetStorage *ppss;
  657. HRESULT hr = psf->BindToStorage(pidl, NULL, IID_PPV_ARG(IPropertySetStorage, &ppss));
  658. if (SUCCEEDED(hr))
  659. {
  660. hr = ReadProperty(ppss, fmtid, pid, pvar);
  661. ppss->Release();
  662. }
  663. return hr;
  664. }
  665. // 66742402-F9B9-11D1-A202-0000F81FEDEE
  666. // const CLSID CLSID_VersionColProvider = {0x66742402,0xF9B9,0x11D1,0xA2,0x02,0x00,0x00,0xF8,0x1F,0xED,0xEE};
  667. // FMTID_ExeDllInformation,
  668. //// {0CEF7D53-FA64-11d1-A203-0000F81FEDEE}
  669. #define PSFMTID_VERSION { 0xcef7d53, 0xfa64, 0x11d1, 0xa2, 0x3, 0x0, 0x0, 0xf8, 0x1f, 0xed, 0xee }
  670. #define PIDVSI_FileDescription 0x003
  671. #define PIDVSI_FileVersion 0x004
  672. #define PIDVSI_InternalName 0x005
  673. #define PIDVSI_OriginalFileName 0x006
  674. #define PIDVSI_ProductName 0x007
  675. #define PIDVSI_ProductVersion 0x008
  676. // Win32 PE (exe, dll) Version Information column identifier defs...
  677. DEFINE_SCID(SCID_FileDescription, PSFMTID_VERSION, PIDVSI_FileDescription);
  678. DEFINE_SCID(SCID_FileVersion, PSFMTID_VERSION, PIDVSI_FileVersion);
  679. DEFINE_SCID(SCID_InternalName, PSFMTID_VERSION, PIDVSI_InternalName);
  680. DEFINE_SCID(SCID_OriginalFileName, PSFMTID_VERSION, PIDVSI_OriginalFileName);
  681. DEFINE_SCID(SCID_ProductName, PSFMTID_VERSION, PIDVSI_ProductName);
  682. DEFINE_SCID(SCID_ProductVersion, PSFMTID_VERSION, PIDVSI_ProductVersion);
  683. const COLUMN_INFO c_rgExeDllColumns[] =
  684. {
  685. DEFINE_COL_STR_ENTRY(SCID_CompanyName, 30, IDS_VN_COMPANYNAME),
  686. DEFINE_COL_STR_ENTRY(SCID_FileDescription, 30, IDS_VN_FILEDESCRIPTION),
  687. DEFINE_COL_STR_ENTRY(SCID_FileVersion, 20, IDS_VN_FILEVERSION),
  688. DEFINE_COL_STR_MENU_ENTRY(SCID_ProductName, 30, IDS_VN_PRODUCTNAME),
  689. DEFINE_COL_STR_MENU_ENTRY(SCID_ProductVersion,20, IDS_VN_PRODUCTVERSION),
  690. };
  691. class CVersionColProvider : public CBaseColumnProvider
  692. {
  693. STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
  694. private:
  695. CVersionColProvider() :
  696. CBaseColumnProvider(&CLSID_VersionColProvider, c_rgExeDllColumns, ARRAYSIZE(c_rgExeDllColumns), NULL)
  697. {
  698. _pvAllTheInfo = NULL;
  699. _szFileCache[0] = 0;
  700. };
  701. virtual ~CVersionColProvider()
  702. {
  703. _ClearCache();
  704. }
  705. FARPROC _GetVerProc(LPCSTR pszName);
  706. HRESULT _CacheFileVerInfo(LPCWSTR pszFile);
  707. void _ClearCache();
  708. WCHAR _szFileCache[MAX_PATH];
  709. void *_pvAllTheInfo;
  710. HRESULT _hrCache;
  711. friend HRESULT CVerColProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv);
  712. };
  713. void CVersionColProvider::_ClearCache()
  714. {
  715. if (_pvAllTheInfo)
  716. {
  717. delete _pvAllTheInfo;
  718. _pvAllTheInfo = NULL;
  719. }
  720. _szFileCache[0] = 0;
  721. }
  722. HRESULT CVersionColProvider::_CacheFileVerInfo(LPCWSTR pszFile)
  723. {
  724. if (StrCmpW(_szFileCache, pszFile))
  725. {
  726. HRESULT hr;
  727. _ClearCache();
  728. DWORD dwVestigial;
  729. DWORD versionISize = GetFileVersionInfoSizeW((LPWSTR)pszFile, &dwVestigial); // cast for bad API design
  730. if (versionISize)
  731. {
  732. _pvAllTheInfo = new BYTE[versionISize];
  733. if (_pvAllTheInfo)
  734. {
  735. // read the data
  736. if (GetFileVersionInfoW((LPWSTR)pszFile, dwVestigial, versionISize, _pvAllTheInfo))
  737. {
  738. hr = S_OK;
  739. }
  740. else
  741. {
  742. _ClearCache();
  743. hr = E_FAIL;
  744. }
  745. }
  746. else
  747. hr = E_OUTOFMEMORY; // error, out of memory.
  748. }
  749. else
  750. hr = S_FALSE;
  751. StrCpyNW(_szFileCache, pszFile, ARRAYSIZE(_szFileCache));
  752. _hrCache = hr;
  753. }
  754. return _hrCache;
  755. }
  756. STDMETHODIMP CVersionColProvider::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData)
  757. {
  758. VariantInit(pvarData);
  759. if (pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  760. return S_FALSE;
  761. HRESULT hr = _CacheFileVerInfo(pscd->wszFile);
  762. if (hr != S_OK)
  763. return hr;
  764. TCHAR szString[128], *pszVersionInfo = NULL; //A pointer to the specific version info I am looking for
  765. LPCTSTR pszVersionField = NULL;
  766. switch (pscid->pid)
  767. {
  768. case PIDVSI_FileVersion:
  769. {
  770. VS_FIXEDFILEINFO *pffi;
  771. UINT uInfoSize;
  772. if (VerQueryValue(_pvAllTheInfo, TEXT("\\"), (void **)&pffi, &uInfoSize))
  773. {
  774. wnsprintf(szString, ARRAYSIZE(szString), TEXT("%d.%d.%d.%d"),
  775. HIWORD(pffi->dwFileVersionMS),
  776. LOWORD(pffi->dwFileVersionMS),
  777. HIWORD(pffi->dwFileVersionLS),
  778. LOWORD(pffi->dwFileVersionLS));
  779. pszVersionInfo = szString;
  780. }
  781. else
  782. pszVersionField = TEXT("FileVersion");
  783. }
  784. break;
  785. case PIDDSI_COMPANY: pszVersionField = TEXT("CompanyName"); break;
  786. case PIDVSI_FileDescription: pszVersionField = TEXT("FileDescription"); break;
  787. case PIDVSI_InternalName: pszVersionField = TEXT("InternalName"); break;
  788. case PIDVSI_OriginalFileName: pszVersionField = TEXT("OriginalFileName"); break;
  789. case PIDVSI_ProductName: pszVersionField = TEXT("ProductName"); break;
  790. case PIDVSI_ProductVersion: pszVersionField = TEXT("ProductVersion"); break;
  791. default:
  792. return E_FAIL;
  793. }
  794. //look for the intended language in the examined object.
  795. if (pszVersionInfo == NULL)
  796. {
  797. struct _VERXLATE
  798. {
  799. WORD wLanguage;
  800. WORD wCodePage;
  801. } *pxlate; /* ptr to translations data */
  802. //this is a fallthrough set of if statements.
  803. //on a failure, it just tries the next one, until it runs out of tries.
  804. UINT uInfoSize;
  805. if (VerQueryValue(_pvAllTheInfo, TEXT("\\VarFileInfo\\Translation"), (void **)&pxlate, &uInfoSize))
  806. {
  807. TCHAR szVersionKey[60]; //a string to hold all the format string for VerQueryValue
  808. wnsprintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\%04X%04X\\%s"),
  809. pxlate[0].wLanguage, pxlate[0].wCodePage, pszVersionField);
  810. if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize))
  811. {
  812. wnsprintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\040904B0\\%s"), pszVersionField);
  813. if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize))
  814. {
  815. wnsprintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\040904E4\\%s"), pszVersionField);
  816. if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize))
  817. {
  818. wnsprintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\04090000\\%s"), pszVersionField);
  819. if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize))
  820. {
  821. pszVersionInfo = NULL;
  822. }
  823. }
  824. }
  825. }
  826. }
  827. }
  828. if (pszVersionInfo)
  829. {
  830. PathRemoveBlanks(pszVersionInfo);
  831. hr = InitVariantFromStr(pvarData, pszVersionInfo);
  832. }
  833. else
  834. {
  835. hr = E_FAIL;
  836. }
  837. return hr;
  838. }
  839. STDAPI CVerColProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
  840. {
  841. HRESULT hr;
  842. CVersionColProvider *pvcp = new CVersionColProvider;
  843. if (pvcp)
  844. {
  845. hr = pvcp->QueryInterface(riid, ppv);
  846. pvcp->Release();
  847. }
  848. else
  849. {
  850. *ppv = NULL;
  851. hr = E_OUTOFMEMORY;
  852. }
  853. return hr;
  854. }