Windows-Server-2003/shell/lib/idhidden.cpp

244 lines
7.4 KiB
C++

#include "stock.h"
#pragma hdrstop
#include <idhidden.h>
// the last word of the pidl is where we store the hidden offset
#define _ILHiddenOffset(pidl) (*((WORD UNALIGNED *)(((BYTE *)_ILNext(pidl)) - sizeof(WORD))))
#define _ILSetHiddenOffset(pidl, cb) ((*((WORD UNALIGNED *)(((BYTE *)_ILNext(pidl)) - sizeof(WORD)))) = (WORD)cb)
#define _ILIsHidden(pidhid) (HIWORD(pidhid->id) == HIWORD(IDLHID_EMPTY))
STDAPI_(PCIDHIDDEN) _ILNextHidden(PCIDHIDDEN pidhid, LPCITEMIDLIST pidlLimit)
{
PCIDHIDDEN pidhidNext = (PCIDHIDDEN) _ILNext((LPCITEMIDLIST)pidhid);
if ((BYTE *)pidhidNext < (BYTE *)pidlLimit && _ILIsHidden(pidhidNext))
{
return pidhidNext;
}
// if we ever go past the limit,
// then this is not really a hidden id
// or we have messed up on some calculation.
ASSERT((BYTE *)pidhidNext == (BYTE *)pidlLimit);
return NULL;
}
STDAPI_(PCIDHIDDEN) _ILFirstHidden(LPCITEMIDLIST pidl)
{
WORD cbHidden = _ILHiddenOffset(pidl);
if (cbHidden && cbHidden + sizeof(HIDDENITEMID) < pidl->mkid.cb)
{
// this means it points to someplace inside the pidl
// maybe this has hidden ids
PCIDHIDDEN pidhid = (PCIDHIDDEN) (((BYTE *)pidl) + cbHidden);
if (_ILIsHidden(pidhid)
&& (pidhid->cb + cbHidden <= pidl->mkid.cb))
{
// this is more than likely a hidden id
// we could walk the chain and verify
// that it adds up right...
return pidhid;
}
}
return NULL;
}
//
// HIDDEN ids are sneakily hidden in the last ID in a pidl.
// we append our data without changing the existing pidl,
// (except it is now bigger) this works because the pidls
// that we will apply this to are flexible in handling different
// sized pidls. specifically this is used in FS pidls.
//
// WARNING - it is the callers responsibility to use hidden IDs
// only on pidls that can handle it. most shell pidls, and
// specifically FS pidls have no problem with this. however
// some shell extensions might have fixed length ids,
// which makes these unadvisable to append to everything.
// possibly add an SFGAO_ bit to allow hidden, otherwise key
// off FILESYSTEM bit.
//
STDAPI ILCloneWithHiddenID(LPCITEMIDLIST pidl, PCIDHIDDEN pidhid, LPITEMIDLIST *ppidl)
{
HRESULT hr;
// If this ASSERT fires, then the caller did not set the pidhid->id
// value properly. For example, the packing settings might be incorrect.
ASSERT(_ILIsHidden(pidhid));
if (ILIsEmpty(pidl))
{
*ppidl = NULL;
hr = E_INVALIDARG;
}
else
{
UINT cbUsed = ILGetSize(pidl);
UINT cbRequired = cbUsed + pidhid->cb + sizeof(pidhid->cb);
*ppidl = (LPITEMIDLIST)SHAlloc(cbRequired);
if (*ppidl)
{
hr = S_OK;
CopyMemory(*ppidl, pidl, cbUsed);
LPITEMIDLIST pidlLast = ILFindLastID(*ppidl);
WORD cbHidden = _ILFirstHidden(pidlLast) ? _ILHiddenOffset(pidlLast) : pidlLast->mkid.cb;
PIDHIDDEN pidhidCopy = (PIDHIDDEN)_ILSkip(*ppidl, cbUsed - sizeof((*ppidl)->mkid.cb));
// Append it, overwriting the terminator
MoveMemory(pidhidCopy, pidhid, pidhid->cb);
// grow the copy to allow the hidden offset.
pidhidCopy->cb += sizeof(pidhid->cb);
// now we need to readjust pidlLast to encompass
// the hidden bits and the hidden offset.
pidlLast->mkid.cb += pidhidCopy->cb;
// set the hidden offset so that we can find our hidden IDs later
_ILSetHiddenOffset((LPITEMIDLIST)pidhidCopy, cbHidden);
// We must put zero-terminator because of LMEM_ZEROINIT.
_ILSkip(*ppidl, cbRequired - sizeof((*ppidl)->mkid.cb))->mkid.cb = 0;
ASSERT(ILGetSize(*ppidl) == cbRequired);
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
// lame API that consumes pidl as input (caller must not touch after callign this)
STDAPI_(LPITEMIDLIST) ILAppendHiddenID(LPITEMIDLIST pidl, PCIDHIDDEN pidhid)
{
//
// FEATURE - we dont handle collisions of multiple hidden ids
// maybe remove IDs of the same IDLHID?
//
// Note: We do not remove IDLHID_EMPTY hidden ids.
// Callers need to call ILExpungeRemovedHiddenIDs explicitly
// if they want empty hidden ids to be compressed out.
//
RIP(pidl); // we require a pidl to attach the hidden id to
if (!ILIsEmpty(pidl))
{
LPITEMIDLIST pidlSave = pidl;
ILCloneWithHiddenID(pidl, pidhid, &pidl);
ILFree(pidlSave);
}
return pidl;
}
STDAPI_(PCIDHIDDEN) ILFindHiddenIDOn(LPCITEMIDLIST pidl, IDLHID id, BOOL fOnLast)
{
RIP(pidl);
if (!ILIsEmpty(pidl))
{
if (fOnLast)
pidl = ILFindLastID(pidl);
PCIDHIDDEN pidhid = _ILFirstHidden(pidl);
// reuse pidl to become the limit.
// so that we cant ever walk out of
// the pidl.
pidl = _ILNext(pidl);
while (pidhid)
{
if (pidhid->id == id)
break;
pidhid = _ILNextHidden(pidhid, pidl);
}
return pidhid;
}
return NULL;
}
STDAPI_(LPITEMIDLIST) ILCreateWithHidden(UINT cbNonHidden, UINT cbHidden)
{
// alloc enough for the two ids plus term and hidden tail
LPITEMIDLIST pidl;
UINT cb = cbNonHidden + cbHidden + sizeof(pidl->mkid.cb);
UINT cbAlloc = cb + sizeof(pidl->mkid.cb);
pidl = (LPITEMIDLIST)SHAlloc(cbAlloc);
if (pidl)
{
// zero-init for external task allocator
memset(pidl, 0, cbAlloc);
PIDHIDDEN pidhid = (PIDHIDDEN)_ILSkip(pidl, cbNonHidden);
// grow the copy to allow the hidden offset.
pidhid->cb = (USHORT) cbHidden + sizeof(pidhid->cb);
// now we need to readjust pidlLast to encompass
// the hidden bits and the hidden offset.
pidl->mkid.cb = (USHORT) cb;
// set the hidden offset so that we can find our hidden IDs later
_ILSetHiddenOffset(pidl, cbNonHidden);
ASSERT(ILGetSize(pidl) == cbAlloc);
ASSERT(_ILNext(pidl) == _ILNext((LPCITEMIDLIST)pidhid));
}
return pidl;
}
// Note: The space occupied by the removed ID is not reclaimed.
// Call ILExpungeRemovedHiddenIDs explicitly to reclaim the space.
STDAPI_(BOOL) ILRemoveHiddenID(LPITEMIDLIST pidl, IDLHID id)
{
PIDHIDDEN pidhid = (PIDHIDDEN)ILFindHiddenID(pidl, id);
if (pidhid)
{
pidhid->id = IDLHID_EMPTY;
return TRUE;
}
return FALSE;
}
STDAPI_(void) ILExpungeRemovedHiddenIDs(LPITEMIDLIST pidl)
{
if (pidl)
{
pidl = ILFindLastID(pidl);
// Note: Each IDHIDDEN has a WORD appended to it, equal to
// _ILHiddenOffset, so we can just keep deleting IDHIDDENs
// and if we delete them all, everything is cleaned up; if
// there are still unremoved IDHIDDENs left, they will provide
// the _ILHiddenOffset.
PIDHIDDEN pidhid;
BOOL fAnyDeleted = FALSE;
while ((pidhid = (PIDHIDDEN)ILFindHiddenID(pidl, IDLHID_EMPTY)) != NULL)
{
fAnyDeleted = TRUE;
LPBYTE pbAfter = (LPBYTE)pidhid + pidhid->cb;
WORD cbDeleted = pidhid->cb;
MoveMemory(pidhid, pbAfter,
(LPBYTE)pidl + pidl->mkid.cb + sizeof(WORD) - pbAfter);
pidl->mkid.cb -= cbDeleted;
}
}
}