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.

473 lines
16 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. class CFolderInfoTip : public IQueryInfo, public ICustomizeInfoTip, public IParentAndItem, public IShellTreeWalkerCallBack
  4. {
  5. public:
  6. CFolderInfoTip(IUnknown *punk, LPCTSTR pszFolder);
  7. // IUnknown
  8. STDMETHODIMP QueryInterface(REFIID, void **);
  9. STDMETHODIMP_(ULONG) AddRef(void);
  10. STDMETHODIMP_(ULONG) Release(void);
  11. // IQueryInfo methods.
  12. STDMETHODIMP GetInfoTip(DWORD dwFlags, WCHAR** ppwszTip);
  13. STDMETHODIMP GetInfoFlags(DWORD *pdwFlags);
  14. // ICustomizeInfoTip
  15. STDMETHODIMP SetPrefixText(LPCWSTR pszPrefix);
  16. STDMETHODIMP SetExtraProperties(const SHCOLUMNID *pscid, UINT cscid);
  17. // IParentAndItem
  18. STDMETHODIMP SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidlChild);
  19. STDMETHODIMP GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidlChild);
  20. // IShellTreeWalkerCallBack methods
  21. STDMETHODIMP FoundFile(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd);
  22. STDMETHODIMP EnterFolder(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd);
  23. STDMETHODIMP LeaveFolder(LPCWSTR pwszPath, TREEWALKERSTATS *ptws);
  24. STDMETHODIMP HandleError(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, HRESULT hrError);
  25. private:
  26. ~CFolderInfoTip();
  27. HRESULT _GetTreeWalkerData(TREEWALKERSTATS *ptws);
  28. HRESULT _BufferInsert(LPWSTR pszBuffer, int *puBufferUsed, int uBufferMaxSize, LPCWSTR pwszPath, int cBufferItems);
  29. HRESULT _WalkTree(LPWSTR pszTip, DWORD cchSize);
  30. HRESULT _BuildSizeBlurb(HRESULT hr, LPWSTR pszSizeBlurb, DWORD cchSize);
  31. HRESULT _BuildFolderBlurb(HRESULT hr, LPWSTR pszFolderBlurb, DWORD cchSize);
  32. HRESULT _BuildFileBlurb(HRESULT hr, LPWSTR pszSizeBlurb, DWORD cchSize);
  33. LONG _cRef; // Reference Counter
  34. LPWSTR _pszFolderName; // File name of the target folder
  35. IQueryInfo *_pqiOuter; // Outer info tip for folders (say, for comments)
  36. ULONGLONG _ulTotalSize; // Total size of encountered files
  37. UINT _nSubFolders; // Total number of subfolders of target
  38. UINT _nFiles; // Total number of subfiles of target folder
  39. DWORD _dwSearchStartTime; // Time when search started
  40. WCHAR _szFileList[60]; // List of files in target folder
  41. int _nFileListCharsUsed; // Number of characters used in buffer
  42. WCHAR _szFolderList[60]; // List of subfolders of target
  43. int _nFolderListCharsUsed; // Number of chars used in folder buffer
  44. };
  45. // Constructor and Destructor do nothing more than set everything to
  46. // 0 and ping the dll
  47. CFolderInfoTip::CFolderInfoTip(IUnknown *punkOutter, LPCTSTR pszFolder) : _cRef(1)
  48. {
  49. // Init everything to 0
  50. _pszFolderName = StrDup(pszFolder);
  51. _szFileList[0] = 0;
  52. _nFileListCharsUsed = 0;
  53. _szFolderList[0] = 0;
  54. _nFolderListCharsUsed = 0;
  55. _ulTotalSize = 0;
  56. _nSubFolders = 0;
  57. _nFiles = 0;
  58. punkOutter->QueryInterface(IID_PPV_ARG(IQueryInfo, &_pqiOuter));
  59. DllAddRef();
  60. }
  61. CFolderInfoTip::~CFolderInfoTip()
  62. {
  63. LocalFree(_pszFolderName);
  64. if (_pqiOuter)
  65. _pqiOuter->Release();
  66. DllRelease();
  67. }
  68. HRESULT CFolderInfoTip::QueryInterface(REFIID riid, void **ppv)
  69. {
  70. static const QITAB qit[] = {
  71. QITABENT(CFolderInfoTip, IQueryInfo),
  72. QITABENT(CFolderInfoTip, ICustomizeInfoTip),
  73. QITABENT(CFolderInfoTip, IParentAndItem),
  74. QITABENT(CFolderInfoTip, IShellTreeWalkerCallBack),
  75. { 0 },
  76. };
  77. return QISearch(this, qit, riid, ppv);
  78. }
  79. ULONG CFolderInfoTip::AddRef()
  80. {
  81. return InterlockedIncrement(&_cRef);
  82. }
  83. ULONG CFolderInfoTip::Release()
  84. {
  85. ASSERT( 0 != _cRef );
  86. ULONG cRef = InterlockedDecrement(&_cRef);
  87. if ( 0 == cRef )
  88. {
  89. delete this;
  90. }
  91. return cRef;
  92. }
  93. // IQueryInfo functions
  94. STDMETHODIMP CFolderInfoTip::GetInfoFlags(DWORD *pdwFlags)
  95. {
  96. *pdwFlags = 0;
  97. return S_OK;
  98. }
  99. //
  100. // Wrapper for FormatMessage. Is this duplicated somewhere else?
  101. DWORD _FormatMessageArg(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageID, DWORD dwLangID, LPWSTR pszBuffer, DWORD cchSize, ...)
  102. {
  103. va_list vaParamList;
  104. va_start(vaParamList, cchSize);
  105. DWORD dwResult = FormatMessage(dwFlags, lpSource, dwMessageID, dwLangID, pszBuffer, cchSize, &vaParamList);
  106. va_end(vaParamList);
  107. return dwResult;
  108. }
  109. // This runs a TreeWalker that gets the info about files and file
  110. // sizes, etc. and then takes those date and stuffs them into a infotip
  111. STDMETHODIMP CFolderInfoTip::GetInfoTip(DWORD dwFlags, LPWSTR *ppwszTip)
  112. {
  113. HRESULT hr = S_OK;
  114. *ppwszTip = NULL;
  115. if (_pszFolderName)
  116. {
  117. WCHAR szTip[INFOTIPSIZE]; // The info tip I build w/ folder contents
  118. szTip[0] = 0;
  119. // If we are to search, then search!
  120. if ((dwFlags & QITIPF_USESLOWTIP) &&
  121. SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("FolderContentsInfoTip"), 0, TRUE))
  122. {
  123. _WalkTree(szTip, ARRAYSIZE(szTip));
  124. }
  125. // Now that we've built or skipped our tip, get the outer tip's info.
  126. if (_pqiOuter)
  127. {
  128. if (szTip[0])
  129. {
  130. LPWSTR pszOuterTip = NULL;
  131. _pqiOuter->GetInfoTip(dwFlags, &pszOuterTip);
  132. // Allocate and build the return tip, ommitting the outer tip if
  133. // it's null, and putting a \n between them
  134. // if they both aren't.
  135. int cch = lstrlen(szTip) + (pszOuterTip ? lstrlen(pszOuterTip) + 1 : 0) + 1;
  136. *ppwszTip = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR));
  137. if (*ppwszTip)
  138. {
  139. **ppwszTip = 0; // zero init string
  140. if (pszOuterTip && *pszOuterTip)
  141. {
  142. // outer tip first
  143. StrCpyN(*ppwszTip, pszOuterTip, cch);
  144. StrCatBuff(*ppwszTip, L"\n", cch);
  145. }
  146. StrCatBuff(*ppwszTip, szTip, cch);
  147. }
  148. if (pszOuterTip)
  149. {
  150. SHFree(pszOuterTip);
  151. }
  152. }
  153. else
  154. {
  155. hr = _pqiOuter->GetInfoTip(dwFlags, ppwszTip);
  156. }
  157. }
  158. }
  159. return hr;
  160. }
  161. STDMETHODIMP CFolderInfoTip::SetPrefixText(LPCWSTR pszPrefix)
  162. {
  163. ICustomizeInfoTip *pcit;
  164. if (_pqiOuter && SUCCEEDED(_pqiOuter->QueryInterface(IID_PPV_ARG(ICustomizeInfoTip, &pcit))))
  165. {
  166. pcit->SetPrefixText(pszPrefix);
  167. pcit->Release();
  168. }
  169. return S_OK;
  170. }
  171. STDMETHODIMP CFolderInfoTip::SetExtraProperties(const SHCOLUMNID *pscid, UINT cscid)
  172. {
  173. ICustomizeInfoTip *pcit;
  174. if (_pqiOuter && SUCCEEDED(_pqiOuter->QueryInterface(IID_PPV_ARG(ICustomizeInfoTip, &pcit))))
  175. {
  176. pcit->SetExtraProperties(pscid, cscid);
  177. pcit->Release();
  178. }
  179. return S_OK;
  180. }
  181. // IParentAndItem
  182. STDMETHODIMP CFolderInfoTip::SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidl)
  183. {
  184. IParentAndItem *ppai;
  185. if (_pqiOuter && SUCCEEDED(_pqiOuter->QueryInterface(IID_PPV_ARG(IParentAndItem, &ppai))))
  186. {
  187. ppai->SetParentAndItem(pidlParent, psf, pidl);
  188. ppai->Release();
  189. }
  190. return S_OK;
  191. }
  192. STDMETHODIMP CFolderInfoTip::GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidl)
  193. {
  194. IParentAndItem *ppai;
  195. if (_pqiOuter && SUCCEEDED(_pqiOuter->QueryInterface(IID_PPV_ARG(IParentAndItem, &ppai))))
  196. {
  197. ppai->GetParentAndItem(ppidlParent, ppsf, ppidl);
  198. ppai->Release();
  199. }
  200. return S_OK;
  201. }
  202. // Helper functions for GetInfoTip
  203. HRESULT CFolderInfoTip::_WalkTree(LPWSTR pszTip, DWORD cchSize)
  204. {
  205. // Get a CShellTreeWalker object to run the search for us.
  206. IShellTreeWalker *pstw;
  207. HRESULT hr = ::CoCreateInstance(CLSID_CShellTreeWalker, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellTreeWalker, &pstw));
  208. if (SUCCEEDED(hr))
  209. {
  210. TCHAR szFolderBlurb[128], szFileBlurb[128], szSizeBlurb[128];
  211. // Remember when we started so we know when to stop
  212. _dwSearchStartTime = GetTickCount();
  213. // Now, if hrTreeWalk is an error, it's not really an error; it just means
  214. // that the search was cut off early, so we don't bother to check
  215. // it. hrTreeWalk is passed to _BuildSizeBlurb so that it know whether or not
  216. // to add "greater than" to the string.
  217. HRESULT hrTreeWalk = pstw->WalkTree(WT_EXCLUDEWALKROOT | WT_NOTIFYFOLDERENTER,
  218. _pszFolderName, L"*.*", 32, SAFECAST(this, IShellTreeWalkerCallBack *));
  219. // Create substrings for size, files, folders (may be empty if there's
  220. // nothing to show)
  221. _BuildSizeBlurb(hrTreeWalk, szSizeBlurb, ARRAYSIZE(szSizeBlurb));
  222. _BuildFileBlurb(hrTreeWalk, szFileBlurb, ARRAYSIZE(szFileBlurb));
  223. _BuildFolderBlurb(hrTreeWalk, szFolderBlurb, ARRAYSIZE(szFolderBlurb));
  224. // Build our local tip
  225. TCHAR szFormatStr[64];
  226. LoadString(HINST_THISDLL, IDS_FIT_TipFormat, szFormatStr, ARRAYSIZE(szFormatStr));
  227. _FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, szFormatStr, 0, 0, pszTip,
  228. cchSize, szSizeBlurb, szFolderBlurb, szFileBlurb);
  229. pstw->Release();
  230. }
  231. return hr;
  232. }
  233. HRESULT CFolderInfoTip::_BuildSizeBlurb(HRESULT hr, LPWSTR pszBlurb, DWORD cchSize)
  234. {
  235. if (_ulTotalSize || (_nFiles || _nSubFolders))
  236. {
  237. WCHAR szSizeString[20];
  238. WCHAR szFormatStr[64];
  239. StrFormatByteSize(_ulTotalSize, szSizeString, ARRAYSIZE(szSizeString));
  240. if (SUCCEEDED(hr))
  241. {
  242. LoadString(HINST_THISDLL, IDS_FIT_Size, szFormatStr, ARRAYSIZE(szFormatStr));
  243. }
  244. else
  245. {
  246. LoadString(HINST_THISDLL, IDS_FIT_Size_LT, szFormatStr, ARRAYSIZE(szFormatStr));
  247. }
  248. _FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, szFormatStr, 0, 0, pszBlurb, cchSize, szSizeString);
  249. }
  250. else
  251. {
  252. LoadString(HINST_THISDLL, IDS_FIT_Size_Empty, pszBlurb, cchSize);
  253. }
  254. return S_OK;
  255. }
  256. HRESULT CFolderInfoTip::_BuildFileBlurb(HRESULT hr, LPWSTR pszBlurb, DWORD cchSize)
  257. {
  258. if (_nFiles && _nFileListCharsUsed)
  259. {
  260. WCHAR szFormatStr[64];
  261. LoadString(HINST_THISDLL, IDS_FIT_Files, szFormatStr, ARRAYSIZE(szFormatStr));
  262. _FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, szFormatStr, 0, 0, pszBlurb, cchSize, _szFileList);
  263. }
  264. else
  265. {
  266. _FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, L"", 0, 0, pszBlurb, cchSize);
  267. }
  268. return S_OK;
  269. }
  270. HRESULT CFolderInfoTip::_BuildFolderBlurb(HRESULT hr, LPWSTR pszBlurb, DWORD cchSize)
  271. {
  272. if (_nSubFolders)
  273. {
  274. WCHAR szFormatStr[64];
  275. LoadString(HINST_THISDLL, IDS_FIT_Folders, szFormatStr, ARRAYSIZE(szFormatStr));
  276. _FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, szFormatStr, 0, 0, pszBlurb, cchSize, _szFolderList);
  277. }
  278. else
  279. {
  280. _FormatMessageArg(FORMAT_MESSAGE_FROM_STRING, L"", 0, 0, pszBlurb, cchSize);
  281. }
  282. return S_OK;
  283. }
  284. //
  285. // A helper func that copies strings into a fixed size buffer,
  286. // taking care of delimeters and everything. Used by EnterFolder
  287. // and FoundFile to build the file and folder lists.
  288. HRESULT CFolderInfoTip::_BufferInsert(LPWSTR pszBuffer, int *pnBufferUsed,
  289. int nBufferMaxSize, LPCWSTR pszPath, int nBufferItems)
  290. {
  291. TCHAR szDelimiter[100], szExtraItems[100];
  292. LoadString(HINST_THISDLL, IDS_FIT_Delimeter, szDelimiter, ARRAYSIZE(szDelimiter));
  293. LoadString(HINST_THISDLL, IDS_FIT_ExtraItems, szExtraItems, ARRAYSIZE(szExtraItems));
  294. // Check to see if the buffer is full, if not, proceed.
  295. if (*pnBufferUsed < nBufferMaxSize)
  296. {
  297. // Holds the file name form the abs. path
  298. // Grab the file name
  299. LPWSTR pszFile = PathFindFileName(pszPath);
  300. if (pszFile)
  301. {
  302. // Calculates if the item will fit, remembering to leave room
  303. // not only for the delimeter, but for for the extra item marker
  304. // that might be added in the future.
  305. if (*pnBufferUsed + lstrlen(pszFile) + lstrlen(szDelimiter) * 2 + lstrlen(szExtraItems) + 1 <
  306. nBufferMaxSize)
  307. {
  308. // Add the delimeter if this is not the 1st item
  309. if (nBufferItems > 1)
  310. {
  311. StrCpyN(&(pszBuffer[*pnBufferUsed]),
  312. szDelimiter, (nBufferMaxSize - *pnBufferUsed));
  313. *pnBufferUsed += lstrlen(szDelimiter);
  314. }
  315. // Add the item to the buffer
  316. StrCpyN(&(pszBuffer[*pnBufferUsed]), pszFile, (nBufferMaxSize - *pnBufferUsed));
  317. *pnBufferUsed += lstrlen(pszFile);
  318. }
  319. else
  320. {
  321. // In this case, the item won't fit, so just add the extra
  322. // items marker and set the buffer to be full
  323. if (nBufferItems > 1)
  324. {
  325. StrCpyN(&(pszBuffer[*pnBufferUsed]), szDelimiter, (nBufferMaxSize - *pnBufferUsed));
  326. *pnBufferUsed += lstrlen(szDelimiter);
  327. }
  328. StrCpyN(&(pszBuffer[*pnBufferUsed]), szExtraItems, (nBufferMaxSize - *pnBufferUsed));
  329. *pnBufferUsed = nBufferMaxSize;
  330. }
  331. }
  332. }
  333. return S_OK;
  334. }
  335. // IShellTreeWalkerCallBack functions
  336. //
  337. // The TreeWalker calls these whenever it finds a file, etc. We grab
  338. // the data out of the passed TREEWALKERSTATS *and use it to build the
  339. // tip. We also take the filenames that are passed to FoundFile and to
  340. // to EnterFolder to build the file and folder listings
  341. STDMETHODIMP CFolderInfoTip::FoundFile(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
  342. {
  343. if (ptws->nDepth == 0)
  344. {
  345. if (!(pwfd->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
  346. _BufferInsert(_szFileList, &_nFileListCharsUsed, ARRAYSIZE(_szFileList), pwszPath, ptws->nFiles);
  347. }
  348. return _GetTreeWalkerData(ptws);
  349. }
  350. STDMETHODIMP CFolderInfoTip::EnterFolder(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
  351. {
  352. if (ptws->nDepth == 0)
  353. {
  354. if (!(pwfd->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
  355. _BufferInsert(_szFolderList, &_nFolderListCharsUsed, ARRAYSIZE(_szFolderList), pwszPath, ptws->nFolders);
  356. }
  357. return _GetTreeWalkerData(ptws);
  358. }
  359. STDMETHODIMP CFolderInfoTip::LeaveFolder(LPCWSTR pwszPath, TREEWALKERSTATS *ptws)
  360. {
  361. return _GetTreeWalkerData(ptws);
  362. }
  363. STDMETHODIMP CFolderInfoTip::HandleError(LPCWSTR pwszPath, TREEWALKERSTATS *ptws, HRESULT hrError)
  364. {
  365. // TODO: look for HRESULT_FROM_WIN32(ACCESS_DENIED) for folders we can't look into.
  366. return _GetTreeWalkerData(ptws);
  367. }
  368. // copies data from the treewalker callback into
  369. // class vars so that they can be used to build the InfoTip. This also cuts
  370. // off the search if too much time has elapsed.
  371. HRESULT CFolderInfoTip::_GetTreeWalkerData(TREEWALKERSTATS *ptws)
  372. {
  373. HRESULT hr = S_OK;
  374. _ulTotalSize = ptws->ulTotalSize;
  375. _nSubFolders = ptws->nFolders;
  376. _nFiles = ptws->nFiles;
  377. if ((GetTickCount() - _dwSearchStartTime) > 3000) // 3 seconds
  378. {
  379. hr = E_UNEXPECTED;
  380. }
  381. return hr;
  382. }
  383. STDAPI CFolderInfoTip_CreateInstance(IUnknown *punkOuter, LPCTSTR pszFolder, REFIID riid, void **ppv)
  384. {
  385. HRESULT hr;
  386. CFolderInfoTip *pdocp = new CFolderInfoTip(punkOuter, pszFolder);
  387. if (pdocp)
  388. {
  389. hr = pdocp->QueryInterface(riid, ppv);
  390. pdocp->Release();
  391. }
  392. else
  393. {
  394. *ppv = NULL;
  395. hr = E_OUTOFMEMORY;
  396. }
  397. return hr;
  398. }