//-------------------------------------------------------------------------// // // 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; }