mirror of https://github.com/tongzx/nt5src
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
2164 lines
50 KiB
/*++
|
|
|
|
Implements population of a listview control with the content from
|
|
the start menu
|
|
|
|
|
|
--*/
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "resource.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <shlobj.h>
|
|
#include <shlwapi.h>
|
|
#include <shellapi.h>
|
|
#include <commctrl.h>
|
|
#include <msi.h>
|
|
#include <sfc.h>
|
|
#include "CompatUI.h"
|
|
#include "progview.h"
|
|
extern "C" {
|
|
#include "shimdb.h"
|
|
}
|
|
|
|
#pragma warning(disable:4786)
|
|
#include <string>
|
|
#include <xstring>
|
|
#include <map>
|
|
#include <algorithm>
|
|
|
|
using namespace std;
|
|
|
|
#ifdef _UNICODE
|
|
typedef wstring tstring;
|
|
#else
|
|
typedef string tstring;
|
|
#endif
|
|
|
|
typedef
|
|
INSTALLSTATE (WINAPI*PMsiGetComponentPath)(
|
|
LPCTSTR szProduct, // product code for client product
|
|
LPCTSTR szComponent, // component ID
|
|
LPTSTR lpPathBuf, // returned path
|
|
DWORD *pcchBuf // buffer character count
|
|
);
|
|
|
|
typedef
|
|
UINT (WINAPI* PMsiGetShortcutTarget)(
|
|
LPCTSTR szShortcutTarget, // path to shortcut link file
|
|
LPTSTR szProductCode, // fixed length buffer for product code
|
|
LPTSTR szFeatureId, // fixed length buffer for feature id
|
|
LPTSTR szComponentCode // fixed length buffer for component code
|
|
);
|
|
|
|
|
|
typedef enum tagPROGRAMINFOCLASS {
|
|
PROGLIST_DISPLAYNAME,
|
|
PROGLIST_LOCATION, //
|
|
PROGLIST_EXENAME, // cracked exe name
|
|
PROGLIST_CMDLINE, // complete exe name + parameters
|
|
PROGLIST_EXECUTABLE, // what we should execute (link or exe, not cracked)
|
|
PROGLIST_ARGUMENTS // just the args
|
|
};
|
|
|
|
|
|
|
|
|
|
class CException {
|
|
public:
|
|
CException(LPCSTR lpszFile = NULL, DWORD nLocation = 0) {
|
|
SetLocation(lpszFile, nLocation);
|
|
}
|
|
virtual ~CException() {}
|
|
|
|
virtual VOID Delete() {
|
|
delete this;
|
|
}
|
|
|
|
int __cdecl FormatV(LPCTSTR lpszFormat, va_list arg) {
|
|
int nch = 0;
|
|
|
|
if (lpszFormat) {
|
|
nch = _vsntprintf(szDescription, CHARCOUNT(szDescription), lpszFormat, arg);
|
|
} else {
|
|
*szDescription = TEXT('\0');
|
|
}
|
|
return nch;
|
|
}
|
|
|
|
int __cdecl Format(LPCTSTR lpszFormat, ...) {
|
|
va_list arg;
|
|
int nch = 0;
|
|
|
|
if (lpszFormat) {
|
|
va_start(arg, lpszFormat);
|
|
nch = _vsntprintf(szDescription, CHARCOUNT(szDescription), lpszFormat, arg);
|
|
va_end(arg);
|
|
} else {
|
|
*szDescription = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
VOID SetLocation(LPCSTR lpszFile, DWORD nLocation) {
|
|
if (lpszFile) {
|
|
strcpy(szLocation, lpszFile);
|
|
} else {
|
|
*szLocation = TEXT('\0');
|
|
}
|
|
m_dwLocation = nLocation;
|
|
}
|
|
|
|
TCHAR szDescription[MAX_PATH];
|
|
CHAR szLocation[MAX_PATH];
|
|
DWORD m_dwLocation;
|
|
};
|
|
|
|
class CMemoryException : public CException {
|
|
public:
|
|
CMemoryException(LPCSTR lpszFile = NULL, DWORD nLocation = 0) :
|
|
CException(lpszFile, nLocation) {}
|
|
VOID Delete() {}
|
|
};
|
|
|
|
class CCancelException : public CException {
|
|
public:
|
|
CCancelException(LPCSTR lpszFile = NULL, DWORD nLocation = 0) :
|
|
CException(lpszFile, nLocation){}
|
|
};
|
|
|
|
static CMemoryException _MemoryExceptionStatic;
|
|
|
|
VOID __cdecl ThrowMemoryException(LPCSTR lpszFile, DWORD nLocation, LPCTSTR lpszFormat = NULL, ...) {
|
|
va_list arg;
|
|
CMemoryException* pMemoryException = &_MemoryExceptionStatic;
|
|
|
|
va_start(arg, lpszFormat);
|
|
pMemoryException->FormatV(lpszFormat, arg);
|
|
va_end(arg);
|
|
|
|
throw pMemoryException;
|
|
}
|
|
|
|
class CProgramList {
|
|
public:
|
|
CProgramList(LPMALLOC pMalloc, HWND hwndListView, LPCTSTR szSystemDirectory) :
|
|
m_pMalloc(pMalloc),
|
|
m_hwndListView(hwndListView),
|
|
m_hMSI(NULL),
|
|
m_pSelectionInfo(NULL),
|
|
m_hbmSort(NULL),
|
|
m_pProgView(NULL),
|
|
m_hEventCancel(NULL) {
|
|
//
|
|
// we are always initializing on populate thread
|
|
//
|
|
m_dwOwnerThreadID = GetCurrentThreadId();
|
|
m_strSystemDirectory = szSystemDirectory;
|
|
}
|
|
|
|
~CProgramList();
|
|
|
|
BOOL PopulateControl(CProgView* pProgView = NULL, HANDLE hEventCancel = NULL);
|
|
|
|
LPMALLOC GetMalloc(VOID) {
|
|
return GetCurrentThreadId() == m_dwOwnerThreadID ? m_pMalloc : m_pMallocUI;
|
|
}
|
|
|
|
BOOL CaptureSelection();
|
|
|
|
BOOL GetSelectionDetails(INT iInformationClass, VARIANT* pVal);
|
|
|
|
LRESULT LVNotifyDispInfo (LPNMHDR pnmhdr, BOOL& bHandled);
|
|
LRESULT LVNotifyColumnClick(LPNMHDR pnmhdr, BOOL& bHandled);
|
|
LRESULT LVNotifyGetInfoTip (LPNMHDR pnmhdr, BOOL& bHandled);
|
|
LRESULT LVNotifyRClick (LPNMHDR pnmhdr, BOOL& bHandled);
|
|
BOOL IsEnabled(VOID);
|
|
|
|
VOID Enable(BOOL);
|
|
|
|
BOOL UpdateListItem(LPCWSTR pwszPath, LPCWSTR pwszKey);
|
|
|
|
protected:
|
|
BOOL ListFolder(LPCTSTR pszLocationParent, IShellFolder* pFolder, LPCITEMIDLIST pidlFull, LPCITEMIDLIST pidlFolder);
|
|
BOOL ListLink(LPCTSTR pszLocationParent, LPCTSTR pszDisplayName, IShellFolder* pFolder, LPCITEMIDLIST pidlFull, LPCITEMIDLIST pidlLink);
|
|
BOOL ListMsiLink(LPCTSTR pszLocationParent, LPCTSTR pszDisplayName, LPCTSTR pszMsiPath, IShellFolder* pFolder, LPCITEMIDLIST pidlFull);
|
|
|
|
LPITEMIDLIST GetNextItemIDL(LPCITEMIDLIST pidl);
|
|
UINT GetSizeIDL (LPCITEMIDLIST pidl);
|
|
LPITEMIDLIST AppendIDL (LPCITEMIDLIST pidlBase,
|
|
LPCITEMIDLIST pidlAdd);
|
|
LPITEMIDLIST GetLastItemIDL(LPCITEMIDLIST pidl);
|
|
|
|
BOOL GetDisplayName(IShellFolder* pFolder, LPCITEMIDLIST pidl, tstring& strDisplay);
|
|
BOOL GetPathFromLink(IShellLink* pLink, WIN32_FIND_DATA* pfd, tstring& strPath);
|
|
BOOL GetArgumentsFromLink(IShellLink* pLink, tstring& strArgs);
|
|
|
|
BOOL AddItem(LPCTSTR pszLocation,
|
|
LPCTSTR pszDisplayName,
|
|
LPCTSTR pszPath,
|
|
LPCTSTR pszArguments,
|
|
IShellFolder* pFolder,
|
|
LPCITEMIDLIST pidlFull,
|
|
BOOL bUsePath = FALSE); // true if we should use path for executable
|
|
|
|
|
|
int GetIconFromLink(LPCITEMIDLIST pidlLinkFull, LPCTSTR lpszExePath);
|
|
|
|
BOOL IsSFCItem(LPCTSTR lpszItem);
|
|
BOOL IsItemInSystemDirectory(LPCTSTR pszPath);
|
|
|
|
private:
|
|
LPMALLOC m_pMalloc;
|
|
LPMALLOC m_pMallocUI;
|
|
HWND m_hwndListView; // list view control
|
|
HBITMAP m_hbmSort;
|
|
typedef struct tagSHITEMINFO {
|
|
|
|
tstring strDisplayName; // descriptive name
|
|
tstring strFolder; // containing folder
|
|
tstring strPath; // actual exe, cracked
|
|
tstring strPathExecute; // link path (this is what we will execute)
|
|
tstring strCmdLine; // command line (cracked link)
|
|
tstring strArgs;
|
|
tstring strKeys;
|
|
LPITEMIDLIST pidl; // full pidl
|
|
} SHITEMINFO, *PSHITEMINFO;
|
|
static CALLBACK SHItemInfoCompareFunc(LPARAM lp1, LPARAM lp2, LPARAM lParamSort);
|
|
|
|
typedef map< tstring, PSHITEMINFO, less<tstring> > MAPSTR2ITEM;
|
|
typedef multimap< tstring, PSHITEMINFO > MULTIMAPSTR2ITEM;
|
|
|
|
//
|
|
// store key->item sequence, the keys are cmdlines (with args)
|
|
//
|
|
MAPSTR2ITEM m_mapItems;
|
|
|
|
//
|
|
// store key->item sequence, where the key is exe name (path)
|
|
//
|
|
MULTIMAPSTR2ITEM m_mmapExeItems;
|
|
|
|
//
|
|
// selected item
|
|
//
|
|
|
|
PSHITEMINFO m_pSelectionInfo;
|
|
|
|
//
|
|
// cached msi.dll handle
|
|
//
|
|
HMODULE m_hMSI;
|
|
|
|
|
|
PMsiGetComponentPath m_pfnGetComponentPath;
|
|
PMsiGetShortcutTarget m_pfnGetShortcutTarget;
|
|
|
|
//
|
|
// cached system directory
|
|
//
|
|
|
|
tstring m_strSystemDirectory;
|
|
|
|
//
|
|
// image list used to show icons
|
|
//
|
|
|
|
HIMAGELIST m_hImageList;
|
|
|
|
//
|
|
// optional pointer to the parent view
|
|
//
|
|
CProgView* m_pProgView;
|
|
|
|
|
|
//
|
|
// event that we use to signal the end of scan
|
|
//
|
|
HANDLE m_hEventCancel;
|
|
|
|
|
|
//
|
|
// owner thread
|
|
//
|
|
DWORD m_dwOwnerThreadID;
|
|
|
|
VOID CheckForCancel() {
|
|
if (m_hEventCancel) {
|
|
if (::WaitForSingleObject(m_hEventCancel, 0) != WAIT_TIMEOUT) {
|
|
// cancelled!!!
|
|
throw new CCancelException();
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
//
|
|
// in upload.cpp
|
|
//
|
|
|
|
wstring StrUpCase(wstring& wstr);
|
|
|
|
//
|
|
// load the string from resources
|
|
//
|
|
wstring LoadResourceString(UINT nID)
|
|
{
|
|
LPTSTR lpszBuffer = NULL;
|
|
int cch;
|
|
wstring str;
|
|
|
|
cch = ::LoadString(_Module.GetModuleInstance(), nID, (LPTSTR)&lpszBuffer, 0);
|
|
//
|
|
// hack! this must work (I know it does)
|
|
//
|
|
if (cch && NULL != lpszBuffer) {
|
|
str = wstring(lpszBuffer, cch);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Utility functions
|
|
//
|
|
|
|
|
|
BOOL
|
|
InitializeProgramList(
|
|
CProgramList** ppProgramList,
|
|
HWND hwndListView
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
BOOL bSuccess = FALSE;
|
|
LPMALLOC pMalloc = NULL;
|
|
TCHAR szSystemWindowsDirectory[MAX_PATH];
|
|
CProgramList* pProgramList = NULL;
|
|
UINT uSize;
|
|
|
|
hr = SHGetMalloc(&pMalloc);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto ErrHandle;
|
|
}
|
|
|
|
|
|
uSize = ::GetSystemWindowsDirectory(szSystemWindowsDirectory,
|
|
CHARCOUNT(szSystemWindowsDirectory));
|
|
if (uSize == 0 || uSize > CHARCOUNT(szSystemWindowsDirectory)) {
|
|
goto ErrHandle;
|
|
}
|
|
|
|
pProgramList = new CProgramList(pMalloc, hwndListView, szSystemWindowsDirectory);
|
|
if (NULL == pProgramList) {
|
|
goto ErrHandle;
|
|
}
|
|
|
|
|
|
*ppProgramList = pProgramList;
|
|
bSuccess = TRUE;
|
|
|
|
ErrHandle:
|
|
|
|
if (!bSuccess) {
|
|
|
|
if (NULL != pMalloc) {
|
|
pMalloc->Release();
|
|
}
|
|
|
|
if (NULL != pProgramList) {
|
|
delete pProgramList;
|
|
}
|
|
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CleanupProgramList(
|
|
CProgramList* pProgramList
|
|
)
|
|
{
|
|
LPMALLOC pMalloc;
|
|
|
|
if (NULL == pProgramList) {
|
|
return FALSE;
|
|
}
|
|
|
|
pMalloc = pProgramList->GetMalloc();
|
|
|
|
delete pProgramList;
|
|
|
|
if (NULL != pMalloc) {
|
|
pMalloc->Release();
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
PopulateProgramList(
|
|
CProgramList* pProgramList,
|
|
CProgView* pProgView,
|
|
HANDLE hEventCancel
|
|
)
|
|
{
|
|
return pProgramList->PopulateControl(pProgView, hEventCancel);
|
|
}
|
|
|
|
|
|
CProgramList::~CProgramList()
|
|
{
|
|
//
|
|
//
|
|
//
|
|
MAPSTR2ITEM::iterator iter;
|
|
|
|
iter = m_mapItems.begin();
|
|
while (iter != m_mapItems.end()) {
|
|
PSHITEMINFO pInfo = (*iter).second;
|
|
|
|
GetMalloc()->Free(pInfo->pidl); // nuke this please
|
|
delete pInfo;
|
|
|
|
++iter;
|
|
}
|
|
|
|
if (NULL != m_hbmSort) {
|
|
DeleteObject(m_hbmSort);
|
|
}
|
|
|
|
|
|
// Image list is destroyed automatically when the control is destroyed
|
|
//
|
|
// if (NULL != m_hImageList) {
|
|
// ImageList_Destroy(m_hImageList);
|
|
// }
|
|
|
|
if (NULL != m_hMSI) {
|
|
FreeLibrary(m_hMSI);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CProgramList::GetDisplayName(
|
|
IShellFolder* pFolder,
|
|
LPCITEMIDLIST pidl,
|
|
tstring& strDisplayName
|
|
)
|
|
{
|
|
STRRET strName;
|
|
HRESULT hr;
|
|
LPTSTR pszName = NULL;
|
|
|
|
|
|
hr = pFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &strName);
|
|
if (!SUCCEEDED(hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
hr = StrRetToStr(&strName, pidl, &pszName);
|
|
if (!SUCCEEDED(hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
// if we have been successful, assign return result
|
|
if (pszName != NULL) {
|
|
strDisplayName = pszName;
|
|
CoTaskMemFree(pszName);
|
|
} else {
|
|
strDisplayName.erase();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CProgramList::GetPathFromLink(
|
|
IShellLink* pLink,
|
|
WIN32_FIND_DATA* pfd,
|
|
tstring& strPath
|
|
)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
HRESULT hr;
|
|
|
|
hr = pLink->GetPath(szPath, sizeof(szPath)/sizeof(szPath[0]), pfd, 0);
|
|
if (hr == S_OK) {
|
|
strPath = szPath;
|
|
}
|
|
|
|
return hr == S_OK;
|
|
}
|
|
|
|
BOOL
|
|
CProgramList::GetArgumentsFromLink(
|
|
IShellLink* pLink,
|
|
tstring& strArgs
|
|
)
|
|
{
|
|
TCHAR szArgs[INFOTIPSIZE];
|
|
|
|
HRESULT hr = pLink->GetArguments(szArgs, sizeof(szArgs)/sizeof(szArgs[0]));
|
|
if (SUCCEEDED(hr)) {
|
|
strArgs = szArgs;
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
|
|
}
|
|
|
|
|
|
|
|
LPITEMIDLIST
|
|
CProgramList::GetNextItemIDL(
|
|
LPCITEMIDLIST pidl
|
|
)
|
|
{
|
|
// Check for valid pidl.
|
|
if (pidl == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// Get the size of the specified item identifier.
|
|
int cb = pidl->mkid.cb;
|
|
|
|
// If the size is zero, it is the end of the list.
|
|
if (cb == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
// Add cb to pidl (casting to increment by bytes).
|
|
pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb);
|
|
|
|
// Return NULL if it is null-terminating, or a pidl otherwise.
|
|
return (pidl->mkid.cb == 0) ? NULL : (LPITEMIDLIST) pidl;
|
|
}
|
|
|
|
LPITEMIDLIST
|
|
CProgramList::GetLastItemIDL(
|
|
LPCITEMIDLIST pidl
|
|
)
|
|
{
|
|
LPITEMIDLIST pidlLast = (LPITEMIDLIST)pidl;
|
|
|
|
if (pidl == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
int cb = pidl->mkid.cb;
|
|
if (cb == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
pidl = GetNextItemIDL(pidlLast);
|
|
if (pidl != NULL) {
|
|
pidlLast = (LPITEMIDLIST)pidl;
|
|
}
|
|
} while (pidl != NULL);
|
|
|
|
return pidlLast;
|
|
}
|
|
|
|
|
|
UINT
|
|
CProgramList::GetSizeIDL(
|
|
LPCITEMIDLIST pidl
|
|
)
|
|
{
|
|
UINT cbTotal = 0;
|
|
if (pidl)
|
|
{
|
|
cbTotal += sizeof(pidl->mkid.cb); // Null terminator
|
|
while (NULL != pidl)
|
|
{
|
|
cbTotal += pidl->mkid.cb;
|
|
pidl = GetNextItemIDL(pidl);
|
|
}
|
|
}
|
|
return cbTotal;
|
|
}
|
|
|
|
LPITEMIDLIST
|
|
CProgramList::AppendIDL(
|
|
LPCITEMIDLIST pidlBase,
|
|
LPCITEMIDLIST pidlAdd
|
|
)
|
|
{
|
|
if (NULL == pidlBase && NULL == pidlAdd) {
|
|
return NULL;
|
|
}
|
|
|
|
LPITEMIDLIST pidlNew, pidlAlloc;
|
|
|
|
UINT cb1 = pidlBase ? GetSizeIDL(pidlBase) : 0;
|
|
UINT cb2 = pidlAdd ? GetSizeIDL(pidlAdd) : 0;
|
|
|
|
UINT size = cb1 + cb2;
|
|
pidlAlloc =
|
|
pidlNew = (LPITEMIDLIST)GetMalloc()->Alloc(size);
|
|
if (pidlNew)
|
|
{
|
|
if (NULL != pidlBase) {
|
|
cb1 = pidlAdd ? cb1 - sizeof(pidlBase->mkid.cb) : cb1;
|
|
RtlMoveMemory(pidlNew, pidlBase, cb1);
|
|
pidlNew = (LPITEMIDLIST)((PBYTE)pidlNew + cb1);
|
|
}
|
|
|
|
if (NULL != pidlAdd) {
|
|
RtlMoveMemory(pidlNew, pidlAdd, cb2);
|
|
}
|
|
}
|
|
|
|
return pidlAlloc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CProgramList::ListMsiLink(
|
|
LPCTSTR pszLocationParent,
|
|
LPCTSTR pszDisplayName,
|
|
LPCTSTR pszMsiPath,
|
|
IShellFolder* pFolder,
|
|
LPCITEMIDLIST pidlFull
|
|
)
|
|
{
|
|
//
|
|
// make sure we have msi module handle
|
|
//
|
|
|
|
if (NULL == m_hMSI) {
|
|
m_hMSI = LoadLibrary(TEXT("msi.dll"));
|
|
if (NULL == m_hMSI) {
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
m_pfnGetComponentPath = (PMsiGetComponentPath )GetProcAddress(m_hMSI, "MsiGetComponentPathW");
|
|
m_pfnGetShortcutTarget = (PMsiGetShortcutTarget)GetProcAddress(m_hMSI, "MsiGetShortcutTargetW");
|
|
|
|
#else
|
|
m_pfnGetComponentPath = (PMsiGetComponentPath )GetProcAddress(m_hMSI, "MsiGetComponentPathA");
|
|
m_pfnGetShortcutTarget = (PMsiGetShortcutTarget)GetProcAddress(m_hMSI, "MsiGetShortcutTargetA");
|
|
#endif
|
|
|
|
}
|
|
|
|
UINT ErrCode;
|
|
TCHAR szProduct[MAX_PATH];
|
|
TCHAR szFeatureId[MAX_PATH];
|
|
TCHAR szComponentCode[MAX_PATH];
|
|
|
|
ErrCode = m_pfnGetShortcutTarget(pszMsiPath, szProduct, szFeatureId, szComponentCode);
|
|
if (ERROR_SUCCESS != ErrCode) {
|
|
return FALSE;
|
|
}
|
|
|
|
INSTALLSTATE is;
|
|
TCHAR szPath[MAX_PATH];
|
|
DWORD cchPath = sizeof(szPath)/sizeof(szPath[0]);
|
|
*szPath = 0;
|
|
|
|
is = m_pfnGetComponentPath(szProduct, szComponentCode, szPath, &cchPath);
|
|
if (INSTALLSTATE_LOCAL == is) {
|
|
//
|
|
// add this item
|
|
//
|
|
return AddItem(pszLocationParent,
|
|
pszDisplayName,
|
|
szPath,
|
|
NULL,
|
|
pFolder,
|
|
pidlFull,
|
|
TRUE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
CProgramList::GetIconFromLink(
|
|
LPCITEMIDLIST pidlLinkFull,
|
|
LPCTSTR lpszExePath
|
|
)
|
|
{
|
|
|
|
HRESULT hr;
|
|
IShellFolder* pFolder = NULL;
|
|
IExtractIcon* pExtractIcon = NULL;
|
|
INT iIconIndex = 0;
|
|
UINT uFlags = 0;
|
|
LPCITEMIDLIST pidlLink = 0;
|
|
HICON hIconLarge = NULL;
|
|
HICON hIconSmall = NULL;
|
|
UINT nIconSize;
|
|
int ImageIndex = -1;
|
|
UINT uiErrorMode;
|
|
DWORD dwAttributes;
|
|
|
|
TCHAR szIconFile[MAX_PATH];
|
|
*szIconFile = TEXT('\0');
|
|
|
|
uiErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
hr = SHBindToParent(pidlLinkFull, IID_IShellFolder, (PVOID*)&pFolder, &pidlLink);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto trySysImage;
|
|
}
|
|
|
|
// get the ui please
|
|
hr = pFolder->GetUIObjectOf(m_hwndListView, 1, (LPCITEMIDLIST*)&pidlLink, IID_IExtractIcon, NULL, (PVOID*)&pExtractIcon);
|
|
|
|
if (!SUCCEEDED(hr)) {
|
|
goto trySysImage;
|
|
}
|
|
|
|
|
|
hr = pExtractIcon->GetIconLocation(0,
|
|
szIconFile,
|
|
sizeof(szIconFile) / sizeof(szIconFile[0]),
|
|
&iIconIndex,
|
|
&uFlags);
|
|
|
|
if (!SUCCEEDED(hr)) {
|
|
goto trySysImage;
|
|
}
|
|
|
|
if (*szIconFile == TEXT('*')) { // this is batch or some such, don't bother
|
|
goto trySysImage;
|
|
}
|
|
|
|
//
|
|
// before doing an extract, check whether it's available
|
|
//
|
|
|
|
dwAttributes = GetFileAttributes(szIconFile);
|
|
|
|
|
|
if (dwAttributes == (DWORD)-1) {
|
|
goto trySysImage;
|
|
}
|
|
|
|
|
|
nIconSize = MAKELONG(0, ::GetSystemMetrics(SM_CXSMICON));
|
|
|
|
//
|
|
// this call is likely to produce a popup, beware of that
|
|
//
|
|
hr = pExtractIcon->Extract(szIconFile,
|
|
iIconIndex,
|
|
&hIconLarge,
|
|
&hIconSmall,
|
|
nIconSize);
|
|
|
|
//
|
|
// if hIconSmall was retrieved - we were successful
|
|
//
|
|
|
|
trySysImage:
|
|
|
|
if (hIconSmall == NULL) {
|
|
//
|
|
// woops -- we could not extract an icon -- what a bummer
|
|
// use shell api then
|
|
SHFILEINFO FileInfo;
|
|
HIMAGELIST hImageSys;
|
|
|
|
hImageSys = (HIMAGELIST)SHGetFileInfo(lpszExePath,
|
|
0,
|
|
&FileInfo, sizeof(FileInfo),
|
|
SHGFI_ICON|SHGFI_SMALLICON|SHGFI_SYSICONINDEX);
|
|
if (hImageSys) {
|
|
hIconSmall = ImageList_GetIcon(hImageSys, FileInfo.iIcon, ILD_TRANSPARENT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// now that we have an icon, we can add it to our image list ?
|
|
//
|
|
if (hIconSmall != NULL) {
|
|
ImageIndex = ImageList_AddIcon(m_hImageList, hIconSmall);
|
|
}
|
|
|
|
///////////////////////// cleanup ///////////////////////////////////////////
|
|
SetErrorMode(uiErrorMode);
|
|
|
|
if (hIconSmall) {
|
|
DestroyIcon(hIconSmall);
|
|
}
|
|
|
|
if (hIconLarge) {
|
|
DestroyIcon(hIconLarge);
|
|
}
|
|
|
|
if (pExtractIcon != NULL) {
|
|
pExtractIcon->Release();
|
|
}
|
|
if (pFolder != NULL) {
|
|
pFolder->Release();
|
|
}
|
|
|
|
|
|
return ImageIndex;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CProgramList::ListLink(
|
|
LPCTSTR pszLocationParent,
|
|
LPCTSTR pszDisplayName,
|
|
IShellFolder* pFolder,
|
|
LPCITEMIDLIST pidlFull,
|
|
LPCITEMIDLIST pidlLink
|
|
)
|
|
{
|
|
IShellLink* psl = NULL;
|
|
WIN32_FIND_DATA wfd;
|
|
HRESULT hr;
|
|
BOOL bSuccess = FALSE;
|
|
tstring strPath;
|
|
tstring strArgs;
|
|
CComBSTR bstr;
|
|
LPCTSTR pszArgs = NULL;
|
|
|
|
IPersistFile* ipf = NULL;
|
|
IShellLinkDataList* pdl;
|
|
DWORD dwFlags;
|
|
BOOL bMsiLink = FALSE;
|
|
|
|
//
|
|
// check whether we need to cancel
|
|
//
|
|
|
|
CheckForCancel();
|
|
|
|
hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IShellLink, (LPVOID*)&psl);
|
|
if (!SUCCEEDED(hr)) {
|
|
return FALSE; // we can't create link object
|
|
}
|
|
|
|
hr = psl->SetIDList(pidlFull); // set the id list
|
|
if (!SUCCEEDED(hr)) {
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// now the shell link is ready to rumble
|
|
//
|
|
if (!GetPathFromLink(psl, &wfd, strPath)) {
|
|
goto out;
|
|
}
|
|
|
|
|
|
// now let's see what is inside of this link -- shall we?
|
|
|
|
|
|
hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ipf);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto out;
|
|
}
|
|
|
|
bstr = strPath.c_str();
|
|
|
|
hr = ipf->Load(bstr, STGM_READ);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// resolve the link for now
|
|
//
|
|
// hr = psl->Resolve(NULL, SLR_NO_UI|SLR_NOUPDATE);
|
|
|
|
|
|
hr = psl->QueryInterface(IID_IShellLinkDataList, (LPVOID*)&pdl);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pdl->GetFlags(&dwFlags);
|
|
|
|
bMsiLink = SUCCEEDED(hr) && (dwFlags & SLDF_HAS_DARWINID);
|
|
|
|
pdl->Release();
|
|
}
|
|
|
|
if (bMsiLink) {
|
|
|
|
bSuccess = ListMsiLink(pszLocationParent, pszDisplayName, strPath.c_str(), pFolder, pidlFull);
|
|
|
|
} else {
|
|
|
|
//
|
|
// we now get the path from the link -- and that's that
|
|
//
|
|
if (GetPathFromLink(psl, &wfd, strPath)) {
|
|
|
|
if (GetArgumentsFromLink(psl, strArgs)) {
|
|
pszArgs = strArgs.c_str();
|
|
}
|
|
|
|
//
|
|
// add this to our list view
|
|
//
|
|
|
|
bSuccess = AddItem(pszLocationParent,
|
|
pszDisplayName,
|
|
strPath.c_str(),
|
|
pszArgs,
|
|
pFolder,
|
|
pidlFull);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (NULL != ipf) {
|
|
ipf->Release();
|
|
}
|
|
|
|
|
|
out:
|
|
if (NULL != psl) {
|
|
psl->Release();
|
|
}
|
|
|
|
return bSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CProgramList::ListFolder(
|
|
LPCTSTR pszLocation, // ui string - where is this folder located?
|
|
IShellFolder* pParent, // parent folder
|
|
LPCITEMIDLIST pidlFull, // idl of the full path to the folder
|
|
LPCITEMIDLIST pidlFolder // idl of this folder relative to the pidlFull
|
|
)
|
|
{
|
|
LPENUMIDLIST penum = NULL;
|
|
LPITEMIDLIST pidl = NULL;
|
|
HRESULT hr;
|
|
|
|
ULONG celtFetched;
|
|
ULONG uAttr;
|
|
tstring strDisplayNameLocation;
|
|
tstring strDisplayName;
|
|
|
|
IShellFolder* pFolder = NULL;
|
|
BOOL bDesktop = FALSE;
|
|
|
|
BOOL bCancel = FALSE;
|
|
CCancelException* pCancelException = NULL;
|
|
|
|
CheckForCancel();
|
|
|
|
if (pParent == NULL) {
|
|
hr = SHGetDesktopFolder(&pParent);
|
|
bDesktop = TRUE;
|
|
}
|
|
|
|
hr = pParent->BindToObject(pidlFolder,
|
|
NULL,
|
|
IID_IShellFolder,
|
|
(LPVOID *) &pFolder);
|
|
|
|
if (NULL == pszLocation) {
|
|
GetDisplayName(pParent, pidlFolder, strDisplayNameLocation);
|
|
} else {
|
|
strDisplayNameLocation = pszLocation;
|
|
}
|
|
|
|
if (bDesktop) {
|
|
pParent->Release();
|
|
}
|
|
|
|
if (!SUCCEEDED(hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
hr = pFolder->EnumObjects(NULL,SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penum);
|
|
if (!SUCCEEDED(hr)) {
|
|
pFolder->Release(); // free the folder- - and go away
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
while( (hr = penum->Next(1,&pidl, &celtFetched)) == S_OK && celtFetched == 1 && !bCancel) {
|
|
LPITEMIDLIST pidlCur;
|
|
|
|
if (pidlFull == NULL) {
|
|
pidlFull = pidlFolder;
|
|
}
|
|
|
|
pidlCur = AppendIDL(pidlFull, pidl);
|
|
|
|
// get the display name of this item
|
|
GetDisplayName(pFolder, pidl, strDisplayName);
|
|
|
|
|
|
uAttr = SFGAO_FOLDER | SFGAO_LINK;
|
|
hr = pFolder->GetAttributesOf(1, (LPCITEMIDLIST *) &pidl, &uAttr);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
try {
|
|
|
|
if (uAttr & SFGAO_FOLDER) {
|
|
//
|
|
// dump folder recursively
|
|
//
|
|
ListFolder(strDisplayName.c_str(), pFolder, pidlCur, pidl);
|
|
|
|
} else if (uAttr & SFGAO_LINK) {
|
|
|
|
ListLink(strDisplayNameLocation.c_str(), strDisplayName.c_str(), pFolder, pidlCur, pidl);
|
|
|
|
} else if (uAttr & SFGAO_FILESYSTEM) {
|
|
//
|
|
// this item is a file
|
|
//
|
|
AddItem(strDisplayNameLocation.c_str(),
|
|
strDisplayName.c_str(),
|
|
NULL,
|
|
NULL,
|
|
pFolder,
|
|
pidlCur,
|
|
TRUE);
|
|
|
|
}
|
|
|
|
} catch(CCancelException* pex) {
|
|
//
|
|
// we need to cancel -- we shall cleanup and do what we need, then re-throw
|
|
//
|
|
bCancel = TRUE;
|
|
pCancelException = pex;
|
|
}
|
|
|
|
}
|
|
GetMalloc()->Free(pidlCur);
|
|
GetMalloc()->Free(pidl);
|
|
|
|
}
|
|
|
|
if (NULL != penum) {
|
|
penum->Release();
|
|
}
|
|
|
|
if (NULL != pFolder) {
|
|
pFolder->Release();
|
|
}
|
|
|
|
if (bCancel && pCancelException) {
|
|
throw pCancelException;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CProgramList::IsSFCItem(
|
|
LPCTSTR pszPath
|
|
)
|
|
{
|
|
|
|
#ifndef _UNICODE
|
|
WCHAR wszBuffer[1024];
|
|
|
|
mbstowcs(wszBuffer, pszPath, sizeof(wszBuffer)/sizeof(wszBuffer[0]));
|
|
return SfcIsFileProtected(NULL, wszBuffer);
|
|
#else
|
|
|
|
return SfcIsFileProtected(NULL, pszPath);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
BOOL
|
|
CProgramList::IsItemInSystemDirectory(
|
|
LPCTSTR pszPath
|
|
)
|
|
{
|
|
TCHAR szCommonPath[MAX_PATH];
|
|
int nch;
|
|
string s;
|
|
|
|
nch = PathCommonPrefix(m_strSystemDirectory.c_str(), pszPath, szCommonPath);
|
|
return nch == m_strSystemDirectory.length();
|
|
}
|
|
|
|
BOOL
|
|
ValidateExecutableFile(
|
|
LPCTSTR pszPath,
|
|
BOOL bValidateFileExists
|
|
)
|
|
{
|
|
LPTSTR rgExt[] = {
|
|
TEXT("EXE"),
|
|
TEXT("BAT"),
|
|
TEXT("CMD"),
|
|
TEXT("PIF"),
|
|
TEXT("COM"),
|
|
TEXT("LNK")
|
|
};
|
|
LPTSTR pExt;
|
|
int i;
|
|
BOOL bValidatedExt = FALSE;
|
|
|
|
pExt = PathFindExtension(pszPath);
|
|
if (pExt == NULL || *pExt == TEXT('\0')) {
|
|
return FALSE;
|
|
}
|
|
++pExt;
|
|
|
|
for (i = 0; i < sizeof(rgExt)/sizeof(rgExt[0]) && !bValidatedExt; ++i) {
|
|
bValidatedExt = !_tcsicmp(pExt, rgExt[i]);
|
|
}
|
|
|
|
if (!bValidatedExt) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
return bValidateFileExists ? PathFileExists(pszPath) : TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CProgramList::AddItem(
|
|
LPCTSTR pszLocation,
|
|
LPCTSTR pszDisplayName,
|
|
LPCTSTR pszPath,
|
|
LPCTSTR pszArguments,
|
|
IShellFolder* pFolder,
|
|
LPCITEMIDLIST pidlFull,
|
|
BOOL bUsePath
|
|
)
|
|
{
|
|
//
|
|
// first test -- is this one of the types we like?
|
|
//
|
|
LPTSTR pchSlash;
|
|
LPTSTR pchDot;
|
|
LPTSTR rgExt[] = { TEXT("EXE"), TEXT("BAT"), TEXT("CMD"), TEXT("PIF"), TEXT("COM"), TEXT("LNK") };
|
|
BOOL bValidatedExt = FALSE;
|
|
BOOL bSuccess = FALSE;
|
|
PSHITEMINFO pInfo = NULL;
|
|
MAPSTR2ITEM::iterator Iter;
|
|
TCHAR szPathExecute[MAX_PATH];
|
|
tstring strKey;
|
|
tstring strKeyExe;
|
|
|
|
LVITEM lvi;
|
|
int ix;
|
|
|
|
|
|
//
|
|
// check for cancelling the search
|
|
//
|
|
CheckForCancel();
|
|
|
|
if (NULL == pszPath) {
|
|
pszPath = szPathExecute;
|
|
|
|
if (!SHGetPathFromIDList(pidlFull, szPathExecute)) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (pszDisplayName && m_pProgView) {
|
|
m_pProgView->UpdatePopulateStatus(pszDisplayName, pszPath);
|
|
}
|
|
|
|
pchSlash = _tcsrchr(pszPath, TEXT('\\'));
|
|
pchDot = _tcsrchr(pszPath, TEXT('.'));
|
|
|
|
if (NULL != pchSlash) {
|
|
if ((ULONG_PTR)pchDot < (ULONG_PTR)pchSlash) {
|
|
pchDot = NULL;
|
|
}
|
|
}
|
|
|
|
if (NULL != pchDot) {
|
|
++pchDot;
|
|
|
|
for (int i = 0; i < sizeof(rgExt)/sizeof(rgExt[0]) && !bValidatedExt; ++i) {
|
|
bValidatedExt = !_tcsicmp(pchDot, rgExt[i]);
|
|
}
|
|
}
|
|
|
|
if (!bValidatedExt) {
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Checks whether the item is in system directory or SFC-protected
|
|
//
|
|
#if 0
|
|
if (IsItemInSystemDirectory(pszPath) || IsSFCItem(pszPath)) {
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// GetBinaryTypeW excludes wow exes
|
|
//
|
|
#if 0
|
|
if (GetBinaryType(pszPath, &dwBinaryType) &&
|
|
dwBinaryType == SCS_WOW_BINARY) {
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
if (IsSFCItem(pszPath)) {
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// this is multimap key
|
|
//
|
|
strKeyExe = StrUpCase(wstring(pszPath));
|
|
|
|
//
|
|
// check whether this has been excluded
|
|
//
|
|
if (m_pProgView->IsFileExcluded(strKeyExe.c_str())) {
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// now compose the key string
|
|
//
|
|
strKey = strKeyExe;
|
|
if (NULL != pszArguments) {
|
|
strKey.append(TEXT(" "));
|
|
strKey.append(pszArguments);
|
|
}
|
|
|
|
//
|
|
// now check whether this item has already been listed
|
|
//
|
|
|
|
Iter = m_mapItems.find(strKey);
|
|
if (Iter != m_mapItems.end()) { // found a duplicate
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// now please add this item to the list view
|
|
//
|
|
pInfo = new CProgramList::SHITEMINFO;
|
|
if (pInfo == NULL) {
|
|
ThrowMemoryException(__FILE__, __LINE__, TEXT("%s\n"), TEXT("Failed to allocate Item Information structure"));
|
|
}
|
|
|
|
pInfo->strDisplayName = pszDisplayName;
|
|
pInfo->strFolder = pszLocation;
|
|
pInfo->strPath = pszPath;
|
|
pInfo->strCmdLine = strKey;
|
|
if (NULL != pszArguments) {
|
|
pInfo->strArgs = pszArguments;
|
|
}
|
|
pInfo->pidl = AppendIDL(NULL, pidlFull);
|
|
|
|
if (bUsePath) {
|
|
pInfo->strPathExecute = pszPath;
|
|
} else {
|
|
|
|
// finally, what are we going to launch ?
|
|
if (SHGetPathFromIDList(pidlFull, szPathExecute)) {
|
|
pInfo->strPathExecute = szPathExecute;
|
|
}
|
|
}
|
|
|
|
|
|
m_mapItems[strKey] = pInfo;
|
|
|
|
m_mmapExeItems.insert(MULTIMAPSTR2ITEM::value_type(strKeyExe, pInfo));
|
|
|
|
ATLTRACE(TEXT("Adding item %s %s %s\n"), pszDisplayName, pszLocation, pszPath);
|
|
|
|
lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE;
|
|
lvi.iItem = ListView_GetItemCount(m_hwndListView); // append at the end please
|
|
lvi.iSubItem = 0;
|
|
lvi.pszText = LPSTR_TEXTCALLBACK;
|
|
lvi.iImage = I_IMAGECALLBACK;
|
|
lvi.lParam = (LPARAM)pInfo;
|
|
ix = ListView_InsertItem(m_hwndListView, &lvi);
|
|
|
|
lvi.mask = LVIF_TEXT;
|
|
lvi.iItem = ix;
|
|
lvi.iSubItem = 1;
|
|
lvi.pszText = LPSTR_TEXTCALLBACK;
|
|
ListView_SetItem(m_hwndListView, &lvi);
|
|
|
|
bSuccess = TRUE;
|
|
|
|
out:
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
BOOL
|
|
CProgramList::PopulateControl(
|
|
CProgView* pProgView,
|
|
HANDLE hevtCancel
|
|
)
|
|
{
|
|
int i;
|
|
HRESULT hr;
|
|
LPITEMIDLIST pidl;
|
|
BOOL bCancel = FALSE;
|
|
struct {
|
|
INT csidl;
|
|
UINT nIDDescription;
|
|
} rgFolders[] = {
|
|
{ CSIDL_DESKTOPDIRECTORY, IDS_DESKTOP },
|
|
{ CSIDL_COMMON_STARTMENU, IDS_COMMON_STARTMENU },
|
|
{ CSIDL_STARTMENU, IDS_STARTMENU },
|
|
{ CSIDL_COMMON_PROGRAMS, IDS_COMMON_PROGRAMS },
|
|
{ CSIDL_PROGRAMS, IDS_PROGRAMS }
|
|
};
|
|
|
|
|
|
//
|
|
// set the progview object pointer so we could update the status
|
|
//
|
|
m_pProgView = pProgView;
|
|
|
|
m_pMallocUI = pProgView->m_pMallocUI;
|
|
|
|
//
|
|
// set the event so that we could cancel the scan
|
|
//
|
|
m_hEventCancel = hevtCancel;
|
|
|
|
|
|
//
|
|
// set extended style
|
|
//
|
|
|
|
ListView_SetExtendedListViewStyleEx(m_hwndListView,
|
|
LVS_EX_INFOTIP|LVS_EX_LABELTIP,
|
|
LVS_EX_INFOTIP|LVS_EX_LABELTIP);
|
|
|
|
//
|
|
// fix columns
|
|
//
|
|
|
|
|
|
LVCOLUMN lvc;
|
|
RECT rc;
|
|
SIZE_T cxProgName;
|
|
SIZE_T cx;
|
|
wstring strCaption;
|
|
|
|
lvc.mask = LVCF_WIDTH;
|
|
if (!ListView_GetColumn(m_hwndListView, 2, &lvc)) {
|
|
|
|
::GetClientRect(m_hwndListView, &rc);
|
|
cx = rc.right - rc.left -
|
|
::GetSystemMetrics(SM_CXVSCROLL) -
|
|
::GetSystemMetrics(SM_CXEDGE) -
|
|
::GetSystemMetrics(SM_CXSIZEFRAME);
|
|
|
|
|
|
cxProgName = cx * 3 / 5;
|
|
strCaption = LoadResourceString(IDS_PROGRAMNAME);
|
|
|
|
lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT;
|
|
lvc.pszText = (LPTSTR)strCaption.c_str();
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = cxProgName;
|
|
lvc.iSubItem= 0;
|
|
ListView_InsertColumn(m_hwndListView, 0, &lvc);
|
|
|
|
cx -= cxProgName;
|
|
|
|
cxProgName = cx / 2;
|
|
strCaption = LoadResourceString(IDS_FOLDER);
|
|
|
|
lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
|
|
lvc.pszText = (LPTSTR)strCaption.c_str();
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = cxProgName;
|
|
lvc.iSubItem= 1;
|
|
ListView_InsertColumn(m_hwndListView, 1, &lvc);
|
|
|
|
strCaption = LoadResourceString(IDS_SETTINGS);
|
|
|
|
lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
|
|
lvc.pszText = (LPTSTR)strCaption.c_str();
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = cx - cxProgName;
|
|
lvc.iSubItem= 2;
|
|
ListView_InsertColumn(m_hwndListView, 2, &lvc);
|
|
|
|
}
|
|
|
|
HDC hDC = GetDC(m_hwndListView);
|
|
int nBitsPixel = ::GetDeviceCaps(hDC, BITSPIXEL);
|
|
int nPlanes = ::GetDeviceCaps(hDC, PLANES);
|
|
UINT flags;
|
|
|
|
nBitsPixel *= nPlanes;
|
|
if (nBitsPixel < 4) {
|
|
flags = ILC_COLOR;
|
|
} else if (nBitsPixel < 8) {
|
|
flags = ILC_COLOR4;
|
|
} else if (nBitsPixel < 16) {
|
|
flags = ILC_COLOR8;
|
|
} else if (nBitsPixel < 24) {
|
|
flags = ILC_COLOR16;
|
|
} else if (nBitsPixel < 32) {
|
|
flags = ILC_COLOR24;
|
|
} else if (nBitsPixel == 32) {
|
|
flags = ILC_COLOR32;
|
|
} else {
|
|
flags = ILC_COLORDDB;
|
|
}
|
|
|
|
flags |= ILC_MASK;
|
|
|
|
ReleaseDC(m_hwndListView, hDC);
|
|
|
|
m_hImageList = ImageList_Create(::GetSystemMetrics(SM_CXSMICON),
|
|
::GetSystemMetrics(SM_CYSMICON),
|
|
flags,
|
|
10,
|
|
25);
|
|
if (m_hImageList == NULL) {
|
|
ATLTRACE(TEXT("Image List creation failure, error 0x%lx\n"), GetLastError());
|
|
}
|
|
|
|
ImageList_SetBkColor(m_hImageList, CLR_NONE);
|
|
|
|
ListView_SetImageList(m_hwndListView, m_hImageList, LVSIL_SMALL);
|
|
|
|
::SendMessage(m_hwndListView, WM_SETREDRAW, FALSE, 0);
|
|
|
|
ListView_DeleteAllItems(m_hwndListView);
|
|
|
|
//
|
|
// AtlTrace(TEXT("Callback Mask: 0x%lx\n"), ListView_GetCallbackMask(m_hwndListView));
|
|
//
|
|
|
|
for (i = 0; i < sizeof(rgFolders)/sizeof(rgFolders[0]) && !bCancel; ++i) {
|
|
wstring strDescription = LoadResourceString(rgFolders[i].nIDDescription);
|
|
|
|
hr = SHGetFolderLocation(NULL, rgFolders[i].csidl, NULL, 0, &pidl);
|
|
if (SUCCEEDED(hr)) {
|
|
try {
|
|
ListFolder(strDescription.c_str(), NULL, NULL, pidl);
|
|
} catch(CCancelException* pex) {
|
|
bCancel = TRUE;
|
|
pex->Delete();
|
|
} catch(CException* pex) {
|
|
bCancel = TRUE;
|
|
pex->Delete();
|
|
}
|
|
GetMalloc()->Free(pidl);
|
|
}
|
|
}
|
|
|
|
::SendMessage(m_hwndListView, WM_SETREDRAW, TRUE, 0);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
BOOL
|
|
CProgramList::CaptureSelection(
|
|
VOID
|
|
)
|
|
{
|
|
INT iSelected;
|
|
LVITEM lvi;
|
|
|
|
m_pSelectionInfo = NULL;
|
|
|
|
iSelected = ListView_GetNextItem(m_hwndListView, -1, LVNI_SELECTED);
|
|
|
|
if (iSelected == -1) {
|
|
return FALSE;
|
|
}
|
|
|
|
lvi.iItem = iSelected;
|
|
lvi.iSubItem = 0;
|
|
lvi.mask = LVIF_PARAM;
|
|
if (ListView_GetItem(m_hwndListView, &lvi)) {
|
|
m_pSelectionInfo = (PSHITEMINFO)lvi.lParam;
|
|
}
|
|
|
|
return m_pSelectionInfo != NULL;
|
|
|
|
}
|
|
|
|
BOOL
|
|
CProgramList::GetSelectionDetails(
|
|
INT iInformationClass,
|
|
VARIANT* pVal
|
|
)
|
|
{
|
|
CComBSTR bstr;
|
|
|
|
if (m_pSelectionInfo == NULL) {
|
|
pVal->vt = VT_NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
switch(iInformationClass) {
|
|
case PROGLIST_DISPLAYNAME:
|
|
bstr = m_pSelectionInfo->strDisplayName.c_str();
|
|
break;
|
|
|
|
case PROGLIST_LOCATION: //
|
|
bstr = m_pSelectionInfo->strFolder.c_str();
|
|
break;
|
|
|
|
case PROGLIST_EXENAME: // cracked exe name
|
|
bstr = m_pSelectionInfo->strPath.c_str(); //
|
|
break;
|
|
|
|
case PROGLIST_CMDLINE: // complete exe name + parameters
|
|
bstr = m_pSelectionInfo->strCmdLine.c_str();
|
|
break;
|
|
|
|
case PROGLIST_EXECUTABLE: // what we should execute (link or exe, not cracked)
|
|
bstr = m_pSelectionInfo->strPathExecute.c_str();
|
|
break;
|
|
|
|
case PROGLIST_ARGUMENTS:
|
|
bstr = m_pSelectionInfo->strArgs.c_str();
|
|
break;
|
|
|
|
default:
|
|
pVal->vt = VT_NULL;
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
|
|
pVal->vt = VT_BSTR;
|
|
pVal->bstrVal = bstr.Copy();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define PROGLIST_SORT_NONE 0
|
|
#define PROGLIST_SORT_ASC 1
|
|
#define PROGLIST_SORT_DSC 2
|
|
|
|
|
|
int CALLBACK
|
|
CProgramList::SHItemInfoCompareFunc(
|
|
LPARAM lp1,
|
|
LPARAM lp2,
|
|
LPARAM lParamSort
|
|
)
|
|
{
|
|
PSHITEMINFO pInfo1 = (PSHITEMINFO)lp1;
|
|
PSHITEMINFO pInfo2 = (PSHITEMINFO)lp2;
|
|
BOOL bEmpty1, bEmpty2;
|
|
int nColSort = (int)LOWORD(lParamSort);
|
|
int nSortOrder = (int)HIWORD(lParamSort);
|
|
int iRet = 0;
|
|
|
|
switch(nColSort) {
|
|
case 0: // SORT_APPNAME:
|
|
iRet = _tcsicmp(pInfo1->strDisplayName.c_str(),
|
|
pInfo2->strDisplayName.c_str());
|
|
break;
|
|
|
|
case 1: // SORT_APPLOCATION:
|
|
iRet = _tcsicmp(pInfo1->strFolder.c_str(),
|
|
pInfo2->strFolder.c_str());
|
|
break;
|
|
|
|
case 2: // SORT_LAYERS:
|
|
bEmpty1 = pInfo1->strKeys.empty();
|
|
bEmpty2 = pInfo2->strKeys.empty();
|
|
if (bEmpty1 || bEmpty2) {
|
|
if (bEmpty1) {
|
|
iRet = bEmpty2 ? 0 : 1;
|
|
} else {
|
|
iRet = bEmpty1 ? 0 : -1;
|
|
}
|
|
} else {
|
|
|
|
iRet = _tcsicmp(pInfo1->strKeys.c_str(),
|
|
pInfo2->strKeys.c_str());
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (nSortOrder == PROGLIST_SORT_DSC) {
|
|
iRet = -iRet;
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
|
|
|
|
LRESULT
|
|
CProgramList::LVNotifyColumnClick(
|
|
LPNMHDR pnmhdr,
|
|
BOOL& bHandled
|
|
)
|
|
{
|
|
LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)pnmhdr;
|
|
|
|
// lpnmlv->iSubItem - this is what we have to sort on
|
|
// check whether we already have something there
|
|
|
|
HWND hwndHeader = ListView_GetHeader(m_hwndListView);
|
|
INT nCols;
|
|
INT i;
|
|
INT nColSort = lpnmlv->iSubItem;
|
|
LPARAM lSortParam; // leave high word blank for now
|
|
LPARAM lSortOrder = PROGLIST_SORT_ASC;
|
|
HDITEM hdi;
|
|
//
|
|
// reset current image - wherever that is
|
|
//
|
|
nCols = Header_GetItemCount(hwndHeader);
|
|
|
|
for (i = 0; i < nCols; ++i) {
|
|
hdi.mask = HDI_BITMAP|HDI_LPARAM|HDI_FORMAT;
|
|
if (!Header_GetItem(hwndHeader, i, &hdi)) {
|
|
continue;
|
|
}
|
|
|
|
if (i == nColSort && (hdi.mask & HDI_LPARAM)) {
|
|
switch(hdi.lParam) {
|
|
case PROGLIST_SORT_NONE:
|
|
case PROGLIST_SORT_DSC:
|
|
lSortOrder = PROGLIST_SORT_ASC;
|
|
break;
|
|
case PROGLIST_SORT_ASC:
|
|
lSortOrder = PROGLIST_SORT_DSC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hdi.mask & HDI_BITMAP) {
|
|
DeleteObject((HGDIOBJ)hdi.hbm);
|
|
}
|
|
|
|
hdi.lParam = PROGLIST_SORT_NONE;
|
|
hdi.fmt &= ~(HDF_BITMAP|HDF_BITMAP_ON_RIGHT);
|
|
hdi.mask |= HDI_BITMAP|HDI_LPARAM|HDI_FORMAT;
|
|
hdi.hbm = NULL;
|
|
Header_SetItem(hwndHeader, i, &hdi);
|
|
}
|
|
|
|
lSortParam = MAKELONG(nColSort, lSortOrder);
|
|
ListView_SortItems(m_hwndListView, (PFNLVCOMPARE)SHItemInfoCompareFunc, lSortParam);
|
|
|
|
// now, load the image please
|
|
m_hbmSort = (HBITMAP)::LoadImage(_Module.GetResourceInstance(),
|
|
MAKEINTRESOURCE(lSortOrder == PROGLIST_SORT_ASC? IDB_SORTUP : IDB_SORTDN),
|
|
IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
|
|
hdi.mask = HDI_BITMAP|HDI_LPARAM|HDI_FORMAT;
|
|
Header_GetItem(hwndHeader, nColSort, &hdi);
|
|
hdi.mask |= HDI_BITMAP|HDI_FORMAT|HDI_LPARAM;
|
|
hdi.hbm = m_hbmSort;
|
|
hdi.fmt |= HDF_BITMAP|HDF_BITMAP_ON_RIGHT;
|
|
hdi.lParam = lSortOrder;
|
|
Header_SetItem(hwndHeader, nColSort, &hdi);
|
|
|
|
|
|
bHandled = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT
|
|
CProgramList::LVNotifyDispInfo(
|
|
LPNMHDR pnmhdr,
|
|
BOOL& bHandled
|
|
)
|
|
{
|
|
|
|
WCHAR wszPermKeys[MAX_PATH];
|
|
DWORD cbSize;
|
|
|
|
LV_ITEM &lvItem = reinterpret_cast<LV_DISPINFO*>(pnmhdr)->item;
|
|
|
|
LV_ITEM lvi;
|
|
PSHITEMINFO pInfo;
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = lvItem.iItem;
|
|
lvi.iSubItem = 0;
|
|
|
|
if (!ListView_GetItem(m_hwndListView, &lvi)) {
|
|
// bummer, we can't retrieve an item -- if we let it go, things will be worse
|
|
lvItem.mask &= ~(LVIF_TEXT|LVIF_IMAGE);
|
|
lvItem.mask |= LVIF_DI_SETITEM;
|
|
bHandled = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
pInfo = reinterpret_cast<PSHITEMINFO> (lvi.lParam);
|
|
|
|
if (lvItem.mask & LVIF_TEXT) {
|
|
switch (lvItem.iSubItem) {
|
|
case 0:
|
|
lvItem.pszText = (LPTSTR)pInfo->strDisplayName.c_str();
|
|
break;
|
|
case 1:
|
|
lvItem.pszText = (LPTSTR)pInfo->strFolder.c_str();
|
|
break;
|
|
case 2:
|
|
// check with SDB
|
|
cbSize = sizeof(wszPermKeys);
|
|
if (pInfo->strKeys.empty()) {
|
|
|
|
if (SdbGetPermLayerKeys(pInfo->strPath.c_str(), wszPermKeys, &cbSize, GPLK_ALL)) {
|
|
pInfo->strKeys = wszPermKeys;
|
|
}
|
|
|
|
}
|
|
|
|
if (!pInfo->strKeys.empty()) {
|
|
lvItem.pszText = (LPTSTR)pInfo->strKeys.c_str();
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (lvItem.mask & LVIF_IMAGE) {
|
|
lvItem.iImage = GetIconFromLink(pInfo->pidl, pInfo->strPathExecute.c_str());
|
|
}
|
|
|
|
lvItem.mask |= LVIF_DI_SETITEM;
|
|
bHandled = TRUE;
|
|
return 0;
|
|
|
|
}
|
|
|
|
LRESULT
|
|
CProgramList::LVNotifyGetInfoTip(
|
|
LPNMHDR pnmhdr,
|
|
BOOL& bHandled
|
|
)
|
|
{
|
|
DWORD cbSize;
|
|
LPNMLVGETINFOTIP pGetInfoTip = (LPNMLVGETINFOTIP)pnmhdr;
|
|
LV_ITEM lvi;
|
|
PSHITEMINFO pInfo;
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = pGetInfoTip->iItem;
|
|
lvi.iSubItem = 0;
|
|
|
|
if (!ListView_GetItem(m_hwndListView, &lvi)) {
|
|
// bupkas
|
|
bHandled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
pInfo = reinterpret_cast<PSHITEMINFO> (lvi.lParam);
|
|
|
|
//
|
|
// now we can fiddle
|
|
//
|
|
|
|
_tcsncpy(pGetInfoTip->pszText, pInfo->strCmdLine.c_str(), pGetInfoTip->cchTextMax);
|
|
*(pGetInfoTip->pszText + pGetInfoTip->cchTextMax - 1) = TEXT('\0');
|
|
|
|
bHandled = TRUE;
|
|
return 0;
|
|
|
|
}
|
|
|
|
LRESULT
|
|
CProgramList::LVNotifyRClick(
|
|
LPNMHDR pnmhdr,
|
|
BOOL& bHandled
|
|
)
|
|
{
|
|
|
|
DWORD dwPos = ::GetMessagePos();
|
|
LVHITTESTINFO hti;
|
|
LV_ITEM lvi;
|
|
PSHITEMINFO pInfo;
|
|
HRESULT hr;
|
|
LPITEMIDLIST pidlItem = NULL;
|
|
IShellFolder* pFolder = NULL;
|
|
IContextMenu* pContextMenu = NULL;
|
|
CMINVOKECOMMANDINFO ici;
|
|
int nCmd;
|
|
HMENU hMenu = NULL;
|
|
UINT idMin, idMax, idCmd;
|
|
WCHAR szCmdVerb[MAX_PATH];
|
|
int nLastSep, i, nLastItem;
|
|
|
|
hti.pt.x = (int) LOWORD (dwPos);
|
|
hti.pt.y = (int) HIWORD (dwPos);
|
|
ScreenToClient (m_hwndListView, &hti.pt);
|
|
|
|
ListView_HitTest (m_hwndListView, &hti);
|
|
|
|
if (!(hti.flags & LVHT_ONITEM)) {
|
|
bHandled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = hti.iItem;
|
|
lvi.iSubItem = 0;
|
|
|
|
if (!ListView_GetItem(m_hwndListView, &lvi)) {
|
|
// bupkas
|
|
bHandled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
pInfo = reinterpret_cast<PSHITEMINFO> (lvi.lParam);
|
|
|
|
//
|
|
// we have an item, show it's context menu then
|
|
//
|
|
|
|
hr = SHBindToParent(pInfo-> pidl, IID_IShellFolder, (PVOID*)&pFolder, (LPCITEMIDLIST*)&pidlItem);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
// get the ui please
|
|
hr = pFolder->GetUIObjectOf(m_hwndListView, 1, (LPCITEMIDLIST*)&pidlItem, IID_IContextMenu, NULL, (PVOID*)&pContextMenu);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
hMenu = CreatePopupMenu();
|
|
if (hMenu == NULL) {
|
|
goto cleanup;
|
|
}
|
|
|
|
hr = pContextMenu->QueryContextMenu(hMenu,
|
|
0,
|
|
1,
|
|
0x7FFF,
|
|
CMF_EXPLORE);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// sanitize
|
|
//
|
|
idMin = 1;
|
|
idMax = HRESULT_CODE(hr);
|
|
|
|
for (idCmd = 0; idCmd < idMax; ++idCmd) {
|
|
hr = pContextMenu->GetCommandString(idCmd, GCS_VERBW, NULL, (LPSTR)szCmdVerb, CHARCOUNT(szCmdVerb));
|
|
if (SUCCEEDED(hr)) {
|
|
if (!_wcsicmp(szCmdVerb, TEXT("cut")) ||
|
|
!_wcsicmp(szCmdVerb, TEXT("delete")) ||
|
|
!_wcsicmp(szCmdVerb, TEXT("rename")) ||
|
|
!_wcsicmp(szCmdVerb, TEXT("link"))) {
|
|
//
|
|
// not allowed
|
|
//
|
|
DeleteMenu(hMenu, idCmd + idMin, MF_BYCOMMAND);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// after doing some basic sanitization against the destructive tendencies --
|
|
// nuke double-separators
|
|
//
|
|
|
|
nLastItem = ::GetMenuItemCount(hMenu) - 1;
|
|
nLastSep = nLastItem + 1;
|
|
for (i = nLastItem; i >= 0; --i) {
|
|
MENUITEMINFO mii;
|
|
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_FTYPE;
|
|
if (GetMenuItemInfo(hMenu, i, TRUE, &mii)) {
|
|
if (mii.fType & MFT_SEPARATOR) {
|
|
if (nLastSep == i + 1 || i == 0) {
|
|
// this sep is dead
|
|
DeleteMenu(hMenu, i, MF_BYPOSITION);
|
|
}
|
|
nLastSep = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ClientToScreen(m_hwndListView, &hti.pt);
|
|
nCmd = TrackPopupMenu(hMenu,
|
|
TPM_LEFTALIGN |
|
|
TPM_LEFTBUTTON |
|
|
TPM_RIGHTBUTTON |
|
|
TPM_RETURNCMD,
|
|
hti.pt.x, hti.pt.y,
|
|
0,
|
|
m_hwndListView,
|
|
NULL);
|
|
|
|
//
|
|
// execute command
|
|
//
|
|
if (nCmd) {
|
|
ici.cbSize = sizeof (CMINVOKECOMMANDINFO);
|
|
ici.fMask = 0;
|
|
ici.hwnd = m_hwndListView;
|
|
ici.lpVerb = MAKEINTRESOURCEA(nCmd - 1);
|
|
ici.lpParameters = NULL;
|
|
ici.lpDirectory = NULL;
|
|
ici.nShow = SW_SHOWNORMAL;
|
|
ici.dwHotKey = 0;
|
|
ici.hIcon = NULL;
|
|
hr = pContextMenu->InvokeCommand(&ici);
|
|
|
|
//
|
|
// requery perm layer keys -- useless here btw
|
|
//
|
|
/* // this code will not work since the call above is always asynchronous
|
|
//
|
|
if (SUCCEEDED(hr)) {
|
|
DWORD cbSize;
|
|
WCHAR wszPermKeys[MAX_PATH];
|
|
|
|
cbSize = sizeof(wszPermKeys);
|
|
if (SdbGetPermLayerKeys(pInfo->strPath.c_str(), wszPermKeys, &cbSize)) {
|
|
pInfo->strKeys = wszPermKeys;
|
|
} else {
|
|
pInfo->strKeys.erase();
|
|
}
|
|
|
|
//
|
|
// set the info into the list box
|
|
//
|
|
ListView_SetItemText(m_hwndListView, lvi.iItem, 2, (LPWSTR)pInfo->strKeys.c_str());
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (hMenu) {
|
|
DestroyMenu(hMenu);
|
|
}
|
|
if (pContextMenu) {
|
|
pContextMenu->Release();
|
|
}
|
|
if (pFolder) {
|
|
pFolder->Release();
|
|
}
|
|
|
|
bHandled = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CProgramList::UpdateListItem(
|
|
LPCWSTR pwszPath,
|
|
LPCWSTR pwszKey
|
|
)
|
|
{
|
|
|
|
// find the item first
|
|
|
|
MAPSTR2ITEM::iterator iter;
|
|
MULTIMAPSTR2ITEM::iterator iterExe;
|
|
MULTIMAPSTR2ITEM::iterator iterFirstExe, iterLastExe;
|
|
|
|
tstring strKey = pwszPath;
|
|
tstring strExeKey;
|
|
PSHITEMINFO pInfo = NULL;
|
|
PSHITEMINFO pInfoExe = NULL;
|
|
|
|
//
|
|
// we need to iterate through all the persisted items
|
|
//
|
|
StrUpCase(strKey);
|
|
|
|
iter = m_mapItems.find(strKey);
|
|
if (iter != m_mapItems.end()) {
|
|
pInfo = (*iter).second;
|
|
}
|
|
|
|
if (pInfo == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// once we have found this single item, get the command and
|
|
// show info for all the other affected items
|
|
//
|
|
strExeKey = pInfo->strPath;
|
|
StrUpCase(strExeKey);
|
|
|
|
iterFirstExe = m_mmapExeItems.lower_bound(strExeKey);
|
|
iterLastExe = m_mmapExeItems.upper_bound(strExeKey);
|
|
|
|
for (iterExe = iterFirstExe; iterExe != m_mmapExeItems.end() && iterExe != iterLastExe; ++iterExe) {
|
|
pInfoExe = (*iterExe).second;
|
|
|
|
|
|
// find this item in a listview
|
|
|
|
LVFINDINFO lvf;
|
|
INT index;
|
|
|
|
lvf.flags = LVFI_PARAM;
|
|
lvf.lParam = (LPARAM)pInfoExe;
|
|
|
|
index = ListView_FindItem(m_hwndListView, -1, &lvf);
|
|
if (index < 0) {
|
|
return FALSE; // inconsistent
|
|
}
|
|
|
|
// else we have both the item and the keys
|
|
if (pwszKey == NULL) {
|
|
pInfoExe->strKeys.erase();
|
|
} else {
|
|
pInfoExe->strKeys = pwszKey;
|
|
}
|
|
|
|
ListView_SetItemText(m_hwndListView, index, 2, (LPWSTR)pInfoExe->strKeys.c_str());
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CProgramList::IsEnabled(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
if (::IsWindow(m_hwndListView)) {
|
|
return ::IsWindowEnabled(m_hwndListView);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
CProgramList::Enable(
|
|
BOOL bEnable
|
|
)
|
|
{
|
|
if (::IsWindow(m_hwndListView)) {
|
|
|
|
::EnableWindow(m_hwndListView, bEnable);
|
|
}
|
|
|
|
}
|
|
|
|
BOOL
|
|
GetProgramListSelection(
|
|
CProgramList* pProgramList
|
|
)
|
|
{
|
|
return pProgramList->CaptureSelection();
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetProgramListSelectionDetails(
|
|
CProgramList* pProgramList,
|
|
INT iInformationClass,
|
|
VARIANT* pVal
|
|
)
|
|
{
|
|
return pProgramList->GetSelectionDetails(iInformationClass, pVal);
|
|
}
|
|
|
|
LRESULT
|
|
NotifyProgramList(
|
|
CProgramList* pProgramList,
|
|
LPNMHDR pnmhdr,
|
|
BOOL& bHandled
|
|
)
|
|
{
|
|
LRESULT lRet = 0;
|
|
|
|
switch (pnmhdr->code) {
|
|
case LVN_GETDISPINFO:
|
|
lRet = pProgramList->LVNotifyDispInfo(pnmhdr, bHandled);
|
|
break;
|
|
|
|
case LVN_COLUMNCLICK:
|
|
lRet = pProgramList->LVNotifyColumnClick(pnmhdr, bHandled);
|
|
break;
|
|
|
|
case LVN_GETINFOTIP:
|
|
lRet = pProgramList->LVNotifyGetInfoTip(pnmhdr, bHandled);
|
|
break;
|
|
|
|
case NM_RCLICK:
|
|
lRet = pProgramList->LVNotifyRClick(pnmhdr, bHandled);
|
|
break;
|
|
|
|
default:
|
|
bHandled = FALSE;
|
|
break;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
BOOL
|
|
GetProgramListEnabled(
|
|
CProgramList* pProgramList
|
|
)
|
|
{
|
|
return pProgramList->IsEnabled();
|
|
}
|
|
|
|
VOID
|
|
EnableProgramList(
|
|
CProgramList* pProgramList,
|
|
BOOL bEnable
|
|
)
|
|
{
|
|
pProgramList->Enable(bEnable);
|
|
}
|
|
|
|
BOOL
|
|
UpdateProgramListItem(
|
|
CProgramList* pProgramList,
|
|
LPCWSTR pwszPath,
|
|
LPCWSTR pwszKeys
|
|
)
|
|
{
|
|
return pProgramList->UpdateListItem(pwszPath, pwszKeys);
|
|
|
|
}
|
|
|