windows-nt/Source/XPSP1/NT/shell/tools/clip/idlist.cpp
2020-09-26 16:20:57 +08:00

650 lines
15 KiB
C++

#include "stdafx.h"
#include <windows.h>
#include <shlobj.h>
#define NO_SHLWAPI_TPS
#include <shlwapi.h>
#include "idlist.h"
#define VALIDATE_PIDL(pidl) // add debugging code behind this
LPITEMIDLIST ILGetNext(LPCITEMIDLIST pidl)
{
if (pidl && pidl->mkid.cb)
{
VALIDATE_PIDL(pidl);
return _ILNext(pidl);
}
return NULL;
}
UINT ILGetSize(LPCITEMIDLIST pidl)
{
UINT cbTotal = 0;
if (pidl)
{
VALIDATE_PIDL(pidl);
cbTotal += sizeof(pidl->mkid.cb); // Null terminator
while (pidl->mkid.cb)
{
cbTotal += pidl->mkid.cb;
pidl = _ILNext(pidl);
}
}
return cbTotal;
}
#define CBIDL_MIN 256
#define CBIDL_INCL 256
LPITEMIDLIST ILCreate()
{
return (LPITEMIDLIST)CoTaskMemAlloc(CBIDL_MIN);
}
/*
** _ILResize
*
* PARAMETERS:
* cbExtra is the amount to add to cbRequired if the block needs to grow,
* or it is 0 if we want to resize to the exact size
*
* DESCRIPTION:
*
* RETURNS:
*
*/
LPITEMIDLIST _ILResize(LPITEMIDLIST pidl, UINT cbRequired, UINT cbExtra)
{
if (pidl == NULL)
{
pidl = (LPITEMIDLIST)CoTaskMemAlloc(cbRequired + cbExtra);
}
else
{
pidl = (LPITEMIDLIST)CoTaskMemRealloc(pidl, cbRequired + cbExtra);
}
return pidl;
}
LPITEMIDLIST ILFindLastID(LPCITEMIDLIST pidl)
{
LPCITEMIDLIST pidlLast = pidl;
LPCITEMIDLIST pidlNext = pidl;
VALIDATE_PIDL(pidl);
if (pidl == NULL)
return NULL;
// Find the last one
while (pidlNext->mkid.cb)
{
pidlLast = pidlNext;
pidlNext = _ILNext(pidlLast);
}
return (LPITEMIDLIST)pidlLast;
}
BOOL ILRemoveLastID(LPITEMIDLIST pidl)
{
BOOL fRemoved = FALSE;
if (pidl == NULL)
return(FALSE);
if (pidl->mkid.cb)
{
LPITEMIDLIST pidlLast = (LPITEMIDLIST)ILFindLastID(pidl);
pidlLast->mkid.cb = 0; // Remove the last one, null-terminator
fRemoved = TRUE;
}
return fRemoved;
}
LPITEMIDLIST ILClone(LPCITEMIDLIST pidl)
{
UINT cb = ILGetSize(pidl);
LPITEMIDLIST pidlRet = (LPITEMIDLIST)CoTaskMemAlloc(cb);
if (pidlRet)
CopyMemory(pidlRet, pidl, cb);
return pidlRet;
}
LPITEMIDLIST ILCloneFirst(LPCITEMIDLIST pidl)
{
UINT cb = pidl->mkid.cb+sizeof(pidl->mkid.cb);
LPITEMIDLIST pidlRet = (LPITEMIDLIST)CoTaskMemAlloc(cb);
if (pidlRet)
{
CopyMemory(pidlRet, pidl, pidl->mkid.cb);
_ILNext(pidlRet)->mkid.cb = 0;
}
return pidlRet;
}
BOOL ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
BOOL bRet = FALSE;
IShellFolder *psfDesktop;
if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop)))
{
bRet = psfDesktop->CompareIDs(0, pidl1, pidl2) == 0;
psfDesktop->Release();
}
return bRet;
}
// test if
// pidl1 is a parent of pidl2
BOOL ILIsParent(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fImmediate)
{
LPITEMIDLIST pidl2Prefix;
UINT cb;
LPCITEMIDLIST pidl1T;
LPCITEMIDLIST pidl2T;
VALIDATE_PIDL(pidl1);
VALIDATE_PIDL(pidl2);
if (!pidl1 || !pidl2)
return FALSE;
for (pidl1T = pidl1, pidl2T = pidl2; !ILIsEmpty(pidl1T);
pidl1T = _ILNext(pidl1T), pidl2T = _ILNext(pidl2T))
{
// if pidl2 is shorter than pidl1, pidl1 can't be its parent.
if (ILIsEmpty(pidl2T))
return FALSE;
}
if (fImmediate)
{
// If fImmediate is TRUE, pidl2T should contain exactly one ID.
if (ILIsEmpty(pidl2T) || !ILIsEmpty(_ILNext(pidl2T)))
return FALSE;
}
//
// Create a new IDList from a portion of pidl2, which contains the
// same number of IDs as pidl1.
//
cb = (UINT)pidl2T - (UINT)pidl2;
pidl2Prefix = (LPITEMIDLIST)CoTaskMemAlloc(cb + sizeof(pidl2->mkid.cb));
if (pidl2Prefix)
{
CopyMemory(pidl2Prefix, pidl2, cb);
*(USHORT *)((BYTE *)pidl2Prefix + cb) = 0;
BOOL fRet = ILIsEqual(pidl1, pidl2Prefix);
CoTaskMemFree(pidl2Prefix);
return fRet;
}
return FALSE;
}
// this returns a pointer to the child id ie:
// given pidlParent = \chicago\desktop
// pidlChild = \chicago\desktop\foo\bar
// the return will point to the ID that represents \foo\bar
// NULL is returned if pidlParent is not a parent of pidlChild
LPITEMIDLIST ILFindChild(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild)
{
if (ILIsParent(pidlParent, pidlChild, FALSE))
{
while (!ILIsEmpty(pidlParent))
{
pidlChild = _ILNext(pidlChild);
pidlParent = _ILNext(pidlParent);
}
return (LPITEMIDLIST)pidlChild;
}
return NULL;
}
LPITEMIDLIST ILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
LPITEMIDLIST pidlNew;
UINT cb1 = ILGetSize(pidl1) - sizeof(pidl1->mkid.cb);
UINT cb2 = ILGetSize(pidl2);
VALIDATE_PIDL(pidl1);
VALIDATE_PIDL(pidl2);
pidlNew = (LPITEMIDLIST)CoTaskMemAlloc(cb1 + cb2);
if (pidlNew)
{
CopyMemory(pidlNew, pidl1, cb1);
CopyMemory(((LPSTR)pidlNew) + cb1, pidl2, cb2);
}
return pidlNew;
}
LPITEMIDLIST ILCreateFromPath(LPCTSTR pszPath)
{
LPITEMIDLIST pidl = NULL;
IShellFolder *psfDesktop;
if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop)))
{
ULONG cchEaten;
WCHAR wszPath[MAX_PATH];
SHTCharToUnicode(pszPath, wszPath, ARRAYSIZE(wszPath));
psfDesktop->ParseDisplayName(NULL, NULL, wszPath, &cchEaten, &pidl, NULL);
psfDesktop->Release();
}
return pidl;
}
//===========================================================================
// IDLIST: Stream access
// FEATURE: check bytes read on Read calls?
//===========================================================================
STDAPI ILLoadFromStream(IStream *pstm, LPITEMIDLIST * ppidl)
{
HRESULT hres;
ULONG cb;
// Delete the old one if any.
if (*ppidl)
{
CoTaskMemFree(*ppidl);
*ppidl = NULL;
}
// Read the size of the IDLIST
cb = 0; // WARNING: We need to fill its HIWORD!
hres = pstm->Read(&cb, sizeof(USHORT), NULL); // Yes, USHORT
if (SUCCEEDED(hres) && cb)
{
// Create a IDLIST
LPITEMIDLIST pidl = (LPITEMIDLIST)CoTaskMemAlloc(cb);
if (pidl)
{
// Read its contents
hres = pstm->Read(pidl, cb, NULL);
if (SUCCEEDED(hres))
{
*ppidl = pidl;
}
else
{
CoTaskMemFree(pidl);
}
}
else
{
hres = E_OUTOFMEMORY;
}
}
return hres;
}
// FEATURE: check bytes written on Write calls?
STDAPI ILSaveToStream(IStream *pstm, LPCITEMIDLIST pidl)
{
HRESULT hres;
ULONG cb = ILGetSize(pidl);
hres = pstm->Write(&cb, sizeof(USHORT), NULL); // Yes, USHORT
if (SUCCEEDED(hres) && cb)
{
if (SUCCEEDED(hres))
{
hres = pstm->Write(pidl, cb, NULL);
}
}
return hres;
}
#ifdef _HIDA
//===========================================================================
// IDLARRAY stuff
//===========================================================================
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
HIDA HIDA_Create(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST * apidl)
{
HIDA hida;
UINT i;
UINT offset = sizeof(CIDA) + sizeof(UINT)*cidl;
UINT cbTotal = offset + ILGetSize(pidlFolder);
for (i=0; i<cidl ; i++) {
cbTotal += ILGetSize(apidl[i]);
}
hida = GlobalAlloc(GPTR, cbTotal); // This MUST be GlobalAlloc!!!
if (hida)
{
LPIDA pida = (LPIDA)hida; // no need to lock
LPCITEMIDLIST pidlNext;
pida->cidl = cidl;
for (i=0, pidlNext=pidlFolder; ; pidlNext=apidl[i++])
{
UINT cbSize = ILGetSize(pidlNext);
pida->aoffset[i] = offset;
CopyMemory(((LPBYTE)pida)+offset, pidlNext, cbSize);
offset += cbSize;
if (i==cidl)
break;
}
}
return hida;
}
HIDA HIDA_Create2(LPVOID pida, UINT cb)
{
HIDA hida = GlobalAlloc(GPTR, cb);
if (hida)
{
CopyMemory((LPIDA)hida, pida, cb); // no need to lock
}
return hida;
}
HIDA HIDA_Clone(HIDA hida)
{
UINT cbTotal = GlobalSize(hida);
HIDA hidaCopy = GlobalAlloc(GPTR, cbTotal);
if (hidaCopy)
{
LPIDA pida = (LPIDA)GlobalLock(hida);
CopyMemory((LPIDA)hidaCopy, pida, cbTotal); // no need to lock
GlobalUnlock(hida);
}
return hidaCopy;
}
UINT HIDA_GetCount(HIDA hida)
{
UINT count = 0;
LPIDA pida = (LPIDA)GlobalLock(hida);
if (pida) {
count = pida->cidl;
GlobalUnlock(hida);
}
return count;
}
UINT HIDA_GetIDList(HIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax)
{
LPIDA pida = (LPIDA)GlobalLock(hida);
if (pida)
{
LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida);
LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem(pida, i);
UINT cbFolder = ILGetSize(pidlFolder)-sizeof(USHORT);
UINT cbItem = ILGetSize(pidlItem);
if (cbMax < cbFolder+cbItem) {
if (pidlOut) {
pidlOut->mkid.cb = 0;
}
} else {
CopyMemory(pidlOut, pidlFolder, cbFolder);
CopyMemory(((LPBYTE)pidlOut)+cbFolder, pidlItem, cbItem);
}
GlobalUnlock(hida);
return (cbFolder+cbItem);
}
return 0;
}
//
// This one reallocated pidl if necessary. NULL is valid to pass in as pidl.
//
LPITEMIDLIST HIDA_FillIDList(HIDA hida, UINT i, LPITEMIDLIST pidl)
{
UINT cbRequired = HIDA_GetIDList(hida, i, NULL, 0);
pidl = _ILResize(pidl, cbRequired, 32); // extra 32-byte if we realloc
if (pidl)
{
HIDA_GetIDList(hida, i, pidl, cbRequired);
}
return pidl;
}
LPCITEMIDLIST IDA_GetIDListPtr(LPIDA pida, UINT i)
{
if (i == (UINT)-1 || i < pida->cidl)
{
return HIDA_GetPIDLItem(pida, i);
}
return NULL;
}
LPITEMIDLIST IDA_ILClone(LPIDA pida, UINT i)
{
if (i < pida->cidl)
return ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, i));
return NULL;
}
LPITEMIDLIST HIDA_ILClone(HIDA hida, UINT i)
{
LPIDA pida = (LPIDA)GlobalLock(hida);
if (pida)
{
LPITEMIDLIST pidl = IDA_ILClone(pida, i);
GlobalUnlock(hida);
return pidl;
}
return NULL;
}
void HIDA_ReleaseStgMedium(LPIDA pida, STGMEDIUM *pmedium)
{
if (pmedium->hGlobal && (pmedium->tymed==TYMED_HGLOBAL))
{
#ifdef DEBUG
if (pida)
{
LPIDA pidaT = (LPIDA)GlobalLock(pmedium->hGlobal);
GlobalUnlock(pmedium->hGlobal);
}
#endif
GlobalUnlock(pmedium->hGlobal);
}
SHReleaseStgMedium(pmedium);
}
#endif // _HIDA
#if 0
//
// This is a helper function to be called from within IShellFolder::CompareIDs.
// When the first IDs of pidl1 and pidl2 are the (logically) same.
//
// Required:
// psf && pidl1 && pidl2 && !ILEmpty(pidl1) && !ILEmpty(pidl2)
//
HRESULT ILCompareRelIDs(IShellFolder *psfParent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
HRESULT hres;
LPCITEMIDLIST pidlRel1 = _ILNext(pidl1);
LPCITEMIDLIST pidlRel2 = _ILNext(pidl2);
if (ILIsEmpty(pidlRel1))
{
if (ILIsEmpty(pidlRel2)) {
hres = 0;
} else {
hres = (HRESULT)-1;
}
}
else
{
if (ILIsEmpty(pidlRel2))
{
hres = 1;
}
else
{
//
// Neither pidlRel1 nor pidlRel2 is empty.
// (1) Bind to the next level of the IShellFolder
// (2) Call its CompareIDs to let it compare the rest of IDs.
//
// Notes: We should create pidlNext not from pidl2 but from pidl1
// because fstreex.c may pass simple pidl2.
//
LPITEMIDLIST pidlNext = ILClone(pidl1);
if (pidlNext)
{
IShellFolder *psfNext;
_ILNext(pidlNext)->mkid.cb = 0;
hres = psfParent->BindToObject(pidlNext, NULL,
&IID_IShellFolder, &psfNext);
if (SUCCEEDED(hres))
{
hres = psfNext->CompareIDs(0, pidlRel1, pidlRel2);
psfNext->Release();
}
CoTaskMemFree(pidlNext);
}
else
{
hres = E_OUTOFMEMORY;
}
}
}
return hres;
}
void StrRetFormat(LPSTRRET pStrRet, LPCITEMIDLIST pidlRel, LPCSTR pszTemplate, LPCSTR pszAppend)
{
LPSTR pszRet;
char szT[MAX_PATH];
StrRetToStrN(szT, sizeof(szT), pStrRet, pidlRel);
pszRet = ShellConstructMessageString(HINST_THISDLL, pszTemplate, pszAppend, szT);
if (pszRet)
{
pStrRet->uType = STRRET_CSTR;
lstrcpyn(pStrRet->cStr, pszRet, sizeof(pStrRet->cStr));
Free(pszRet);
}
}
//
// Notes: This one passes SHGDN_FORMARSING to ISF::GetDisplayNameOf.
//
HRESULT ILGetRelDisplayName(IShellFolder *psf, LPSTRRET pStrRet,
LPCITEMIDLIST pidlRel, LPCSTR pszName,
LPCSTR pszTemplate)
{
HRESULT hres;
LPITEMIDLIST pidlLeft = ILCloneFirst(pidlRel);
if (pidlLeft)
{
IShellFolder *psfNext;
hres = psf->BindToObject(pidlLeft, NULL, &IID_IShellFolder, &psfNext);
if (SUCCEEDED(hres))
{
LPCITEMIDLIST pidlRight = _ILNext(pidlRel);
hres = psfNext->GetDisplayNameOf(pidlRight, SHGDN_INFOLDER|SHGDN_FORPARSING, pStrRet);
if (SUCCEEDED(hres))
{
if (pszTemplate)
{
StrRetFormat(pStrRet, pidlRight, pszTemplate, pszName);
}
else
{
hres = StrRetCatLeft(pszName, pStrRet, pidlRight);
}
}
psfNext->Release();
}
CoTaskMemFree(pidlLeft);
}
else
{
hres = E_OUTOFMEMORY;
}
return hres;
}
LPITEMIDLIST ILAppendID(LPITEMIDLIST pidl, LPCSHITEMID pmkid, BOOL fAppend)
{
UINT cbUsed, cbRequired;
// Create the ID list, if it is not given.
if (!pidl)
{
pidl = ILCreate();
if (!pidl)
return NULL; // memory overflow
}
cbUsed = ILGetSize(pidl);
cbRequired = cbUsed + pmkid->cb;
pidl = _ILResize(pidl, cbRequired, CBIDL_INCL);
if (!pidl)
return NULL; // memory overflow
if (fAppend)
{
// Append it.
CopyMemory(_ILSkip(pidl, cbUsed-sizeof(pidl->mkid.cb)), pmkid, pmkid->cb);
}
else
{
// Put it at the top
MoveMemory(_ILSkip(pidl, pmkid->cb), pidl, cbUsed);
CopyMemory(pidl, pmkid, pmkid->cb);
}
// We must put zero-terminator because of LMEM_ZEROINIT.
_ILSkip(pidl, cbRequired-sizeof(pidl->mkid.cb))->mkid.cb = 0;
return pidl;
}
BOOL ILGetDisplayName(LPCITEMIDLIST pidl, LPSTR pszPath)
{
BOOL fSuccess = FALSE; // assume error
STRRET srName;
IShellFolder *psfDesktop;
SHGetDesktopFolder(&psfDesktop);
VALIDATE_PIDL(pidl);
if (SUCCEEDED(psfDesktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &srName)))
{
StrRetToStrN(pszPath, MAX_PATH, &srName, pidl);
fSuccess = TRUE;
}
return fSuccess;
}
#endif // not used