Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3383 lines
110 KiB

//-------------------------------------------------------------------------//
//
// AugMisf.cpp - Augmented Merge IShellFolder class implementation.
//
//-------------------------------------------------------------------------//
#include "priv.h"
#include "augmisf.h"
#include "resource.h"
#include "mluisupp.h"
#define TF_AUGM 0x10000000
//-------------------------------------------------------------------------//
// BUGBUG: Shell allocator bullchit, inserted here because SHRealloc
// isn't imported into browseui, this module's hosting executable.
// If we get SHRealloc, the following block can be removed:
#define _EXPL_SHELL_ALLOCATOR_
#ifdef _EXPL_SHELL_ALLOCATOR_
#define SHRealloc( pv, cb ) shrealloc( pv, cb )
void* shrealloc( void* pv, size_t cb )
{
IMalloc* pMalloc ;
void* pvRet = NULL ;
if( SUCCEEDED( SHGetMalloc( &pMalloc ) ) ) {
pvRet = pMalloc->Realloc( pv, cb ) ;
ATOMICRELEASE( pMalloc ) ;
}
return pvRet ;
}
#endif _EXPL_SHELL_ALLOCATOR_
BOOL AffectAllUsers(HWND hwnd);
// id - verb mappings for IContextMenu impl
const struct
{
UINT id;
LPCSTR pszVerb;
} c_sIDVerbMap[] =
{
{SMIDM_DELETE, "delete"},
{SMIDM_RENAME, "rename"},
{SMIDM_PROPERTIES, "properties"},
//{SMIDM_OPEN, "open"},
//{SMIDM_EXPLORE, "explore"},
};
// augmisf context menu
class CAugMergeISFContextMenu : public IContextMenu2
{
public:
// *** IUnknown methods ***
STDMETHOD (QueryInterface)(REFIID, void**);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// *** IContextMenu methods ***
STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax);
// *** IContextMenu2 methods ***
STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
CAugMergeISFContextMenu(IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon,
IShellFolder *psfUser, LPCITEMIDLIST pidlUser, LPITEMIDLIST pidl,
HWND hwnd, UINT * prgfInOut);
~CAugMergeISFContextMenu();
friend class CAugmentedMergeISF;
friend CAugMergeISFContextMenu* CreateMergeISFContextMenu(
IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon,
IShellFolder *psfUser, LPCITEMIDLIST pidlUser, LPITEMIDLIST pidl,
HWND hwnd, UINT * prgfInOut);
protected:
LPITEMIDLIST _pidlItem;
IShellFolder * _psfCommon;
IShellFolder * _psfUser;
IContextMenu * _pcmCommon;
IContextMenu * _pcmUser;
LPITEMIDLIST _pidlCommon;
LPITEMIDLIST _pidlUser;
UINT _idFirst;
LONG _cRef;
HWND _hwnd;
};
CAugMergeISFContextMenu* CreateMergeISFContextMenu(
IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon,
IShellFolder *psfUser, LPCITEMIDLIST pidlUser, LPITEMIDLIST pidl,
HWND hwnd, UINT * prgfInOut)
{
CAugMergeISFContextMenu* pcm = new CAugMergeISFContextMenu(psfCommon, pidlCommon,
psfUser, pidlUser,
pidl, hwnd, prgfInOut);
if (pcm)
{
if (!pcm->_pidlItem)
{
delete pcm;
pcm = NULL;
}
}
return pcm;
}
CAugMergeISFContextMenu::CAugMergeISFContextMenu(IShellFolder *psfCommon, LPCITEMIDLIST pidlCommon,
IShellFolder *psfUser, LPCITEMIDLIST pidlUser,
LPITEMIDLIST pidl, HWND hwnd, UINT * prgfInOut)
{
_cRef = 1;
HRESULT hres;
_hwnd = hwnd;
_psfCommon = psfCommon;
if (_psfCommon)
{
_psfCommon->AddRef();
hres = _psfCommon->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidl, IID_IContextMenu, prgfInOut, (void **)&_pcmCommon);
ASSERT(SUCCEEDED(hres) || !_pcmCommon);
_pidlCommon = ILClone(pidlCommon);
}
_psfUser = psfUser;
if (_psfUser)
{
_psfUser->AddRef();
hres = _psfUser->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidl, IID_IContextMenu, prgfInOut, (void **)&_pcmUser);
ASSERT(SUCCEEDED(hres) || !_pcmUser);
_pidlUser = ILClone(pidlUser);
}
_pidlItem = ILClone(pidl);
ASSERT(_psfCommon || _psfUser);
}
CAugMergeISFContextMenu::~CAugMergeISFContextMenu()
{
ATOMICRELEASE(_psfCommon);
ATOMICRELEASE(_pcmCommon);
ATOMICRELEASE(_psfUser);
ATOMICRELEASE(_pcmUser);
ILFree(_pidlCommon);
ILFree(_pidlUser);
ILFree(_pidlItem);
}
STDMETHODIMP CAugMergeISFContextMenu::QueryInterface(REFIID riid, LPVOID *ppvOut)
{
static const QITAB qit[] = {
QITABENTMULTI(CAugMergeISFContextMenu, IContextMenu, IContextMenu2),
QITABENT(CAugMergeISFContextMenu, IContextMenu2),
{ 0 },
};
return QISearch(this, qit, riid, ppvOut);
}
STDMETHODIMP_(ULONG) CAugMergeISFContextMenu::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CAugMergeISFContextMenu::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
HRESULT CAugMergeISFContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
HRESULT hres = E_INVALIDARG;
if (hmenu)
{
HMENU hmContext = LoadMenuPopup(MENU_SM_CONTEXTMENU);
if (hmContext)
{
if (!_psfCommon || !_psfUser)
{
DeleteMenu(hmContext, SMIDM_OPENCOMMON, MF_BYCOMMAND);
DeleteMenu(hmContext, SMIDM_EXPLORECOMMON, MF_BYCOMMAND);
}
_idFirst = idCmdFirst;
Shell_MergeMenus(hmenu, hmContext, -1, idCmdFirst, idCmdLast, MM_ADDSEPARATOR);
DestroyMenu(hmContext);
// Make it look "Shell Like"
SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
hres = S_OK;
}
else
hres = E_OUTOFMEMORY;
}
return hres;
}
HRESULT CAugMergeISFContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
UINT id = -1;
HRESULT hres = E_FAIL;
CMINVOKECOMMANDINFO ici = *pici;
if (pici->cbSize < SIZEOF(CMINVOKECOMMANDINFO))
return E_INVALIDARG;
if (HIWORD(pici->lpVerb))
{
for (int i=0; i < ARRAYSIZE(c_sIDVerbMap); i++)
{
if (lstrcmpiA(pici->lpVerb, c_sIDVerbMap[i].pszVerb) == 0)
{
id = c_sIDVerbMap[i].id;
break;
}
}
}
else
id = (UINT) PtrToUlong( pici->lpVerb ); // Win64: should be ok since MAKEINTRESOURCE assumed
switch (id)
{
case -1:
hres = E_INVALIDARG;
break;
case SMIDM_OPEN:
case SMIDM_EXPLORE:
case SMIDM_OPENCOMMON:
case SMIDM_EXPLORECOMMON:
{
IShellFolder * psf;
LPITEMIDLIST pidl;
if (id == SMIDM_OPEN || id == SMIDM_EXPLORE)
{
if (_psfUser)
{
psf = _psfUser;
pidl = _pidlUser;
}
else
{
psf = _psfCommon;
pidl = _pidlCommon;
}
}
else
{
psf = _psfCommon;
pidl = _pidlCommon;
}
if (psf && pidl)
{
SHELLEXECUTEINFO shei = {0};
shei.lpIDList = ILCombine(pidl, _pidlItem);
if (shei.lpIDList)
{
shei.cbSize = sizeof(shei);
shei.fMask = SEE_MASK_IDLIST;
shei.nShow = SW_SHOWNORMAL;
if (id == SMIDM_EXPLORE || id == SMIDM_EXPLORECOMMON)
shei.lpVerb = TEXT("explore");
else
shei.lpVerb = TEXT("open");
hres = ShellExecuteEx(&shei) ? S_OK : E_FAIL;
ILFree((LPITEMIDLIST)shei.lpIDList);
}
}
}
break;
case SMIDM_PROPERTIES:
{
IContextMenu * pcm = _pcmUser ? _pcmUser : _pcmCommon;
if (pcm)
{
ici.lpVerb = "properties";
hres = pcm->InvokeCommand(&ici);
}
}
break;
case SMIDM_DELETE:
ici.lpVerb = "delete";
hres = S_OK;
if (_pcmUser)
{
hres = _pcmUser->InvokeCommand(&ici);
}
else if (_pcmCommon)
{
ici.fMask |= CMIC_MASK_FLAG_NO_UI;
if (AffectAllUsers(_hwnd))
hres = _pcmCommon->InvokeCommand(&ici);
else
hres = E_FAIL;
}
break;
case SMIDM_RENAME:
ASSERT(0);
hres = E_NOTIMPL; // sftbar picks this off
break;
}
return hres;
}
HRESULT CAugMergeISFContextMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax)
{
HRESULT hres = E_NOTIMPL;
// if hiword in not null then a string is passed to us. we don't handle that case (yet?)
if (!HIWORD(idCmd) && (uType == GCS_VERBA || uType == GCS_VERBW))
{
hres = E_INVALIDARG;
for (int i = 0; hres != S_OK && i < ARRAYSIZE(c_sIDVerbMap); i++)
{
if (c_sIDVerbMap[i].id == idCmd)
{
if (uType == GCS_VERBA)
lstrcpynA(pszName, c_sIDVerbMap[i].pszVerb, cchMax);
else
SHAnsiToUnicode(c_sIDVerbMap[i].pszVerb, (LPWSTR)pszName, cchMax);
hres = S_OK;
}
}
}
return hres;
}
// we need IContextMenu2 although HandleMenuMsg is not impl because of the way sftbar
// works -- it caches only IContextMenu2 so if we don't have ICM2 sftbar will think
// that it does not have context menu so it will eat the messages intended for the hmenu
// that way, with context menu up, if user presses esc it will kill start menu sub menu
// not the context menu.
HRESULT CAugMergeISFContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return E_NOTIMPL;
}
//-------------------------------------------------------------------------//
// Augmented Merge Shell Folder's pidl wrapper package consists of a versioned
// header followed by n 'source namespace' pidl wrappers.
// Each individual pidl wrapper consists of a header containing a
// collection lookup index followed by the source pidl itself. The
// source pidl's mkid.cb member is used to seek the next pidl wrap in
// the package.
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
//--- Augmented Merge Shell Folder's pidl wrapper header
typedef struct tagAUGM_IDWRAP {
USHORT cb ; // pidl wrap length
USHORT Reserved ; // reserved.
ULONG tag ; // AugMergeISF pidl signature
ULONG version ; // AugMergeISF pidl version
ULONG cSrcs ; // Number of source namespace objects backing this composite pidl
} AUGM_IDWRAP;
typedef UNALIGNED AUGM_IDWRAP *PAUGM_IDWRAP;
//--- Source pidl header. One or more of these records will be concatenated
// within the wrap following the wrap header.
typedef struct tagAUGM_IDSRC {
UINT nID ; // source namespace index
BYTE pidl[0] ; // source pidl
} AUGM_IDSRC;
typedef UNALIGNED AUGM_IDSRC *PAUGM_IDSRC;
//-------------------------------------------------------------------------//
// Constants
//-------------------------------------------------------------------------//
#define AUGM_WRAPTAG MAKELONG( MAKEWORD('A','u'), MAKEWORD('g','M') )
#define AUGM_WRAPVERSION_1_0 MAKELONG( 1, 0 )
#define AUGM_WRAPCURRENTVERSION AUGM_WRAPVERSION_1_0
#define INVALID_NAMESPACE_INDEX ((UINT)-1)
#define CB_IDLIST_TERMINATOR sizeof(USHORT)
//-------------------------------------------------------------------------//
// Augmented Merge shell folder pidl wrap utilities
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Resolves the wrap header from the indicated pidl.
#define AugMergeISF_GetWrap( p ) ((PAUGM_IDWRAP)(p))
//-------------------------------------------------------------------------//
// Determines whether the indicated pidl is an Augmented Merge
// shell folder pidl wrapper.
HRESULT AugMergeISF_IsWrap(
IN LPCITEMIDLIST pidlTest,
IN ULONG nVersion = AUGM_WRAPCURRENTVERSION )
{
ASSERT(IS_VALID_PIDL( pidlTest ));
if (pidlTest)
{
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlTest ) ;
return (pWrap->cb >= sizeof(AUGM_IDWRAP) &&
pWrap->tag == AUGM_WRAPTAG &&
pWrap->version == nVersion) ?
S_OK : E_UNEXPECTED ; //BUGBUG: better error code for version mismatch?
}
else
{
return E_INVALIDARG;
}
}
//-------------------------------------------------------------------------//
// Retrieves the number of source namespace pidls in the wrap.
// If the pidl was not wrapped, the return value is -1.
ULONG AugMergeISF_GetSourceCount( IN LPCITEMIDLIST pidl )
{
ASSERT(SUCCEEDED(AugMergeISF_IsWrap(pidl)));
return AugMergeISF_GetWrap(pidl)->cSrcs;
}
//-------------------------------------------------------------------------//
// Creates an IDLIST wrapper object based on the indicated source pidl.
HRESULT AugMergeISF_CreateWrap(
IN LPCITEMIDLIST pidlSrc,
IN UINT nSrcID,
OUT LPITEMIDLIST* ppidlWrap )
{
ASSERT( ppidlWrap ) ;
ASSERT( IS_VALID_PIDL( pidlSrc ) && INVALID_NAMESPACE_INDEX != nSrcID ) ;
*ppidlWrap = NULL ;
// Allocate a header and terminator
LPBYTE pBuf = NULL ;
WORD cbAlloc = sizeof(AUGM_IDWRAP) +
sizeof(AUGM_IDSRC) + pidlSrc->mkid.cb +
// we need two terminators, one for pidlSrc and one for the wrap
// the one for pidlSrc is necessary for the ILClone to work
// because it gets confused with the nSrcID that follows the pidl
CB_IDLIST_TERMINATOR +
CB_IDLIST_TERMINATOR ;
if( NULL == (pBuf = (LPBYTE)IEILCreate( cbAlloc )) )
return E_OUTOFMEMORY ;
// Initialize wrap header members
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pBuf ) ;
pWrap->cb = cbAlloc - CB_IDLIST_TERMINATOR ;
pWrap->tag = AUGM_WRAPTAG ;
pWrap->version = AUGM_WRAPCURRENTVERSION ;
if( pidlSrc )
{
PAUGM_IDSRC pSrc = (PAUGM_IDSRC)(pBuf + sizeof(AUGM_IDWRAP)) ;
pSrc->nID = nSrcID ;
memcpy( pSrc->pidl, pidlSrc, pidlSrc->mkid.cb ) ;
pWrap->cSrcs = 1 ;
}
*ppidlWrap = (LPITEMIDLIST)pWrap ;
return S_OK ;
}
BOOL WrappedPidlContainsSrcID(LPCITEMIDLIST pidlWrap, UINT uSrcID)
{
ASSERT(SUCCEEDED(AugMergeISF_IsWrap(pidlWrap)));
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ;
if( pWrap->cSrcs > 0 )
{
LPBYTE p = ((LPBYTE)pWrap) + sizeof(AUGM_IDWRAP) ; // position of first pidl header.
PAUGM_IDSRC pSrc = (PAUGM_IDSRC)p ;
// offset to next pidl header, needs terminator so that ILClone below can work
UINT cbPidl= ((LPITEMIDLIST)pSrc->pidl)->mkid.cb + CB_IDLIST_TERMINATOR;
if (pSrc->nID != uSrcID && pWrap->cSrcs > 1)
{
pSrc = (PAUGM_IDSRC)(p + sizeof(AUGM_IDSRC) + cbPidl) ;
}
if (pSrc->nID == uSrcID)
return TRUE;
}
return FALSE;
}
HRESULT AugMergeISF_WrapRemovePidl(
IN LPITEMIDLIST pidlWrap,
IN UINT nSrcID,
OUT LPITEMIDLIST* ppidlRet )
{
ASSERT( IS_VALID_WRITE_PTR( ppidlRet, LPITEMIDLIST )) ;
*ppidlRet = NULL ;
HRESULT hr = AugMergeISF_IsWrap(pidlWrap);
if (SUCCEEDED(hr))
{
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ;
ASSERT(pWrap->cSrcs > 1);
LPBYTE p = ((LPBYTE)pWrap) + sizeof(AUGM_IDWRAP) ; // position of first pidl header.
PAUGM_IDSRC pSrc = (PAUGM_IDSRC)p ;
// offset to next pidl header, needs terminator so that ILClone below can work
UINT cbPidl= ((LPITEMIDLIST)pSrc->pidl)->mkid.cb + CB_IDLIST_TERMINATOR;
// We want to look for the Other SrcID. So we loop while the source id we're removing is
// equal. When it's not equal, we've got the ID.
if (pSrc->nID == nSrcID)
{
pSrc = (PAUGM_IDSRC)(p + sizeof(AUGM_IDSRC) + cbPidl) ;
}
if (pSrc->nID != nSrcID)
{
hr = AugMergeISF_CreateWrap((LPITEMIDLIST)pSrc->pidl, pSrc->nID, ppidlRet);
ILFree(pidlWrap);
}
else
{
hr = E_FAIL;
}
}
return hr;
}
//-------------------------------------------------------------------------//
// Adds a source pidl to the indicated pidl wrap.
HRESULT AugMergeISF_WrapAddPidl(
IN LPCITEMIDLIST pidlSrc,
IN UINT nSrcID,
IN OUT LPITEMIDLIST* ppidlWrap )
{
ASSERT (ppidlWrap && IS_VALID_PIDL( *ppidlWrap ));
ASSERT (IS_VALID_PIDL( pidlSrc ));
ASSERT (INVALID_NAMESPACE_INDEX != nSrcID );
HRESULT hr ;
if (FAILED((hr = AugMergeISF_IsWrap(*ppidlWrap))))
return hr ;
// AHHHHHHHHHHH Rewrite this.
if (WrappedPidlContainsSrcID(*ppidlWrap, nSrcID))
{
if (AugMergeISF_GetSourceCount(*ppidlWrap) > 1)
{
hr = AugMergeISF_WrapRemovePidl((LPITEMIDLIST)*ppidlWrap, nSrcID, ppidlWrap);
}
else
{
ILFree(*ppidlWrap);
return AugMergeISF_CreateWrap(pidlSrc, nSrcID, ppidlWrap);
}
if (FAILED(hr))
{
return hr;
}
}
// Retrieve wrap header
PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)*ppidlWrap ;
// Reallocate a block large enough to contain our new record.
WORD offTerm0 = pWrap->cb, // offset to end of existing wrap
offTerm1 = offTerm0 + sizeof(AUGM_IDSRC) + pidlSrc->mkid.cb, // offset to end of next record
cbRealloc= offTerm1 + 2*CB_IDLIST_TERMINATOR ; // total bytes to reallocate
LPBYTE pRealloc ;
if( NULL == (pRealloc = (LPBYTE)SHRealloc( pWrap, cbRealloc )) )
return E_OUTOFMEMORY ;
// Adjust our pointers if memory moved
pWrap = (PAUGM_IDWRAP)pRealloc ;
// Initialize new record in the wrap
UNALIGNED AUGM_IDSRC* pSrc = (PAUGM_IDSRC)(pRealloc + offTerm0 ) ;
pSrc->nID = nSrcID ;
memcpy( pSrc->pidl, pidlSrc, pidlSrc->mkid.cb ) ;
// Terminate new record
ZeroMemory( pRealloc + offTerm1, 2*CB_IDLIST_TERMINATOR ) ;
// Update our header
pWrap->cb = cbRealloc - CB_IDLIST_TERMINATOR ;
pWrap->cSrcs++ ;
*ppidlWrap = (LPITEMIDLIST)pWrap ;
return S_OK ;
}
//-------------------------------------------------------------------------//
// Private pidl enumeration block (GetFirst/GetNext)
typedef struct tagAUGM_IDWRAP_ENUM
{
ULONG cbStruct ; // structure size
PAUGM_IDWRAP pWrap ; // wrap header.
PAUGM_IDSRC pSrcNext ; // pointer to next src header
} AUGM_IDWRAP_ENUM, *PAUGM_IDWRAP_ENUM ;
//-------------------------------------------------------------------------//
// Begins enumeration of source pidls in the indicated pidl wrap.
HANDLE AugMergeISF_EnumFirstSrcPidl(
IN LPCITEMIDLIST pidlWrap,
OUT UINT* pnSrcID,
OUT LPITEMIDLIST* ppidlRet )
{
ASSERT( IS_VALID_WRITE_PTR( ppidlRet, LPITEMIDLIST ) && IS_VALID_WRITE_PTR( pnSrcID, UINT ) ) ;
PAUGM_IDWRAP_ENUM pEnum = NULL ;
*ppidlRet = NULL ;
*pnSrcID = (UINT)-1 ;
HRESULT hr = AugMergeISF_IsWrap(pidlWrap);
if(SUCCEEDED(hr))
{
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ;
if( pWrap->cSrcs > 0 )
{
LPBYTE p = ((LPBYTE)pWrap) + sizeof(AUGM_IDWRAP) ; // position of first pidl header.
PAUGM_IDSRC pSrc = (PAUGM_IDSRC)p ;
// offset to next pidl header, needs terminator so that ILClone below can work
UINT cbPidl= ((LPITEMIDLIST)pSrc->pidl)->mkid.cb + CB_IDLIST_TERMINATOR;
if( NULL != (pEnum = new AUGM_IDWRAP_ENUM) )
{
pEnum->cbStruct = sizeof(*pEnum) ;
pEnum->pWrap = pWrap ;
pEnum->pSrcNext = (PAUGM_IDSRC)(p + sizeof(AUGM_IDSRC) + cbPidl) ;
*pnSrcID = pSrc->nID ;
*ppidlRet = ILClone( (LPITEMIDLIST)pSrc->pidl ) ;
if ( NULL == *ppidlRet )
{
delete pEnum;
pEnum = NULL;
}
}
}
}
return pEnum ;
}
//-------------------------------------------------------------------------//
// Continues source pidl enumeration
BOOL AugMergeISF_EnumNextSrcPidl(
IN HANDLE hEnum,
OUT UINT* pnSrcID,
OUT LPITEMIDLIST* ppidlRet )
{
PAUGM_IDWRAP_ENUM pEnum = (PAUGM_IDWRAP_ENUM)hEnum ;
HRESULT hr = E_UNEXPECTED ;
ASSERT( IS_VALID_WRITE_PTR( pEnum, AUGM_IDWRAP_ENUM ) ) ;
ASSERT( sizeof(*pEnum) == pEnum->cbStruct ) ;
ASSERT( sizeof(*pEnum) == pEnum->cbStruct );
*ppidlRet = NULL ;
*pnSrcID = (UINT)-1 ;
if (SUCCEEDED((hr = AugMergeISF_IsWrap((LPCITEMIDLIST)pEnum->pWrap))))
{
if ((LPBYTE)(pEnum->pWrap) + pEnum->pWrap->cb <= (LPBYTE)pEnum->pSrcNext)
hr = S_FALSE ;
else
{
UNALIGNED AUGM_IDSRC* pualSrcNext = pEnum->pSrcNext;
*pnSrcID = pualSrcNext->nID;
*ppidlRet = ILClone((LPITEMIDLIST)pualSrcNext->pidl);
pEnum->pSrcNext = (PAUGM_IDSRC)(
((LPBYTE)pualSrcNext) +
sizeof(AUGM_IDSRC) +
((LPITEMIDLIST)pualSrcNext->pidl)->mkid.cb +
CB_IDLIST_TERMINATOR);
hr = S_OK ;
return TRUE ;
}
}
return FALSE ;
}
//-------------------------------------------------------------------------//
// Terminates source pidl enumeration
void AugMergeISF_EndEnumSrcPidls(
IN OUT HANDLE& hEnum )
{
PAUGM_IDWRAP_ENUM pEnum = (PAUGM_IDWRAP_ENUM)hEnum ;
ASSERT( IS_VALID_WRITE_PTR( pEnum, AUGM_IDWRAP_ENUM ) &&
sizeof(*pEnum) == pEnum->cbStruct );
delete pEnum ;
hEnum = NULL ;
}
//-------------------------------------------------------------------------//
// Allocates and returns a copy of the specified source pidl
// from the wrapped pidl.
HRESULT AugMergeISF_GetSrcPidl(
IN LPCITEMIDLIST pidlWrap,
IN UINT nSrcID,
OUT LPITEMIDLIST* ppidlRet )
{
ASSERT( ppidlRet ) ;
*ppidlRet = NULL ;
HANDLE hEnum ;
BOOL bEnum ;
UINT nSrcIDEnum ;
LPITEMIDLIST pidlEnum ;
for( hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcIDEnum, &pidlEnum ), bEnum = TRUE ;
hEnum && bEnum ;
bEnum = AugMergeISF_EnumNextSrcPidl( hEnum, &nSrcIDEnum, &pidlEnum ) )
{
if( nSrcIDEnum == nSrcID )
{
*ppidlRet = pidlEnum ;
AugMergeISF_EndEnumSrcPidls(hEnum);
return S_OK ;
}
ILFree( pidlEnum ) ;
}
AugMergeISF_EndEnumSrcPidls( hEnum ) ;
return E_FAIL ;
}
#ifdef DEBUG
BOOL IsValidWrappedPidl(LPCITEMIDLIST pidlWrap)
{
BOOL fValid = FALSE;
if (pidlWrap == NULL)
return FALSE;
if (FAILED(AugMergeISF_IsWrap(pidlWrap)))
return FALSE;
HANDLE hEnum ;
UINT nSrcIDEnum ;
LPITEMIDLIST pidlEnum ;
hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcIDEnum, &pidlEnum );
do
{
fValid = IS_VALID_PIDL(pidlEnum);
ILFree(pidlEnum);
}
while( fValid && AugMergeISF_EnumNextSrcPidl( hEnum, &nSrcIDEnum, &pidlEnum ));
AugMergeISF_EndEnumSrcPidls( hEnum ) ;
return fValid;
}
#endif
//-------------------------------------------------------------------------//
int AugmEnumCompare(void *pv1, void *pv2, LPARAM lParam)
{
CAugISFEnumItem* paugmEnum1 = (CAugISFEnumItem*)pv1;
CAugISFEnumItem* paugmEnum2 = (CAugISFEnumItem*)pv2;
int iRet = -1;
if (paugmEnum1 && paugmEnum2)
{
// Are these two items of different types?
if (BOOLIFY(paugmEnum1->_rgfAttrib & SFGAO_FOLDER) ^ BOOLIFY(paugmEnum2->_rgfAttrib & SFGAO_FOLDER))
{
// Yes. Then Folders sort before items.
iRet = BOOLIFY(paugmEnum1->_rgfAttrib & SFGAO_FOLDER) ? 1 : -1;
}
else // They are of the same type. Then compare by name
{
iRet = lstrcmpi(paugmEnum1->_pszDisplayName, paugmEnum2->_pszDisplayName);
}
}
return iRet;
}
LPVOID AugmEnumMerge(UINT uMsg, void * pv1, void * pv2, LPARAM lParam)
{
void * pvRet = pv1;
switch (uMsg)
{
case DPAMM_MERGE:
{
HANDLE hEnum;
UINT nSrcID;
LPITEMIDLIST pidl;
CAugISFEnumItem* paugmeDest = (CAugISFEnumItem*)pv1;
CAugISFEnumItem* paugmeSrc = (CAugISFEnumItem*)pv2;
ASSERT(paugmeDest && paugmeSrc);
hEnum = AugMergeISF_EnumFirstSrcPidl(paugmeSrc->_pidlWrap, &nSrcID, &pidl);
if (hEnum)
{
// add pidl from src to dest
AugMergeISF_WrapAddPidl(pidl, nSrcID, &paugmeDest->_pidlWrap);
// no longer need hEnum
AugMergeISF_EndEnumSrcPidls(hEnum);
// this was copied to paugmeDest->_pidlWrap
ILFree(pidl);
}
}
break;
case DPAMM_INSERT:
{
CAugISFEnumItem* paugmNew = new CAugISFEnumItem;
CAugISFEnumItem* paugmSrc = (CAugISFEnumItem*)pv1;
if (paugmNew)
{
paugmNew->_pidlWrap = ILClone(paugmSrc->_pidlWrap);
if (paugmNew->_pidlWrap)
{
paugmNew->SetDisplayName(paugmSrc->_pszDisplayName);
paugmNew->_rgfAttrib = paugmSrc->_rgfAttrib;
}
else
{
delete paugmNew;
paugmNew = NULL;
}
}
pvRet = paugmNew;
}
break;
default:
ASSERT(0);
}
return pvRet;
}
typedef struct
{
LPTSTR pszDisplayName;
BOOL fFolder;
} AUGMISFSEARCHFORPIDL;
int CALLBACK AugMISFSearchForOnePidlByDisplayName(LPVOID p1, LPVOID p2, LPARAM lParam)
{
AUGMISFSEARCHFORPIDL* pSearchFor = (AUGMISFSEARCHFORPIDL*)p1;
CAugISFEnumItem* paugmEnum = (CAugISFEnumItem*)p2;
// Are they of different types?
if (BOOLIFY(paugmEnum->_rgfAttrib & SFGAO_FOLDER) ^ pSearchFor->fFolder)
{
// Yes.
return pSearchFor->fFolder ? 1 : -1;
}
else // They are of the same type. Then compare by name
{
return StrCmpI(pSearchFor->pszDisplayName, paugmEnum->_pszDisplayName);
}
}
//-------------------------------------------------------------------------------------------------//
// DPA utilities
#define DPA_GETPTRCOUNT( hdpa ) ((NULL != (hdpa)) ? DPA_GetPtrCount((hdpa)) : 0)
#define DPA_GETPTR( hdpa, i, type ) ((NULL != (hdpa)) ? (type*)DPA_GetPtr((hdpa), i) : (type*)NULL)
#define DPA_DESTROY( hdpa, pfn ) { if( NULL != hdpa ) \
{ DPA_DestroyCallback( hdpa, pfn, NULL ) ; \
hdpa = NULL ; }}
//-------------------------------------------------------------------------------------------------//
// Forwards...
class CEnum ;
class CChildObject ;
//-------------------------------------------------------------------------------------------------//
// Augmented Merge Shell Folder source namespace descriptor.
//
// Objects of class CNamespace are created by CAugmentedMergeISF in
// the AddNameSpace() method impl, and are maintained in the collection
// CAugmentedMergeISF::_hdpaNamespaces.
//
class CNamespace
//-------------------------------------------------------------------------------------------------//
{
public:
CNamespace( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib ) ;
~CNamespace() ;
IShellFolder* ShellFolder() { return _psf ; }
REFGUID Guid() { return _guid ; }
ULONG Attrib() const { return _dwAttrib ; }
LPITEMIDLIST GetPidl() const { return _pidl; }
void Assign( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib ) ;
void Unassign() ;
HRESULT RegisterNotify( HWND, UINT, ULONG ) ;
HRESULT UnregisterNotify() ;
BOOL SetOwner( IUnknown *punk ) ;
protected:
IShellFolder* _psf ; // IShellFolder interface pointer
GUID _guid ; // optional GUID for specialized UI handling
LPITEMIDLIST _pidl ; // optional pidl
ULONG _dwAttrib ; // optional flags
UINT _uChangeReg ; // Shell change notify registration ID.
} ;
//-------------------------------------------------------------------------------------------------//
inline CNamespace::CNamespace( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib )
: _psf(NULL),
_pidl(NULL),
_guid(GUID_NULL),
_dwAttrib(0),
_uChangeReg(0)
{
Assign( pguidUIObject, psf, pidl, dwAttrib ) ;
}
//-------------------------------------------------------------------------------------------------//
inline CNamespace::~CNamespace() {
UnregisterNotify() ;
Unassign() ;
}
//-------------------------------------------------------------------------------------------------//
// Assigns data members.
void CNamespace::Assign( const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib )
{
Unassign() ;
if( NULL != (_psf = psf) )
_psf->AddRef() ;
_pidl = ILClone( pidl ) ;
_guid = pguidUIObject ? *pguidUIObject : GUID_NULL ;
_dwAttrib = dwAttrib ;
}
//-------------------------------------------------------------------------------------------------//
// Unassigns data members.
void CNamespace::Unassign()
{
ATOMICRELEASE( _psf ) ;
ILFree( _pidl ) ;
_pidl = NULL ;
_guid = GUID_NULL ;
_dwAttrib = 0L ;
}
//-------------------------------------------------------------------------------------------------//
// Register change notification for the namespace
HRESULT CNamespace::RegisterNotify( HWND hwnd, UINT uMsg, ULONG lEvents )
{
if( 0 == _uChangeReg )
_uChangeReg = ::RegisterNotify(hwnd,
uMsg,
_pidl,
lEvents,
SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt,
TRUE);
return 0 != _uChangeReg ? S_OK : E_FAIL ;
}
//-------------------------------------------------------------------------------------------------//
// Unregister change notification for the namespace
HRESULT CNamespace::UnregisterNotify()
{
if( 0 != _uChangeReg )
{
UINT uID = _uChangeReg;
_uChangeReg = 0;
::SHChangeNotifyDeregister(uID);
}
return S_OK;
}
//-------------------------------------------------------------------------------------------------//
inline BOOL CNamespace::SetOwner(IUnknown *punkOwner)
{
if (_psf)
{
IUnknown_SetOwner(_psf, punkOwner);
return TRUE ;
}
return FALSE ;
}
//-------------------------------------------------------------------------//
CAugmentedMergeISF::CAugmentedMergeISF() : _cRef(1)
{
ASSERT(_hdpaNamespaces == NULL);
ASSERT(_punkOwner == NULL);
ASSERT(_pdt == NULL);
DllAddRef() ;
}
//-------------------------------------------------------------------------//
CAugmentedMergeISF::~CAugmentedMergeISF()
{
SetOwner(NULL);
FreeNamespaces();
DllRelease();
}
//-------------------------------------------------------------------------//
// CAugmentedMergeISF global CreateInstance method for da class factory
//-------------------------------------------------------------------------//
STDAPI CAugmentedISF2_CreateInstance( IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi )
{
// aggregation checking is handled in class factory
CAugmentedMergeISF* pObj;
if( NULL == (pObj = new CAugmentedMergeISF) )
return E_OUTOFMEMORY ;
*ppunk = SAFECAST( pObj, IAugmentedShellFolder2 * ) ;
return S_OK;
}
//-------------------------------------------------------------------------//
// CAugmentedMergeISF - IUnknown methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENTMULTI( CAugmentedMergeISF, IShellFolder, IAugmentedShellFolder ),
QITABENT( CAugmentedMergeISF, IAugmentedShellFolder ),
QITABENT( CAugmentedMergeISF, IAugmentedShellFolder2 ),
QITABENT( CAugmentedMergeISF, IShellFolder2 ),
QITABENT( CAugmentedMergeISF, IShellService ),
QITABENT( CAugmentedMergeISF, ITranslateShellChangeNotify ),
QITABENT( CAugmentedMergeISF, IDropTarget ),
{ 0 },
};
return QISearch( this, qit, riid, ppvObj ) ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CAugmentedMergeISF::AddRef()
{
return InterlockedIncrement(&_cRef);
}
//-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CAugmentedMergeISF::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
//-------------------------------------------------------------------------//
// CAugmentedMergeISF - IShellFolder methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
HRESULT hr = E_FAIL;
if (_hdpaNamespaces)
{
// BUGBUG (lamadio): This does not work if you have 2 enumerators. But,
// when asking for a new enumerator, we should flush the cache.
FreeObjects();
*ppenumIDList = new CEnum(this, grfFlags);
if (NULL == *ppenumIDList)
return E_OUTOFMEMORY ;
hr = S_OK ;
}
return hr;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::BindToObject( LPCITEMIDLIST pidlWrap, LPBC pbc, REFIID riid, LPVOID *ppvOut )
{
ASSERT(IS_VALID_PIDL( pidlWrap ) && NULL != ppvOut);
*ppvOut = NULL ;
if (SUCCEEDED(AugMergeISF_IsWrap(pidlWrap)))
{
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap( pidlWrap ) ;
ASSERT(IsValidWrappedPidl(pidlWrap));
ASSERT( pWrap ) ;
ASSERT( pWrap->cSrcs > 0 ) ; // should never, never happen
HANDLE hEnum ;
BOOL bEnum ;
UINT nIDSrc = -1 ;
DEBUG_CODE(int iNumBound = 0);
LPITEMIDLIST pidlSrc ;
HRESULT hr = E_UNEXPECTED ;
CNamespace* pSrc = NULL ;
CAugmentedMergeISF* pISF ;
if (NULL == (pISF = new CAugmentedMergeISF))
return E_OUTOFMEMORY ;
for (hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nIDSrc, &pidlSrc ), bEnum = TRUE ;
hEnum && bEnum ;
bEnum = AugMergeISF_EnumNextSrcPidl( hEnum, &nIDSrc, &pidlSrc))
{
if (SUCCEEDED((hr = QueryNameSpace(nIDSrc, (PVOID*)&pSrc))) && pSrc)
{
IShellFolder *psf;
hr = S_FALSE;
if (SUCCEEDED(pSrc->ShellFolder()->BindToObject(pidlSrc, NULL, IID_IShellFolder, (void **)&psf)))
{
LPCITEMIDLIST pidlParent = pSrc->GetPidl();
LPITEMIDLIST pidlFull = ILCombine(pidlParent, pidlSrc);
hr = pISF->AddNameSpace(NULL, psf, pidlFull, pSrc->Attrib());
#ifdef DEBUG
if (SUCCEEDED(hr))
iNumBound++;
#endif
ILFree(pidlFull);
psf->Release();
}
ASSERT(SUCCEEDED(hr));
}
ILFree(pidlSrc);
}
// If this hits, then something is terribly wrong. Either we were unable to bind to the
// ShellFolders, or the add failed. This could be caused by a bad wrapped pidl.
ASSERT(iNumBound > 0);
AugMergeISF_EndEnumSrcPidls( hEnum ) ;
hr = pISF->QueryInterface(riid, ppvOut);
pISF->Release();
return hr ;
}
return E_UNEXPECTED ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::BindToStorage( LPCITEMIDLIST, LPBC, REFIID, void ** )
{
return E_NOTIMPL ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::CompareIDs(
LPARAM lParam,
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2)
{
IShellFolder *psf1 = NULL, *psf2 = NULL;
LPITEMIDLIST pidlItem1 = NULL, pidlItem2 = NULL;
int iRet = 0 ;
HRESULT hr1, hr2, hr ;
hr1 = GetDefNamespace( pidl1, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf1, &pidlItem1 ) ;
hr2 = GetDefNamespace( pidl2, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf2, &pidlItem2 ) ;
if( SUCCEEDED( hr1 ) && SUCCEEDED( hr2 ) )
{
ULONG dwAttrib1 = SFGAO_FOLDER, dwAttrib2 = SFGAO_FOLDER;
// Same namespace? Just forward the request.
if( psf1 == psf2 )
{
hr = psf1->CompareIDs( lParam, pidlItem1, pidlItem2 ) ;
ILFree( pidlItem1 ) ;
ILFree( pidlItem2 ) ;
return hr ;
}
hr1 = psf1->GetAttributesOf( 1, (LPCITEMIDLIST*)&pidlItem1, &dwAttrib1 ) ;
hr2 = psf2->GetAttributesOf( 1, (LPCITEMIDLIST*)&pidlItem2, &dwAttrib2 ) ;
if( SUCCEEDED( hr1 ) && SUCCEEDED( hr2 ) )
{
// Comparison heuristics:
// (1) folders take precedence over nonfolders, (2) alphanum comparison
if( 0 != (dwAttrib1 & SFGAO_FOLDER) &&
0 == (dwAttrib2 & SFGAO_FOLDER) )
iRet = -1 ;
else if( 0 == (dwAttrib1 & SFGAO_FOLDER) &&
0 != (dwAttrib2 & SFGAO_FOLDER) )
iRet = 1 ;
else
{
STRRET strName1, strName2;
HRESULT hres1 = E_FAIL;
HRESULT hres2 = E_FAIL;
TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
hr1 = psf1->GetDisplayNameOf(pidlItem1, SHGDN_FORPARSING | SHGDN_INFOLDER, &strName1);
hr2 = psf2->GetDisplayNameOf(pidlItem2, SHGDN_FORPARSING | SHGDN_INFOLDER, &strName2);
if (SUCCEEDED(hr1) && SUCCEEDED(hr2))
{
// must call StrRetToBuf because it frees StrRet strings if allocated
hres1 = StrRetToBuf(&strName1, pidlItem1, szName1, ARRAYSIZE(szName1));
hres2 = StrRetToBuf(&strName2, pidlItem2, szName2, ARRAYSIZE(szName2));
}
// if the names match we return -1 because they are different pidls with
// the same name
if (SUCCEEDED(hr1) && SUCCEEDED(hr2) && SUCCEEDED(hres1) && SUCCEEDED(hres2))
{
iRet = lstrcmp(szName1, szName2); // Comparisons are by name with items of the same type.
}
}
}
}
hr = FAILED( hr1 ) ? hr1 :
FAILED( hr2 ) ? hr2 :
S_OK ;
if( pidlItem1 )
ILFree( pidlItem1 ) ;
if( pidlItem2 )
ILFree( pidlItem2 ) ;
return MAKE_HRESULT( HRESULT_SEVERITY( hr ), HRESULT_FACILITY( hr ), iRet ) ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::CreateViewObject(
HWND hwndOwner,
REFIID riid,
LPVOID * ppvOut )
{
HRESULT hr ;
CNamespace *pSrc, *pSrc0 ;
pSrc = pSrc0 = NULL ;
// TODO: Handle IDropTarget here, delegate for all others.
if (IsEqualIID(riid, IID_IDropTarget))
{
hr = QueryInterface(riid, ppvOut);
if (SUCCEEDED(hr))
_hwnd = hwndOwner;
return hr;
}
// Search for default namespace for CreateViewObj()
if( FAILED( (hr = GetDefNamespace( ASFF_DEFNAMESPACE_VIEWOBJ, (PVOID*)&pSrc, NULL, (PVOID*)&pSrc0 )) ) )
return hr ;
if( NULL == pSrc )
pSrc = pSrc0 ;
if( NULL != pSrc )
{
ASSERT( pSrc->ShellFolder() ) ;
hr = pSrc->ShellFolder()->CreateViewObject( hwndOwner, riid, ppvOut ) ;
}
return hr ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetAttributesOf(
UINT cidl,
LPCITEMIDLIST * apidl,
ULONG * rgfInOut )
{
IShellFolder* pISF ;
LPITEMIDLIST pidlItem ;
HRESULT hr ;
if( cidl > 1 ) // support 1 only.
return E_NOTIMPL ;
if( !apidl )
return E_INVALIDARG ;
// Forward to default namespace for item attributes
if( FAILED( (hr = GetDefNamespace(
apidl[0], ASFF_DEFNAMESPACE_ATTRIB, &pISF, &pidlItem )) ) )
return hr ;
hr = pISF->GetAttributesOf( 1, (LPCITEMIDLIST*)&pidlItem, rgfInOut ) ;
ILFree( pidlItem ) ;
return hr ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetUIObjectOf(
HWND hwndOwner,
UINT cidl,
LPCITEMIDLIST * apidl,
REFIID riid,
UINT * prgfInOut,
LPVOID * ppvOut )
{
IShellFolder* pISF ;
LPITEMIDLIST pidlItem ;
HRESULT hr ;
if (cidl > 1) // support 1 only.
return E_NOTIMPL ;
if (!apidl)
return E_INVALIDARG ;
if (IsEqualGUID(riid, IID_IContextMenu))
{
hr = _GetContextMenu(hwndOwner, cidl, apidl, prgfInOut, ppvOut);
}
else
{
// Forward to default namespace for UI object
if (FAILED((hr = GetDefNamespace(apidl[0], ASFF_DEFNAMESPACE_UIOBJ, &pISF, &pidlItem))))
return hr ;
hr = pISF->GetUIObjectOf(hwndOwner, 1, (LPCITEMIDLIST*)&pidlItem, riid, prgfInOut, ppvOut);
ILFree(pidlItem);
}
return hr ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetDisplayNameOf(
LPCITEMIDLIST pidl,
DWORD grfFlags,
LPSTRRET pstrName )
{
IShellFolder* pISF ;
LPITEMIDLIST pidlItem ;
HRESULT hr ;
// Forward to default namespace for display name
if (FAILED((hr = GetDefNamespace(
pidl, ASFF_DEFNAMESPACE_DISPLAYNAME, &pISF, &pidlItem))))
return hr ;
if (SUCCEEDED((hr = pISF->GetDisplayNameOf(pidlItem, grfFlags, pstrName))))
{
// STRRET_OFFSET has no meaning in context of the pidl wrapper.
// We can either calculate the offset into the wrapper, or allocate
// a wide char for the name. For expedience, we'll allocate the name.
if (pstrName->uType == STRRET_OFFSET)
{
UINT cch = lstrlenA( STRRET_OFFPTR( pidlItem, pstrName ) ) ;
LPWSTR pwszName = (LPWSTR)SHAlloc( (cch + 1) * sizeof(WCHAR));
if (NULL != pwszName)
{
SHAnsiToUnicode( STRRET_OFFPTR( pidlItem, pstrName ), pwszName, cch+1 );
pwszName[cch] = (WCHAR)0 ;
}
pstrName->pOleStr = pwszName ;
pstrName->uType = STRRET_WSTR ;
}
#ifdef DEBUG
// If the trace flags are set, and this is not comming from an internal query,
// Then append the location where this name came from
if (g_qwTraceFlags & TF_AUGM && _fInternalGDNO == FALSE)
{
if (pstrName->uType == STRRET_WSTR)
{
LPWSTR wszOldName = pstrName->pOleStr;
UINT cch = lstrlenW(wszOldName);
pstrName->pOleStr = (LPWSTR)SHAlloc( (cch + 50) * sizeof(WCHAR));
if (pstrName->pOleStr)
{
StrCpyW(pstrName->pOleStr, wszOldName);
if (AugMergeISF_GetSourceCount(pidl) > 1)
StrCatW(pstrName->pOleStr, L" (Merged)");
else if (WrappedPidlContainsSrcID(pidl, 0))
StrCatW(pstrName->pOleStr, L" (1)");
else
StrCatW(pstrName->pOleStr, L" (2)");
SHFree(wszOldName);
}
else
{
pstrName->pOleStr = wszOldName;
}
}
else if (pstrName->uType == STRRET_CSTR)
{
if (AugMergeISF_GetSourceCount(pidl) > 1)
StrCatA(pstrName->cStr, " (Merged)");
else if (WrappedPidlContainsSrcID(pidl, 0))
StrCatA(pstrName->cStr, " (1)");
else
StrCatA(pstrName->cStr, " (2)");
}
}
#endif
}
ILFree( pidlItem ) ;
return hr ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::ParseDisplayName(
HWND hwndOwner,
LPBC pbcReserved,
LPOLESTR pwszName,
ULONG * pchEaten,
LPITEMIDLIST * ppidl,
ULONG * pdwAttrib )
{
int iIndex;
LPITEMIDLIST pidl;
*ppidl = NULL;
// This ParseDisplayName should iterate through all our delegates until one works.
for (iIndex = NamespaceCount() - 1; iIndex >=0 ; iIndex--)
{
CNamespace* pSrc = Namespace(iIndex) ;
if (pSrc)
{
if (SUCCEEDED(pSrc->ShellFolder()->ParseDisplayName(hwndOwner, pbcReserved, pwszName, pchEaten,
&pidl, pdwAttrib)))
{
ASSERT(pidl); // Make sure a valid pidl comes out.
if (*ppidl == NULL)
AugMergeISF_CreateWrap(pidl, iIndex, ppidl);
else
AugMergeISF_WrapAddPidl(pidl, iIndex, ppidl);
ILFree(pidl);
}
}
}
return *ppidl? S_OK : E_FAIL;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::SetNameOf(
HWND hwndOwner,
LPCITEMIDLIST pidl,
LPCOLESTR pwszName,
DWORD uFlags,
LPITEMIDLIST *ppidlOut )
{
CNamespace* pnsCommon;
CNamespace* pnsUser;
LPITEMIDLIST pidlItem;
HRESULT hres;
UINT uiUser;
UINT uiCommon;
hres = _GetNamespaces(pidl, &pnsCommon, &uiCommon, &pnsUser, &uiUser, &pidlItem, NULL);
if (SUCCEEDED(hres))
{
LPITEMIDLIST pidlNew = NULL;
UINT uiNamespace = INVALID_NAMESPACE_INDEX;
if (pnsUser)
{
hres = pnsUser->ShellFolder()->SetNameOf(hwndOwner, pidlItem, pwszName, uFlags, &pidlNew);
uiNamespace = uiUser;
}
else if (pnsCommon)
{
hres = E_FAIL;
if (AffectAllUsers(hwndOwner))
{
hres = pnsCommon->ShellFolder()->SetNameOf(hwndOwner, pidlItem, pwszName, uFlags, &pidlNew);
uiNamespace = uiCommon;
}
}
if (ppidlOut)
{
*ppidlOut = NULL;
// wrap the pidl
if (SUCCEEDED(hres) && pidlNew)
AugMergeISF_CreateWrap(pidlNew, uiNamespace, ppidlOut);
}
ILFree(pidlNew);
ILFree(pidlItem);
}
return hres;
}
//-------------------------------------------------------------------------//
// IAugmentedShellFolder methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Adds a source namespace to the Augmented Merge shell folder object.
STDMETHODIMP CAugmentedMergeISF::AddNameSpace(
const GUID * pguidObject,
IShellFolder * psf,
LPCITEMIDLIST pidl,
DWORD dwFlags )
{
ASSERT (IS_VALID_CODE_PTR(psf, IShellFolder*));
ASSERT (IS_VALID_PIDL(pidl));
// Check for duplicate via full display name
for( int i=0, max = NamespaceCount() ; i < max; i++ )
{
CNamespace* pSrc = Namespace( i ) ;
if (pSrc)
{
if (ILIsEqual(pSrc->GetPidl(), pidl))
{
// Found! Reassign attributes
pSrc->Assign( pguidObject, psf, pidl, dwFlags ) ;
return S_OK ;
}
}
}
// No match; safe to append it to collection, creating DPA if necessary.
if( NULL == _hdpaNamespaces &&
NULL == (_hdpaNamespaces= DPA_Create( 2 )) )
return E_OUTOFMEMORY ;
CNamespace *pSrc = new CNamespace( pguidObject, psf, pidl, dwFlags );
if( NULL == pSrc )
return E_OUTOFMEMORY ;
return DPA_AppendPtr( _hdpaNamespaces, pSrc ) >= 0 ? S_OK : E_FAIL;
}
//-------------------------------------------------------------------------//
// Retrieves the primary namespace iid for the wrapped pidl.
STDMETHODIMP CAugmentedMergeISF::GetNameSpaceID(
LPCITEMIDLIST pidl,
GUID * pguidOut )
{
HRESULT hr ;
if (FAILED((hr = AugMergeISF_IsWrap( pidl ))))
return hr ;
// BUGBUG: need to enumerate wrapped source pidls
return E_NOTIMPL ;
}
//-------------------------------------------------------------------------//
// Retrieves a pointer to a source namespace descriptor associated with
// the specified lookup index.
STDMETHODIMP CAugmentedMergeISF::QueryNameSpace( ULONG nID, PVOID* ppSrc )
{
if (!ppSrc)
return E_INVALIDARG;
*ppSrc = NULL;
LONG cSrcs;
if ((cSrcs = NamespaceCount()) <=0)
return E_FAIL;
if(nID >= (ULONG)cSrcs)
return E_INVALIDARG;
if (NULL == (*ppSrc = Namespace(nID)))
return E_UNEXPECTED;
return S_OK;
}
//-------------------------------------------------------------------------//
// Retrieves data for the namespace identified by dwID.
STDMETHODIMP CAugmentedMergeISF::QueryNameSpace(
ULONG nID,
GUID * pguidOut,
IShellFolder ** ppsf )
{
CNamespace* pSrc = NULL ;
HRESULT hr = QueryNameSpace( nID, (PVOID*)&pSrc ) ;
if( pguidOut )
*pguidOut = NULL != pSrc ? pSrc->Guid() : GUID_NULL ;
if( ppsf )
{
if( (*ppsf = (NULL != pSrc) ? pSrc->ShellFolder() : NULL) != NULL )
(*ppsf)->AddRef() ;
}
return hr ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::EnumNameSpace(
DWORD uNameSpace,
DWORD * pdwID )
{
return E_NOTIMPL ;
}
//-------------------------------------------------------------------------//
// IAugmentedShellFolder2 methods
//-------------------------------------------------------------------------//
//GetNameSpaceCount and GetIDListWrapCount are not used anywhere
#if 0
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetNameSpaceCount( OUT LONG* pcNamespaces )
{
if( !pcNamespaces )
return E_INVALIDARG ;
*pcNamespaces = (LONG)NamespaceCount() ;
return S_OK ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetIDListWrapCount(
LPCITEMIDLIST pidlWrap,
OUT LONG * pcPidls)
{
if( NULL == pidlWrap || NULL == pcPidls )
return E_INVALIDARG ;
*pcPidls = 0 ;
HRESULT hr ;
if (SUCCEEDED((hr = AugMergeISF_IsWrap(pidlWrap))))
{
PAUGM_IDWRAP pWrap = AugMergeISF_GetWrap(pidlWrap);
*pcPidls = pWrap->cSrcs;
hr = S_OK;
}
return hr;
}
#endif // #if 0
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::UnWrapIDList(
LPCITEMIDLIST pidlWrap,
LONG cPidls,
IShellFolder** apsf,
LPITEMIDLIST* apidlFolder,
LPITEMIDLIST* apidlItems,
LONG* pcFetched )
{
HRESULT hr ;
HANDLE hEnum ;
BOOL bEnum = TRUE ;
UINT nSrcID ;
LPITEMIDLIST pidlItem ;
LONG cFetched = 0;
if (NULL == pidlWrap || cPidls <= 0)
return E_INVALIDARG ;
if (FAILED((hr = AugMergeISF_IsWrap(pidlWrap))))
return hr ;
// Enumerate pidls in wrap
for (hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcID, &pidlItem);
cFetched < cPidls && hEnum && bEnum ;
bEnum = AugMergeISF_EnumNextSrcPidl( hEnum, &nSrcID, &pidlItem))
{
// Retrieve namespace data
CNamespace* pSrc ;
if (SUCCEEDED((hr = QueryNameSpace(nSrcID, (PVOID*)&pSrc))))
{
if (apsf)
{
apsf[cFetched] = pSrc->ShellFolder() ;
if (apsf[cFetched])
apsf[cFetched]->AddRef();
}
if (apidlFolder)
apidlFolder[cFetched] = ILClone(pSrc->GetPidl());
if (apidlItems)
{
apidlItems[cFetched] = pidlItem;
pidlItem = NULL; // paranoia -- just making sure we, somehow, don't free this guy at the end of the for loop
}
cFetched++ ;
}
else
{
ILFree( pidlItem ) ;
}
}
ILFree(pidlItem); // AugMergeISF_EnumNextSrcPidl is called (if there are 2 wrapped pidls and we ask for only one)
// right before we exit the for loop so we have to free pidl if allocated.
if (hEnum)
AugMergeISF_EndEnumSrcPidls( hEnum );
if( pcFetched )
*pcFetched = cFetched ;
return cFetched == cPidls ? S_OK : S_FALSE ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::SetOwner( IUnknown* punkOwner )
{
HRESULT hr = S_OK ;
int cSrcs = NamespaceCount() ;
if( cSrcs > 0 )
DPA_EnumCallback( _hdpaNamespaces, SetOwnerProc, NULL ) ;
ATOMICRELEASE( _punkOwner ) ;
if( punkOwner )
{
hr = punkOwner->QueryInterface(IID_IUnknown, (LPVOID *)&_punkOwner ) ;
if( cSrcs )
DPA_EnumCallback( _hdpaNamespaces, SetOwnerProc, (void *)_punkOwner);
}
return hr ;
}
//-------------------------------------------------------------------------//
int CAugmentedMergeISF::SetOwnerProc( LPVOID pv, LPVOID pvParam )
{
CNamespace* pSrc = (CNamespace*) pv ;
ASSERT( pSrc ) ;
return pSrc->SetOwner( (IUnknown*)pvParam ) ;
}
//-------------------------------------------------------------------------//
// ITranslateShellChangeNotify methods
//-------------------------------------------------------------------------//
LPITEMIDLIST ILCombineBase(LPCITEMIDLIST pidlContainingBase, LPCITEMIDLIST pidlRel)
{
// This routine differs from ILCombine in that it takes the First pidl's base, and
// cats on the last id of the second pidl. We need this so Wrapped pidls
// end up with the same base, and we get a valid full pidl.
LPITEMIDLIST pidlRet = NULL;
LPITEMIDLIST pidlBase = ILClone(pidlContainingBase);
if (pidlBase)
{
ILRemoveLastID(pidlBase);
pidlRet = ILCombine(pidlBase, pidlRel);
ILFree(pidlBase);
}
return pidlRet;
}
BOOL IsFolderEvent(LONG lEvent)
{
return lEvent == SHCNE_MKDIR || lEvent == SHCNE_RMDIR || lEvent == SHCNE_RENAMEFOLDER;
}
#ifdef DEBUG
void CAugmentedMergeISF::DumpObjects()
{
if (g_dwDumpFlags & TF_AUGM)
{
ASSERT(_hdpaObjects);
int iObjectCount = DPA_GetPtrCount(_hdpaObjects);
TraceMsg(TF_AUGM, "CAugMISF::DumpObjects: Number of items: %d", iObjectCount);
CNamespace* pns = (CNamespace *)DPA_FastGetPtr(_hdpaNamespaces, 0);
if (pns)
DebugDumpPidl(TF_AUGM, TEXT("CAugMISF::DumpObjects Namespace 1"), pns->GetPidl());
pns = (CNamespace *)DPA_FastGetPtr(_hdpaNamespaces, 1);
if (pns)
DebugDumpPidl(TF_AUGM, TEXT("CAugMISF::DumpObjects Namespace 2"), pns->GetPidl());
for (int i = 0; i < iObjectCount; i++)
{
CAugISFEnumItem* pEnumItem = (CAugISFEnumItem*)DPA_FastGetPtr(_hdpaObjects, i);
TraceMsg(TF_ALWAYS, "CAugMISF::DumpObjects: %s, Folder: %s Merged: %s",
pEnumItem->_pszDisplayName,
BOOLIFY(pEnumItem->_rgfAttrib & SFGAO_FOLDER) ? TEXT("Yes") : TEXT("No"),
(AugMergeISF_GetSourceCount(pEnumItem->_pidlWrap) > 1)? TEXT("Yes") : TEXT("No"));
}
}
}
#endif
BOOL GetRealPidlFromSimple(LPCITEMIDLIST pidlSimple, LPITEMIDLIST* ppidlReal)
{
// Similar to SHGetRealIDL in Function, but SHGetRealIDL does SHGDN_FORPARSING | INFOLDER.
// I need the parsing name. I can't rev SHGetRealIDL very easily, so here's this one!
TCHAR szFullName[MAX_PATH];
if (SUCCEEDED(SHGetNameAndFlags(pidlSimple, SHGDN_FORPARSING, szFullName, SIZECHARS(szFullName), NULL)))
{
*ppidlReal = ILCreateFromPath(szFullName);
}
if (*ppidlReal == NULL) // Unable to create? Then use the simple pidl. This is because it does not exist any more
{ // For say, a Delete Notify
*ppidlReal = ILClone(pidlSimple);
}
return *ppidlReal != NULL;
}
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::TranslateIDs(
LONG *plEvent,
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2,
LPITEMIDLIST * ppidlOut1,
LPITEMIDLIST * ppidlOut2,
LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2,
LPITEMIDLIST *ppidlOut2Event2)
{
HRESULT hres = E_FAIL;
switch (*plEvent)
{
case SHCNE_EXTENDED_EVENT:
case SHCNE_ASSOCCHANGED:
case SHCNE_UPDATEIMAGE:
return S_OK;
case SHCNE_UPDATEDIR:
FreeObjects();
return S_OK;
}
ASSERT(ppidlOut1);
ASSERT(ppidlOut2);
LONG lEvent = *plEvent;
*plEvent2 = (LONG)-1;
*ppidlOut1Event2 = NULL;
*ppidlOut2Event2 = NULL;
*ppidlOut1 = (LPITEMIDLIST)pidl1;
*ppidlOut2 = (LPITEMIDLIST)pidl2;
if (!plEvent)
return E_FAIL;
// If they are already wrapped, don't wrap twice.
if ((pidl1 && SUCCEEDED(AugMergeISF_IsWrap(ILFindLastID(pidl1)))) ||
(pidl2 && SUCCEEDED(AugMergeISF_IsWrap(ILFindLastID(pidl2)))))
{
// We don't want to wrap twice.
return E_FAIL;
}
if (!_hdpaNamespaces)
return E_FAIL;
if (!_hdpaObjects)
return E_FAIL;
CAugISFEnumItem* pEnumItem;
int iIndex;
int iShellFolder1 = -1;
int iShellFolder2 = -1;
IShellFolder* psf1 = NULL;
IShellFolder* psf2 = NULL;
LPITEMIDLIST pidlReal1 = NULL;
LPITEMIDLIST pidlReal2 = NULL;
LPITEMIDLIST pidlRealRel1 = NULL;
LPITEMIDLIST pidlRealRel2 = NULL;
BOOL fFolder = IsFolderEvent(*plEvent);
// Get the information about these Simple pidls: Are they our Children? If so, what namespace?
BOOL fChild1 = IsChildIDInternal(pidl1, TRUE, &iShellFolder1);
BOOL fChild2 = IsChildIDInternal(pidl2, TRUE, &iShellFolder2);
// Is either a child?
if (!(fChild1 || fChild2))
return hres;
// Ok, pidl1 is a child, can we get the Real pidl from the simple one?
if (pidl1 && !GetRealPidlFromSimple(pidl1, &pidlReal1))
goto Cleanup;
// Ok, pidl2 is a child, can we get the Real pidl from the simple one?
if (pidl2 && !GetRealPidlFromSimple(pidl2, &pidlReal2))
goto Cleanup;
// These are for code clarity later on. We deal with Relative pidls from here until the very end,
// when we combine the base of the in pidls with the outgoing wrapped pidls.
if (pidlReal1)
pidlRealRel1 = ILFindLastID(pidlReal1);
if (pidlReal2)
pidlRealRel2 = ILFindLastID(pidlReal2);
// Is Pidl1 in our namespaces?
if (iShellFolder1 != -1)
{
// Yes, lets get the non-refcounted shell folder that know's about this pidl.
CNamespace * pns = (CNamespace *)DPA_GetPtr(_hdpaNamespaces, iShellFolder1);
psf1 = pns->ShellFolder(); // Non ref counted.
}
// Is Pidl2 in our namespaces?
if (iShellFolder2 != -1)
{
// Yes, lets get the non-refcounted shell folder that know's about this pidl.
CNamespace * pns = (CNamespace *)DPA_GetPtr(_hdpaNamespaces, iShellFolder2);
psf2 = pns->ShellFolder(); // Non ref counted.
}
hres = S_OK;
DEBUG_CODE(_fInternalGDNO = TRUE);
switch(*plEvent)
{
case 0: // Just look up the pidls and return.
{
DWORD rgfAttrib = SFGAO_FOLDER;
if (iShellFolder1 != -1)
{
psf1->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel1, &rgfAttrib);
if (S_OK == _SearchForPidl(psf1, pidlRealRel1, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pEnumItem))
{
*ppidlOut1 = ILCombineBase(pidlReal1, pEnumItem->_pidlWrap);
if (!*ppidlOut1)
hres = E_OUTOFMEMORY;
}
}
rgfAttrib = SFGAO_FOLDER;
if (iShellFolder2 != -1 && SUCCEEDED(hres))
{
psf2->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel2, &rgfAttrib);
if (S_OK == _SearchForPidl(psf2, pidlRealRel2, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pEnumItem))
{
*ppidlOut2 = ILCombineBase(pidlReal2, pEnumItem->_pidlWrap);
if (!*ppidlOut2)
hres = E_OUTOFMEMORY;
}
}
}
break;
case SHCNE_CREATE:
case SHCNE_MKDIR:
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s", fFolder?
TEXT("SHCNE_MKDIR") : TEXT("SHCNE_CREATE"));
// Is there a thing of this name already?
if (S_OK == _SearchForPidl(psf1, pidlRealRel1, fFolder, &iIndex, &pEnumItem))
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s needs to be merged. Converting to Rename", pEnumItem->_pszDisplayName);
// Yes; Then we need to merge this new pidl into the wrapped pidl, and change this
// to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
// as the second arg. I have to be careful about the freeing:
// Free *ppidlOut1
// Clone pEnumItem->_pidlWrap -> *ppidlOut1.
// Add pidl1 to pEnumItem->_pidlWrap.
// Clone new pEnumItem->_pidlWrap -> *ppidlOut2. ASSERT(*ppidlOut2 == NULL)
*ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (*ppidlOut1)
{
AugMergeISF_WrapAddPidl(pidlRealRel1, iShellFolder1, &pEnumItem->_pidlWrap);
*ppidlOut2 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (!*ppidlOut2)
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl2");
*plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
}
else
{
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl1");
}
}
else
{
LPITEMIDLIST pidlWrap;
CAugISFEnumItem* paugmEnum = new CAugISFEnumItem;
if (paugmEnum)
{
if (SUCCEEDED(AugMergeISF_CreateWrap(pidlRealRel1, (UINT)iShellFolder1, &pidlWrap)) &&
paugmEnum->InitWithWrappedToOwn(SAFECAST(this, IAugmentedShellFolder2*),
iShellFolder1, pidlWrap))
{
AUGMISFSEARCHFORPIDL AugMSearch;
AugMSearch.pszDisplayName = paugmEnum->_pszDisplayName;
AugMSearch.fFolder = fFolder;
int iInsertIndex = DPA_Search(_hdpaObjects, (LPVOID)&AugMSearch, 0,
AugMISFSearchForOnePidlByDisplayName, NULL, DPAS_SORTED | DPAS_INSERTAFTER);
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Creating new unmerged %s at %d",
paugmEnum->_pszDisplayName, iInsertIndex);
if (iInsertIndex < 0)
iInsertIndex = DA_LAST;
if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, paugmEnum) == -1)
{
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Was unable to add %s for some reason. Bailing",
paugmEnum->_pszDisplayName);
DestroyObjectsProc(paugmEnum, NULL);
}
else
{
*ppidlOut1 = ILCombineBase(pidl1, paugmEnum->_pidlWrap);
}
}
else
DestroyObjectsProc(paugmEnum, NULL);
}
}
}
break;
case SHCNE_DELETE:
case SHCNE_RMDIR:
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s", fFolder?
TEXT("SHCNE_RMDIR") : TEXT("SHCNE_DELETE"));
int iDeleteIndex;
// Is there a folder of this name already?
if (S_OK == _SearchForPidl(psf1, pidlRealRel1,
fFolder, &iDeleteIndex, &pEnumItem))
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Found %s checking merge state.", pEnumItem->_pszDisplayName);
// Yes; Then we need to unmerge this pidl from the wrapped pidl, and change this
// to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
// as the second arg. I have to be careful about the freeing:
// Free *ppidlOut1
// Clone pEnumItem->_pidlWrap -> *ppidlOut1.
// Remove pidl1 from pEnumItem->_pidlWrap
// Convert to rename, pass new wrapped as second arg.
if (AugMergeISF_GetSourceCount( pEnumItem->_pidlWrap ) > 1 )
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is Merged. Removing pidl, convert to rename", pEnumItem->_pszDisplayName);
*ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (*ppidlOut1)
{
EVAL(SUCCEEDED(AugMergeISF_WrapRemovePidl(pEnumItem->_pidlWrap,
iShellFolder1, &pEnumItem->_pidlWrap)));
*ppidlOut2 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (!*ppidlOut2)
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl2");
*plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
}
else
{
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl1");
}
}
else
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is not Merged. deleteing", pEnumItem->_pszDisplayName);
pEnumItem = (CAugISFEnumItem*)DPA_DeletePtr(_hdpaObjects, iDeleteIndex);
if (EVAL(pEnumItem))
{
*ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
DestroyObjectsProc(pEnumItem, NULL);
}
else
{
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to get %d from DPA", iDeleteIndex);
}
}
}
}
break;
case SHCNE_RENAMEITEM:
case SHCNE_RENAMEFOLDER:
{
// BUGBUG (lamadio): When renaming an item in the menu, this code will split it into
// a Delete and a Create. We need to detect this situation and convert it to 1 rename. This
// will solve the problem of the lost order during a rename....
BOOL fEvent1Set = FALSE;
BOOL fFirstPidlInNamespace = FALSE;
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s", fFolder?
TEXT("SHCNE_RENAMEFOLDER") : TEXT("SHCNE_RENAMEITEM"));
// Is this item being renamed FROM the Folder?
if (iShellFolder1 != -1 && // Is this pidl a child of the Folder?
S_OK == _SearchForPidl(psf1, pidlRealRel1,
fFolder, &iIndex, &pEnumItem)) // Is it found?
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Old pidl %s is in the Folder", pEnumItem->_pszDisplayName);
// Yes.
// Then we need to see if the item that it's being renamed from was Merged
// Need this for reentrancy
if (WrappedPidlContainsSrcID(pEnumItem->_pidlWrap, iShellFolder1))
{
// Was it merged?
if (AugMergeISF_GetSourceCount(pEnumItem->_pidlWrap) > 1) // Case 3)
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is Merged. Removing pidl. Convert to rename for event 1",
pEnumItem->_pszDisplayName);
// Yes;
// Then we need to unmerge that item.
*ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (*ppidlOut1)
{
// UnWrap
AugMergeISF_WrapRemovePidl(pEnumItem->_pidlWrap, iShellFolder1, &pEnumItem->_pidlWrap);
*ppidlOut2 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
if (!*ppidlOut2)
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl2");
// This We need to "Rename" the old wrapped pidl, to this new one
// that does not contain the old item.
fEvent1Set = TRUE;
}
else
{
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to create new pidl1");
}
}
else
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is not merged. Nuking item Convert to Delete for event 1.",
pEnumItem->_pszDisplayName);
// No, This was not a wrapped pidl. Then, convert to a delete:
pEnumItem = (CAugISFEnumItem*)DPA_DeletePtr(_hdpaObjects, iIndex);
if (EVAL(pEnumItem))
{
// If we're renaming from this folder, into this folder, Then the first event stays a rename.
if (iShellFolder2 == -1)
{
fEvent1Set = TRUE;
*plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE;
}
else
{
fFirstPidlInNamespace = TRUE;
}
*ppidlOut1 = ILCombineBase(pidl1, pEnumItem->_pidlWrap);
DestroyObjectsProc(pEnumItem, NULL);
}
else
{
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Failure. Was unable to find Item at %d", iIndex);
}
}
}
else
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Skipping this because we already processed it."
"Dragging To Desktop?");
hres = E_FAIL;
}
}
// Is this item is being rename INTO the Start Menu?
if (iShellFolder2 != -1)
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: New pidl is in the Folder");
LPITEMIDLIST* ppidlNewWrapped1 = ppidlOut1;
LPITEMIDLIST* ppidlNewWrapped2 = ppidlOut2;
LONG* plNewEvent = plEvent;
if (fEvent1Set)
{
plNewEvent = plEvent2;
ppidlNewWrapped1 = ppidlOut1Event2;
ppidlNewWrapped2 = ppidlOut2Event2;
}
if (S_OK == _SearchForPidl(psf2, pidlRealRel2,
fFolder, &iIndex, &pEnumItem))
{
// If we're renaming from this folder, into this folder, Check to see if the destination has a
// conflict. If there is a confict (This case), then convert first event to a remove,
// and the second event to the rename.
if (fFirstPidlInNamespace)
{
fEvent1Set = TRUE;
*plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE;
plNewEvent = plEvent2;
ppidlNewWrapped1 = ppidlOut1Event2;
ppidlNewWrapped2 = ppidlOut2Event2;
}
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is in Folder", pEnumItem->_pszDisplayName);
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Adding pidl to %s. Convert to Rename for event %s",
pEnumItem->_pszDisplayName, fEvent1Set? TEXT("2") : TEXT("1"));
// Then the destination needs to be merged.
*ppidlNewWrapped1 = ILCombineBase(pidl2, pEnumItem->_pidlWrap);
if (*ppidlNewWrapped1)
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Successfully created out pidl1");
AugMergeISF_WrapAddPidl(pidlRealRel2, iShellFolder2, &pEnumItem->_pidlWrap);
*ppidlNewWrapped2 = ILCombineBase(pidl2, pEnumItem->_pidlWrap);
*plNewEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
}
}
else
{
LPITEMIDLIST pidlWrap;
CAugISFEnumItem* paugmEnum = new CAugISFEnumItem;
if (paugmEnum)
{
if (SUCCEEDED(AugMergeISF_CreateWrap(pidlRealRel2, (UINT)iShellFolder2, &pidlWrap)) &&
paugmEnum->InitWithWrappedToOwn(SAFECAST(this, IAugmentedShellFolder2*),
iShellFolder2, pidlWrap))
{
AUGMISFSEARCHFORPIDL AugMSearch;
AugMSearch.pszDisplayName = paugmEnum->_pszDisplayName;
AugMSearch.fFolder = BOOLIFY(paugmEnum->_rgfAttrib & SFGAO_FOLDER);
int iInsertIndex = DPA_Search(_hdpaObjects, &AugMSearch, 0,
AugMISFSearchForOnePidlByDisplayName, NULL, DPAS_SORTED | DPAS_INSERTAFTER);
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: %s is a new item. Converting to Create",
paugmEnum->_pszDisplayName);
if (iInsertIndex < 0)
iInsertIndex = DA_LAST;
if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, paugmEnum) == -1)
{
TraceMsg(TF_ERROR, "CAugMISF::TranslateIDs: Was unable to add %s for some reason. Bailing",
paugmEnum->_pszDisplayName);
DestroyObjectsProc(paugmEnum, NULL);
}
else
{
TraceMsg(TF_AUGM, "CAugMISF::TranslateIDs: Creating new item %s at %d for event %s",
paugmEnum->_pszDisplayName, iInsertIndex, fEvent1Set? TEXT("2") : TEXT("1"));
// If we're renaming from this folder, into this folder, Then the first event stays
// a rename.
if (!fFirstPidlInNamespace)
{
*plNewEvent = fFolder ? SHCNE_MKDIR : SHCNE_CREATE;
*ppidlNewWrapped1 = ILCombineBase(pidl2, pidlWrap);
*ppidlNewWrapped2 = NULL;
}
else
*ppidlOut2 = ILCombineBase(pidl2, pidlWrap);
}
}
else
DestroyObjectsProc(paugmEnum, NULL);
}
}
}
}
break;
default:
break;
}
Cleanup:
ILFree(pidlReal1);
ILFree(pidlReal2);
#ifdef DEBUG
DumpObjects();
_fInternalGDNO = FALSE;
#endif
return hres;
}
BOOL CAugmentedMergeISF::IsChildIDInternal(LPCITEMIDLIST pidlKid, BOOL fImmediate, int* piShellFolder)
{
// This is basically the same Method as the interface method, but returns the shell folder
// that it came from.
BOOL fChild = FALSE;
//At this point we should have a translated pidl
if (pidlKid)
{
if (SUCCEEDED(AugMergeISF_IsWrap(pidlKid)))
{
LPCITEMIDLIST pidlRelKid = ILFindLastID(pidlKid);
if (pidlRelKid)
{
UINT uiId;
LPITEMIDLIST pidl;
HANDLE hEnum = AugMergeISF_EnumFirstSrcPidl(pidlRelKid, &uiId, &pidl);
if (hEnum)
{
do
{
ILFree(pidl);
for (int i = 0; fChild == FALSE && i < DPA_GetPtrCount(_hdpaNamespaces); i++)
{
CNamespace * pns = (CNamespace *)DPA_GetPtr(_hdpaNamespaces, i);
// reuse pidl
if (pns && (pidl = pns->GetPidl()) != NULL)
{
if (ILIsParent(pidl, pidlKid, fImmediate) &&
!ILIsEqual(pidl, pidlKid))
{
fChild = TRUE;
if (piShellFolder)
*piShellFolder = i;
}
}
}
}
while (fChild == FALSE && AugMergeISF_EnumNextSrcPidl(hEnum, &uiId, &pidl));
AugMergeISF_EndEnumSrcPidls(hEnum);
}
}
}
else
{
int cSrcs = NamespaceCount();
for(int i = 0; fChild == FALSE && i < cSrcs ; i++)
{
CNamespace* pSrc = Namespace(i);
if (pSrc && ILIsParent(pSrc->GetPidl(), pidlKid, fImmediate) &&
!ILIsEqual(pSrc->GetPidl(), pidlKid))
{
fChild = TRUE;
if (piShellFolder)
*piShellFolder = i;
}
}
}
}
return fChild;
}
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::IsChildID( LPCITEMIDLIST pidlKid, BOOL fImmediate)
{
return IsChildIDInternal(pidlKid, fImmediate, NULL) ? S_OK : S_FALSE;
}
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::IsEqualID( LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2 )
{
// This used to return E_NOTIMPL. I'm kinda overloading the interface to mean:
// is this equal tp any of your namespaces.
HRESULT hres = S_FALSE;
int cSrcs = NamespaceCount();
for(int i = 0; hres == S_FALSE && i < cSrcs ; i++)
{
CNamespace* pSrc = Namespace(i);
if (pidl1)
{
if (pSrc && ILIsEqual(pSrc->GetPidl(), pidl1))
hres = S_OK;
}
else if (pidl2) // If you pass a pidl2 it means: Is pidl2 a parent of one of my namespaces?
{
if (pSrc && ILIsParent(pidl2, pSrc->GetPidl(), FALSE))
hres = S_OK;
}
}
return hres;
}
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::Register(
HWND hwnd,
UINT uMsg,
long lEvents )
{
int i, cSrcs ;
if( 0 >= (cSrcs = NamespaceCount()) )
return E_FAIL ;
for( i = 0; i < cSrcs ; i++ )
{
CNamespace* pSrc ;
if( NULL != (pSrc = Namespace( i )) )
pSrc->RegisterNotify( hwnd, uMsg, lEvents ) ;
}
return S_OK ;
}
//-------------------------------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::Unregister ()
{
int i, cSrcs = NamespaceCount() ;
if( cSrcs <= 0 )
return E_FAIL ;
for( i = 0; i < cSrcs ; i++ )
{
CNamespace* pSrc ;
if( NULL != (pSrc = Namespace( i )) )
pSrc->UnregisterNotify() ;
}
return S_OK ;
}
// *** IDropTarget methods ***
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
HRESULT CAugmentedMergeISF::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
ASSERT(!_fCommon);
ASSERT(_pdt == NULL);
if (pDataObj)
{
InitClipboardFormats();
FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
medium.pUnkForRelease = NULL;
medium.hGlobal = NULL;
if (SUCCEEDED(pDataObj->GetData(&fmte, &medium)))
{
LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal);
if (pida)
{
LPCITEMIDLIST pidlItem = HIDA_GetPIDLFolder(pida);
_fCommon = BOOLIFY(_IsCommonPidl(pidlItem));
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
}
CNamespace *pSrc = NULL;
ULONG gdnsAttribs = 0;
if (!_fCommon)
gdnsAttribs = ASFF_DEFNAMESPACE_ALL;
if (SUCCEEDED(GetDefNamespace(gdnsAttribs, (PVOID*)&pSrc, NULL, NULL)))
{
if (SUCCEEDED(pSrc->ShellFolder()->CreateViewObject(_hwnd, IID_IDropTarget, (void **)&_pdt)))
{
_pdt->DragEnter(pDataObj, grfKeyState, pt, pdwEffect);
}
}
}
_grfDragEnterKeyState = grfKeyState;
_dwDragEnterEffect = *pdwEffect;
return S_OK;
}
HRESULT CAugmentedMergeISF::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
HRESULT hres = S_OK;
if (_pdt)
hres = _pdt->DragOver(grfKeyState, pt, pdwEffect);
return hres;
}
HRESULT CAugmentedMergeISF::DragLeave(void)
{
HRESULT hres = S_OK;
_fCommon = 0;
if (_pdt)
{
hres = _pdt->DragLeave();
ATOMICRELEASE(_pdt);
}
return hres;
}
HRESULT CAugmentedMergeISF::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
HRESULT hres = S_OK;
BOOL bNoUI = !_fCommon;
BOOL bConfirmed = !_fCommon;
if (!_pdt && pDataObj)
{
LPITEMIDLIST pidlParent = NULL,
pidlOther = NULL;
int csidl = _fCommon ? CSIDL_COMMON_STARTMENU : CSIDL_STARTMENU,
csidlOther = _fCommon ? CSIDL_STARTMENU : CSIDL_COMMON_STARTMENU;
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, &pidlParent)) &&
SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidlOther, &pidlOther)))
{
FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
medium.pUnkForRelease = NULL;
medium.hGlobal = NULL;
if (SUCCEEDED(pDataObj->GetData(&fmte, &medium)))
{
LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal);
if (pida)
{
IShellFolder *psfParent = NULL,
*psfOther = NULL;
if (SUCCEEDED(IEBindToObject(pidlParent, &psfParent)) &&
SUCCEEDED(IEBindToObject(pidlOther, &psfOther)))
{
LPCITEMIDLIST pidlItem, pidl;
LPITEMIDLIST pidlRel;
pidlItem = HIDA_GetPIDLItem(pida, 0);
// we came here because we don't have pdt which means that
// there is only one folder in our namespace and that's not
// the one we have to drop IDataObj on.
CNamespace* pCNamespace = Namespace(0);
if (pCNamespace)
{
pidl = pCNamespace->GetPidl(); // don't need to free pidl.
if (pidl)
{
pidlRel = ILClone(ILFindChild(pidlOther, pidl));
if (pidlRel)
{
STRRET strret;
TCHAR szDir[MAX_PATH];
strret.uType = STRRET_CSTR;
if (SUCCEEDED(psfParent->GetDisplayNameOf(pidlRel, SHGDN_FORPARSING, &strret)) &&
SUCCEEDED(StrRetToBuf(&strret, pidlRel, szDir, ARRAYSIZE(szDir))))
{
if (_fCommon)
{
bConfirmed = AffectAllUsers(_hwnd);
bNoUI = TRUE;
}
if (bConfirmed)
{
BOOL bCreated = FALSE;
switch (SHCreateDirectory(_hwnd, szDir))
{
case ERROR_FILENAME_EXCED_RANGE:
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
case 0: // It was created successfully.
bCreated = TRUE;
}
if (bCreated)
{
IShellFolder *psf;
if (SUCCEEDED(psfParent->BindToObject(pidlRel, NULL, IID_IShellFolder, (void **)&psf)))
{
psf->CreateViewObject(_hwnd, IID_IDropTarget, (void **)&_pdt);
// we're going to call drop on it, call dragenter first
if (_pdt)
_pdt->DragEnter(pDataObj, _grfDragEnterKeyState, pt, &_dwDragEnterEffect);
psf->Release();
}
}
}
}
ILFree(pidlRel);
}
}
}
}
if (psfParent)
psfParent->Release();
if (psfOther)
psfOther->Release();
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
}
}
ILFree(pidlParent);
ILFree(pidlOther);
}
if (_pdt)
{
hres = E_FAIL;
if ((bNoUI || (bConfirmed = AffectAllUsers(_hwnd))) && bConfirmed)
hres = _pdt->Drop(pDataObj, grfKeyState, pt, pdwEffect);
else
hres = _pdt->DragLeave();
ATOMICRELEASE(_pdt);
}
_fCommon = 0;
return hres;
}
//-------------------------------------------------------------------------------------------------//
LPITEMIDLIST CAugmentedMergeISF::GetNativePidl(LPCITEMIDLIST pidlWrap, LPARAM nSrcID /*int nID*/)
{
LPITEMIDLIST pidlRet = NULL;
if (SUCCEEDED(AugMergeISF_GetSrcPidl(pidlWrap, (UINT)nSrcID, &pidlRet)))
return pidlRet ;
// not wrapped by me.
return NULL;
}
BOOL AffectAllUsers(HWND hwnd)
{
TCHAR szMessage[255];
TCHAR szTitle[20];
BOOL bRet = FALSE;
if (MLLoadShellLangString(IDS_ALLUSER_WARNING, szMessage, ARRAYSIZE(szMessage)) > 0 &&
MLLoadShellLangString(IDS_ALLUSER_WARNING_TITLE, szTitle, ARRAYSIZE(szTitle)) > 0)
{
bRet = IDYES == MessageBox(hwnd, szMessage, szTitle, MB_YESNO | MB_ICONINFORMATION);
}
return bRet;
}
BOOL CAugmentedMergeISF::_IsCommonPidl(LPCITEMIDLIST pidlItem)
{
BOOL bRet = FALSE;
LPITEMIDLIST pidlCommon;
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommon)))
{
bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
ILFree(pidlCommon);
}
if (!bRet &&
SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlCommon)))
{
bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
ILFree(pidlCommon);
}
if (!bRet &&
SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &pidlCommon)))
{
bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
ILFree(pidlCommon);
}
return bRet;
}
HRESULT CAugmentedMergeISF::_SearchForPidl(IShellFolder* psf, LPCITEMIDLIST pidl, BOOL fFolder, int* piIndex, CAugISFEnumItem** ppEnumItem)
{
STRRET str;
TCHAR szDisplayName[MAX_PATH];
int iIndex = -1;
*ppEnumItem = NULL;
if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str)) &&
SUCCEEDED(StrRetToBuf(&str, pidl, szDisplayName, ARRAYSIZE(szDisplayName))))
{
AUGMISFSEARCHFORPIDL SearchFor;
SearchFor.pszDisplayName = szDisplayName;
SearchFor.fFolder = fFolder;
iIndex = DPA_Search(_hdpaObjects, (LPVOID)&SearchFor, 0,
AugMISFSearchForOnePidlByDisplayName, NULL, DPAS_SORTED);
if (iIndex >= 0)
{
*ppEnumItem = DPA_GETPTR( _hdpaObjects, iIndex, CAugISFEnumItem);
}
}
if (piIndex)
*piIndex = iIndex;
if (*ppEnumItem)
return S_OK;
return S_FALSE;
}
// given a wrapped pidl
// f-n returns common and user namespaces (if they are in wrapped pidl) -- note that they are not addrefed
// unwrapped pidl, and if the unwrapped pidl is folder or not
HRESULT CAugmentedMergeISF::_GetNamespaces(LPCITEMIDLIST pidlWrap,
CNamespace** ppnsCommon,
UINT* pnCommonID,
CNamespace** ppnsUser,
UINT* pnUserID,
LPITEMIDLIST* ppidl,
BOOL *pbIsFolder)
{
HRESULT hres;
UINT nSrcID;
CNamespace * pns;
int cWrapped;
HANDLE hEnum;
ASSERT(ppnsCommon && ppnsUser && ppidl);
*ppnsCommon = NULL;
*ppnsUser = NULL;
*ppidl = NULL;
ASSERT(SUCCEEDED(AugMergeISF_IsWrap(pidlWrap)));
cWrapped = AugMergeISF_GetSourceCount(pidlWrap);
if (NULL == _hdpaNamespaces || 0 >= cWrapped ||
NULL == (hEnum = AugMergeISF_EnumFirstSrcPidl(pidlWrap, &nSrcID, ppidl)))
return E_FAIL;
hres = QueryNameSpace(nSrcID, (void **)&pns);
if (EVAL(SUCCEEDED(hres)))
{
IShellFolder * psf;
ULONG rgf = SFGAO_FOLDER;
psf = pns->ShellFolder(); // no addref
ASSERT(psf);
if (SUCCEEDED(psf->GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, &rgf)))
{
if (pbIsFolder)
*pbIsFolder = rgf & SFGAO_FOLDER;
LPITEMIDLIST pidlItem;
UINT nCommonID;
CNamespace* pnsCommonTemp;
// get common namespace (attribs = 0)
hres = GetDefNamespace(0, (void **)&pnsCommonTemp, &nCommonID, NULL);
ASSERT(NamespaceCount() == 2 && SUCCEEDED(hres) || NamespaceCount() == 1);
if (FAILED(hres))
nCommonID = 1;
if (nCommonID == nSrcID)
{
*ppnsCommon = pns;
if (pnCommonID)
*pnCommonID = nCommonID;
}
else
{
*ppnsUser = pns;
if (pnUserID)
*pnUserID = nSrcID;
}
if (AugMergeISF_EnumNextSrcPidl(hEnum, &nSrcID, &pidlItem))
{
ASSERT(ILIsEqual(*ppidl, pidlItem));
ILFree(pidlItem);
if (SUCCEEDED(QueryNameSpace(nSrcID, (void **)&pns)))
{
ASSERT(pns);
if (nCommonID == nSrcID)
{
*ppnsCommon = pns;
if (pnCommonID)
*pnCommonID = nCommonID;
}
else
{
*ppnsUser = pns;
if (pnUserID)
*pnUserID = nSrcID;
}
}
}
hres = S_OK;
}
}
AugMergeISF_EndEnumSrcPidls(hEnum);
return hres;
}
HRESULT CAugmentedMergeISF::_GetContextMenu(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
UINT * prgfInOut, void ** ppvOut)
{
HRESULT hres;
LPITEMIDLIST pidl;
BOOL bIsFolder;
CNamespace * pnsCommon;
CNamespace * pnsUser;
ASSERT(cidl == 1);
// psfCommon and psfUser are not addrefed
hres = _GetNamespaces(apidl[0], &pnsCommon, NULL, &pnsUser, NULL, &pidl, &bIsFolder);
if (SUCCEEDED(hres))
{
ASSERT(pnsCommon || pnsUser);
if (bIsFolder)
{
// folder? need our context menu
IShellFolder * psfCommon = NULL;
IShellFolder * psfUser = NULL;
LPCITEMIDLIST pidlCommon = NULL;
LPCITEMIDLIST pidlUser = NULL;
if (pnsCommon)
{
psfCommon = pnsCommon->ShellFolder();
pidlCommon = pnsCommon->GetPidl();
}
if (pnsUser)
{
psfUser = pnsUser->ShellFolder();
pidlUser = pnsUser->GetPidl();
}
CAugMergeISFContextMenu * pcm = CreateMergeISFContextMenu(psfCommon, pidlCommon,
psfUser, pidlUser,
pidl, hwnd, prgfInOut);
if (pcm)
{
hres = pcm->QueryInterface(IID_IContextMenu, ppvOut);
pcm->Release();
}
else
hres = E_OUTOFMEMORY;
}
else
{ // it's not a folder
// delegate to the isf
IShellFolder * psf = pnsUser ? pnsUser->ShellFolder() : pnsCommon->ShellFolder();
hres = psf->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidl, IID_IContextMenu, prgfInOut, ppvOut);
}
ILFree(pidl);
}
return hres;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CAugmentedMergeISF::GetDefNamespace(
LPCITEMIDLIST pidlWrap,
ULONG dwAttrib,
OUT IShellFolder** ppsf,
OUT LPITEMIDLIST* ppidlItem )
{
HRESULT hr ;
LPITEMIDLIST pidl ;
CNamespace* pSrc ;
ULONG dwDefAttrib = dwAttrib & ASFF_DEFNAMESPACE_ALL ;
int cWrapped ;
UINT nSrcID ;
HANDLE hEnum ;
ASSERT( ppsf ) ;
*ppsf = NULL ;
if (ppidlItem)
*ppidlItem = NULL ;
if (FAILED((hr = AugMergeISF_IsWrap( pidlWrap ))))
return hr ;
cWrapped = AugMergeISF_GetSourceCount( pidlWrap ) ;
// No namespaces?
if (NULL == _hdpaNamespaces || 0 >= cWrapped ||
NULL == (hEnum = AugMergeISF_EnumFirstSrcPidl( pidlWrap, &nSrcID, &pidl)))
return E_FAIL ;
// Only one namespace in wrap? Give up the shell folder and item ID.
if (1 == cWrapped || 0==dwDefAttrib)
{
AugMergeISF_EndEnumSrcPidls( hEnum ) ; // no need to go further
// Retrieve the namespace object identified by nSrcID.
if( SUCCEEDED( (hr = QueryNameSpace( nSrcID, (PVOID*)&pSrc )) ) )
{
*ppsf = pSrc->ShellFolder() ;
if( ppidlItem )
*ppidlItem = pidl ;
return S_OK ;
}
ILFree( pidl ) ;
return hr ;
}
// More than one namespace in wrap?
if( cWrapped > 1 )
{
LPITEMIDLIST pidl0 = NULL ;
CNamespace* pSrc0 = NULL ; // get this below.
for (BOOL bEnum = TRUE ; bEnum ;
bEnum = AugMergeISF_EnumNextSrcPidl(hEnum, &nSrcID, &pidl))
{
if (SUCCEEDED((hr = QueryNameSpace(nSrcID, (PVOID*)&pSrc))))
{
if (dwDefAttrib & pSrc->Attrib())
{
// Matched attributes; we're done.
AugMergeISF_EndEnumSrcPidls(hEnum);
*ppsf = pSrc->ShellFolder() ;
if (ppidlItem)
*ppidlItem = pidl;
if(pidl0)
ILFree(pidl0);
return S_OK ;
}
// Stash first namespace object and item pidl.
// We'll default to these if
if( NULL == pSrc0 )
{
pSrc0 = pSrc ;
pidl0 = ILClone( pidl ) ;
}
}
ILFree( pidl ) ;
}
AugMergeISF_EndEnumSrcPidls( hEnum ) ;
// Default to first namespace
if( pSrc0 && pidl0 )
{
*ppsf = pSrc0->ShellFolder() ;
if( ppidlItem )
*ppidlItem = pidl0 ;
return S_OK ;
}
}
return E_UNEXPECTED ;
}
//-------------------------------------------------------------------------//
// Retrieves the default namespaces for the indicated attibutes.
// The dwAttrib arg must be initialized prior to function entry,
STDMETHODIMP CAugmentedMergeISF::GetDefNamespace(
ULONG dwAttrib,
OUT PVOID* ppSrc, UINT *pnSrcID, PVOID* ppSrc0 )
{
CNamespace* pSrc ;
ULONG dwDefAttrib = dwAttrib & ASFF_DEFNAMESPACE_ALL ;
// this is an internal helper so we better make sure we pass the correct params!
//if (NULL == ppSrc)
// return E_INVALIDARG ;
*ppSrc = NULL ;
if( ppSrc0 )
*ppSrc0 = NULL ;
for( int i = 0, cSrcs = NamespaceCount(); i < cSrcs ; i++ )
{
if( NULL != (pSrc = Namespace( i )) )
{
if( 0 == i && ppSrc0 )
*ppSrc0 = pSrc ;
if( dwDefAttrib & pSrc->Attrib() ||
dwDefAttrib == 0 && !(pSrc->Attrib() & ASFF_DEFNAMESPACE_ALL))
{
*ppSrc = pSrc;
if (NULL != pnSrcID)
*pnSrcID = i;
return S_OK ;
}
}
}
return E_FAIL ;
}
// BUGBUG(lamadio): Move this to a better location, This is a nice generic function
#ifdef DEBUG
BOOL DPA_VerifySorted(HDPA hdpa, PFNDPACOMPARE pfn, LPARAM lParam)
{
if (!EVAL(hdpa))
return FALSE;
for (int i = 0; i < DPA_GetPtrCount(hdpa) - 1; i++)
{
if (pfn(DPA_FastGetPtr(hdpa, i), DPA_FastGetPtr(hdpa, i + 1), lParam) > 0)
return FALSE;
}
return TRUE;
}
#else
#define DPA_VerifySorted(hdpa, pfn, lParam)
#endif
int CAugmentedMergeISF::AcquireObjects()
{
HDPA hdpa2 = NULL;
DEBUG_CODE(_fInternalGDNO = TRUE);
for (int i = 0; i < DPA_GETPTRCOUNT(_hdpaNamespaces); i++)
{
CNamespace * pns = DPA_GETPTR(_hdpaNamespaces, i, CNamespace);
IShellFolder * psf;
IEnumIDList * peid;
HDPA * phdpa;
ASSERT(pns);
psf = pns->ShellFolder(); // no addref here!
if (i == 0)
{
phdpa = &_hdpaObjects;
_hdpaObjects = DPA_Create(4); // We should always create the DPA
}
else
{
ASSERT(i == 1); // no support for more than 2 isf's
phdpa = &hdpa2;
}
HRESULT hres = psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &peid);
if (SUCCEEDED(hres))
{
if (!*phdpa)
*phdpa = DPA_Create(4);
if (*phdpa)
{
LPITEMIDLIST pidl;
ULONG cEnum;
while (SUCCEEDED(peid->Next(1, &pidl, &cEnum)) && 1 == cEnum)
{
CAugISFEnumItem* paugmEnum = new CAugISFEnumItem;
if (paugmEnum)
{
if (paugmEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), i, pidl))
{
if (DPA_AppendPtr(*phdpa, paugmEnum) == -1)
DestroyObjectsProc(paugmEnum, NULL);
}
else
delete paugmEnum;
}
ILFree(pidl);
}
}
peid->Release();
}
else
{
TraceMsg(TF_WARNING, "CAugMISF::AcquireObjects: Failed to get enumerator 0x%x", hres);
}
}
// now that we have both hdpa's (or one) let's merge them.
if (DPA_GETPTRCOUNT(_hdpaNamespaces) == 2 && hdpa2)
{
DPA_Merge(_hdpaObjects, hdpa2, DPAM_UNION, AugmEnumCompare, AugmEnumMerge, (LPARAM)0);
DPA_DESTROY(hdpa2, DestroyObjectsProc);
}
else
{
DPA_Sort(_hdpaObjects, AugmEnumCompare, 0);
}
ASSERT(DPA_VerifySorted(_hdpaObjects, AugmEnumCompare, 0));
DEBUG_CODE(_fInternalGDNO = FALSE);
#ifdef DEBUG
TraceMsg(TF_AUGM, "CAugMISF::AcquireObjects");
DumpObjects();
#endif
_count = DPA_GETPTRCOUNT(_hdpaObjects);
return _count;
}
//-------------------------------------------------------------------------//
void CAugmentedMergeISF::FreeObjects()
{
DPA_DESTROY( _hdpaObjects, DestroyObjectsProc ) ;
_hdpaObjects = NULL;
_count = 0 ;
}
//-------------------------------------------------------------------------//
int CAugmentedMergeISF::DestroyObjectsProc( LPVOID pv, LPVOID pvData )
{
CAugISFEnumItem* paugmEnum = (CAugISFEnumItem*)pv;
if (EVAL(NULL != paugmEnum))
{
delete paugmEnum;
}
return TRUE ;
}
//-------------------------------------------------------------------------//
void CAugmentedMergeISF::FreeNamespaces()
{
DPA_DESTROY( _hdpaNamespaces, DestroyNamespacesProc ) ;
}
//-------------------------------------------------------------------------//
int CAugmentedMergeISF::DestroyNamespacesProc( LPVOID pv, LPVOID pvData )
{
CNamespace* p ;
if( NULL != (p = (CNamespace*)pv) )
delete p ;
return TRUE ;
}
STDMETHODIMP CAugmentedMergeISF::GetPidl(int* piPos, DWORD grfEnumFlags, LPITEMIDLIST* ppidl)
{
*ppidl = NULL;
if (_hdpaObjects == NULL)
AcquireObjects();
if (_hdpaObjects == NULL)
return E_OUTOFMEMORY;
BOOL fWantFolders = 0 != (grfEnumFlags & SHCONTF_FOLDERS),
fWantNonFolders = 0 != (grfEnumFlags & SHCONTF_NONFOLDERS),
fWantHidden = 0 != (grfEnumFlags & SHCONTF_INCLUDEHIDDEN) ;
while (*piPos < _count)
{
CAugISFEnumItem* paugmEnum = DPA_GETPTR( _hdpaObjects, *piPos, CAugISFEnumItem);
if ( NULL != paugmEnum )
{
BOOL fFolder = 0 != (paugmEnum->_rgfAttrib & SFGAO_FOLDER),
fHidden = 0 != (paugmEnum->_rgfAttrib & SFGAO_HIDDEN);
if ((!fHidden || fWantHidden) &&
((fFolder && fWantFolders) || (!fFolder && fWantNonFolders)))
{
// Copy out the pidl ;
*ppidl = ILClone(paugmEnum->_pidlWrap);
break;
}
else
{
(*piPos)++;
}
}
}
if (*ppidl)
return S_OK;
return S_FALSE;
}
//-------------------------------------------------------------------------//
CEnum::CEnum(IAugmentedMergedShellFolderInternal* psmsfi, DWORD grfEnumFlags, int iPos) :
_cRef(1),
_iPos(iPos),
_psmsfi(psmsfi),
_grfEnumFlags(grfEnumFlags)
{
_psmsfi->AddRef();
}
CEnum::~CEnum()
{
ATOMICRELEASE(_psmsfi);
}
//-------------------------------------------------------------------------//
// class CEnum - IUnknown methods
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
STDMETHODIMP CEnum::QueryInterface( REFIID riid, LPVOID * ppvObj )
{
static const QITAB qit[] = {
QITABENT(CEnum, IEnumIDList),
{ 0 }
};
return QISearch(this, qit, riid, ppvObj);
}
//-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CEnum::AddRef ()
{
return InterlockedIncrement((LONG*)&_cRef);
}
//-------------------------------------------------------------------------//
STDMETHODIMP_(ULONG) CEnum::Release ()
{
if (InterlockedDecrement((LONG*)&_cRef))
return _cRef;
delete this ;
return 0;
}
//-------------------------------------------------------------------------//
// class CEnum - IEnumIDList methods
//-------------------------------------------------------------------------//
STDMETHODIMP CEnum::Next(
ULONG celt,
LPITEMIDLIST *rgelt,
ULONG *pceltFetched )
{
int iStart = _iPos;
int cFetched = 0;
HRESULT hres = S_OK;
if( !(celt > 0 && rgelt) || (NULL == pceltFetched && celt > 1 ) )
return E_INVALIDARG ;
*rgelt = 0;
while(hres == S_OK && (_iPos - iStart) < (int)celt)
{
LPITEMIDLIST pidl;
hres = _psmsfi->GetPidl(&_iPos, _grfEnumFlags, &pidl);
if (hres == S_OK)
{
rgelt[cFetched] = pidl;
cFetched++ ;
}
_iPos++;
}
if( pceltFetched )
*pceltFetched = cFetched ;
return celt == (ULONG)cFetched ? S_OK : S_FALSE ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CEnum::Skip(ULONG celt)
{
_iPos += celt;
return S_OK ;
}
//-------------------------------------------------------------------------//
STDMETHODIMP CEnum::Reset()
{
_iPos = 0;
return S_OK ;
}
//-------------------------------------------------------------------------//
// REVIEW: Can probably be E_NOTIMPL
STDMETHODIMP CEnum::Clone( IEnumIDList **ppenum )
{
if( NULL == (*ppenum = new CEnum( _psmsfi, _grfEnumFlags, _iPos )) )
return E_OUTOFMEMORY;
return S_OK;
}
BOOL CAugISFEnumItem::Init(IShellFolder* psf, int iShellFolder, LPCITEMIDLIST pidl)
{
// This is ok, the memory just gets written to twice.
if (SUCCEEDED(AugMergeISF_CreateWrap(pidl, iShellFolder, &_pidlWrap)))
{
// Takes ownership of passed in pidl.
return InitWithWrappedToOwn(psf, iShellFolder, _pidlWrap);
}
return FALSE;
}
BOOL CAugISFEnumItem::InitWithWrappedToOwn(IShellFolder* psf, int iShellFolder, LPITEMIDLIST pidl)
{
BOOL fRet = FALSE;
STRRET str;
TCHAR szDisplayName[MAX_PATH];
_pidlWrap = pidl;
_rgfAttrib = SFGAO_FOLDER | SFGAO_HIDDEN;
psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidl, &_rgfAttrib);
if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str)) &&
SUCCEEDED(StrRetToBuf(&str, pidl, szDisplayName, ARRAYSIZE(szDisplayName))))
{
SetDisplayName(szDisplayName);
fRet = TRUE;
}
return fRet;
}