Notepad3/src/Dlapi.c
2019-02-27 02:18:31 +01:00

1510 lines
39 KiB
C

/******************************************************************************
* *
* *
* Notepad3 *
* *
* Dlapi.c *
* Directory Listing APIs used in Notepad3 *
* Based on code from Notepad2, (c) Florian Balmer 1996-2011 *
* *
* (c) Rizonesoft 2008-2019 *
* https://rizonesoft.com *
* *
* *
*******************************************************************************/
#include "Helpers.h"
#include <commctrl.h>
#include <shlobj.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <string.h>
#include "Dlapi.h"
//==== DirList ================================================================
//==== DLDATA Structure =======================================================
typedef struct tagDLDATA // dl
{
HWND hwnd; // HWND of ListView Control
UINT cbidl; // Size of pidl
LPITEMIDLIST pidl; // Directory Id
LPSHELLFOLDER lpsf; // IShellFolder Interface to pidl
WCHAR szPath[MAX_PATH]; // Pathname to Directory Id
int iDefIconFolder; // Default Folder Icon
int iDefIconFile; // Default File Icon
bool bNoFadeHidden; // Flag passed from GetDispInfo()
HANDLE hThread; // Thread Handle
HANDLE hExitThread; // Flag is set when Icon Thread should terminate
HANDLE hTerminatedThread; // Flag is set when Icon Thread has terminated
} DLDATA, *LPDLDATA;
//==== Property Name ==========================================================
static const WCHAR *pDirListProp = L"DirListData";
//=============================================================================
//
// DirList_Init()
//
// Initializes the DLDATA structure and sets up the listview control
//
bool DirList_Init(HWND hwnd,LPCWSTR pszHeader)
{
UNUSED(pszHeader);
HIMAGELIST hil;
SHFILEINFO shfi;
ZeroMemory(&shfi, sizeof(SHFILEINFO));
// Allocate DirListData Property
LPDLDATA lpdl = (LPDLDATA)AllocMem(sizeof(DLDATA),HEAP_ZERO_MEMORY);
if (lpdl)
{
SetProp(hwnd, pDirListProp, (HANDLE)lpdl);
// Setup dl
lpdl->hwnd = hwnd;
lpdl->cbidl = 0;
lpdl->pidl = NULL;
lpdl->lpsf = NULL;
StringCchCopy(lpdl->szPath, COUNTOF(lpdl->szPath), L"");
// Add Imagelists
hil = (HIMAGELIST)SHGetFileInfo(L"C:\\", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(SHFILEINFO),
SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
ListView_SetImageList(hwnd, hil, LVSIL_SMALL);
hil = (HIMAGELIST)SHGetFileInfo(L"C:\\", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(SHFILEINFO),
SHGFI_LARGEICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
ListView_SetImageList(hwnd, hil, LVSIL_NORMAL);
// Initialize default icons - done in DirList_Fill()
//SHGetFileInfo(L"Icon",FILE_ATTRIBUTE_DIRECTORY,&shfi,sizeof(SHFILEINFO),
// SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
//lpdl->iDefIconFolder = shfi.iIcon;
//SHGetFileInfo(L"Icon",FILE_ATTRIBUTE_NORMAL,&shfi,sizeof(SHFILEINFO),
// SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
//lpdl->iDefIconFile = shfi.iIcon;
lpdl->iDefIconFolder = 0;
lpdl->iDefIconFile = 0;
// Icon thread control
lpdl->hExitThread = CreateEvent(NULL, true, false, NULL);
lpdl->hTerminatedThread = CreateEvent(NULL, true, true, NULL);
}
return true;
}
//=============================================================================
//
// DirList_Destroy()
//
// Free memory used by dl structure
//
bool DirList_Destroy(HWND hwnd)
{
LPDLDATA lpdl = (LPVOID)GetProp(hwnd,pDirListProp);
// Release multithreading objects
DirList_TerminateIconThread(hwnd);
CloseHandle(lpdl->hExitThread);
CloseHandle(lpdl->hTerminatedThread);
if (lpdl->pidl) {
CoTaskMemFree((LPVOID)lpdl->pidl);
}
if (lpdl->lpsf) {
lpdl->lpsf->lpVtbl->Release(lpdl->lpsf);
}
// Free DirListData Property
RemoveProp(hwnd,pDirListProp);
FreeMem(lpdl);
return false;
}
//=============================================================================
//
// DirList_StartIconThread()
//
// Start thread to extract file icons in the background
//
bool DirList_StartIconThread(HWND hwnd)
{
DWORD dwtid;
LPDLDATA lpdl = (LPVOID)GetProp(hwnd,pDirListProp);
DirList_TerminateIconThread(hwnd);
ResetEvent(lpdl->hExitThread);
//ResetEvent(lpdl->hTerminatedThread);
lpdl->hThread = CreateThread(NULL, 0, DirList_IconThread, (LPVOID)lpdl, 0, &dwtid);
if (lpdl->hThread == NULL) {
ResetEvent(lpdl->hExitThread);
SetEvent(lpdl->hTerminatedThread);
return false;
}
return true;
}
//=============================================================================
//
// DirList_TerminateIconThread()
//
// Terminate Icon Thread and reset multithreading control structures
//
bool DirList_TerminateIconThread(HWND hwnd)
{
LPDLDATA lpdl = (LPDLDATA)GetProp(hwnd,pDirListProp);
if (!lpdl->hThread)
return false;
SetEvent(lpdl->hExitThread);
//WaitForSingleObject(lpdl->hTerminatedThread,INFINITE);
while (WaitForSingleObject(lpdl->hTerminatedThread,0) != WAIT_OBJECT_0)
{
MSG msg;
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
ResetEvent(lpdl->hExitThread);
SetEvent(lpdl->hTerminatedThread);
lpdl->hThread = NULL;
return true;
}
//=============================================================================
//
// DirList_Fill()
//
// Snapshots a directory and displays the items in the listview control
//
int DirList_Fill(HWND hwnd,LPCWSTR lpszDir,DWORD grfFlags,LPCWSTR lpszFileSpec,
bool bExcludeFilter,bool bNoFadeHidden,
int iSortFlags,bool fSortRev)
{
WCHAR wszDir[MAX_PATH] = { L'\0' };
LPSHELLFOLDER lpsfDesktop = NULL;
LPSHELLFOLDER lpsf = NULL;
LPITEMIDLIST pidl = NULL;
LPITEMIDLIST pidlEntry = NULL;
LPENUMIDLIST lpe = NULL;
ULONG chParsed = 0;
ULONG dwAttributes = 0;
// First of all terminate running icon thread
DirList_TerminateIconThread(hwnd);
LPDLDATA lpdl = (LPDLDATA)GetProp(hwnd,pDirListProp);
// Initialize default icons
SHFILEINFO shfi;
ZeroMemory(&shfi, sizeof(shfi));
SHGetFileInfo(L"Icon",FILE_ATTRIBUTE_DIRECTORY,&shfi,sizeof(SHFILEINFO),
SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
lpdl->iDefIconFolder = shfi.iIcon;
SHGetFileInfo(L"Icon",FILE_ATTRIBUTE_NORMAL,&shfi,sizeof(SHFILEINFO),
SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
lpdl->iDefIconFile = shfi.iIcon;
// A Directory is strongly required
if (!lpszDir || !*lpszDir) {
return(-1);
}
StringCchCopyN(lpdl->szPath, COUNTOF(lpdl->szPath), lpszDir, MAX_PATH);
// Init ListView
SendMessage(hwnd,WM_SETREDRAW,0,0);
ListView_DeleteAllItems(hwnd);
// Init Filter
DL_FILTER dlf;
ZeroMemory(&dlf, sizeof(DL_FILTER));
DirList_CreateFilter(&dlf,lpszFileSpec,bExcludeFilter);
// Init lvi
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(LV_ITEM));
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
lvi.iItem = 0;
lvi.iSubItem = 0;
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.cchTextMax = MAX_PATH;
lvi.iImage = I_IMAGECALLBACK;
// Convert Directory to a UNICODE string
/*MultiByteToWideChar(CP_ACP,
MB_PRECOMPOSED,
lpszDir,
-1,
wszDir,
MAX_PATH);*/
StringCchCopy(wszDir,COUNTOF(wszDir),lpszDir);
// Get Desktop Folder
if (NOERROR == SHGetDesktopFolder(&lpsfDesktop))
{
// Convert wszDir into a pidl
if (NOERROR == lpsfDesktop->lpVtbl->ParseDisplayName(
lpsfDesktop,
hwnd,
NULL,
wszDir,
&chParsed,
&pidl,
&dwAttributes))
{
// Bind pidl to IShellFolder
if (NOERROR == lpsfDesktop->lpVtbl->BindToObject(
lpsfDesktop,
pidl,
NULL,
&IID_IShellFolder,
(void**)&lpsf))
{
// Create an Enumeration object for lpsf
if (NOERROR == lpsf->lpVtbl->EnumObjects(
lpsf,
hwnd,
grfFlags,
&lpe))
{
// Enumerate the contents of lpsf
while (NOERROR == lpe->lpVtbl->Next(
lpe,
1,
&pidlEntry,
NULL))
{
// Add found item to the List
// Check if it's part of the Filesystem
dwAttributes = SFGAO_FILESYSTEM | SFGAO_FOLDER;
lpsf->lpVtbl->GetAttributesOf(
lpsf,
1,
(LPCITEMIDLIST*)&pidlEntry,
&dwAttributes);
if (dwAttributes & SFGAO_FILESYSTEM)
{
// Check if item matches specified filter
if (DirList_MatchFilter(lpsf,pidlEntry,&dlf))
{
LPLV_ITEMDATA lplvid = CoTaskMemAlloc(sizeof(LV_ITEMDATA));
if (lplvid) {
lplvid->pidl = pidlEntry;
lplvid->lpsf = lpsf;
lpsf->lpVtbl->AddRef(lpsf);
lvi.lParam = (LPARAM)lplvid;
// Setup default Icon - Folder or File
lvi.iImage = (dwAttributes & SFGAO_FOLDER) ?
lpdl->iDefIconFolder : lpdl->iDefIconFile;
ListView_InsertItem(hwnd, &lvi);
lvi.iItem++;
}
}
}
} // IEnumIDList::Next()
lpe->lpVtbl->Release(lpe);
} // IShellFolder::EnumObjects()
} // IShellFolder::BindToObject()
} // IShellFolder::ParseDisplayName()
lpsfDesktop->lpVtbl->Release(lpsfDesktop);
} // SHGetDesktopFolder()
if (lpdl->pidl)
CoTaskMemFree((LPVOID)lpdl->pidl);
if (lpdl->lpsf && lpdl->lpsf->lpVtbl)
lpdl->lpsf->lpVtbl->Release(lpdl->lpsf);
// Set lpdl
lpdl->cbidl = IL_GetSize(pidl);
lpdl->pidl = pidl;
lpdl->lpsf = lpsf;
lpdl->bNoFadeHidden = bNoFadeHidden;
// Set column width to fit window
ListView_SetColumnWidth(hwnd,0,LVSCW_AUTOSIZE_USEHEADER);
// Sort before display is updated
DirList_Sort(hwnd,iSortFlags,fSortRev);
// Redraw Listview
SendMessage(hwnd,WM_SETREDRAW,1,0);
// Return number of items in the control
return (ListView_GetItemCount(hwnd));
}
//=============================================================================
//
// DirList_IconThread()
//
// Thread to extract file icons in the background
//
DWORD WINAPI DirList_IconThread(LPVOID lpParam)
{
if (!lpParam)
return(0);
LPDLDATA lpdl = (LPDLDATA)lpParam;
ResetEvent(lpdl->hTerminatedThread);
// Exit immediately if DirList_Fill() hasn't been called
if (!(lpdl->lpsf)) {
SetEvent(lpdl->hTerminatedThread);
ExitThread(0);
//return(0);
}
HWND hwnd = lpdl->hwnd;
int iMaxItem = ListView_GetItemCount(hwnd);
(void)CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY);
// Get IShellIcon
IShellIcon* lpshi = NULL;
lpdl->lpsf->lpVtbl->QueryInterface(lpdl->lpsf,&IID_IShellIcon, (void**)&lpshi);
int iItem = 0;
while (iItem < iMaxItem && WaitForSingleObject(lpdl->hExitThread,0) != WAIT_OBJECT_0) {
LV_ITEM lvi = { 0 };
lvi.iItem = iItem;
lvi.mask = LVIF_PARAM;
if (ListView_GetItem(hwnd,&lvi)) {
SHFILEINFO shfi;
ZeroMemory(&shfi, sizeof(SHFILEINFO));
LPITEMIDLIST pidl;
DWORD dwAttributes = SFGAO_LINK | SFGAO_SHARE;
LPLV_ITEMDATA lplvid = (LPLV_ITEMDATA)lvi.lParam;
lvi.mask = LVIF_IMAGE;
if (!lpshi || NOERROR != lpshi->lpVtbl->GetIconOf(lpshi,lplvid->pidl,GIL_FORSHELL,&lvi.iImage))
{
// get attributes of the shell object using its pidl.
lplvid->lpsf->lpVtbl->GetAttributesOf(lplvid->lpsf, 1, (LPCITEMIDLIST*)&lplvid->pidl, &dwAttributes);
DWORD attr = 0;
if ((dwAttributes & SFGAO_FOLDER) == SFGAO_FOLDER)
attr = FILE_ATTRIBUTE_DIRECTORY;
else
attr = FILE_ATTRIBUTE_NORMAL;
pidl = IL_Create(lpdl->pidl, lpdl->cbidl, lplvid->pidl, 0);
SHGetFileInfo((LPCWSTR)pidl,attr,&shfi,sizeof(SHFILEINFO),
SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES);
CoTaskMemFree((LPVOID)pidl);
lvi.iImage = shfi.iIcon;
}
// It proved necessary to reset the state bits...
lvi.stateMask = 0;
lvi.state = 0;
// Link and Share Overlay
lplvid->lpsf->lpVtbl->GetAttributesOf(lplvid->lpsf,1, (LPCITEMIDLIST*)&lplvid->pidl,&dwAttributes);
if (dwAttributes & SFGAO_LINK)
{
lvi.mask |= LVIF_STATE;
lvi.stateMask |= LVIS_OVERLAYMASK;
lvi.state |= INDEXTOOVERLAYMASK(2);
}
if (dwAttributes & SFGAO_SHARE)
{
lvi.mask |= LVIF_STATE;
lvi.stateMask |= LVIS_OVERLAYMASK;
lvi.state |= INDEXTOOVERLAYMASK(1);
}
// Fade hidden/system files
if (!lpdl->bNoFadeHidden)
{
WIN32_FIND_DATA fd;
if (NOERROR == SHGetDataFromIDList(lplvid->lpsf,lplvid->pidl,
SHGDFIL_FINDDATA,&fd,sizeof(WIN32_FIND_DATA)))
{
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
(fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
{
lvi.mask |= LVIF_STATE;
lvi.stateMask |= LVIS_CUT;
lvi.state |= LVIS_CUT;
}
}
}
lvi.iSubItem = 0;
ListView_SetItem(hwnd,&lvi);
}
iItem++;
}
if (lpshi)
lpshi->lpVtbl->Release(lpshi);
CoUninitialize();
SetEvent(lpdl->hTerminatedThread);
ExitThread(0);
//return(0);
}
//=============================================================================
//
// DirList_GetDispInfo()
//
// Must be called in response to a WM_NOTIFY/LVN_GETDISPINFO message from
// the listview control
//
bool DirList_GetDispInfo(HWND hwnd,LPARAM lParam,bool bNoFadeHidden)
{
LV_DISPINFO *lpdi = (LPVOID)lParam;
LPLV_ITEMDATA lplvid = (LPLV_ITEMDATA)lpdi->item.lParam;
// SubItem 0 is handled only
if (lpdi->item.iSubItem != 0)
return false;
// Text
if (lpdi->item.mask & LVIF_TEXT)
IL_GetDisplayName(lplvid->lpsf,lplvid->pidl,SHGDN_INFOLDER,
lpdi->item.pszText,lpdi->item.cchTextMax);
// Set values
lpdi->item.mask |= LVIF_DI_SETITEM;
UNUSED(hwnd);
UNUSED(bNoFadeHidden);
return true;
}
//=============================================================================
//
// DirList_DeleteItem()
//
// Must be called in response to a WM_NOTIFY/LVN_DELETEITEM message
// from the control
//
bool DirList_DeleteItem(HWND hwnd,LPARAM lParam)
{
if (!lParam)
return false;
LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(LV_ITEM));
lvi.iItem = lpnmlv->iItem;
lvi.iSubItem = 0;
lvi.mask = LVIF_PARAM;
if (ListView_GetItem(hwnd,&lvi))
{
// Free mem
LPLV_ITEMDATA lplvid = (LPLV_ITEMDATA)lvi.lParam;
CoTaskMemFree((LPVOID)lplvid->pidl);
lplvid->lpsf->lpVtbl->Release(lplvid->lpsf);
CoTaskMemFree(lplvid);
return true;
}
return false;
}
//=============================================================================
//
// DirList_CompareProc()
//
// Compares two list items
//
int CALLBACK DirList_CompareProcFw(LPARAM lp1,LPARAM lp2,LPARAM lFlags)
{
LPLV_ITEMDATA lplvid1 = (LPLV_ITEMDATA)lp1;
LPLV_ITEMDATA lplvid2 = (LPLV_ITEMDATA)lp2;
HRESULT hr = (lplvid1->lpsf->lpVtbl->CompareIDs(lplvid1->lpsf,lFlags,lplvid1->pidl,lplvid2->pidl));
int result = (short)(SCODE_CODE(GetScode(hr)));
if (result != 0 || lFlags == 0)
return result;
hr = (lplvid1->lpsf->lpVtbl->CompareIDs(lplvid1->lpsf,0,lplvid1->pidl,lplvid2->pidl));
result = (short)(SCODE_CODE(GetScode(hr)));
return result;
}
int CALLBACK DirList_CompareProcRw(LPARAM lp1,LPARAM lp2,LPARAM lFlags)
{
LPLV_ITEMDATA lplvid1 = (LPLV_ITEMDATA)lp1;
LPLV_ITEMDATA lplvid2 = (LPLV_ITEMDATA)lp2;
HRESULT hr = (lplvid1->lpsf->lpVtbl->CompareIDs(lplvid1->lpsf,lFlags,lplvid1->pidl,lplvid2->pidl));
int result = -(short)(SCODE_CODE(GetScode(hr)));
if (result != 0)
return result;
hr = (lplvid1->lpsf->lpVtbl->CompareIDs(lplvid1->lpsf,0,lplvid1->pidl,lplvid2->pidl));
result = -(short)(SCODE_CODE(GetScode(hr)));
return result;
}
//=============================================================================
//
// DirList_Sort()
//
// Sorts the listview control by the specified order
//
bool DirList_Sort(HWND hwnd,int lFlags,bool fRev)
{
if (fRev) {
return ListView_SortItems(hwnd, DirList_CompareProcRw, lFlags);
}
return ListView_SortItems(hwnd,DirList_CompareProcFw,lFlags);
}
//=============================================================================
//
// DirList_GetItem()
//
// Copies the data of the specified item in the listview control to a buffer
//
int DirList_GetItem(HWND hwnd,int iItem,LPDLITEM lpdli)
{
if (iItem == -1)
{
if (ListView_GetSelectedCount(hwnd))
iItem = ListView_GetNextItem(hwnd,-1,LVNI_ALL | LVNI_SELECTED);
else
return(-1);
}
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(LV_ITEM));
lvi.mask = LVIF_PARAM;
lvi.iItem = iItem;
lvi.iSubItem = 0;
if (!ListView_GetItem(hwnd,&lvi))
{
if (lpdli->mask & DLI_TYPE)
lpdli->ntype = DLE_NONE;
return(-1);
}
LPLV_ITEMDATA lplvid = (LPLV_ITEMDATA)lvi.lParam;
// Filename
if (lpdli->mask & DLI_FILENAME)
IL_GetDisplayName(lplvid->lpsf,lplvid->pidl,SHGDN_FORPARSING,
lpdli->szFileName,MAX_PATH);
// Displayname
if (lpdli->mask & DLI_DISPNAME)
IL_GetDisplayName(lplvid->lpsf,lplvid->pidl,SHGDN_INFOLDER,
lpdli->szDisplayName,MAX_PATH);
// Type (File / Directory)
if (lpdli->mask & DLI_TYPE)
{
WIN32_FIND_DATA fd;
if (NOERROR == SHGetDataFromIDList(lplvid->lpsf,
lplvid->pidl,
SHGDFIL_FINDDATA,
&fd,
sizeof(WIN32_FIND_DATA)))
lpdli->ntype = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
DLE_DIR : DLE_FILE;
//ULONG dwAttributes = SFGAO_FILESYSTEM;
/*lplvid->lpsf->lpVtbl->GetAttributesOf(
lplvid->lpsf,
1,
&lplvid->pidl,
&dwAttributes);
lpdli->ntype = (dwAttributes & SFGAO_FOLDER) ? DLE_DIR : DLE_FILE;*/
}
return iItem;
}
//=============================================================================
//
// DirList_GetItemEx()
//
// Retrieves extended infomration on a dirlist item
//
int DirList_GetItemEx(HWND hwnd,int iItem,LPWIN32_FIND_DATA pfd)
{
LV_ITEM lvi;
LPLV_ITEMDATA lplvid;
if (iItem == -1)
{
if (ListView_GetSelectedCount(hwnd))
iItem = ListView_GetNextItem(hwnd,-1,LVNI_ALL | LVNI_SELECTED);
else
return(-1);
}
lvi.mask = LVIF_PARAM;
lvi.iItem = iItem;
lvi.iSubItem = 0;
if (!ListView_GetItem(hwnd, &lvi)) {
return(-1);
}
lplvid = (LPLV_ITEMDATA)lvi.lParam;
if (NOERROR == SHGetDataFromIDList(lplvid->lpsf,
lplvid->pidl,
SHGDFIL_FINDDATA,
pfd,
sizeof(WIN32_FIND_DATA))) {
return iItem;
}
return(-1);
}
//=============================================================================
//
// DirList_PropertyDlg()
//
// Shows standard Win95 Property Dlg for selected Item
//
bool DirList_PropertyDlg(HWND hwnd,int iItem)
{
LV_ITEM lvi;
LPLV_ITEMDATA lplvid;
LPCONTEXTMENU lpcm;
CMINVOKECOMMANDINFO cmi;
bool bSuccess = true;
static const char *lpVerb = "properties";
if (iItem == -1)
{
if (ListView_GetSelectedCount(hwnd))
iItem = ListView_GetNextItem(hwnd,-1,LVNI_ALL | LVNI_SELECTED);
else
return false;
}
lvi.mask = LVIF_PARAM;
lvi.iItem = iItem;
lvi.iSubItem = 0;
if (!ListView_GetItem(hwnd,&lvi))
return false;
lplvid = (LPLV_ITEMDATA)lvi.lParam;
if (NOERROR == lplvid->lpsf->lpVtbl->GetUIObjectOf(lplvid->lpsf,
GetParent(hwnd), // Owner
1, // Number of objects
(LPCITEMIDLIST*)&lplvid->pidl, // pidl
&IID_IContextMenu,
NULL,
(void**)&lpcm))
{
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmi.fMask = 0;
cmi.hwnd = GetParent(hwnd);
cmi.lpVerb = lpVerb;
cmi.lpParameters = NULL;
cmi.lpDirectory = NULL;
cmi.nShow = SW_SHOWNORMAL;
cmi.dwHotKey = 0;
cmi.hIcon = NULL;
if (NOERROR != lpcm->lpVtbl->InvokeCommand(lpcm,&cmi))
bSuccess = false;
lpcm->lpVtbl->Release(lpcm);
}
else
bSuccess = false;
return(bSuccess);
}
//=============================================================================
//
// DirList_GetLongPathName()
//
// Get long pathname for currently displayed directory
//
bool DirList_GetLongPathName(HWND hwnd,LPWSTR lpszLongPath,int length)
{
WCHAR tch[MAX_PATH] = { L'\0' };
LPDLDATA lpdl = (LPVOID)GetProp(hwnd,pDirListProp);
if (SHGetPathFromIDList(lpdl->pidl,tch))
{
StringCchCopy(lpszLongPath,length,tch);
return true;
}
return false;
}
//=============================================================================
//
// DirList_SelectItem()
//
// Select specified item in the list
//
bool DirList_SelectItem(HWND hwnd,LPCWSTR lpszDisplayName,LPCWSTR lpszFullPath)
{
#define LVIS_FLAGS LVIS_SELECTED|LVIS_FOCUSED
WCHAR szShortPath[MAX_PATH] = { L'\0' };
SHFILEINFO shfi;
ZeroMemory(&shfi, sizeof(SHFILEINFO));
LV_FINDINFO lvfi;
DLITEM dli;
int i = -1;
if (!lpszFullPath || !StringCchLen(lpszFullPath, MAX_PATH)) {
return false;
}
GetShortPathName(lpszFullPath,szShortPath,MAX_PATH);
if (!lpszDisplayName || !StringCchLen(lpszDisplayName, MAX_PATH)) {
SHGetFileInfo(lpszFullPath, 0, &shfi, sizeof(SHFILEINFO), SHGFI_DISPLAYNAME);
}
else {
StringCchCopyN(shfi.szDisplayName, COUNTOF(shfi.szDisplayName), lpszDisplayName, MAX_PATH);
}
lvfi.flags = LVFI_STRING;
lvfi.psz = shfi.szDisplayName;
dli.mask = DLI_ALL;
while ((i = ListView_FindItem(hwnd,i,&lvfi)) != -1)
{
DirList_GetItem(hwnd,i,&dli);
GetShortPathName(dli.szFileName,dli.szFileName,MAX_PATH);
if (!StringCchCompareNI(dli.szFileName,COUNTOF(dli.szFileName),szShortPath,COUNTOF(szShortPath)))
{
ListView_SetItemState(hwnd,i,LVIS_FLAGS,LVIS_FLAGS);
ListView_EnsureVisible(hwnd,i,false);
return true;
}
}
return false;
}
//=============================================================================
//
// DirList_CreateFilter()
//
// Create a valid DL_FILTER structure
//
void DirList_CreateFilter(PDL_FILTER pdlf,LPCWSTR lpszFileSpec,
bool bExcludeFilter)
{
if (!lpszFileSpec)
return;
ZeroMemory(pdlf,sizeof(DL_FILTER));
StringCchCopyN(pdlf->tFilterBuf,COUNTOF(pdlf->tFilterBuf),lpszFileSpec,DL_FILTER_BUFSIZE);
pdlf->bExcludeFilter = bExcludeFilter;
if (!StringCchCompareX(lpszFileSpec,L"*.*") || !StringCchLen(lpszFileSpec,DL_FILTER_BUFSIZE))
return;
pdlf->nCount = 1;
pdlf->pFilter[0] = &pdlf->tFilterBuf[0]; // Zeile zum Ausprobieren
WCHAR* p = StrChr(pdlf->pFilter[pdlf->nCount - 1], L';');
while (p)
{
*p = L'\0'; // Replace L';' by L'\0'
pdlf->pFilter[pdlf->nCount] = (p + 1); // Next position after L';'
p = StrChr(pdlf->pFilter[pdlf->nCount], L';');
pdlf->nCount++; // Increase number of filters
}
}
//=============================================================================
//
// DirList_MatchFilter()
//
// Check if a specified item matches a given filter
//
bool DirList_MatchFilter(LPSHELLFOLDER lpsf,LPCITEMIDLIST pidl,PDL_FILTER pdlf)
{
int i;
WIN32_FIND_DATA fd;
bool bMatchSpec;
// Immediately return true if lpszFileSpec is *.* or NULL
if (pdlf->nCount == 0 && !pdlf->bExcludeFilter)
return true;
SHGetDataFromIDList(lpsf,pidl,SHGDFIL_FINDDATA,&fd,sizeof(WIN32_FIND_DATA));
// All the directories are added
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return true;
// Check if exclude *.* after directories have been added
if (pdlf->nCount == 0 && pdlf->bExcludeFilter)
return false;
for (i = 0; i < pdlf->nCount; i++)
{
if (*pdlf->pFilter[i]) // Filters like L"\0" are ignored
{
bMatchSpec = PathMatchSpec(fd.cFileName,pdlf->pFilter[i]);
if (bMatchSpec)
{
if (!pdlf->bExcludeFilter) {
return true;
}
return false;
}
}
}
// No matching
return(pdlf->bExcludeFilter)?true:false;
}
//==== DriveBox ===============================================================
//=============================================================================
//
// Internal Itemdata Structure
//
typedef struct tagDC_ITEMDATA
{
LPITEMIDLIST pidl;
LPSHELLFOLDER lpsf;
} DC_ITEMDATA, *LPDC_ITEMDATA;
//=============================================================================
//
// DriveBox_Init()
//
// Initializes the drive box
//
bool DriveBox_Init(HWND hwnd)
{
SHFILEINFO shfi;
ZeroMemory(&shfi, sizeof(SHFILEINFO));
HIMAGELIST hil = (HIMAGELIST)SHGetFileInfo(L"C:\\",FILE_ATTRIBUTE_DIRECTORY,&shfi,sizeof(SHFILEINFO),
SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
SendMessage(hwnd,CBEM_SETIMAGELIST,0,(LPARAM)hil);
SendMessage(hwnd,CBEM_SETEXTENDEDSTYLE,CBES_EX_NOSIZELIMIT,CBES_EX_NOSIZELIMIT);
return true;
}
//=============================================================================
//
// DriveBox_Fill
//
int DriveBox_Fill(HWND hwnd)
{
LPSHELLFOLDER lpsfDesktop;
LPSHELLFOLDER lpsf; // Workspace == CSIDL_DRIVES
LPITEMIDLIST pidl;
LPITEMIDLIST pidlEntry;
LPENUMIDLIST lpe;
COMBOBOXEXITEM cbei;
LPDC_ITEMDATA lpdcid;
ULONG dwAttributes = 0;
DWORD grfFlags = SHCONTF_FOLDERS;
// Init ComboBox
SendMessage(hwnd,WM_SETREDRAW,0,0);
SendMessage(hwnd,CB_RESETCONTENT,0,0);
ZeroMemory(&cbei,sizeof(COMBOBOXEXITEM));
cbei.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_LPARAM;
cbei.pszText = LPSTR_TEXTCALLBACK;
cbei.cchTextMax = MAX_PATH;
cbei.iImage = I_IMAGECALLBACK;
cbei.iSelectedImage = I_IMAGECALLBACK;
// Get pidl to [My Computer]
if (NOERROR == SHGetSpecialFolderLocation(hwnd,
CSIDL_DRIVES,
&pidl))
{
// Get Desktop Folder
if (NOERROR == SHGetDesktopFolder(&lpsfDesktop))
{
// Bind pidl to IShellFolder
if (NOERROR == lpsfDesktop->lpVtbl->BindToObject(
lpsfDesktop,
pidl,
NULL,
&IID_IShellFolder,
(void**)&lpsf))
{
// Create an Enumeration object for lpsf
if (NOERROR == lpsf->lpVtbl->EnumObjects(
lpsf,
hwnd,
grfFlags,
&lpe))
{
// Enumerate the contents of [My Computer]
while (NOERROR == lpe->lpVtbl->Next(
lpe,
1,
&pidlEntry,
NULL))
{
// Add item to the List if it is part of the
// Filesystem
dwAttributes = SFGAO_FILESYSTEM;
lpsf->lpVtbl->GetAttributesOf(
lpsf,
1,
(LPCITEMIDLIST*)&pidlEntry,
&dwAttributes);
if (dwAttributes & SFGAO_FILESYSTEM)
{
// Windows XP: check if pidlEntry is a drive
SHDESCRIPTIONID di;
HRESULT hr;
hr = SHGetDataFromIDList(lpsf,pidlEntry,SHGDFIL_DESCRIPTIONID,
&di,sizeof(SHDESCRIPTIONID));
if (hr != NOERROR || (di.dwDescriptionId >= SHDID_COMPUTER_DRIVE35 &&
di.dwDescriptionId <= SHDID_COMPUTER_OTHER))
{
lpdcid = CoTaskMemAlloc(sizeof(DC_ITEMDATA));
if (lpdcid) {
//lpdcid->pidl = IL_Copy(pidlEntry);
lpdcid->pidl = pidlEntry;
lpdcid->lpsf = lpsf;
lpsf->lpVtbl->AddRef(lpsf);
// Insert sorted ...
COMBOBOXEXITEM cbei2;
cbei2.mask = CBEIF_LPARAM;
cbei2.iItem = 0;
while ((SendMessage(hwnd, CBEM_GETITEM, 0, (LPARAM)&cbei2))) {
LPDC_ITEMDATA lpdcid2 = (LPDC_ITEMDATA)cbei2.lParam;
HRESULT hr2 = (lpdcid->lpsf->lpVtbl->CompareIDs(
lpdcid->lpsf,
0,
lpdcid->pidl,
lpdcid2->pidl));
if ((short)(SCODE_CODE(GetScode(hr2))) < 0) {
break;
}
++cbei2.iItem;
}
cbei.iItem = cbei2.iItem;
cbei.lParam = (LPARAM)lpdcid;
SendMessage(hwnd, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
}
}
}
} // IEnumIDList::Next()
lpe->lpVtbl->Release(lpe);
} // IShellFolder::EnumObjects()
lpsf->lpVtbl->Release(lpsf);
} // IShellFolder::BindToObject()
CoTaskMemFree((LPVOID)pidl);
} // SHGetSpecialFolderLocation()
lpsfDesktop->lpVtbl->Release(lpsfDesktop);
} // SHGetDesktopFolder()
SendMessage(hwnd,WM_SETREDRAW,1,0);
// Return number of items added to combo box
return ((int)SendMessage(hwnd,CB_GETCOUNT,0,0));
}
//=============================================================================
//
// DriveBox_GetSelDrive
//
bool DriveBox_GetSelDrive(HWND hwnd,LPWSTR lpszDrive,int nDrive,bool fNoSlash)
{
COMBOBOXEXITEM cbei;
LPDC_ITEMDATA lpdcid;
int i = (int)SendMessage(hwnd,CB_GETCURSEL,0,0);
// CB_ERR means no Selection
if (i == CB_ERR)
return false;
// Get DC_ITEMDATA* of selected Item
cbei.mask = CBEIF_LPARAM;
cbei.iItem = i;
SendMessage(hwnd,CBEM_GETITEM,0,(LPARAM)&cbei);
lpdcid = (LPDC_ITEMDATA)cbei.lParam;
// Get File System Path for Drive
IL_GetDisplayName(lpdcid->lpsf,lpdcid->pidl,SHGDN_FORPARSING,lpszDrive,nDrive);
// Remove Backslash if required (makes Drive relative!!!)
if (fNoSlash)
PathRemoveBackslash(lpszDrive);
return true;
}
//=============================================================================
//
// DriveBox_SelectDrive
//
bool DriveBox_SelectDrive(HWND hwnd,LPCWSTR lpszPath)
{
COMBOBOXEXITEM cbei;
WCHAR szRoot[64] = { L'\0' };
int i;
int cbItems = (int)SendMessage(hwnd,CB_GETCOUNT,0,0);
// No Drives in Combo Box
if (!cbItems)
return false;
cbei.mask = CBEIF_LPARAM;
for (i = 0; i < cbItems; i++)
{
// Get DC_ITEMDATA* of Item i
cbei.iItem = i;
SendMessage(hwnd,CBEM_GETITEM,0,(LPARAM)&cbei);
LPDC_ITEMDATA lpdcid = (LPDC_ITEMDATA)cbei.lParam;
// Get File System Path for Drive
IL_GetDisplayName(lpdcid->lpsf,lpdcid->pidl,SHGDN_FORPARSING,szRoot,64);
// Compare Root Directory with Path
if (PathIsSameRoot(lpszPath,szRoot))
{
// Select matching Drive
SendMessage(hwnd,CB_SETCURSEL,i,0);
return true;
}
}
// Don't select anything
SendMessage(hwnd,CB_SETCURSEL,(WPARAM)1,0);
return false;
}
//=============================================================================
//
// DriveBox_PropertyDlg()
//
// Shows standard Win95 Property Dlg for selected Drive
//
bool DriveBox_PropertyDlg(HWND hwnd)
{
COMBOBOXEXITEM cbei;
LPDC_ITEMDATA lpdcid;
int iItem;
LPCONTEXTMENU lpcm;
CMINVOKECOMMANDINFO cmi;
bool bSuccess = true;
static const char *lpVerb = "properties";
iItem = (int)SendMessage(hwnd,CB_GETCURSEL,0,0);
if (iItem == CB_ERR)
return false;
cbei.mask = CBEIF_LPARAM;
cbei.iItem = iItem;
SendMessage(hwnd,CBEM_GETITEM,0,(LPARAM)&cbei);
lpdcid = (LPDC_ITEMDATA)cbei.lParam;
if (NOERROR == lpdcid->lpsf->lpVtbl->GetUIObjectOf(
lpdcid->lpsf,
GetParent(hwnd), // Owner
1, // Number of objects
(LPCITEMIDLIST*)&lpdcid->pidl, // pidl
&IID_IContextMenu,
NULL,
(void**)&lpcm))
{
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmi.fMask = 0;
cmi.hwnd = GetParent(hwnd);
cmi.lpVerb = lpVerb;
cmi.lpParameters = NULL;
cmi.lpDirectory = NULL;
cmi.nShow = SW_SHOWNORMAL;
cmi.dwHotKey = 0;
cmi.hIcon = NULL;
if (NOERROR != lpcm->lpVtbl->InvokeCommand(lpcm,&cmi))
bSuccess = false;
lpcm->lpVtbl->Release(lpcm);
}
else
bSuccess = false;
return(bSuccess);
}
//=============================================================================
//
// DriveBox_DeleteItem
//
LRESULT DriveBox_DeleteItem(HWND hwnd,LPARAM lParam)
{
NMCOMBOBOXEX *lpnmcbe;
COMBOBOXEXITEM cbei;
LPDC_ITEMDATA lpdcid;
lpnmcbe = (LPVOID)lParam;
cbei.iItem = lpnmcbe->ceItem.iItem;
cbei.mask = CBEIF_LPARAM;
SendMessage(hwnd,CBEM_GETITEM,0,(LPARAM)&cbei);
lpdcid = (LPDC_ITEMDATA)cbei.lParam;
// Free pidl
CoTaskMemFree((LPVOID)lpdcid->pidl);
// Release lpsf
lpdcid->lpsf->lpVtbl->Release((LPVOID)lpdcid->lpsf);
// Free lpdcid itself
CoTaskMemFree(lpdcid);
return true;
}
//=============================================================================
//
// DriveBox_GetDispInfo
//
LRESULT DriveBox_GetDispInfo(HWND hwnd,LPARAM lParam)
{
NMCOMBOBOXEX *lpnmcbe;
LPDC_ITEMDATA lpdcid;
SHFILEINFO shfi;
ZeroMemory(&shfi, sizeof(SHFILEINFO));
lpnmcbe = (LPVOID)lParam;
lpdcid = (LPDC_ITEMDATA)lpnmcbe->ceItem.lParam;
if (!lpdcid)
return false;
// Get Display Name
if (lpnmcbe->ceItem.mask & CBEIF_TEXT)
IL_GetDisplayName(lpdcid->lpsf,lpdcid->pidl,SHGDN_NORMAL,lpnmcbe->ceItem.pszText,lpnmcbe->ceItem.cchTextMax);
// Get Icon Index
if (lpnmcbe->ceItem.mask & (CBEIF_IMAGE | CBEIF_SELECTEDIMAGE))
{
DWORD dwAttributes = 0;
// get attributes of the shell object using its pidl.
lpdcid->lpsf->lpVtbl->GetAttributesOf(lpdcid->lpsf, 1, (LPCITEMIDLIST*)&lpdcid->pidl, &dwAttributes);
DWORD attr = 0;
if ((dwAttributes & SFGAO_FOLDER) == SFGAO_FOLDER)
attr = FILE_ATTRIBUTE_DIRECTORY;
else
attr = FILE_ATTRIBUTE_NORMAL;
WCHAR szTemp[MAX_PATH] = { L'\0' };
IL_GetDisplayName(lpdcid->lpsf,lpdcid->pidl,SHGDN_FORPARSING,szTemp,MAX_PATH);
SHGetFileInfo(szTemp,attr,&shfi,sizeof(SHFILEINFO),
SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES);
lpnmcbe->ceItem.iImage = shfi.iIcon;
lpnmcbe->ceItem.iSelectedImage = shfi.iIcon;
}
// Set values
lpnmcbe->ceItem.mask |= CBEIF_DI_SETITEM;
UNUSED(hwnd);
return true;
}
//==== ItemID =================================================================
//=============================================================================
//
// IL_Create()
//
// Creates an ITEMIDLIST by concatenating pidl1 and pidl2
// cb1 and cb2 indicate the sizes of the pidls, where cb1
// can be zero and pidl1 can be NULL
//
// If cb2 is zero, the size of pidl2 is retrieved using
// IL_GetSize(pidl2)
//
LPITEMIDLIST IL_Create(LPCITEMIDLIST pidl1,UINT cb1,
LPCITEMIDLIST pidl2,UINT cb2)
{
LPITEMIDLIST pidl;
if (!pidl2)
return NULL;
if (!cb2)
cb2 = IL_GetSize(pidl2) + 2; // Space for terminating Bytes
if (!cb1)
cb1 = IL_GetSize(pidl1);
// Allocate Memory
pidl = CoTaskMemAlloc(cb1 + cb2);
// Init new ITEMIDLIST
if (pidl && pidl1) {
CopyMemory((LPBYTE)pidl, (const void*)pidl1, cb1);
}
// pidl2 can't be NULL here
if (pidl) {
CopyMemory((LPBYTE)pidl + cb1, (const void*)pidl2, cb2);
}
return pidl;
}
//=============================================================================
//
// IL_GetSize()
//
// Retrieves the number of bytes in a pidl
// Does not add space for zero terminators !!
//
UINT IL_GetSize(LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlTmp;
UINT cb = 0;
if (!pidl)
return 0;
for (pidlTmp = (LPITEMIDLIST)pidl;
pidlTmp->mkid.cb;
pidlTmp = _IL_Next(pidlTmp))
cb += pidlTmp->mkid.cb;
return cb;
}
//=============================================================================
//
// IL_GetDisplayName()
//
// Gets the Display Name of a pidl. lpsf is the parent IShellFolder Interface
// dwFlags specify a SHGDN_xx value
//
bool IL_GetDisplayName(LPSHELLFOLDER lpsf,
LPCITEMIDLIST pidl,
DWORD dwFlags,
LPWSTR lpszDisplayName,
int nDisplayName)
{
STRRET str;
if (NOERROR == lpsf->lpVtbl->GetDisplayNameOf(lpsf,
pidl,
dwFlags,
&str))
{
return StrRetToBuf(&str,pidl,lpszDisplayName,nDisplayName);
}
return false;
}
/// End of Dlapi.c ///