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.

2170 lines
52 KiB

  1. /*++
  2. Implements population of a listview control with the content from
  3. the start menu
  4. --*/
  5. #include "stdafx.h"
  6. #include "resource.h"
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <shlobj.h>
  10. #include <shlwapi.h>
  11. #include <shellapi.h>
  12. #include <commctrl.h>
  13. #include <msi.h>
  14. #include <sfc.h>
  15. #include "progview.h"
  16. extern "C" {
  17. #include <shimdb.h>
  18. }
  19. #pragma warning(disable:4786)
  20. #include <string>
  21. #include <xstring>
  22. #include <map>
  23. #include <algorithm>
  24. using namespace std;
  25. #ifdef _UNICODE
  26. typedef wstring tstring;
  27. #else
  28. typedef string tstring;
  29. #endif
  30. typedef
  31. INSTALLSTATE (WINAPI*PMsiGetComponentPath)(
  32. LPCTSTR szProduct, // product code for client product
  33. LPCTSTR szComponent, // component ID
  34. LPTSTR lpPathBuf, // returned path
  35. DWORD *pcchBuf // buffer character count
  36. );
  37. typedef
  38. UINT (WINAPI* PMsiGetShortcutTarget)(
  39. LPCTSTR szShortcutTarget, // path to shortcut link file
  40. LPTSTR szProductCode, // fixed length buffer for product code
  41. LPTSTR szFeatureId, // fixed length buffer for feature id
  42. LPTSTR szComponentCode // fixed length buffer for component code
  43. );
  44. class CException {
  45. public:
  46. CException(LPCSTR lpszFile = NULL, DWORD nLocation = 0) {
  47. SetLocation(lpszFile, nLocation);
  48. }
  49. virtual ~CException() {}
  50. virtual VOID Delete() {
  51. delete this;
  52. }
  53. int __cdecl FormatV(LPCTSTR lpszFormat, va_list arg) {
  54. int nch = 0;
  55. if (lpszFormat) {
  56. nch = _vsntprintf(szDescription, CHARCOUNT(szDescription), lpszFormat, arg);
  57. } else {
  58. *szDescription = TEXT('\0');
  59. }
  60. return nch;
  61. }
  62. int __cdecl Format(LPCTSTR lpszFormat, ...) {
  63. va_list arg;
  64. int nch = 0;
  65. if (lpszFormat) {
  66. va_start(arg, lpszFormat);
  67. nch = _vsntprintf(szDescription, CHARCOUNT(szDescription), lpszFormat, arg);
  68. va_end(arg);
  69. } else {
  70. *szDescription = TEXT('\0');
  71. }
  72. }
  73. VOID SetLocation(LPCSTR lpszFile, DWORD nLocation) {
  74. if (lpszFile) {
  75. strcpy(szLocation, lpszFile);
  76. } else {
  77. *szLocation = TEXT('\0');
  78. }
  79. m_dwLocation = nLocation;
  80. }
  81. TCHAR szDescription[MAX_PATH];
  82. CHAR szLocation[MAX_PATH];
  83. DWORD m_dwLocation;
  84. };
  85. class CMemoryException : public CException {
  86. public:
  87. CMemoryException(LPCSTR lpszFile = NULL, DWORD nLocation = 0) :
  88. CException(lpszFile, nLocation) {}
  89. VOID Delete() {}
  90. };
  91. class CCancelException : public CException {
  92. public:
  93. CCancelException(LPCSTR lpszFile = NULL, DWORD nLocation = 0) :
  94. CException(lpszFile, nLocation){}
  95. };
  96. static CMemoryException _MemoryExceptionStatic;
  97. VOID __cdecl ThrowMemoryException(LPCSTR lpszFile, DWORD nLocation, LPCTSTR lpszFormat = NULL, ...) {
  98. va_list arg;
  99. CMemoryException* pMemoryException = &_MemoryExceptionStatic;
  100. va_start(arg, lpszFormat);
  101. pMemoryException->FormatV(lpszFormat, arg);
  102. va_end(arg);
  103. throw pMemoryException;
  104. }
  105. class CProgramList {
  106. public:
  107. CProgramList(LPMALLOC pMalloc, HWND hwndListView, LPCTSTR szSystemDirectory) :
  108. m_pMalloc(pMalloc),
  109. m_hwndListView(hwndListView),
  110. m_hMSI(NULL),
  111. m_pSelectionInfo(NULL),
  112. m_hbmSort(NULL),
  113. m_pProgView(NULL),
  114. m_hEventCancel(NULL) {
  115. //
  116. // we are always initializing on populate thread
  117. //
  118. m_dwOwnerThreadID = GetCurrentThreadId();
  119. m_strSystemDirectory = szSystemDirectory;
  120. }
  121. ~CProgramList();
  122. BOOL PopulateControl(CProgView* pProgView = NULL, HANDLE hEventCancel = NULL);
  123. LPMALLOC GetMalloc(VOID) {
  124. return GetCurrentThreadId() == m_dwOwnerThreadID ? m_pMalloc : m_pMallocUI;
  125. }
  126. BOOL CaptureSelection();
  127. BOOL GetSelectionDetails(INT iInformationClass, LPWSTR pBuffer, ULONG BufferSize);
  128. LRESULT LVNotifyDispInfo (LPNMHDR pnmhdr, BOOL& bHandled);
  129. LRESULT LVNotifyColumnClick(LPNMHDR pnmhdr, BOOL& bHandled);
  130. LRESULT LVNotifyGetInfoTip (LPNMHDR pnmhdr, BOOL& bHandled);
  131. LRESULT LVNotifyRClick (LPNMHDR pnmhdr, BOOL& bHandled);
  132. BOOL IsEnabled(VOID);
  133. VOID Enable(BOOL);
  134. BOOL UpdateListItem(LPCWSTR pwszPath, LPCWSTR pwszKey);
  135. protected:
  136. BOOL ListFolder(LPCTSTR pszLocationParent, IShellFolder* pFolder, LPCITEMIDLIST pidlFull, LPCITEMIDLIST pidlFolder);
  137. BOOL ListLink(LPCTSTR pszLocationParent, LPCTSTR pszDisplayName, IShellFolder* pFolder, LPCITEMIDLIST pidlFull, LPCITEMIDLIST pidlLink);
  138. BOOL ListMsiLink(LPCTSTR pszLocationParent, LPCTSTR pszDisplayName, LPCTSTR pszMsiPath, IShellFolder* pFolder, LPCITEMIDLIST pidlFull);
  139. LPITEMIDLIST GetNextItemIDL(LPCITEMIDLIST pidl);
  140. UINT GetSizeIDL (LPCITEMIDLIST pidl);
  141. LPITEMIDLIST AppendIDL (LPCITEMIDLIST pidlBase,
  142. LPCITEMIDLIST pidlAdd);
  143. LPITEMIDLIST GetLastItemIDL(LPCITEMIDLIST pidl);
  144. BOOL GetDisplayName(IShellFolder* pFolder, LPCITEMIDLIST pidl, tstring& strDisplay);
  145. BOOL GetPathFromLink(IShellLink* pLink, WIN32_FIND_DATA* pfd, tstring& strPath);
  146. BOOL GetArgumentsFromLink(IShellLink* pLink, tstring& strArgs);
  147. BOOL AddItem(LPCTSTR pszLocation,
  148. LPCTSTR pszDisplayName,
  149. LPCTSTR pszPath,
  150. LPCTSTR pszArguments,
  151. IShellFolder* pFolder,
  152. LPCITEMIDLIST pidlFull,
  153. BOOL bUsePath = FALSE); // true if we should use path for executable
  154. int GetIconFromLink(LPCITEMIDLIST pidlLinkFull, LPCTSTR lpszExePath);
  155. BOOL IsSFCItem(LPCTSTR lpszItem);
  156. BOOL IsItemInSystemDirectory(LPCTSTR pszPath);
  157. private:
  158. LPMALLOC m_pMalloc;
  159. LPMALLOC m_pMallocUI;
  160. HWND m_hwndListView; // list view control
  161. HBITMAP m_hbmSort;
  162. typedef struct tagSHITEMINFO {
  163. tstring strDisplayName; // descriptive name
  164. tstring strFolder; // containing folder
  165. tstring strPath; // actual exe, cracked
  166. tstring strPathExecute; // link path (this is what we will execute)
  167. tstring strCmdLine; // command line (cracked link)
  168. tstring strArgs;
  169. tstring strKeys;
  170. LPITEMIDLIST pidl; // full pidl
  171. } SHITEMINFO, *PSHITEMINFO;
  172. static CALLBACK SHItemInfoCompareFunc(LPARAM lp1, LPARAM lp2, LPARAM lParamSort);
  173. typedef map< tstring, PSHITEMINFO, less<tstring> > MAPSTR2ITEM;
  174. typedef multimap< tstring, PSHITEMINFO > MULTIMAPSTR2ITEM;
  175. //
  176. // store key->item sequence, the keys are cmdlines (with args)
  177. //
  178. MAPSTR2ITEM m_mapItems;
  179. //
  180. // store key->item sequence, where the key is exe name (path)
  181. //
  182. MULTIMAPSTR2ITEM m_mmapExeItems;
  183. //
  184. // selected item
  185. //
  186. PSHITEMINFO m_pSelectionInfo;
  187. //
  188. // cached msi.dll handle
  189. //
  190. HMODULE m_hMSI;
  191. PMsiGetComponentPath m_pfnGetComponentPath;
  192. PMsiGetShortcutTarget m_pfnGetShortcutTarget;
  193. //
  194. // cached system directory
  195. //
  196. tstring m_strSystemDirectory;
  197. //
  198. // image list used to show icons
  199. //
  200. HIMAGELIST m_hImageList;
  201. //
  202. // optional pointer to the parent view
  203. //
  204. CProgView* m_pProgView;
  205. //
  206. // event that we use to signal the end of scan
  207. //
  208. HANDLE m_hEventCancel;
  209. //
  210. // owner thread
  211. //
  212. DWORD m_dwOwnerThreadID;
  213. VOID CheckForCancel() {
  214. if (m_hEventCancel) {
  215. if (::WaitForSingleObject(m_hEventCancel, 0) != WAIT_TIMEOUT) {
  216. // cancelled!!!
  217. throw new CCancelException();
  218. }
  219. }
  220. }
  221. };
  222. //
  223. // in upload.cpp
  224. //
  225. wstring StrUpCase(wstring& wstr);
  226. //
  227. // load the string from resources
  228. //
  229. wstring LoadResourceString(UINT nID)
  230. {
  231. LPTSTR lpszBuffer = NULL;
  232. int cch;
  233. wstring str;
  234. cch = ::LoadString(_Module.GetModuleInstance(), nID, (LPTSTR)&lpszBuffer, 0);
  235. //
  236. // hack! this must work (I know it does)
  237. //
  238. if (cch && NULL != lpszBuffer) {
  239. str = wstring(lpszBuffer, cch);
  240. }
  241. return str;
  242. }
  243. /////////////////////////////////////////////////////////////////////////////////////////////////
  244. //
  245. // Utility functions
  246. //
  247. BOOL
  248. InitializeProgramList(
  249. CProgramList** ppProgramList,
  250. HWND hwndListView
  251. )
  252. {
  253. HRESULT hr;
  254. BOOL bSuccess = FALSE;
  255. LPMALLOC pMalloc = NULL;
  256. TCHAR szSystemWindowsDirectory[MAX_PATH];
  257. CProgramList* pProgramList = NULL;
  258. UINT uSize;
  259. hr = SHGetMalloc(&pMalloc);
  260. if (!SUCCEEDED(hr)) {
  261. goto ErrHandle;
  262. }
  263. uSize = ::GetSystemWindowsDirectory(szSystemWindowsDirectory,
  264. CHARCOUNT(szSystemWindowsDirectory));
  265. if (uSize == 0 || uSize > CHARCOUNT(szSystemWindowsDirectory)) {
  266. goto ErrHandle;
  267. }
  268. pProgramList = new CProgramList(pMalloc, hwndListView, szSystemWindowsDirectory);
  269. if (NULL == pProgramList) {
  270. goto ErrHandle;
  271. }
  272. *ppProgramList = pProgramList;
  273. bSuccess = TRUE;
  274. ErrHandle:
  275. if (!bSuccess) {
  276. if (NULL != pMalloc) {
  277. pMalloc->Release();
  278. }
  279. if (NULL != pProgramList) {
  280. delete pProgramList;
  281. }
  282. }
  283. return bSuccess;
  284. }
  285. BOOL
  286. CleanupProgramList(
  287. CProgramList* pProgramList
  288. )
  289. {
  290. LPMALLOC pMalloc;
  291. if (NULL == pProgramList) {
  292. return FALSE;
  293. }
  294. pMalloc = pProgramList->GetMalloc();
  295. delete pProgramList;
  296. if (NULL != pMalloc) {
  297. pMalloc->Release();
  298. }
  299. return TRUE;
  300. }
  301. BOOL
  302. PopulateProgramList(
  303. CProgramList* pProgramList,
  304. CProgView* pProgView,
  305. HANDLE hEventCancel
  306. )
  307. {
  308. return pProgramList->PopulateControl(pProgView, hEventCancel);
  309. }
  310. CProgramList::~CProgramList()
  311. {
  312. //
  313. //
  314. //
  315. MAPSTR2ITEM::iterator iter;
  316. iter = m_mapItems.begin();
  317. while (iter != m_mapItems.end()) {
  318. PSHITEMINFO pInfo = (*iter).second;
  319. GetMalloc()->Free(pInfo->pidl); // nuke this please
  320. delete pInfo;
  321. ++iter;
  322. }
  323. if (NULL != m_hbmSort) {
  324. DeleteObject(m_hbmSort);
  325. }
  326. // Image list is destroyed automatically when the control is destroyed
  327. //
  328. // if (NULL != m_hImageList) {
  329. // ImageList_Destroy(m_hImageList);
  330. // }
  331. if (NULL != m_hMSI && (HMODULE)-1 != m_hMSI) {
  332. FreeLibrary(m_hMSI);
  333. }
  334. }
  335. BOOL
  336. CProgramList::GetDisplayName(
  337. IShellFolder* pFolder,
  338. LPCITEMIDLIST pidl,
  339. tstring& strDisplayName
  340. )
  341. {
  342. STRRET strName;
  343. HRESULT hr;
  344. LPTSTR pszName = NULL;
  345. hr = pFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &strName);
  346. if (!SUCCEEDED(hr)) {
  347. return FALSE;
  348. }
  349. hr = StrRetToStr(&strName, pidl, &pszName);
  350. if (!SUCCEEDED(hr)) {
  351. return FALSE;
  352. }
  353. // if we have been successful, assign return result
  354. if (pszName != NULL) {
  355. strDisplayName = pszName;
  356. CoTaskMemFree(pszName);
  357. } else {
  358. strDisplayName.erase();
  359. }
  360. return TRUE;
  361. }
  362. BOOL
  363. CProgramList::GetPathFromLink(
  364. IShellLink* pLink,
  365. WIN32_FIND_DATA* pfd,
  366. tstring& strPath
  367. )
  368. {
  369. TCHAR szPath[MAX_PATH];
  370. HRESULT hr;
  371. hr = pLink->GetPath(szPath, sizeof(szPath)/sizeof(szPath[0]), pfd, 0);
  372. if (hr == S_OK) {
  373. strPath = szPath;
  374. }
  375. return hr == S_OK;
  376. }
  377. BOOL
  378. CProgramList::GetArgumentsFromLink(
  379. IShellLink* pLink,
  380. tstring& strArgs
  381. )
  382. {
  383. TCHAR szArgs[INFOTIPSIZE];
  384. HRESULT hr = pLink->GetArguments(szArgs, sizeof(szArgs)/sizeof(szArgs[0]));
  385. if (SUCCEEDED(hr)) {
  386. strArgs = szArgs;
  387. }
  388. return SUCCEEDED(hr);
  389. }
  390. LPITEMIDLIST
  391. CProgramList::GetNextItemIDL(
  392. LPCITEMIDLIST pidl
  393. )
  394. {
  395. // Check for valid pidl.
  396. if (pidl == NULL) {
  397. return NULL;
  398. }
  399. // Get the size of the specified item identifier.
  400. int cb = pidl->mkid.cb;
  401. // If the size is zero, it is the end of the list.
  402. if (cb == 0) {
  403. return NULL;
  404. }
  405. // Add cb to pidl (casting to increment by bytes).
  406. pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb);
  407. // Return NULL if it is null-terminating, or a pidl otherwise.
  408. return (pidl->mkid.cb == 0) ? NULL : (LPITEMIDLIST) pidl;
  409. }
  410. LPITEMIDLIST
  411. CProgramList::GetLastItemIDL(
  412. LPCITEMIDLIST pidl
  413. )
  414. {
  415. LPITEMIDLIST pidlLast = (LPITEMIDLIST)pidl;
  416. if (pidl == NULL) {
  417. return NULL;
  418. }
  419. int cb = pidl->mkid.cb;
  420. if (cb == 0) {
  421. return NULL;
  422. }
  423. do {
  424. pidl = GetNextItemIDL(pidlLast);
  425. if (pidl != NULL) {
  426. pidlLast = (LPITEMIDLIST)pidl;
  427. }
  428. } while (pidl != NULL);
  429. return pidlLast;
  430. }
  431. UINT
  432. CProgramList::GetSizeIDL(
  433. LPCITEMIDLIST pidl
  434. )
  435. {
  436. UINT cbTotal = 0;
  437. if (pidl)
  438. {
  439. cbTotal += sizeof(pidl->mkid.cb); // Null terminator
  440. while (NULL != pidl)
  441. {
  442. cbTotal += pidl->mkid.cb;
  443. pidl = GetNextItemIDL(pidl);
  444. }
  445. }
  446. return cbTotal;
  447. }
  448. LPITEMIDLIST
  449. CProgramList::AppendIDL(
  450. LPCITEMIDLIST pidlBase,
  451. LPCITEMIDLIST pidlAdd
  452. )
  453. {
  454. if (NULL == pidlBase && NULL == pidlAdd) {
  455. return NULL;
  456. }
  457. LPITEMIDLIST pidlNew, pidlAlloc;
  458. UINT cb1 = pidlBase ? GetSizeIDL(pidlBase) : 0;
  459. UINT cb2 = pidlAdd ? GetSizeIDL(pidlAdd) : 0;
  460. UINT size = cb1 + cb2;
  461. pidlAlloc =
  462. pidlNew = (LPITEMIDLIST)GetMalloc()->Alloc(size);
  463. if (pidlNew)
  464. {
  465. if (NULL != pidlBase) {
  466. cb1 = pidlAdd ? cb1 - sizeof(pidlBase->mkid.cb) : cb1;
  467. RtlMoveMemory(pidlNew, pidlBase, cb1);
  468. pidlNew = (LPITEMIDLIST)((PBYTE)pidlNew + cb1);
  469. }
  470. if (NULL != pidlAdd) {
  471. RtlMoveMemory(pidlNew, pidlAdd, cb2);
  472. }
  473. }
  474. return pidlAlloc;
  475. }
  476. BOOL
  477. CProgramList::ListMsiLink(
  478. LPCTSTR pszLocationParent,
  479. LPCTSTR pszDisplayName,
  480. LPCTSTR pszMsiPath,
  481. IShellFolder* pFolder,
  482. LPCITEMIDLIST pidlFull
  483. )
  484. {
  485. //
  486. // make sure we have msi module handle
  487. //
  488. if (NULL == m_hMSI) {
  489. m_hMSI = LoadLibrary(TEXT("msi.dll"));
  490. if (NULL == m_hMSI) {
  491. m_hMSI = (HMODULE)-1;
  492. return FALSE;
  493. }
  494. #ifdef _UNICODE
  495. m_pfnGetComponentPath = (PMsiGetComponentPath )GetProcAddress(m_hMSI, "MsiGetComponentPathW");
  496. m_pfnGetShortcutTarget = (PMsiGetShortcutTarget)GetProcAddress(m_hMSI, "MsiGetShortcutTargetW");
  497. #else
  498. m_pfnGetComponentPath = (PMsiGetComponentPath )GetProcAddress(m_hMSI, "MsiGetComponentPathA");
  499. m_pfnGetShortcutTarget = (PMsiGetShortcutTarget)GetProcAddress(m_hMSI, "MsiGetShortcutTargetA");
  500. #endif
  501. if (m_pfnGetComponentPath == NULL || m_pfnGetShortcutTarget == NULL) {
  502. FreeLibrary(m_hMSI);
  503. m_hMSI = (HMODULE)-1;
  504. return FALSE;
  505. }
  506. } else if (m_hMSI == (HMODULE)-1) {
  507. return FALSE;
  508. }
  509. UINT ErrCode;
  510. TCHAR szProduct[MAX_PATH];
  511. TCHAR szFeatureId[MAX_PATH];
  512. TCHAR szComponentCode[MAX_PATH];
  513. ErrCode = m_pfnGetShortcutTarget(pszMsiPath, szProduct, szFeatureId, szComponentCode);
  514. if (ERROR_SUCCESS != ErrCode) {
  515. return FALSE;
  516. }
  517. INSTALLSTATE is;
  518. TCHAR szPath[MAX_PATH];
  519. DWORD cchPath = sizeof(szPath)/sizeof(szPath[0]);
  520. *szPath = 0;
  521. is = m_pfnGetComponentPath(szProduct, szComponentCode, szPath, &cchPath);
  522. if (INSTALLSTATE_LOCAL == is) {
  523. //
  524. // add this item
  525. //
  526. return AddItem(pszLocationParent,
  527. pszDisplayName,
  528. szPath,
  529. NULL,
  530. pFolder,
  531. pidlFull,
  532. TRUE);
  533. }
  534. return FALSE;
  535. }
  536. int
  537. CProgramList::GetIconFromLink(
  538. LPCITEMIDLIST pidlLinkFull,
  539. LPCTSTR lpszExePath
  540. )
  541. {
  542. HRESULT hr;
  543. IShellFolder* pFolder = NULL;
  544. IExtractIcon* pExtractIcon = NULL;
  545. INT iIconIndex = 0;
  546. UINT uFlags = 0;
  547. LPCITEMIDLIST pidlLink = 0;
  548. HICON hIconLarge = NULL;
  549. HICON hIconSmall = NULL;
  550. UINT nIconSize;
  551. int ImageIndex = -1;
  552. UINT uiErrorMode;
  553. DWORD dwAttributes;
  554. TCHAR szIconFile[MAX_PATH];
  555. *szIconFile = TEXT('\0');
  556. uiErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  557. hr = SHBindToParent(pidlLinkFull, IID_IShellFolder, (PVOID*)&pFolder, &pidlLink);
  558. if (!SUCCEEDED(hr)) {
  559. goto trySysImage;
  560. }
  561. // get the ui please
  562. hr = pFolder->GetUIObjectOf(m_hwndListView, 1, (LPCITEMIDLIST*)&pidlLink, IID_IExtractIcon, NULL, (PVOID*)&pExtractIcon);
  563. if (!SUCCEEDED(hr)) {
  564. goto trySysImage;
  565. }
  566. hr = pExtractIcon->GetIconLocation(0,
  567. szIconFile,
  568. sizeof(szIconFile) / sizeof(szIconFile[0]),
  569. &iIconIndex,
  570. &uFlags);
  571. if (!SUCCEEDED(hr)) {
  572. goto trySysImage;
  573. }
  574. if (*szIconFile == TEXT('*')) { // this is batch or some such, don't bother
  575. goto trySysImage;
  576. }
  577. //
  578. // before doing an extract, check whether it's available
  579. //
  580. dwAttributes = GetFileAttributes(szIconFile);
  581. if (dwAttributes == (DWORD)-1) {
  582. goto trySysImage;
  583. }
  584. nIconSize = MAKELONG(0, ::GetSystemMetrics(SM_CXSMICON));
  585. //
  586. // this call is likely to produce a popup, beware of that
  587. //
  588. hr = pExtractIcon->Extract(szIconFile,
  589. iIconIndex,
  590. &hIconLarge,
  591. &hIconSmall,
  592. nIconSize);
  593. //
  594. // if hIconSmall was retrieved - we were successful
  595. //
  596. trySysImage:
  597. if (hIconSmall == NULL) {
  598. //
  599. // woops -- we could not extract an icon -- what a bummer
  600. // use shell api then
  601. SHFILEINFO FileInfo;
  602. HIMAGELIST hImageSys;
  603. hImageSys = (HIMAGELIST)SHGetFileInfo(lpszExePath,
  604. 0,
  605. &FileInfo, sizeof(FileInfo),
  606. SHGFI_ICON|SHGFI_SMALLICON|SHGFI_SYSICONINDEX);
  607. if (hImageSys) {
  608. hIconSmall = ImageList_GetIcon(hImageSys, FileInfo.iIcon, ILD_TRANSPARENT);
  609. }
  610. }
  611. //
  612. // now that we have an icon, we can add it to our image list ?
  613. //
  614. if (hIconSmall != NULL) {
  615. ImageIndex = ImageList_AddIcon(m_hImageList, hIconSmall);
  616. }
  617. ///////////////////////// cleanup ///////////////////////////////////////////
  618. SetErrorMode(uiErrorMode);
  619. if (hIconSmall) {
  620. DestroyIcon(hIconSmall);
  621. }
  622. if (hIconLarge) {
  623. DestroyIcon(hIconLarge);
  624. }
  625. if (pExtractIcon != NULL) {
  626. pExtractIcon->Release();
  627. }
  628. if (pFolder != NULL) {
  629. pFolder->Release();
  630. }
  631. return ImageIndex;
  632. }
  633. BOOL
  634. CProgramList::ListLink(
  635. LPCTSTR pszLocationParent,
  636. LPCTSTR pszDisplayName,
  637. IShellFolder* pFolder,
  638. LPCITEMIDLIST pidlFull,
  639. LPCITEMIDLIST pidlLink
  640. )
  641. {
  642. IShellLink* psl = NULL;
  643. WIN32_FIND_DATA wfd;
  644. HRESULT hr;
  645. BOOL bSuccess = FALSE;
  646. tstring strPath;
  647. tstring strArgs;
  648. CComBSTR bstr;
  649. LPCTSTR pszArgs = NULL;
  650. IPersistFile* ipf = NULL;
  651. IShellLinkDataList* pdl;
  652. DWORD dwFlags;
  653. BOOL bMsiLink = FALSE;
  654. //
  655. // check whether we need to cancel
  656. //
  657. CheckForCancel();
  658. hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
  659. IID_IShellLink, (LPVOID*)&psl);
  660. if (!SUCCEEDED(hr)) {
  661. return FALSE; // we can't create link object
  662. }
  663. hr = psl->SetIDList(pidlFull); // set the id list
  664. if (!SUCCEEDED(hr)) {
  665. goto out;
  666. }
  667. //
  668. // now the shell link is ready to rumble
  669. //
  670. if (!GetPathFromLink(psl, &wfd, strPath)) {
  671. goto out;
  672. }
  673. // now let's see what is inside of this link -- shall we?
  674. hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ipf);
  675. if (!SUCCEEDED(hr)) {
  676. goto out;
  677. }
  678. bstr = strPath.c_str();
  679. hr = ipf->Load(bstr, STGM_READ);
  680. if (SUCCEEDED(hr)) {
  681. //
  682. // resolve the link for now
  683. //
  684. // hr = psl->Resolve(NULL, SLR_NO_UI|SLR_NOUPDATE);
  685. hr = psl->QueryInterface(IID_IShellLinkDataList, (LPVOID*)&pdl);
  686. if (SUCCEEDED(hr)) {
  687. hr = pdl->GetFlags(&dwFlags);
  688. bMsiLink = SUCCEEDED(hr) && (dwFlags & SLDF_HAS_DARWINID);
  689. pdl->Release();
  690. }
  691. if (bMsiLink) {
  692. bSuccess = ListMsiLink(pszLocationParent, pszDisplayName, strPath.c_str(), pFolder, pidlFull);
  693. } else {
  694. //
  695. // we now get the path from the link -- and that's that
  696. //
  697. if (GetPathFromLink(psl, &wfd, strPath)) {
  698. if (GetArgumentsFromLink(psl, strArgs)) {
  699. pszArgs = strArgs.c_str();
  700. }
  701. //
  702. // add this to our list view
  703. //
  704. bSuccess = AddItem(pszLocationParent,
  705. pszDisplayName,
  706. strPath.c_str(),
  707. pszArgs,
  708. pFolder,
  709. pidlFull);
  710. }
  711. }
  712. }
  713. if (NULL != ipf) {
  714. ipf->Release();
  715. }
  716. out:
  717. if (NULL != psl) {
  718. psl->Release();
  719. }
  720. return bSuccess;
  721. }
  722. BOOL
  723. CProgramList::ListFolder(
  724. LPCTSTR pszLocation, // ui string - where is this folder located?
  725. IShellFolder* pParent, // parent folder
  726. LPCITEMIDLIST pidlFull, // idl of the full path to the folder
  727. LPCITEMIDLIST pidlFolder // idl of this folder relative to the pidlFull
  728. )
  729. {
  730. LPENUMIDLIST penum = NULL;
  731. LPITEMIDLIST pidl = NULL;
  732. HRESULT hr;
  733. ULONG celtFetched;
  734. ULONG uAttr;
  735. tstring strDisplayNameLocation;
  736. tstring strDisplayName;
  737. IShellFolder* pFolder = NULL;
  738. BOOL bDesktop = FALSE;
  739. BOOL bCancel = FALSE;
  740. CCancelException* pCancelException = NULL;
  741. CheckForCancel();
  742. if (pParent == NULL) {
  743. hr = SHGetDesktopFolder(&pParent);
  744. bDesktop = TRUE;
  745. }
  746. hr = pParent->BindToObject(pidlFolder,
  747. NULL,
  748. IID_IShellFolder,
  749. (LPVOID *) &pFolder);
  750. if (NULL == pszLocation) {
  751. GetDisplayName(pParent, pidlFolder, strDisplayNameLocation);
  752. } else {
  753. strDisplayNameLocation = pszLocation;
  754. }
  755. if (bDesktop) {
  756. pParent->Release();
  757. }
  758. if (!SUCCEEDED(hr)) {
  759. return FALSE;
  760. }
  761. hr = pFolder->EnumObjects(NULL,SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penum);
  762. if (!SUCCEEDED(hr)) {
  763. pFolder->Release(); // free the folder- - and go away
  764. return FALSE;
  765. }
  766. while( (hr = penum->Next(1,&pidl, &celtFetched)) == S_OK && celtFetched == 1 && !bCancel) {
  767. LPITEMIDLIST pidlCur;
  768. if (pidlFull == NULL) {
  769. pidlFull = pidlFolder;
  770. }
  771. pidlCur = AppendIDL(pidlFull, pidl);
  772. // get the display name of this item
  773. GetDisplayName(pFolder, pidl, strDisplayName);
  774. uAttr = SFGAO_FOLDER | SFGAO_LINK;
  775. hr = pFolder->GetAttributesOf(1, (LPCITEMIDLIST *) &pidl, &uAttr);
  776. if (SUCCEEDED(hr)) {
  777. try {
  778. if (uAttr & SFGAO_FOLDER) {
  779. //
  780. // dump folder recursively
  781. //
  782. ListFolder(strDisplayName.c_str(), pFolder, pidlCur, pidl);
  783. } else if (uAttr & SFGAO_LINK) {
  784. ListLink(strDisplayNameLocation.c_str(), strDisplayName.c_str(), pFolder, pidlCur, pidl);
  785. } else if (uAttr & SFGAO_FILESYSTEM) {
  786. //
  787. // this item is a file
  788. //
  789. AddItem(strDisplayNameLocation.c_str(),
  790. strDisplayName.c_str(),
  791. NULL,
  792. NULL,
  793. pFolder,
  794. pidlCur,
  795. TRUE);
  796. }
  797. } catch(CCancelException* pex) {
  798. //
  799. // we need to cancel -- we shall cleanup and do what we need, then re-throw
  800. //
  801. bCancel = TRUE;
  802. pCancelException = pex;
  803. }
  804. }
  805. GetMalloc()->Free(pidlCur);
  806. GetMalloc()->Free(pidl);
  807. }
  808. if (NULL != penum) {
  809. penum->Release();
  810. }
  811. if (NULL != pFolder) {
  812. pFolder->Release();
  813. }
  814. if (bCancel && pCancelException) {
  815. throw pCancelException;
  816. }
  817. return TRUE;
  818. }
  819. BOOL
  820. CProgramList::IsSFCItem(
  821. LPCTSTR pszPath
  822. )
  823. {
  824. #ifndef _UNICODE
  825. WCHAR wszBuffer[1024];
  826. mbstowcs(wszBuffer, pszPath, sizeof(wszBuffer)/sizeof(wszBuffer[0]));
  827. return SfcIsFileProtected(NULL, wszBuffer);
  828. #else
  829. return SfcIsFileProtected(NULL, pszPath);
  830. #endif
  831. }
  832. BOOL
  833. CProgramList::IsItemInSystemDirectory(
  834. LPCTSTR pszPath
  835. )
  836. {
  837. TCHAR szCommonPath[MAX_PATH];
  838. int nch;
  839. string s;
  840. nch = PathCommonPrefix(m_strSystemDirectory.c_str(), pszPath, szCommonPath);
  841. return nch == m_strSystemDirectory.length();
  842. }
  843. BOOL
  844. ValidateExecutableFile(
  845. LPCTSTR pszPath,
  846. BOOL bValidateFileExists
  847. )
  848. {
  849. LPTSTR rgExt[] = {
  850. TEXT("EXE"),
  851. TEXT("BAT"),
  852. TEXT("CMD"),
  853. TEXT("PIF"),
  854. TEXT("COM"),
  855. TEXT("LNK")
  856. };
  857. LPTSTR pExt;
  858. int i;
  859. BOOL bValidatedExt = FALSE;
  860. pExt = PathFindExtension(pszPath);
  861. if (pExt == NULL || *pExt == TEXT('\0')) {
  862. return FALSE;
  863. }
  864. ++pExt;
  865. for (i = 0; i < sizeof(rgExt)/sizeof(rgExt[0]) && !bValidatedExt; ++i) {
  866. bValidatedExt = !_tcsicmp(pExt, rgExt[i]);
  867. }
  868. if (!bValidatedExt) {
  869. return FALSE;
  870. }
  871. return bValidateFileExists ? PathFileExists(pszPath) : TRUE;
  872. }
  873. BOOL
  874. CProgramList::AddItem(
  875. LPCTSTR pszLocation,
  876. LPCTSTR pszDisplayName,
  877. LPCTSTR pszPath,
  878. LPCTSTR pszArguments,
  879. IShellFolder* pFolder,
  880. LPCITEMIDLIST pidlFull,
  881. BOOL bUsePath
  882. )
  883. {
  884. //
  885. // first test -- is this one of the types we like?
  886. //
  887. LPTSTR pchSlash;
  888. LPTSTR pchDot;
  889. LPTSTR rgExt[] = { TEXT("EXE"), TEXT("BAT"), TEXT("CMD"), TEXT("PIF"), TEXT("COM"), TEXT("LNK") };
  890. BOOL bValidatedExt = FALSE;
  891. BOOL bSuccess = FALSE;
  892. PSHITEMINFO pInfo = NULL;
  893. MAPSTR2ITEM::iterator Iter;
  894. TCHAR szPathExecute[MAX_PATH];
  895. tstring strKey;
  896. tstring strKeyExe;
  897. DWORD dwBinaryType = 0;
  898. LVITEM lvi;
  899. int ix;
  900. //
  901. // check for cancelling the search
  902. //
  903. CheckForCancel();
  904. if (NULL == pszPath) {
  905. pszPath = szPathExecute;
  906. if (!SHGetPathFromIDList(pidlFull, szPathExecute)) {
  907. goto out;
  908. }
  909. }
  910. if (pszDisplayName && m_pProgView) {
  911. m_pProgView->UpdatePopulateStatus(pszDisplayName, pszPath);
  912. }
  913. pchSlash = _tcsrchr(pszPath, TEXT('\\'));
  914. pchDot = _tcsrchr(pszPath, TEXT('.'));
  915. if (NULL != pchSlash) {
  916. if ((ULONG_PTR)pchDot < (ULONG_PTR)pchSlash) {
  917. pchDot = NULL;
  918. }
  919. }
  920. if (NULL != pchDot) {
  921. ++pchDot;
  922. for (int i = 0; i < sizeof(rgExt)/sizeof(rgExt[0]) && !bValidatedExt; ++i) {
  923. bValidatedExt = !_tcsicmp(pchDot, rgExt[i]);
  924. }
  925. }
  926. if (!bValidatedExt) {
  927. goto out;
  928. }
  929. //
  930. // Checks whether the item is in system directory or SFC-protected
  931. //
  932. #if 0
  933. if (IsItemInSystemDirectory(pszPath) || IsSFCItem(pszPath)) {
  934. goto out;
  935. }
  936. #endif
  937. //
  938. // GetBinaryTypeW excludes exes on the basis of binary type
  939. //
  940. if (GetBinaryType(pszPath, &dwBinaryType) &&
  941. dwBinaryType == SCS_64BIT_BINARY) {
  942. goto out;
  943. }
  944. if (IsSFCItem(pszPath)) {
  945. goto out;
  946. }
  947. //
  948. // this is multimap key
  949. //
  950. strKeyExe = StrUpCase(wstring(pszPath));
  951. //
  952. // now compose the key string
  953. //
  954. strKey = strKeyExe;
  955. if (NULL != pszArguments) {
  956. strKey.append(TEXT(" "));
  957. strKey.append(pszArguments);
  958. }
  959. //
  960. // now check whether this item has already been listed
  961. //
  962. Iter = m_mapItems.find(strKey);
  963. if (Iter != m_mapItems.end()) { // found a duplicate
  964. goto out;
  965. }
  966. //
  967. // now please add this item to the list view
  968. //
  969. pInfo = new CProgramList::SHITEMINFO;
  970. if (pInfo == NULL) {
  971. ThrowMemoryException(__FILE__, __LINE__, TEXT("%s\n"), TEXT("Failed to allocate Item Information structure"));
  972. }
  973. pInfo->strDisplayName = pszDisplayName;
  974. pInfo->strFolder = pszLocation;
  975. pInfo->strPath = pszPath;
  976. pInfo->strCmdLine = strKey;
  977. if (NULL != pszArguments) {
  978. pInfo->strArgs = pszArguments;
  979. }
  980. pInfo->pidl = AppendIDL(NULL, pidlFull);
  981. if (bUsePath) {
  982. pInfo->strPathExecute = pszPath;
  983. } else {
  984. // finally, what are we going to launch ?
  985. if (SHGetPathFromIDList(pidlFull, szPathExecute)) {
  986. pInfo->strPathExecute = szPathExecute;
  987. }
  988. }
  989. m_mapItems[strKey] = pInfo;
  990. m_mmapExeItems.insert(MULTIMAPSTR2ITEM::value_type(strKeyExe, pInfo));
  991. ATLTRACE(TEXT("Adding item %s %s %s\n"), pszDisplayName, pszLocation, pszPath);
  992. lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE;
  993. lvi.iItem = ListView_GetItemCount(m_hwndListView); // append at the end please
  994. lvi.iSubItem = 0;
  995. lvi.pszText = LPSTR_TEXTCALLBACK;
  996. lvi.iImage = I_IMAGECALLBACK;
  997. lvi.lParam = (LPARAM)pInfo;
  998. ix = ListView_InsertItem(m_hwndListView, &lvi);
  999. lvi.mask = LVIF_TEXT;
  1000. lvi.iItem = ix;
  1001. lvi.iSubItem = 1;
  1002. lvi.pszText = LPSTR_TEXTCALLBACK;
  1003. ListView_SetItem(m_hwndListView, &lvi);
  1004. bSuccess = TRUE;
  1005. out:
  1006. return bSuccess;
  1007. }
  1008. BOOL
  1009. CProgramList::PopulateControl(
  1010. CProgView* pProgView,
  1011. HANDLE hevtCancel
  1012. )
  1013. {
  1014. int i;
  1015. HRESULT hr;
  1016. LPITEMIDLIST pidl;
  1017. BOOL bCancel = FALSE;
  1018. struct {
  1019. INT csidl;
  1020. UINT nIDDescription;
  1021. } rgFolders[] = {
  1022. { CSIDL_DESKTOPDIRECTORY, IDS_DESKTOP },
  1023. { CSIDL_COMMON_STARTMENU, IDS_COMMON_STARTMENU },
  1024. { CSIDL_STARTMENU, IDS_STARTMENU },
  1025. { CSIDL_COMMON_PROGRAMS, IDS_COMMON_PROGRAMS },
  1026. { CSIDL_PROGRAMS, IDS_PROGRAMS }
  1027. };
  1028. //
  1029. // set the progview object pointer so we could update the status
  1030. //
  1031. m_pProgView = pProgView;
  1032. m_pMallocUI = pProgView->m_pMallocUI;
  1033. //
  1034. // set the event so that we could cancel the scan
  1035. //
  1036. m_hEventCancel = hevtCancel;
  1037. //
  1038. // set extended style
  1039. //
  1040. ListView_SetExtendedListViewStyleEx(m_hwndListView,
  1041. LVS_EX_INFOTIP|LVS_EX_LABELTIP,
  1042. LVS_EX_INFOTIP|LVS_EX_LABELTIP);
  1043. //
  1044. // fix columns
  1045. //
  1046. LVCOLUMN lvc;
  1047. RECT rc;
  1048. SIZE_T cxProgName;
  1049. SIZE_T cx;
  1050. wstring strCaption;
  1051. lvc.mask = LVCF_WIDTH;
  1052. if (!ListView_GetColumn(m_hwndListView, 2, &lvc)) {
  1053. ::GetClientRect(m_hwndListView, &rc);
  1054. cx = rc.right - rc.left -
  1055. ::GetSystemMetrics(SM_CXVSCROLL) -
  1056. ::GetSystemMetrics(SM_CXEDGE) -
  1057. ::GetSystemMetrics(SM_CXSIZEFRAME);
  1058. cxProgName = cx * 3 / 5;
  1059. strCaption = LoadResourceString(IDS_PROGRAMNAME);
  1060. lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT;
  1061. lvc.pszText = (LPTSTR)strCaption.c_str();
  1062. lvc.fmt = LVCFMT_LEFT;
  1063. lvc.cx = cxProgName;
  1064. lvc.iSubItem= 0;
  1065. ListView_InsertColumn(m_hwndListView, 0, &lvc);
  1066. cx -= cxProgName;
  1067. cxProgName = cx / 2;
  1068. strCaption = LoadResourceString(IDS_FOLDER);
  1069. lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
  1070. lvc.pszText = (LPTSTR)strCaption.c_str();
  1071. lvc.fmt = LVCFMT_LEFT;
  1072. lvc.cx = cxProgName;
  1073. lvc.iSubItem= 1;
  1074. ListView_InsertColumn(m_hwndListView, 1, &lvc);
  1075. strCaption = LoadResourceString(IDS_SETTINGS);
  1076. lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
  1077. lvc.pszText = (LPTSTR)strCaption.c_str();
  1078. lvc.fmt = LVCFMT_LEFT;
  1079. lvc.cx = cx - cxProgName;
  1080. lvc.iSubItem= 2;
  1081. ListView_InsertColumn(m_hwndListView, 2, &lvc);
  1082. }
  1083. HDC hDC = GetDC(m_hwndListView);
  1084. int nBitsPixel = ::GetDeviceCaps(hDC, BITSPIXEL);
  1085. int nPlanes = ::GetDeviceCaps(hDC, PLANES);
  1086. UINT flags;
  1087. nBitsPixel *= nPlanes;
  1088. if (nBitsPixel < 4) {
  1089. flags = ILC_COLOR;
  1090. } else if (nBitsPixel < 8) {
  1091. flags = ILC_COLOR4;
  1092. } else if (nBitsPixel < 16) {
  1093. flags = ILC_COLOR8;
  1094. } else if (nBitsPixel < 24) {
  1095. flags = ILC_COLOR16;
  1096. } else if (nBitsPixel < 32) {
  1097. flags = ILC_COLOR24;
  1098. } else if (nBitsPixel == 32) {
  1099. flags = ILC_COLOR32;
  1100. } else {
  1101. flags = ILC_COLORDDB;
  1102. }
  1103. flags |= ILC_MASK;
  1104. ReleaseDC(m_hwndListView, hDC);
  1105. m_hImageList = ImageList_Create(::GetSystemMetrics(SM_CXSMICON),
  1106. ::GetSystemMetrics(SM_CYSMICON),
  1107. flags,
  1108. 10,
  1109. 25);
  1110. if (m_hImageList == NULL) {
  1111. ATLTRACE(TEXT("Image List creation failure, error 0x%lx\n"), GetLastError());
  1112. }
  1113. ImageList_SetBkColor(m_hImageList, CLR_NONE);
  1114. ListView_SetImageList(m_hwndListView, m_hImageList, LVSIL_SMALL);
  1115. ::SendMessage(m_hwndListView, WM_SETREDRAW, FALSE, 0);
  1116. ListView_DeleteAllItems(m_hwndListView);
  1117. //
  1118. // AtlTrace(TEXT("Callback Mask: 0x%lx\n"), ListView_GetCallbackMask(m_hwndListView));
  1119. //
  1120. for (i = 0; i < sizeof(rgFolders)/sizeof(rgFolders[0]) && !bCancel; ++i) {
  1121. wstring strDescription = LoadResourceString(rgFolders[i].nIDDescription);
  1122. hr = SHGetFolderLocation(NULL, rgFolders[i].csidl, NULL, 0, &pidl);
  1123. if (SUCCEEDED(hr)) {
  1124. try {
  1125. ListFolder(strDescription.c_str(), NULL, NULL, pidl);
  1126. } catch(CCancelException* pex) {
  1127. bCancel = TRUE;
  1128. pex->Delete();
  1129. } catch(CException* pex) {
  1130. bCancel = TRUE;
  1131. pex->Delete();
  1132. }
  1133. GetMalloc()->Free(pidl);
  1134. }
  1135. }
  1136. ::SendMessage(m_hwndListView, WM_SETREDRAW, TRUE, 0);
  1137. return TRUE;
  1138. }
  1139. BOOL
  1140. CProgramList::CaptureSelection(
  1141. VOID
  1142. )
  1143. {
  1144. INT iSelected;
  1145. LVITEM lvi;
  1146. m_pSelectionInfo = NULL;
  1147. iSelected = ListView_GetNextItem(m_hwndListView, -1, LVNI_SELECTED);
  1148. if (iSelected == -1) {
  1149. return FALSE;
  1150. }
  1151. lvi.iItem = iSelected;
  1152. lvi.iSubItem = 0;
  1153. lvi.mask = LVIF_PARAM;
  1154. if (ListView_GetItem(m_hwndListView, &lvi)) {
  1155. m_pSelectionInfo = (PSHITEMINFO)lvi.lParam;
  1156. }
  1157. return m_pSelectionInfo != NULL;
  1158. }
  1159. BOOL
  1160. CProgramList::GetSelectionDetails(
  1161. INT iInformationClass,
  1162. LPWSTR pBuffer,
  1163. ULONG BufferSize
  1164. )
  1165. {
  1166. LPCWSTR wstr;
  1167. if (m_pSelectionInfo == NULL) {
  1168. *pBuffer = 0;
  1169. return TRUE;
  1170. }
  1171. switch(iInformationClass) {
  1172. case PROGLIST_DISPLAYNAME:
  1173. wstr = m_pSelectionInfo->strDisplayName.c_str();
  1174. break;
  1175. case PROGLIST_LOCATION: //
  1176. wstr = m_pSelectionInfo->strFolder.c_str();
  1177. break;
  1178. case PROGLIST_EXENAME: // cracked exe name
  1179. wstr = m_pSelectionInfo->strPath.c_str(); //
  1180. break;
  1181. case PROGLIST_CMDLINE: // complete exe name + parameters
  1182. wstr = m_pSelectionInfo->strCmdLine.c_str();
  1183. break;
  1184. case PROGLIST_EXECUTABLE: // what we should execute (link or exe, not cracked)
  1185. wstr = m_pSelectionInfo->strPathExecute.c_str();
  1186. break;
  1187. case PROGLIST_ARGUMENTS:
  1188. wstr = m_pSelectionInfo->strArgs.c_str();
  1189. break;
  1190. default:
  1191. *pBuffer = 0;
  1192. return TRUE;
  1193. break;
  1194. }
  1195. if (wcslen(wstr) < BufferSize/sizeof(WCHAR))
  1196. {
  1197. wcscpy(pBuffer, wstr);
  1198. }
  1199. return TRUE;
  1200. }
  1201. #define PROGLIST_SORT_NONE 0
  1202. #define PROGLIST_SORT_ASC 1
  1203. #define PROGLIST_SORT_DSC 2
  1204. int CALLBACK
  1205. CProgramList::SHItemInfoCompareFunc(
  1206. LPARAM lp1,
  1207. LPARAM lp2,
  1208. LPARAM lParamSort
  1209. )
  1210. {
  1211. PSHITEMINFO pInfo1 = (PSHITEMINFO)lp1;
  1212. PSHITEMINFO pInfo2 = (PSHITEMINFO)lp2;
  1213. BOOL bEmpty1, bEmpty2;
  1214. int nColSort = (int)LOWORD(lParamSort);
  1215. int nSortOrder = (int)HIWORD(lParamSort);
  1216. int iRet = 0;
  1217. switch(nColSort) {
  1218. case 0: // SORT_APPNAME:
  1219. iRet = _tcsicmp(pInfo1->strDisplayName.c_str(),
  1220. pInfo2->strDisplayName.c_str());
  1221. break;
  1222. case 1: // SORT_APPLOCATION:
  1223. iRet = _tcsicmp(pInfo1->strFolder.c_str(),
  1224. pInfo2->strFolder.c_str());
  1225. break;
  1226. case 2: // SORT_LAYERS:
  1227. bEmpty1 = pInfo1->strKeys.empty();
  1228. bEmpty2 = pInfo2->strKeys.empty();
  1229. if (bEmpty1 || bEmpty2) {
  1230. if (bEmpty1) {
  1231. iRet = bEmpty2 ? 0 : 1;
  1232. } else {
  1233. iRet = bEmpty1 ? 0 : -1;
  1234. }
  1235. } else {
  1236. iRet = _tcsicmp(pInfo1->strKeys.c_str(),
  1237. pInfo2->strKeys.c_str());
  1238. }
  1239. break;
  1240. }
  1241. if (nSortOrder == PROGLIST_SORT_DSC) {
  1242. iRet = -iRet;
  1243. }
  1244. return iRet;
  1245. }
  1246. LRESULT
  1247. CProgramList::LVNotifyColumnClick(
  1248. LPNMHDR pnmhdr,
  1249. BOOL& bHandled
  1250. )
  1251. {
  1252. LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)pnmhdr;
  1253. // lpnmlv->iSubItem - this is what we have to sort on
  1254. // check whether we already have something there
  1255. HWND hwndHeader = ListView_GetHeader(m_hwndListView);
  1256. INT nCols;
  1257. INT i;
  1258. INT nColSort = lpnmlv->iSubItem;
  1259. LPARAM lSortParam; // leave high word blank for now
  1260. LPARAM lSortOrder = PROGLIST_SORT_ASC;
  1261. HDITEM hdi;
  1262. //
  1263. // reset current image - wherever that is
  1264. //
  1265. nCols = Header_GetItemCount(hwndHeader);
  1266. for (i = 0; i < nCols; ++i) {
  1267. hdi.mask = HDI_BITMAP|HDI_LPARAM|HDI_FORMAT;
  1268. if (!Header_GetItem(hwndHeader, i, &hdi)) {
  1269. continue;
  1270. }
  1271. if (i == nColSort && (hdi.mask & HDI_LPARAM)) {
  1272. switch(hdi.lParam) {
  1273. case PROGLIST_SORT_NONE:
  1274. case PROGLIST_SORT_DSC:
  1275. lSortOrder = PROGLIST_SORT_ASC;
  1276. break;
  1277. case PROGLIST_SORT_ASC:
  1278. lSortOrder = PROGLIST_SORT_DSC;
  1279. break;
  1280. }
  1281. }
  1282. if (hdi.mask & HDI_BITMAP) {
  1283. DeleteObject((HGDIOBJ)hdi.hbm);
  1284. }
  1285. hdi.lParam = PROGLIST_SORT_NONE;
  1286. hdi.fmt &= ~(HDF_BITMAP|HDF_BITMAP_ON_RIGHT);
  1287. hdi.mask |= HDI_BITMAP|HDI_LPARAM|HDI_FORMAT;
  1288. hdi.hbm = NULL;
  1289. Header_SetItem(hwndHeader, i, &hdi);
  1290. }
  1291. lSortParam = MAKELONG(nColSort, lSortOrder);
  1292. ListView_SortItems(m_hwndListView, (PFNLVCOMPARE)SHItemInfoCompareFunc, lSortParam);
  1293. // now, load the image please
  1294. m_hbmSort = (HBITMAP)::LoadImage(_Module.GetResourceInstance(),
  1295. MAKEINTRESOURCE(lSortOrder == PROGLIST_SORT_ASC? IDB_SORTUP : IDB_SORTDN),
  1296. IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
  1297. hdi.mask = HDI_BITMAP|HDI_LPARAM|HDI_FORMAT;
  1298. Header_GetItem(hwndHeader, nColSort, &hdi);
  1299. hdi.mask |= HDI_BITMAP|HDI_FORMAT|HDI_LPARAM;
  1300. hdi.hbm = m_hbmSort;
  1301. hdi.fmt |= HDF_BITMAP|HDF_BITMAP_ON_RIGHT;
  1302. hdi.lParam = lSortOrder;
  1303. Header_SetItem(hwndHeader, nColSort, &hdi);
  1304. bHandled = TRUE;
  1305. return 0;
  1306. }
  1307. LRESULT
  1308. CProgramList::LVNotifyDispInfo(
  1309. LPNMHDR pnmhdr,
  1310. BOOL& bHandled
  1311. )
  1312. {
  1313. WCHAR wszPermKeys[MAX_PATH];
  1314. DWORD cbSize;
  1315. LV_ITEM &lvItem = reinterpret_cast<LV_DISPINFO*>(pnmhdr)->item;
  1316. LV_ITEM lvi;
  1317. PSHITEMINFO pInfo;
  1318. lvi.mask = LVIF_PARAM;
  1319. lvi.iItem = lvItem.iItem;
  1320. lvi.iSubItem = 0;
  1321. if (!ListView_GetItem(m_hwndListView, &lvi)) {
  1322. // bummer, we can't retrieve an item -- if we let it go, things will be worse
  1323. lvItem.mask &= ~(LVIF_TEXT|LVIF_IMAGE);
  1324. lvItem.mask |= LVIF_DI_SETITEM;
  1325. bHandled = TRUE;
  1326. return 0;
  1327. }
  1328. pInfo = reinterpret_cast<PSHITEMINFO> (lvi.lParam);
  1329. if (lvItem.mask & LVIF_TEXT) {
  1330. switch (lvItem.iSubItem) {
  1331. case 0:
  1332. lvItem.pszText = (LPTSTR)pInfo->strDisplayName.c_str();
  1333. break;
  1334. case 1:
  1335. lvItem.pszText = (LPTSTR)pInfo->strFolder.c_str();
  1336. break;
  1337. case 2:
  1338. // check with SDB
  1339. cbSize = sizeof(wszPermKeys);
  1340. if (pInfo->strKeys.empty()) {
  1341. if (SdbGetPermLayerKeys(pInfo->strPath.c_str(), wszPermKeys, &cbSize, GPLK_ALL)) {
  1342. pInfo->strKeys = wszPermKeys;
  1343. }
  1344. }
  1345. if (!pInfo->strKeys.empty()) {
  1346. lvItem.pszText = (LPTSTR)pInfo->strKeys.c_str();
  1347. }
  1348. break;
  1349. default:
  1350. break;
  1351. }
  1352. }
  1353. if (lvItem.mask & LVIF_IMAGE) {
  1354. lvItem.iImage = GetIconFromLink(pInfo->pidl, pInfo->strPathExecute.c_str());
  1355. }
  1356. lvItem.mask |= LVIF_DI_SETITEM;
  1357. bHandled = TRUE;
  1358. return 0;
  1359. }
  1360. LRESULT
  1361. CProgramList::LVNotifyGetInfoTip(
  1362. LPNMHDR pnmhdr,
  1363. BOOL& bHandled
  1364. )
  1365. {
  1366. DWORD cbSize;
  1367. LPNMLVGETINFOTIP pGetInfoTip = (LPNMLVGETINFOTIP)pnmhdr;
  1368. LV_ITEM lvi;
  1369. PSHITEMINFO pInfo;
  1370. lvi.mask = LVIF_PARAM;
  1371. lvi.iItem = pGetInfoTip->iItem;
  1372. lvi.iSubItem = 0;
  1373. if (!ListView_GetItem(m_hwndListView, &lvi)) {
  1374. // bupkas
  1375. bHandled = FALSE;
  1376. return 0;
  1377. }
  1378. pInfo = reinterpret_cast<PSHITEMINFO> (lvi.lParam);
  1379. //
  1380. // now we can fiddle
  1381. //
  1382. _tcsncpy(pGetInfoTip->pszText, pInfo->strCmdLine.c_str(), pGetInfoTip->cchTextMax);
  1383. *(pGetInfoTip->pszText + pGetInfoTip->cchTextMax - 1) = TEXT('\0');
  1384. bHandled = TRUE;
  1385. return 0;
  1386. }
  1387. LRESULT
  1388. CProgramList::LVNotifyRClick(
  1389. LPNMHDR pnmhdr,
  1390. BOOL& bHandled
  1391. )
  1392. {
  1393. DWORD dwPos = ::GetMessagePos();
  1394. LVHITTESTINFO hti;
  1395. LV_ITEM lvi;
  1396. PSHITEMINFO pInfo;
  1397. HRESULT hr;
  1398. LPITEMIDLIST pidlItem = NULL;
  1399. IShellFolder* pFolder = NULL;
  1400. IContextMenu* pContextMenu = NULL;
  1401. CMINVOKECOMMANDINFO ici;
  1402. int nCmd;
  1403. HMENU hMenu = NULL;
  1404. UINT idMin, idMax, idCmd;
  1405. WCHAR szCmdVerb[MAX_PATH];
  1406. int nLastSep, i, nLastItem;
  1407. hti.pt.x = (int) LOWORD (dwPos);
  1408. hti.pt.y = (int) HIWORD (dwPos);
  1409. ScreenToClient (m_hwndListView, &hti.pt);
  1410. ListView_HitTest (m_hwndListView, &hti);
  1411. if (!(hti.flags & LVHT_ONITEM)) {
  1412. bHandled = FALSE;
  1413. return 0;
  1414. }
  1415. lvi.mask = LVIF_PARAM;
  1416. lvi.iItem = hti.iItem;
  1417. lvi.iSubItem = 0;
  1418. if (!ListView_GetItem(m_hwndListView, &lvi)) {
  1419. // bupkas
  1420. bHandled = FALSE;
  1421. return 0;
  1422. }
  1423. pInfo = reinterpret_cast<PSHITEMINFO> (lvi.lParam);
  1424. //
  1425. // we have an item, show it's context menu then
  1426. //
  1427. hr = SHBindToParent(pInfo-> pidl, IID_IShellFolder, (PVOID*)&pFolder, (LPCITEMIDLIST*)&pidlItem);
  1428. if (!SUCCEEDED(hr)) {
  1429. goto cleanup;
  1430. }
  1431. // get the ui please
  1432. hr = pFolder->GetUIObjectOf(m_hwndListView, 1, (LPCITEMIDLIST*)&pidlItem, IID_IContextMenu, NULL, (PVOID*)&pContextMenu);
  1433. if (!SUCCEEDED(hr)) {
  1434. goto cleanup;
  1435. }
  1436. hMenu = CreatePopupMenu();
  1437. if (hMenu == NULL) {
  1438. goto cleanup;
  1439. }
  1440. hr = pContextMenu->QueryContextMenu(hMenu,
  1441. 0,
  1442. 1,
  1443. 0x7FFF,
  1444. CMF_EXPLORE);
  1445. if (!SUCCEEDED(hr)) {
  1446. goto cleanup;
  1447. }
  1448. //
  1449. // sanitize
  1450. //
  1451. idMin = 1;
  1452. idMax = HRESULT_CODE(hr);
  1453. for (idCmd = 0; idCmd < idMax; ++idCmd) {
  1454. hr = pContextMenu->GetCommandString(idCmd, GCS_VERBW, NULL, (LPSTR)szCmdVerb, CHARCOUNT(szCmdVerb));
  1455. if (SUCCEEDED(hr)) {
  1456. if (!_wcsicmp(szCmdVerb, TEXT("cut")) ||
  1457. !_wcsicmp(szCmdVerb, TEXT("delete")) ||
  1458. !_wcsicmp(szCmdVerb, TEXT("rename")) ||
  1459. !_wcsicmp(szCmdVerb, TEXT("link"))) {
  1460. //
  1461. // not allowed
  1462. //
  1463. DeleteMenu(hMenu, idCmd + idMin, MF_BYCOMMAND);
  1464. }
  1465. }
  1466. }
  1467. //
  1468. // after doing some basic sanitization against the destructive tendencies --
  1469. // nuke double-separators
  1470. //
  1471. nLastItem = ::GetMenuItemCount(hMenu) - 1;
  1472. nLastSep = nLastItem + 1;
  1473. for (i = nLastItem; i >= 0; --i) {
  1474. MENUITEMINFO mii;
  1475. mii.cbSize = sizeof(mii);
  1476. mii.fMask = MIIM_FTYPE;
  1477. if (GetMenuItemInfo(hMenu, i, TRUE, &mii)) {
  1478. if (mii.fType & MFT_SEPARATOR) {
  1479. if (nLastSep == i + 1 || i == 0) {
  1480. // this sep is dead
  1481. DeleteMenu(hMenu, i, MF_BYPOSITION);
  1482. }
  1483. nLastSep = i;
  1484. }
  1485. }
  1486. }
  1487. ClientToScreen(m_hwndListView, &hti.pt);
  1488. nCmd = TrackPopupMenu(hMenu,
  1489. TPM_LEFTALIGN |
  1490. TPM_LEFTBUTTON |
  1491. TPM_RIGHTBUTTON |
  1492. TPM_RETURNCMD,
  1493. hti.pt.x, hti.pt.y,
  1494. 0,
  1495. m_hwndListView,
  1496. NULL);
  1497. //
  1498. // execute command
  1499. //
  1500. if (nCmd) {
  1501. ici.cbSize = sizeof (CMINVOKECOMMANDINFO);
  1502. ici.fMask = 0;
  1503. ici.hwnd = m_hwndListView;
  1504. ici.lpVerb = MAKEINTRESOURCEA(nCmd - 1);
  1505. ici.lpParameters = NULL;
  1506. ici.lpDirectory = NULL;
  1507. ici.nShow = SW_SHOWNORMAL;
  1508. ici.dwHotKey = 0;
  1509. ici.hIcon = NULL;
  1510. hr = pContextMenu->InvokeCommand(&ici);
  1511. //
  1512. // requery perm layer keys -- useless here btw
  1513. //
  1514. /* // this code will not work since the call above is always asynchronous
  1515. //
  1516. if (SUCCEEDED(hr)) {
  1517. DWORD cbSize;
  1518. WCHAR wszPermKeys[MAX_PATH];
  1519. cbSize = sizeof(wszPermKeys);
  1520. if (SdbGetPermLayerKeys(pInfo->strPath.c_str(), wszPermKeys, &cbSize)) {
  1521. pInfo->strKeys = wszPermKeys;
  1522. } else {
  1523. pInfo->strKeys.erase();
  1524. }
  1525. //
  1526. // set the info into the list box
  1527. //
  1528. ListView_SetItemText(m_hwndListView, lvi.iItem, 2, (LPWSTR)pInfo->strKeys.c_str());
  1529. }
  1530. */
  1531. }
  1532. cleanup:
  1533. if (hMenu) {
  1534. DestroyMenu(hMenu);
  1535. }
  1536. if (pContextMenu) {
  1537. pContextMenu->Release();
  1538. }
  1539. if (pFolder) {
  1540. pFolder->Release();
  1541. }
  1542. bHandled = TRUE;
  1543. return 0;
  1544. }
  1545. BOOL
  1546. CProgramList::UpdateListItem(
  1547. LPCWSTR pwszPath,
  1548. LPCWSTR pwszKey
  1549. )
  1550. {
  1551. // find the item first
  1552. MAPSTR2ITEM::iterator iter;
  1553. MULTIMAPSTR2ITEM::iterator iterExe;
  1554. MULTIMAPSTR2ITEM::iterator iterFirstExe, iterLastExe;
  1555. tstring strKey = pwszPath;
  1556. tstring strExeKey;
  1557. PSHITEMINFO pInfo = NULL;
  1558. PSHITEMINFO pInfoExe = NULL;
  1559. //
  1560. // we need to iterate through all the persisted items
  1561. //
  1562. StrUpCase(strKey);
  1563. iter = m_mapItems.find(strKey);
  1564. if (iter != m_mapItems.end()) {
  1565. pInfo = (*iter).second;
  1566. }
  1567. if (pInfo == NULL) {
  1568. return FALSE;
  1569. }
  1570. //
  1571. // once we have found this single item, get the command and
  1572. // show info for all the other affected items
  1573. //
  1574. strExeKey = pInfo->strPath;
  1575. StrUpCase(strExeKey);
  1576. iterFirstExe = m_mmapExeItems.lower_bound(strExeKey);
  1577. iterLastExe = m_mmapExeItems.upper_bound(strExeKey);
  1578. for (iterExe = iterFirstExe; iterExe != m_mmapExeItems.end() && iterExe != iterLastExe; ++iterExe) {
  1579. pInfoExe = (*iterExe).second;
  1580. // find this item in a listview
  1581. LVFINDINFO lvf;
  1582. INT index;
  1583. lvf.flags = LVFI_PARAM;
  1584. lvf.lParam = (LPARAM)pInfoExe;
  1585. index = ListView_FindItem(m_hwndListView, -1, &lvf);
  1586. if (index < 0) {
  1587. return FALSE; // inconsistent
  1588. }
  1589. // else we have both the item and the keys
  1590. if (pwszKey == NULL) {
  1591. pInfoExe->strKeys.erase();
  1592. } else {
  1593. pInfoExe->strKeys = pwszKey;
  1594. }
  1595. ListView_SetItemText(m_hwndListView, index, 2, (LPWSTR)pInfoExe->strKeys.c_str());
  1596. }
  1597. return TRUE;
  1598. }
  1599. BOOL
  1600. CProgramList::IsEnabled(
  1601. VOID
  1602. )
  1603. {
  1604. if (::IsWindow(m_hwndListView)) {
  1605. return ::IsWindowEnabled(m_hwndListView);
  1606. }
  1607. return FALSE;
  1608. }
  1609. VOID
  1610. CProgramList::Enable(
  1611. BOOL bEnable
  1612. )
  1613. {
  1614. if (::IsWindow(m_hwndListView)) {
  1615. ::EnableWindow(m_hwndListView, bEnable);
  1616. }
  1617. }
  1618. BOOL
  1619. GetProgramListSelection(
  1620. CProgramList* pProgramList
  1621. )
  1622. {
  1623. return pProgramList->CaptureSelection();
  1624. }
  1625. BOOL
  1626. GetProgramListSelectionDetails(
  1627. CProgramList* pProgramList,
  1628. INT iInformationClass,
  1629. LPWSTR pBuffer,
  1630. ULONG Size
  1631. )
  1632. {
  1633. return pProgramList->GetSelectionDetails(iInformationClass, pBuffer, Size);
  1634. }
  1635. LRESULT
  1636. NotifyProgramList(
  1637. CProgramList* pProgramList,
  1638. LPNMHDR pnmhdr,
  1639. BOOL& bHandled
  1640. )
  1641. {
  1642. LRESULT lRet = 0;
  1643. switch (pnmhdr->code) {
  1644. case LVN_GETDISPINFO:
  1645. lRet = pProgramList->LVNotifyDispInfo(pnmhdr, bHandled);
  1646. break;
  1647. case LVN_COLUMNCLICK:
  1648. lRet = pProgramList->LVNotifyColumnClick(pnmhdr, bHandled);
  1649. break;
  1650. case LVN_GETINFOTIP:
  1651. lRet = pProgramList->LVNotifyGetInfoTip(pnmhdr, bHandled);
  1652. break;
  1653. case NM_RCLICK:
  1654. lRet = pProgramList->LVNotifyRClick(pnmhdr, bHandled);
  1655. break;
  1656. default:
  1657. bHandled = FALSE;
  1658. break;
  1659. }
  1660. return lRet;
  1661. }
  1662. BOOL
  1663. GetProgramListEnabled(
  1664. CProgramList* pProgramList
  1665. )
  1666. {
  1667. return pProgramList->IsEnabled();
  1668. }
  1669. VOID
  1670. EnableProgramList(
  1671. CProgramList* pProgramList,
  1672. BOOL bEnable
  1673. )
  1674. {
  1675. pProgramList->Enable(bEnable);
  1676. }
  1677. BOOL
  1678. UpdateProgramListItem(
  1679. CProgramList* pProgramList,
  1680. LPCWSTR pwszPath,
  1681. LPCWSTR pwszKeys
  1682. )
  1683. {
  1684. return pProgramList->UpdateListItem(pwszPath, pwszKeys);
  1685. }
  1686. wstring StrUpCase(wstring& wstr)
  1687. {
  1688. ctype<wchar_t> _ct;
  1689. wstring::iterator iter;
  1690. for (iter = wstr.begin(); iter != wstr.end(); ++iter) {
  1691. (*iter) = _ct.toupper(*iter);
  1692. }
  1693. return wstr;
  1694. }