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.

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