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

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