mirror of
https://github.com/holub/mame
synced 2025-06-07 05:13:46 +03:00
822 lines
23 KiB
C++
822 lines
23 KiB
C++
// Windows/Shell.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "../Common/MyCom.h"
|
|
#include "../Common/StringConvert.h"
|
|
|
|
#include "COM.h"
|
|
#include "FileName.h"
|
|
#include "MemoryGlobal.h"
|
|
#include "Shell.h"
|
|
|
|
#ifndef _UNICODE
|
|
extern bool g_IsNT;
|
|
#endif
|
|
|
|
// MSVC6 and old SDK don't support this function:
|
|
// #define LWSTDAPI EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE
|
|
// LWSTDAPI StrRetToStrW(STRRET *pstr, LPCITEMIDLIST pidl, LPWSTR *ppsz);
|
|
|
|
// #define SHOW_DEBUG_SHELL
|
|
|
|
#ifdef SHOW_DEBUG_SHELL
|
|
|
|
#include "../Common/IntToString.h"
|
|
|
|
static void Print_Number(UInt32 number, const char *s)
|
|
{
|
|
AString s2;
|
|
s2.Add_UInt32(number);
|
|
s2.Add_Space();
|
|
s2 += s;
|
|
OutputDebugStringA(s2);
|
|
}
|
|
|
|
#define ODS(sz) { OutputDebugStringA(sz); }
|
|
#define ODS_U(s) { OutputDebugStringW(s); }
|
|
#define ODS_(op) { op; }
|
|
|
|
#else
|
|
|
|
#define ODS(sz)
|
|
#define ODS_U(s)
|
|
#define ODS_(op)
|
|
|
|
#endif
|
|
|
|
|
|
namespace NWindows {
|
|
namespace NShell {
|
|
|
|
#ifndef UNDER_CE
|
|
|
|
// SHGetMalloc is unsupported in Windows Mobile?
|
|
|
|
void CItemIDList::Free()
|
|
{
|
|
if (!m_Object)
|
|
return;
|
|
/* DOCs:
|
|
SHGetMalloc was introduced in Windows 95 and Microsoft Windows NT 4.0,
|
|
but as of Windows 2000 it is no longer necessary.
|
|
In its place, programs can call the equivalent (and easier to use) CoTaskMemAlloc and CoTaskMemFree.
|
|
Description from oldnewthings:
|
|
shell functions could work without COM (if OLE32.DLL is not loaded),
|
|
but now if OLE32.DLL is loaded, then shell functions and com functions do same things.
|
|
22.02: so we use OLE32.DLL function to free memory:
|
|
*/
|
|
/*
|
|
CMyComPtr<IMalloc> shellMalloc;
|
|
if (::SHGetMalloc(&shellMalloc) != NOERROR)
|
|
throw 41099;
|
|
shellMalloc->Free(m_Object);
|
|
*/
|
|
CoTaskMemFree(m_Object);
|
|
m_Object = NULL;
|
|
}
|
|
|
|
/*
|
|
CItemIDList::(LPCITEMIDLIST itemIDList): m_Object(NULL)
|
|
{ *this = itemIDList; }
|
|
CItemIDList::(const CItemIDList& itemIDList): m_Object(NULL)
|
|
{ *this = itemIDList; }
|
|
|
|
CItemIDList& CItemIDList::operator=(LPCITEMIDLIST object)
|
|
{
|
|
Free();
|
|
if (object != 0)
|
|
{
|
|
UINT32 size = GetSize(object);
|
|
m_Object = (LPITEMIDLIST)CoTaskMemAlloc(size);
|
|
if (m_Object != NULL)
|
|
MoveMemory(m_Object, object, size);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
CItemIDList& CItemIDList::operator=(const CItemIDList &object)
|
|
{
|
|
Free();
|
|
if (object.m_Object != NULL)
|
|
{
|
|
UINT32 size = GetSize(object.m_Object);
|
|
m_Object = (LPITEMIDLIST)CoTaskMemAlloc(size);
|
|
if (m_Object != NULL)
|
|
MoveMemory(m_Object, object.m_Object, size);
|
|
}
|
|
return *this;
|
|
}
|
|
*/
|
|
|
|
|
|
static HRESULT ReadUnicodeStrings(const wchar_t *p, size_t size, UStringVector &names)
|
|
{
|
|
names.Clear();
|
|
const wchar_t *lim = p + size;
|
|
UString s;
|
|
/*
|
|
if (size == 0 || p[size - 1] != 0)
|
|
return E_INVALIDARG;
|
|
if (size == 1)
|
|
return S_OK;
|
|
if (p[size - 2] != 0)
|
|
return E_INVALIDARG;
|
|
*/
|
|
for (;;)
|
|
{
|
|
const wchar_t *start = p;
|
|
for (;;)
|
|
{
|
|
if (p == lim) return E_INVALIDARG; // S_FALSE
|
|
if (*p++ == 0)
|
|
break;
|
|
}
|
|
const size_t num = (size_t)(p - start);
|
|
if (num == 1)
|
|
{
|
|
if (p != lim) return E_INVALIDARG; // S_FALSE
|
|
return S_OK;
|
|
}
|
|
s.SetFrom(start, (unsigned)(num - 1));
|
|
ODS_U(s)
|
|
names.Add(s);
|
|
// names.ReserveOnePosition();
|
|
// names.AddInReserved_Ptr_of_new(new UString((unsigned)num - 1, start));
|
|
}
|
|
}
|
|
|
|
|
|
static HRESULT ReadAnsiStrings(const char *p, size_t size, UStringVector &names)
|
|
{
|
|
names.Clear();
|
|
AString name;
|
|
for (; size != 0; size--)
|
|
{
|
|
const char c = *p++;
|
|
if (c == 0)
|
|
{
|
|
if (name.IsEmpty())
|
|
return S_OK;
|
|
names.Add(GetUnicodeString(name));
|
|
name.Empty();
|
|
}
|
|
else
|
|
name += c;
|
|
}
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
#define INIT_FORMATETC_HGLOBAL(type) { (type), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }
|
|
|
|
static HRESULT DataObject_GetData_HGLOBAL(IDataObject *dataObject, CLIPFORMAT cf, NCOM::CStgMedium &medium)
|
|
{
|
|
FORMATETC etc = INIT_FORMATETC_HGLOBAL(cf);
|
|
RINOK(dataObject->GetData(&etc, &medium))
|
|
if (medium.tymed != TYMED_HGLOBAL)
|
|
return E_INVALIDARG;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT DataObject_GetData_HDROP_Names(IDataObject *dataObject, UStringVector &names)
|
|
{
|
|
names.Clear();
|
|
NCOM::CStgMedium medium;
|
|
|
|
/* Win10 : if (dataObject) is from IContextMenu::Initialize() and
|
|
if (len_of_path >= MAX_PATH (260) for some file in data object)
|
|
{
|
|
GetData() returns HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
|
|
"The data area passed to a system call is too small",
|
|
Is there a way to fix this code for long paths?
|
|
} */
|
|
|
|
RINOK(DataObject_GetData_HGLOBAL(dataObject, CF_HDROP, medium))
|
|
const size_t blockSize = GlobalSize(medium.hGlobal);
|
|
if (blockSize < sizeof(DROPFILES))
|
|
return E_INVALIDARG;
|
|
NMemory::CGlobalLock dropLock(medium.hGlobal);
|
|
const DROPFILES *dropFiles = (const DROPFILES *)dropLock.GetPointer();
|
|
if (!dropFiles)
|
|
return E_INVALIDARG;
|
|
if (blockSize < dropFiles->pFiles
|
|
|| dropFiles->pFiles < sizeof(DROPFILES)
|
|
// || dropFiles->pFiles != sizeof(DROPFILES)
|
|
)
|
|
return E_INVALIDARG;
|
|
const size_t size = blockSize - dropFiles->pFiles;
|
|
const void *namesData = (const Byte *)(const void *)dropFiles + dropFiles->pFiles;
|
|
HRESULT hres;
|
|
if (dropFiles->fWide)
|
|
{
|
|
if (size % sizeof(wchar_t) != 0)
|
|
return E_INVALIDARG;
|
|
hres = ReadUnicodeStrings((const wchar_t *)namesData, size / sizeof(wchar_t), names);
|
|
}
|
|
else
|
|
hres = ReadAnsiStrings((const char *)namesData, size, names);
|
|
|
|
ODS_(Print_Number(names.Size(), "DataObject_GetData_HDROP_Names"))
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
// CF_IDLIST:
|
|
#define MYWIN_CFSTR_SHELLIDLIST TEXT("Shell IDList Array")
|
|
|
|
typedef struct
|
|
{
|
|
UINT cidl;
|
|
UINT aoffset[1];
|
|
} MYWIN_CIDA;
|
|
/*
|
|
cidl : number of PIDLs that are being transferred, not including the parent folder.
|
|
aoffset : An array of offsets, relative to the beginning of this structure.
|
|
aoffset[0] - fully qualified PIDL of a parent folder.
|
|
If this PIDL is empty, the parent folder is the desktop.
|
|
aoffset[1] ... aoffset[cidl] : offset to one of the PIDLs to be transferred.
|
|
All of these PIDLs are relative to the PIDL of the parent folder.
|
|
*/
|
|
|
|
static HRESULT DataObject_GetData_IDLIST(IDataObject *dataObject, UStringVector &names)
|
|
{
|
|
names.Clear();
|
|
NCOM::CStgMedium medium;
|
|
RINOK(DataObject_GetData_HGLOBAL(dataObject, (CLIPFORMAT)
|
|
RegisterClipboardFormat(MYWIN_CFSTR_SHELLIDLIST), medium))
|
|
const size_t blockSize = GlobalSize(medium.hGlobal);
|
|
if (blockSize < sizeof(MYWIN_CIDA) || blockSize >= (UInt32)((UInt32)0 - 1))
|
|
return E_INVALIDARG;
|
|
NMemory::CGlobalLock dropLock(medium.hGlobal);
|
|
const MYWIN_CIDA *cida = (const MYWIN_CIDA *)dropLock.GetPointer();
|
|
if (!cida)
|
|
return E_INVALIDARG;
|
|
if (cida->cidl == 0)
|
|
{
|
|
// is it posssible to have no selected items?
|
|
// it's unexpected case.
|
|
return E_INVALIDARG;
|
|
}
|
|
if (cida->cidl >= (blockSize - (UInt32)sizeof(MYWIN_CIDA)) / sizeof(UINT))
|
|
return E_INVALIDARG;
|
|
const UInt32 start = cida->cidl * (UInt32)sizeof(UINT) + (UInt32)sizeof(MYWIN_CIDA);
|
|
|
|
STRRET strret;
|
|
CMyComPtr<IShellFolder> parentFolder;
|
|
{
|
|
const UINT offset = cida->aoffset[0];
|
|
if (offset < start || offset >= blockSize
|
|
// || offset != start
|
|
)
|
|
return E_INVALIDARG;
|
|
|
|
CMyComPtr<IShellFolder> desktopFolder;
|
|
RINOK(::SHGetDesktopFolder(&desktopFolder))
|
|
if (!desktopFolder)
|
|
return E_FAIL;
|
|
|
|
LPCITEMIDLIST const lpcItem = (LPCITEMIDLIST)(const void *)((const Byte *)cida + offset);
|
|
|
|
#ifdef SHOW_DEBUG_SHELL
|
|
{
|
|
const HRESULT res = desktopFolder->GetDisplayNameOf(
|
|
lpcItem, SHGDN_FORPARSING, &strret);
|
|
if (res == S_OK && strret.uType == STRRET_WSTR)
|
|
{
|
|
ODS_U(strret.pOleStr)
|
|
/* if lpcItem is empty, the path will be
|
|
"C:\Users\user_name\Desktop"
|
|
if lpcItem is "My Computer" folder, the path will be
|
|
"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" */
|
|
CoTaskMemFree(strret.pOleStr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
RINOK(desktopFolder->BindToObject(lpcItem,
|
|
NULL, IID_IShellFolder, (void **)&parentFolder))
|
|
if (!parentFolder)
|
|
return E_FAIL;
|
|
}
|
|
|
|
names.ClearAndReserve(cida->cidl);
|
|
UString path;
|
|
|
|
// for (int y = 0; y < 1; y++) // for debug
|
|
for (unsigned i = 1; i <= cida->cidl; i++)
|
|
{
|
|
const UINT offset = cida->aoffset[i];
|
|
if (offset < start || offset >= blockSize)
|
|
return E_INVALIDARG;
|
|
const void *p = (const Byte *)(const void *)cida + offset;
|
|
/* ITEMIDLIST of file can contain more than one SHITEMID item.
|
|
In win10 only SHGDN_FORPARSING returns path that contains
|
|
all path parts related to parts of ITEMIDLIST.
|
|
So we can use only SHGDN_FORPARSING here.
|
|
Don't use (SHGDN_INFOLDER)
|
|
Don't use (SHGDN_INFOLDER | SHGDN_FORPARSING)
|
|
*/
|
|
RINOK(parentFolder->GetDisplayNameOf((LPCITEMIDLIST)p, SHGDN_FORPARSING, &strret))
|
|
|
|
/*
|
|
// MSVC6 and old SDK do not support StrRetToStrW().
|
|
LPWSTR lpstr;
|
|
RINOK (StrRetToStrW(&strret, NULL, &lpstr))
|
|
ODS_U(lpstr)
|
|
path = lpstr;
|
|
CoTaskMemFree(lpstr);
|
|
*/
|
|
if (strret.uType != STRRET_WSTR)
|
|
return E_INVALIDARG;
|
|
ODS_U(strret.pOleStr)
|
|
path = strret.pOleStr;
|
|
// the path could have super path prefix "\\\\?\\"
|
|
// we can remove super path prefix here, if we don't need that prefix
|
|
#ifdef Z7_LONG_PATH
|
|
// we remove super prefix, if we can work without that prefix
|
|
NFile::NName::If_IsSuperPath_RemoveSuperPrefix(path);
|
|
#endif
|
|
names.AddInReserved(path);
|
|
CoTaskMemFree(strret.pOleStr);
|
|
}
|
|
|
|
ODS_(Print_Number(cida->cidl, "CFSTR_SHELLIDLIST END"))
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT DataObject_GetData_HDROP_or_IDLIST_Names(IDataObject *dataObject, UStringVector &paths)
|
|
{
|
|
ODS("-- DataObject_GetData_HDROP_or_IDLIST_Names START")
|
|
HRESULT hres = NShell::DataObject_GetData_HDROP_Names(dataObject, paths);
|
|
// if (hres == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
|
|
if (hres != S_OK)
|
|
{
|
|
ODS("-- DataObject_GetData_IDLIST START")
|
|
// for (int y = 0; y < 10000; y++) // for debug
|
|
hres = NShell::DataObject_GetData_IDLIST(dataObject, paths);
|
|
}
|
|
ODS("-- DataObject_GetData_HDROP_or_IDLIST_Names END")
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
// #if (NTDDI_VERSION >= NTDDI_VISTA)
|
|
typedef struct
|
|
{
|
|
UINT cItems; // number of items in rgdwFileAttributes array
|
|
DWORD dwSumFileAttributes; // all of the attributes ORed together
|
|
DWORD dwProductFileAttributes; // all of the attributes ANDed together
|
|
DWORD rgdwFileAttributes[1]; // array
|
|
} MYWIN_FILE_ATTRIBUTES_ARRAY;
|
|
|
|
#define MYWIN_CFSTR_FILE_ATTRIBUTES_ARRAY TEXT("File Attributes Array")
|
|
|
|
HRESULT DataObject_GetData_FILE_ATTRS(IDataObject *dataObject, CFileAttribs &attribs)
|
|
{
|
|
attribs.Clear();
|
|
NCOM::CStgMedium medium;
|
|
RINOK(DataObject_GetData_HGLOBAL(dataObject, (CLIPFORMAT)
|
|
RegisterClipboardFormat(MYWIN_CFSTR_FILE_ATTRIBUTES_ARRAY), medium))
|
|
const size_t blockSize = GlobalSize(medium.hGlobal);
|
|
if (blockSize < sizeof(MYWIN_FILE_ATTRIBUTES_ARRAY))
|
|
return E_INVALIDARG;
|
|
NMemory::CGlobalLock dropLock(medium.hGlobal);
|
|
const MYWIN_FILE_ATTRIBUTES_ARRAY *faa = (const MYWIN_FILE_ATTRIBUTES_ARRAY *)dropLock.GetPointer();
|
|
if (!faa)
|
|
return E_INVALIDARG;
|
|
const unsigned numFiles = faa->cItems;
|
|
if (numFiles == 0)
|
|
{
|
|
// is it posssible to have empty array here?
|
|
return E_INVALIDARG;
|
|
}
|
|
if ((blockSize - (sizeof(MYWIN_FILE_ATTRIBUTES_ARRAY) - sizeof(DWORD)))
|
|
/ sizeof(DWORD) != numFiles)
|
|
return E_INVALIDARG;
|
|
// attribs.Sum = faa->dwSumFileAttributes;
|
|
// attribs.Product = faa->dwProductFileAttributes;
|
|
// attribs.Vals.SetFromArray(faa->rgdwFileAttributes, numFiles);
|
|
// attribs.IsDirVector.ClearAndSetSize(numFiles);
|
|
|
|
if ((faa->dwSumFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
|
{
|
|
/* in win10: if selected items are volumes (c:\, d:\ ..) in My Compter,
|
|
all items have FILE_ATTRIBUTE_DIRECTORY attribute
|
|
ntfs volume also have FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM
|
|
udf volume: FILE_ATTRIBUTE_READONLY
|
|
dvd-rom device: (-1) : all bits are set
|
|
*/
|
|
const DWORD *attr = faa->rgdwFileAttributes;
|
|
// DWORD product = (UInt32)0 - 1, sum = 0;
|
|
for (unsigned i = 0; i < numFiles; i++)
|
|
{
|
|
if (attr[i] & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
// attribs.ThereAreDirs = true;
|
|
attribs.FirstDirIndex = (int)i;
|
|
break;
|
|
}
|
|
// attribs.IsDirVector[i] = (attr[i] & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
// product &= v;
|
|
// sum |= v;
|
|
}
|
|
// ODS_(Print_Number(product, "Product calc FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names"))
|
|
// ODS_(Print_Number(sum, "Sum calc FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names"))
|
|
}
|
|
// ODS_(Print_Number(attribs.Product, "Product FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names"))
|
|
// ODS_(Print_Number(attribs.Sum, "Sum FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names"))
|
|
ODS_(Print_Number(numFiles, "FILE_ATTRIBUTES_ARRAY ==== DataObject_GetData_HDROP_Names"))
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/////////////////////////////
|
|
// CDrop
|
|
|
|
/*
|
|
win10:
|
|
DragQueryFile() implementation code is not effective because
|
|
there is no pointer inside DROP internal file list, so
|
|
DragQueryFile(fileIndex) runs all names in range [0, fileIndex].
|
|
DragQueryFile(,, buf, bufSize)
|
|
if (buf == NULL) by spec
|
|
{
|
|
returns value is the required size
|
|
in characters, of the buffer, not including the terminating null character
|
|
tests show that if (bufSize == 0), then it also returns required size.
|
|
}
|
|
if (bufSize != NULL)
|
|
{
|
|
returns: the count of the characters copied, not including null character.
|
|
win10: null character is also copied at position buf[ret_count];
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void CDrop::Attach(HDROP object)
|
|
{
|
|
Free();
|
|
m_Object = object;
|
|
m_Assigned = true;
|
|
}
|
|
|
|
void CDrop::Free()
|
|
{
|
|
if (m_MustBeFinished && m_Assigned)
|
|
Finish();
|
|
m_Assigned = false;
|
|
}
|
|
|
|
UINT CDrop::QueryCountOfFiles()
|
|
{
|
|
return QueryFile(0xFFFFFFFF, (LPTSTR)NULL, 0);
|
|
}
|
|
|
|
void CDrop::QueryFileName(UINT fileIndex, UString &fileName)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
AString fileNameA;
|
|
const UINT len = QueryFile(fileIndex, (LPTSTR)NULL, 0);
|
|
const UINT numCopied = QueryFile(fileIndex, fileNameA.GetBuf(len + 2), len + 2);
|
|
fileNameA.ReleaseBuf_CalcLen(len);
|
|
if (numCopied != len)
|
|
throw 20221223;
|
|
fileName = GetUnicodeString(fileNameA);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// kReserve must be >= 3 for additional buffer size
|
|
// safety and for optimal performance
|
|
const unsigned kReserve = 3;
|
|
{
|
|
unsigned len = 0;
|
|
wchar_t *buf = fileName.GetBuf_GetMaxAvail(len);
|
|
if (len >= kReserve)
|
|
{
|
|
const UINT numCopied = QueryFile(fileIndex, buf, len);
|
|
if (numCopied < len - 1)
|
|
{
|
|
// (numCopied < len - 1) case means that it have copied full string.
|
|
fileName.ReleaseBuf_CalcLen(numCopied);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
const UINT len = QueryFile(fileIndex, (LPWSTR)NULL, 0);
|
|
const UINT numCopied = QueryFile(fileIndex,
|
|
fileName.GetBuf(len + kReserve), len + kReserve);
|
|
fileName.ReleaseBuf_CalcLen(len);
|
|
if (numCopied != len)
|
|
throw 20221223;
|
|
}
|
|
}
|
|
|
|
|
|
void CDrop::QueryFileNames(UStringVector &fileNames)
|
|
{
|
|
UINT numFiles = QueryCountOfFiles();
|
|
|
|
Print_Number(numFiles, "\n====== CDrop::QueryFileNames START ===== \n");
|
|
|
|
fileNames.ClearAndReserve(numFiles);
|
|
UString s;
|
|
for (UINT i = 0; i < numFiles; i++)
|
|
{
|
|
QueryFileName(i, s);
|
|
if (!s.IsEmpty())
|
|
fileNames.AddInReserved(s);
|
|
}
|
|
Print_Number(numFiles, "\n====== CDrop::QueryFileNames END ===== \n");
|
|
}
|
|
*/
|
|
|
|
|
|
// #if (NTDDI_VERSION >= NTDDI_VISTA)
|
|
// SHGetPathFromIDListEx returns a win32 file system path for the item in the name space.
|
|
typedef int Z7_WIN_GPFIDL_FLAGS;
|
|
|
|
extern "C" {
|
|
typedef BOOL (WINAPI * Func_SHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath);
|
|
typedef BOOL (WINAPI * Func_SHGetPathFromIDListEx)(LPCITEMIDLIST pidl, PWSTR pszPath, DWORD cchPath, Z7_WIN_GPFIDL_FLAGS uOpts);
|
|
}
|
|
|
|
#ifndef _UNICODE
|
|
|
|
bool GetPathFromIDList(LPCITEMIDLIST itemIDList, AString &path)
|
|
{
|
|
path.Empty();
|
|
const unsigned len = MAX_PATH + 16;
|
|
const bool result = BOOLToBool(::SHGetPathFromIDList(itemIDList, path.GetBuf(len)));
|
|
path.ReleaseBuf_CalcLen(len);
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
bool GetPathFromIDList(LPCITEMIDLIST itemIDList, UString &path)
|
|
{
|
|
path.Empty();
|
|
unsigned len = MAX_PATH + 16;
|
|
|
|
#ifdef _UNICODE
|
|
bool result = BOOLToBool(::SHGetPathFromIDList(itemIDList, path.GetBuf(len)));
|
|
#else
|
|
const
|
|
Func_SHGetPathFromIDListW
|
|
shGetPathFromIDListW = Z7_GET_PROC_ADDRESS(
|
|
Func_SHGetPathFromIDListW, ::GetModuleHandleW(L"shell32.dll"),
|
|
"SHGetPathFromIDListW");
|
|
if (!shGetPathFromIDListW)
|
|
return false;
|
|
bool result = BOOLToBool(shGetPathFromIDListW(itemIDList, path.GetBuf(len)));
|
|
#endif
|
|
|
|
if (!result)
|
|
{
|
|
ODS("==== GetPathFromIDList() SHGetPathFromIDList() returned false")
|
|
/* for long path we need SHGetPathFromIDListEx().
|
|
win10: SHGetPathFromIDListEx() for long path returns path with
|
|
with super path prefix "\\\\?\\". */
|
|
const
|
|
Func_SHGetPathFromIDListEx
|
|
func_SHGetPathFromIDListEx = Z7_GET_PROC_ADDRESS(
|
|
Func_SHGetPathFromIDListEx, ::GetModuleHandleW(L"shell32.dll"),
|
|
"SHGetPathFromIDListEx");
|
|
if (func_SHGetPathFromIDListEx)
|
|
{
|
|
ODS("==== GetPathFromIDList() (SHGetPathFromIDListEx)")
|
|
do
|
|
{
|
|
len *= 4;
|
|
result = BOOLToBool(func_SHGetPathFromIDListEx(itemIDList, path.GetBuf(len), len, 0));
|
|
if (result)
|
|
break;
|
|
}
|
|
while (len <= (1 << 16));
|
|
}
|
|
}
|
|
|
|
path.ReleaseBuf_CalcLen(len);
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef UNDER_CE
|
|
|
|
bool BrowseForFolder(LPBROWSEINFO, CSysString)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool BrowseForFolder(HWND, LPCTSTR, UINT, LPCTSTR, CSysString &)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool BrowseForFolder(HWND /* owner */, LPCTSTR /* title */,
|
|
LPCTSTR /* initialFolder */, CSysString & /* resultPath */)
|
|
{
|
|
/*
|
|
// SHBrowseForFolder doesn't work before CE 6.0 ?
|
|
if (GetProcAddress(LoadLibrary(L"ceshell.dll", L"SHBrowseForFolder") == 0)
|
|
MessageBoxW(0, L"no", L"", 0);
|
|
else
|
|
MessageBoxW(0, L"yes", L"", 0);
|
|
*/
|
|
/*
|
|
UString s = "all files";
|
|
s += " (*.*)";
|
|
return MyGetOpenFileName(owner, title, initialFolder, s, resultPath, true);
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
#else
|
|
|
|
/* win10: SHBrowseForFolder() doesn't support long paths,
|
|
even if long path suppport is enabled in registry and in manifest.
|
|
and SHBrowseForFolder() doesn't support super path prefix "\\\\?\\". */
|
|
|
|
bool BrowseForFolder(LPBROWSEINFO browseInfo, CSysString &resultPath)
|
|
{
|
|
resultPath.Empty();
|
|
NWindows::NCOM::CComInitializer comInitializer;
|
|
LPITEMIDLIST itemIDList = ::SHBrowseForFolder(browseInfo);
|
|
if (!itemIDList)
|
|
return false;
|
|
CItemIDList itemIDListHolder;
|
|
itemIDListHolder.Attach(itemIDList);
|
|
return GetPathFromIDList(itemIDList, resultPath);
|
|
}
|
|
|
|
|
|
static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM /* lp */, LPARAM data)
|
|
{
|
|
#ifndef UNDER_CE
|
|
switch (uMsg)
|
|
{
|
|
case BFFM_INITIALIZED:
|
|
{
|
|
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, data);
|
|
break;
|
|
}
|
|
/*
|
|
case BFFM_SELCHANGED:
|
|
{
|
|
TCHAR dir[MAX_PATH];
|
|
if (::SHGetPathFromIDList((LPITEMIDLIST) lp , dir))
|
|
SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)dir);
|
|
else
|
|
SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)TEXT(""));
|
|
break;
|
|
}
|
|
*/
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
static bool BrowseForFolder(HWND owner, LPCTSTR title, UINT ulFlags,
|
|
LPCTSTR initialFolder, CSysString &resultPath)
|
|
{
|
|
CSysString displayName;
|
|
BROWSEINFO browseInfo;
|
|
browseInfo.hwndOwner = owner;
|
|
browseInfo.pidlRoot = NULL;
|
|
|
|
// there are Unicode/Astring problems in some WinCE SDK ?
|
|
/*
|
|
#ifdef UNDER_CE
|
|
browseInfo.pszDisplayName = (LPSTR)displayName.GetBuf(MAX_PATH);
|
|
browseInfo.lpszTitle = (LPCSTR)title;
|
|
#else
|
|
*/
|
|
browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH);
|
|
browseInfo.lpszTitle = title;
|
|
// #endif
|
|
browseInfo.ulFlags = ulFlags;
|
|
browseInfo.lpfn = initialFolder ? BrowseCallbackProc : NULL;
|
|
browseInfo.lParam = (LPARAM)initialFolder;
|
|
return BrowseForFolder(&browseInfo, resultPath);
|
|
}
|
|
|
|
#ifdef Z7_OLD_WIN_SDK
|
|
// ShlObj.h:
|
|
#ifndef BIF_NEWDIALOGSTYLE
|
|
#define BIF_NEWDIALOGSTYLE 0x0040
|
|
#endif
|
|
#endif
|
|
|
|
bool BrowseForFolder(HWND owner, LPCTSTR title,
|
|
LPCTSTR initialFolder, CSysString &resultPath)
|
|
{
|
|
return BrowseForFolder(owner, title,
|
|
#ifndef UNDER_CE
|
|
BIF_NEWDIALOGSTYLE |
|
|
#endif
|
|
BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT, initialFolder, resultPath);
|
|
// BIF_STATUSTEXT; BIF_USENEWUI (Version 5.0)
|
|
}
|
|
|
|
#ifndef _UNICODE
|
|
|
|
extern "C" {
|
|
typedef LPITEMIDLIST (WINAPI * Func_SHBrowseForFolderW)(LPBROWSEINFOW lpbi);
|
|
}
|
|
|
|
static bool BrowseForFolder(LPBROWSEINFOW browseInfo, UString &resultPath)
|
|
{
|
|
NWindows::NCOM::CComInitializer comInitializer;
|
|
const
|
|
Func_SHBrowseForFolderW
|
|
f_SHBrowseForFolderW = Z7_GET_PROC_ADDRESS(
|
|
Func_SHBrowseForFolderW, ::GetModuleHandleW(L"shell32.dll"),
|
|
"SHBrowseForFolderW");
|
|
if (!f_SHBrowseForFolderW)
|
|
return false;
|
|
LPITEMIDLIST itemIDList = f_SHBrowseForFolderW(browseInfo);
|
|
if (!itemIDList)
|
|
return false;
|
|
CItemIDList itemIDListHolder;
|
|
itemIDListHolder.Attach(itemIDList);
|
|
return GetPathFromIDList(itemIDList, resultPath);
|
|
}
|
|
|
|
static
|
|
int CALLBACK BrowseCallbackProc2(HWND hwnd, UINT uMsg, LPARAM /* lp */, LPARAM data)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case BFFM_INITIALIZED:
|
|
{
|
|
SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, data);
|
|
break;
|
|
}
|
|
/*
|
|
case BFFM_SELCHANGED:
|
|
{
|
|
wchar_t dir[MAX_PATH * 2];
|
|
|
|
if (shGetPathFromIDListW((LPITEMIDLIST)lp , dir))
|
|
SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM)dir);
|
|
else
|
|
SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM)L"");
|
|
break;
|
|
}
|
|
*/
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static bool BrowseForFolder(HWND owner, LPCWSTR title, UINT ulFlags,
|
|
LPCWSTR initialFolder, UString &resultPath)
|
|
{
|
|
UString displayName;
|
|
BROWSEINFOW browseInfo;
|
|
browseInfo.hwndOwner = owner;
|
|
browseInfo.pidlRoot = NULL;
|
|
browseInfo.pszDisplayName = displayName.GetBuf(MAX_PATH);
|
|
browseInfo.lpszTitle = title;
|
|
browseInfo.ulFlags = ulFlags;
|
|
browseInfo.lpfn = initialFolder ? BrowseCallbackProc2 : NULL;
|
|
browseInfo.lParam = (LPARAM)initialFolder;
|
|
return BrowseForFolder(&browseInfo, resultPath);
|
|
}
|
|
|
|
bool BrowseForFolder(HWND owner, LPCWSTR title, LPCWSTR initialFolder, UString &resultPath)
|
|
{
|
|
if (g_IsNT)
|
|
return BrowseForFolder(owner, title,
|
|
BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS
|
|
// | BIF_STATUSTEXT // This flag is not supported when BIF_NEWDIALOGSTYLE is specified.
|
|
, initialFolder, resultPath);
|
|
// BIF_STATUSTEXT; BIF_USENEWUI (Version 5.0)
|
|
CSysString s;
|
|
bool res = BrowseForFolder(owner, GetSystemString(title),
|
|
BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS
|
|
// | BIF_STATUSTEXT // This flag is not supported when BIF_NEWDIALOGSTYLE is specified.
|
|
, GetSystemString(initialFolder), s);
|
|
resultPath = GetUnicodeString(s);
|
|
return res;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}}
|