// OXShellNamespaceNavigator.cpp : implementation file // // Version: 9.3 #include "stdafx.h" #include "OXShellNamespaceNavigator.h" #include "UTBStrOp.h" #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // COXShellNamespaceNavigator COXShellNamespaceNavigator::COXShellNamespaceNavigator() { //get the shell's IMalloc pointer //we'll keep this until we get destroyed if(FAILED(SHGetMalloc(&m_pMalloc))) { AfxThrowMemoryException(); return; } m_pOwnerWnd=NULL; m_lpeidl=NULL; m_bEnumeratorInitialized=FALSE; m_lpEnumerateFolder=NULL; m_lpEnumerateParentFullIDL=NULL; m_bAutoCleanUp=FALSE; } COXShellNamespaceNavigator::~COXShellNamespaceNavigator() { if(m_pMalloc) { if(m_bAutoCleanUp) { // free memory allocated for objects POSITION pos=m_mapObjectsToFree.GetStartPosition(); while(pos!=NULL) { DWORD_PTR dwKey=NULL; DWORD_PTR dwData=NULL; m_mapObjectsToFree.GetNextAssoc(pos,dwKey,dwData); m_pMalloc->Free((void*)dwKey); } } // release IMalloc interface m_pMalloc->Release(); } if(m_bAutoCleanUp) { // release requested interfaces POSITION pos=m_mapIShellFolderToRelease.GetStartPosition(); while(pos!=NULL) { DWORD_PTR dwKey=NULL; DWORD_PTR dwData=NULL; m_mapIShellFolderToRelease.GetNextAssoc(pos,dwKey,dwData); ((LPSHELLFOLDER)dwKey)->Release(); } } // release enumerator if(m_bEnumeratorInitialized) ReleaseObjectsEnumerator(); } ///////////////////////////////////////////////////////////////////////////// int CALLBACK COXShellNamespaceNavigator::CompareObjectsProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif UNREFERENCED_PARAMETER(lParamSort); LPNAMESPACEOBJECT lpFolder1=(LPNAMESPACEOBJECT)lParam1; LPNAMESPACEOBJECT lpFolder2=(LPNAMESPACEOBJECT)lParam2; ASSERT(lpFolder1->lpsfParent==lpFolder2->lpsfParent); // Use IShellBrowser::CompareIDs method to compare two folders. // The first argument have to be zero. // If this method is successful, the CODE field of the status code (SCODE) // contains one of the following values: // Less than zero - The first item should precede the second // (pidl1 < pidl2). // Greater than zero - The first item should follow the second // (pidl1 > pidl2). // Zero - The two items are the same (pidl1 = pidl2). // // If this method is unsuccessful, it returns an OLE-defined error code. HRESULT hResult=lpFolder1->lpsfParent-> CompareIDs(0,lpFolder1->lpRelativeIDL,lpFolder2->lpRelativeIDL); // if above method failed let's take it as items are equal if(FAILED(hResult)) return 0; // return CODE field of status code of result value return (short)SCODE_CODE(GetScode(hResult)); } ///////////////////////////////////////////////////////////////////////////// HIMAGELIST COXShellNamespaceNavigator:: GetShellImageList(BOOL bSmallIcon/*=TRUE*/) const { // Retrieves the handle to the system image list // Use SHGetFileInfo function in order to do that // /* WINSHELLAPI DWORD WINAPI SHGetFileInfo( LPCTSTR pszPath, DWORD dwFileAttributes, SHFILEINFO FAR *psfi, UINT cbFileInfo, UINT uFlags ); Retrieves information about an object in the file system, such as a file, a folder, a directory, or a drive root. If uFlags contains SHGFI_SYSICONINDEX, the return value is the handle to the system image list that contains the large icon images. If SHGFI_SMALLICON is included with SHGFI_SYSICONINDEX, the return value is the handle to the image list that contains the small icon images. pszPath - Address of a buffer that contains the path and file name. Both absolute and relative paths are valid. This string can use either short (the 8.3 form) or long file names. dwFileAttributes- Combination of one or more file attribute flags (FILE_ATTRIBUTE_ values). If uFlags does not include the SHGFI_USEFILEATTRIBUTES flag, this parameter is ignored. psfi - Address of a SHFILEINFO structure to receive the file information. cbFileInfo - Size, in bytes, of the SHFILEINFO structure pointed to by the psfi parameter. uFlags - Flags that specify the file information to retrieve. This parameter can be a combination of the following values (not all of them are listed): SHGFI_ICON - Retrieve the handle to the icon that represents the file and the index of the icon within the system image list. The handle is copied to the hIcon member of the structure specified by psfi, and the index is copied to the iIcon member. The return value is the handle to the system image list. SHGFI_ICONLOCATION - Retrieve the name of the file that contains the icon representing the file. The name is copied to the szDisplayName member of the structure specified in psfi. SHGFI_LARGEICON - Modify SHGFI_ICON, causing the function to retrieve the file's large icon. SHGFI_LINKOVERLAY - Modify SHGFI_ICON, causing the function to add the link overlay to the file's icon. SHGFI_OPENICON - Modify SHGFI_ICON, causing the function to retrieve the file's open icon. A container object displays an open icon to indicate that the container is open. SHGFI_SELECTED - Modify SHGFI_ICON, causing the function to blend the file's icon with the system highlight color. SHGFI_SHELLICONSIZE - Modify SHGFI_ICON, causing the function to retrieve a shell-sized icon. If this flag is not specified the function sizes the icon according to the system metric values. SHGFI_SMALLICON - Modify SHGFI_ICON, causing the function to retrieve the file's small icon. SHGFI_SYSICONINDEX - Retrieve the index of the icon within the system image list. The index is copied to the iIcon member of the structure specified by psfi. The return value is the handle to the system image list. */ SHFILEINFO shfi; // use fake path, ignore dwFileAttributes and set uFlags to retrieve // small icons HIMAGELIST hImageList=(HIMAGELIST)SHGetFileInfo(/*_T("C:\\")*/_T(""),0, &shfi,sizeof(SHFILEINFO),SHGFI_SYSICONINDEX| (bSmallIcon ? SHGFI_SMALLICON : SHGFI_LARGEICON)); return hImageList; } LPSHELLFOLDER COXShellNamespaceNavigator:: GetShellFolder(CString sFolderFullPath) const { // First of all get the Desktop's IShellFolder interface. LPSHELLFOLDER lpDesktopFolder; if(FAILED(SHGetDesktopFolder(&lpDesktopFolder))) return NULL; // If lpcsFolderFullPath is NULL then just return the Desktop's IShellFolder if(sFolderFullPath.IsEmpty()) { ASSERT(lpDesktopFolder!=NULL); if(lpDesktopFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpDesktopFolder,NULL); return lpDesktopFolder; } // Now we should try to get IShellFolder interface of the folder full path of // which is lpcsFolderFullPath. In order to do that we use // IShellFolder::ParseDisplayName method: // /* HRESULT ParseDisplayName( HWND hwndOwner, LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes ); Translates a file object's or folder's display name into an item identifier list. Returns NOERROR if successful, or an OLE-defined error value otherwise. hwndOwner - Handle to the owner window that the client should specify if it displays a dialog box or message box. pbcReserved - Reserved; this parameter is always NULL. lpszDisplayName - Address of a null-terminated UNICODE string specifying the display name. This parameter must be a display name for parsing—that is, a display name retrieved using the SHGDN_FORPARSING value. pchEaten - Address of a ULONG value that receives the number of characters of the display name that was parsed. ppidl - Address of a pointer to the new item identifier list for the object. If an error occurs, NULL is returned in this address. The returned item identifier list specifies the relative path (from the parent folder) that corresponds to the specified display name. It contains only one SHITEMID structure followed by a terminating zero. pdwAttributes - Address of the attributes of the file object. Can be NULL if the caller does not need attribute data. */ // // Note that the display name should be in UNICODE and that resulting PIDL is // relative to parent folder // // The MultiByteToWideChar function maps a character string to a wide-character // (Unicode) string. The character string mapped by this function is not // necessarily from a multibyte character set. // Use CP_ACP as indicator of translating from ANSI code page. Refer to the // documentation to get info on other parameters. // OLECHAR unicodeFolderPath[MAX_PATH]; #ifndef _UNICODE UTBStr::mbstowcs(unicodeFolderPath,MAX_PATH,sFolderFullPath,MAX_PATH); #else UTBStr::tcscpy(unicodeFolderPath, MAX_PATH, sFolderFullPath); #endif ULONG chEaten; LPITEMIDLIST lpidl; ULONG dwAttributes; HRESULT hResult=lpDesktopFolder-> ParseDisplayName(m_pOwnerWnd!=NULL ? m_pOwnerWnd->GetSafeHwnd() : NULL, NULL,unicodeFolderPath,&chEaten,&lpidl,&dwAttributes); if(FAILED(hResult)) { m_pMalloc->Free(lpidl); // just return the desktop folder ASSERT(lpDesktopFolder!=NULL); if(lpDesktopFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpDesktopFolder,NULL); return lpDesktopFolder; } // After we get IDL of lpcsFolderFullPath we should retrieve its IShellFolder // interface. We accomplish it using IShellFolder::BindToObject // /* HRESULT BindToObject( LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut ); Retrieves an IShellFolder object for a subfolder. Returns NOERROR if successful, or an OLE-defined error value otherwise. pidl - Address of an ITEMIDLIST structure that identifies the subfolder relative to its parent folder. pbcReserved - Reserved. Callers should specify NULL for this parameter; those called should ignore it. riid - Identifier of the interface to return. This parameter must point to the IID_IShellFolder interface identifier. ppvOut - Address that receives the interface pointer. If an error occurs, a NULL pointer is returned in this address. */ // // Note that pidl have to be relative to its parent folder but as long as we are // going to call this method of desktop folder interface then the fully qualified // pidl we got using ParseDisplayName method can be used. // LPSHELLFOLDER lpFolder; hResult=lpDesktopFolder-> BindToObject(lpidl,0, IID_IShellFolder,(LPVOID*)&lpFolder); m_pMalloc->Free(lpidl); if(FAILED(hResult)) { ASSERT(lpDesktopFolder!=NULL); if(lpDesktopFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpDesktopFolder,NULL); return lpDesktopFolder; } else { // release desktop folder lpDesktopFolder->Release(); ASSERT(lpFolder!=NULL); if(lpFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpFolder,NULL); return lpFolder; } } LPSHELLFOLDER COXShellNamespaceNavigator::GetShellFolder(LPCITEMIDLIST pidlFull) const { // First of all get the Desktop's IShellFolder interface. LPSHELLFOLDER lpDesktopFolder; if(FAILED(SHGetDesktopFolder(&lpDesktopFolder))) return NULL; // If pidlFull is NULL then just return the Desktop's IShellFolder if(pidlFull==NULL) { ASSERT(lpDesktopFolder!=NULL); if(lpDesktopFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpDesktopFolder,NULL); return lpDesktopFolder; } // If we have valid IDL we should retrieve its IShellFolder interface. We accomplish it // using IShellFolder::BindToObject // /* HRESULT BindToObject( LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut ); Retrieves an IShellFolder object for a subfolder. Returns NOERROR if successful, or an OLE-defined error value otherwise. pidl - Address of an ITEMIDLIST structure that identifies the subfolder relative to its parent folder. pbcReserved - Reserved. Callers should specify NULL for this parameter; those called should ignore it. riid - Identifier of the interface to return. This parameter must point to the IID_IShellFolder interface identifier. ppvOut - Address that receives the interface pointer. If an error occurs, a NULL pointer is returned in this address. */ // // Note that pidl have to be relative to its parent folder but as long as we are // going to call this method of desktop folder interface then the fully qualified // pidl we got using ParseDisplayName method can be used. // LPSHELLFOLDER lpFolder; HRESULT hResult=lpDesktopFolder-> BindToObject(pidlFull,0,IID_IShellFolder,(LPVOID*)&lpFolder); if(FAILED(hResult)) { ASSERT(lpDesktopFolder!=NULL); if(lpDesktopFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpDesktopFolder,NULL); return lpDesktopFolder; } else { // release desktop folder lpDesktopFolder->Release(); ASSERT(lpFolder!=NULL); if(lpFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpFolder,NULL); return lpFolder; } } LPSHELLFOLDER COXShellNamespaceNavigator:: GetShellFolder(const LPSHELLFOLDER lpParentFolder, const LPITEMIDLIST lpRelativeIDL) const { ASSERT(lpParentFolder!=NULL); LPSHELLFOLDER lpsf; // We use IShellFolder::BindToObject method to get IShellFolder interface // of expanded item. Don't forget that this interface take relative PIDL as // an argument HRESULT hResult=lpParentFolder->BindToObject(lpRelativeIDL,0, IID_IShellFolder,(LPVOID*)&lpsf); if(SUCCEEDED(hResult)) return lpsf; else return NULL; } BOOL COXShellNamespaceNavigator:: GetShellFolderFullIDL(CString sFolderFullPath, LPITEMIDLIST* ppidlFull) const { ASSERT(ppidlFull!=NULL); // If lpcsFolderFullPath is NULL then just return the Desktop's IDL which is NULL if(sFolderFullPath.IsEmpty()) { *ppidlFull=NULL; return TRUE; } // First of all get the Desktop's IShellFolder interface. LPSHELLFOLDER lpDesktopFolder; if(FAILED(SHGetDesktopFolder(&lpDesktopFolder))) return FALSE; OLECHAR unicodeFolderPath[MAX_PATH]; #ifndef _UNICODE UTBStr::mbstowcs(unicodeFolderPath,MAX_PATH,sFolderFullPath,MAX_PATH); #else UTBStr::tcscpy(unicodeFolderPath, MAX_PATH, sFolderFullPath); #endif LPITEMIDLIST lpidl; ULONG chEaten; ULONG dwAttributes; HRESULT hResult=lpDesktopFolder-> ParseDisplayName(m_pOwnerWnd!=NULL ? m_pOwnerWnd->GetSafeHwnd() : NULL, NULL,unicodeFolderPath,&chEaten,&lpidl,&dwAttributes); if(FAILED(hResult)) { lpDesktopFolder->Release(); m_pMalloc->Free(lpidl); return FALSE; } *ppidlFull=CopyPIDL(lpidl); lpDesktopFolder->Release(); m_pMalloc->Free(lpidl); return TRUE; } BOOL COXShellNamespaceNavigator:: GetShellFolderRelativeIDL(const LPSHELLFOLDER lpParentFolder, CString sFolderRelativePath, LPITEMIDLIST* ppidlRelative) const { ASSERT(lpParentFolder!=NULL); ASSERT(ppidlRelative!=NULL); OLECHAR unicodeFolderPath[MAX_PATH]; #ifndef _UNICODE UTBStr::mbstowcs(unicodeFolderPath,MAX_PATH,sFolderRelativePath,MAX_PATH); #else UTBStr::tcscpy(unicodeFolderPath, MAX_PATH, sFolderRelativePath); #endif LPITEMIDLIST lpidl; ULONG chEaten; ULONG dwAttributes; HRESULT hResult=lpParentFolder-> ParseDisplayName(m_pOwnerWnd!=NULL ? m_pOwnerWnd->GetSafeHwnd() : NULL, NULL,unicodeFolderPath,&chEaten,&lpidl,&dwAttributes); if(FAILED(hResult)) { m_pMalloc->Free(lpidl); return FALSE; } *ppidlRelative=CopyPIDL(lpidl); m_pMalloc->Free(lpidl); return TRUE; } BOOL COXShellNamespaceNavigator:: InitObjectsEnumerator(const LPSHELLFOLDER lpParentFolder, const LPITEMIDLIST lpParentFullIDL, DWORD dwFlags/*=SHCONTF_FOLDERS|SHCONTF_NONFOLDERS| SHCONTF_INCLUDEHIDDEN*/) { ASSERT(lpParentFolder!=NULL); if(m_bEnumeratorInitialized) { TRACE(_T("COXShellNamespaceNavigator::InitObjectsEnumerator: folder items enumerator has already been initialized.\n")); TRACE(_T("call COXShellNamespaceNavigator::ReleaseObjectsEnumerator() before.\n")); return FALSE; } // To enumerate object within given folder we use IShellFolder::EnumObjects // method to get a pointer to IEnumIDList interface. // /* HRESULT EnumObjects( HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST *ppenumIDList ); Determines the contents of a folder by creating an item enumeration object (a set of item identifiers) that can be retrieved using the IEnumIDList interface. Returns NOERROR if successful, or an OLE-defined error value otherwise. hwndOwner - Handle to the owner window that the client should specify if it displays a dialog box or message box. grfFlags - Flags indicating which items to include in the enumeration. SHCONTF_FOLDERS - Include items that are folders in the enumeration. SHCONTF_NONFOLDERS - Include items that are not folders in the enumeration. SHCONTF_INCLUDEHIDDEN - Include hidden items in the enumeration. ppenumIDList - Address that receives a pointer to the IEnumIDList interface created by this method. If an error occurs, a NULL pointer is returned in this address. */ ASSERT(m_lpeidl==NULL); ASSERT(m_lpEnumerateFolder==NULL); HRESULT hResult=lpParentFolder-> EnumObjects(m_pOwnerWnd!=NULL ? m_pOwnerWnd->GetSafeHwnd() : NULL, dwFlags,&m_lpeidl); if(FAILED(hResult)) return FALSE; // Call IEnumIDList::Reset() method to make sure we are at the begining of // the enumerating list m_lpeidl->Reset(); // After we get pointer to IEnumIDList interface we can use its methods to // retrieve information about all objects of given lpFolder // m_lpEnumerateFolder=lpParentFolder; m_bEnumeratorInitialized=TRUE; m_lpEnumerateParentFullIDL=lpParentFullIDL; return TRUE; } BOOL COXShellNamespaceNavigator::ReleaseObjectsEnumerator() { if(!m_bEnumeratorInitialized) { TRACE(_T("COXShellNamespaceNavigator::ReleaseObjectsEnumerator: there is nothing to release.\n")); return FALSE; } ASSERT(m_lpeidl!=NULL); m_lpeidl->Release(); m_lpeidl=NULL; ASSERT(m_lpEnumerateFolder!=NULL); m_lpEnumerateFolder=NULL; m_lpEnumerateParentFullIDL=NULL; m_bEnumeratorInitialized=FALSE; return TRUE; } LPNAMESPACEOBJECT COXShellNamespaceNavigator:: EnumerateObjectNext(BOOL& bLastReached) { ASSERT(m_bEnumeratorInitialized); ASSERT(m_lpeidl!=NULL); bLastReached=FALSE; // Loop throught the list using IEnumIDList::Next method /* HRESULT Next( ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched ); Retrieves the specified number of item identifiers in the enumeration sequence and advances the current position by the number of items retrieved. Returns NOERROR if successful, S_FALSE if there are no more items in the enumeration sequence, or an OLE-defined error value otherwise. celt - Specifies the number of elements in the array pointed to by the rgelt parameter. rgelt - Address of an array of ITEMIDLIST pointers that receives the item identifiers. The implementation must allocate these item identifiers using the shell's allocator (retrieved by the SHGetMalloc function). The calling application is responsible for freeing the item identifiers using the shell's allocator. pceltFetched - Address of a value that receives a count of the item identifiers actually returned in rgelt. The count can be smaller than the value specified in the celt parameter. This parameter can be NULL only if celt is one. If this method returns any value other than NOERROR, no entries in the rgelt array are valid on exit. They are all in an indeterminate state. */ LPITEMIDLIST lpidl; ULONG celtFetched; if(m_lpeidl->Next(1,&lpidl,&celtFetched)!=S_OK) { bLastReached=TRUE; return NULL; } LPNAMESPACEOBJECT lpNameSpaceObject= GetNameSpaceObject(m_lpEnumerateFolder,lpidl, ConcatenatePIDLs(m_lpEnumerateParentFullIDL,lpidl)); m_pMalloc->Free(lpidl); return lpNameSpaceObject; } LPNAMESPACEOBJECT COXShellNamespaceNavigator:: GetNameSpaceObject(const LPSHELLFOLDER lpsfParent, const LPITEMIDLIST lpRelativeIDL, const LPITEMIDLIST lpFullIDL) const { ASSERT(lpsfParent!=NULL); ASSERT(lpRelativeIDL!=NULL); // Information about currently enumerated object can be retrieved using // IShellFolder::GetAttributesOf method // /* HRESULT GetAttributesOf( UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut ); Retrieves the attributes of one or more file objects or subfolders. Returns NOERROR if successful, or an OLE-defined error value otherwise. cidl - Number of file objects from which to retrieve attributes. apidl - Address of an array of pointers to ITEMIDLIST structures, each of which uniquely identifies a file object relative to the parent folder. Each ITEMIDLIST structure must contain exactly one SHITEMID structure followed by a terminating zero. rgfInOut - Address of a single ULONG value that, on entry, contains the attributes that the caller is requesting. On exit, this value contains the requested attributes that are common to all of the specified objects. Note that this is the address of a single ULONG value, not an array of ULONG values. The lists below describe the possible flags for this parameter. For list of flags refer to the documentation. I list here only ones we are particular interested in: SFGAO_HASSUBFOLDER - The specified folders have subfolders (and are, therefore, expandable in the left pane of Windows Explorer). SFGAO_FILESYSTEM - The specified folders or file objects are part of the file system (that is, they are files, directories, or root directories). SFGAO_FILESYSANCESTOR - The specified folders contain one or more file system folders. SFGAO_FOLDER - The specified items are folders. SFGAO_REMOVABLE - The specified file objects or folders are on removable media. SFGAO_VALIDATE - Validate cached information. The shell will validate that the objects specified in a pidl still exist and will not used cached information when retrieving the attributes. If one or more of the items specified in a pidl no longer exist, this method will return an error code. If cidl is zero, the shell will discard all cached information for the shell folder. This is similar to doing a refresh of the folder. */ DWORD dwFilter=0xffffffff; lpsfParent->GetAttributesOf(1,(LPCITEMIDLIST*)&lpRelativeIDL,&dwFilter); // First of all, allocate memory for NAMESPACEOBJECT structure // using our m_pMalloc object LPNAMESPACEOBJECT lpNameSpaceObject=(LPNAMESPACEOBJECT)m_pMalloc-> Alloc(sizeof(NAMESPACEOBJECT)); // IMalloc::Alloc returns NULL if insufficient memory if(lpNameSpaceObject==NULL) return NULL; // store parent folder's IShellFolder lpNameSpaceObject->lpsfParent=lpsfParent; // whenever you save a pointer to COM object don't forget to // add reference lpsfParent->AddRef(); // make copy of enumerated shell object relative PIDL to save // it in NAMESPACEOBJECT structure lpNameSpaceObject->lpRelativeIDL=CopyPIDL(lpRelativeIDL); // Create full qualified PIDL by concatenating parent's full // qualified PIDL lpParentFullIDL with current relative PIDL lpidl lpNameSpaceObject->lpFullIDL=CopyPIDL(lpFullIDL); // We save text, images (including selected) and filter flags // for the object lpNameSpaceObject->dwFlags=dwFilter; // The item text is the display name of the object. CString sDisplayName=GetDisplayName(lpsfParent,lpRelativeIDL); if(sDisplayName.IsEmpty()) return NULL; lstrcpy(lpNameSpaceObject->szDisplayName,(LPCTSTR)sDisplayName); // The last thing we have to get are item's icons for normal // and selected state. Use COXShellNamespaceNavigator::GetShellImageList // function in order to retrieve image list of all icons used to // display shell objects. Now we need to request item index of // particular shell object. To accomplish this task we use // SHGetFileInfo function (remember that we have to use fully qualified // PIDL with that function) // // SHFILEINFO structure to be used with SH* functions SHFILEINFO sfi; // Request icon index for normal state SHGetFileInfo((LPCTSTR)lpNameSpaceObject->lpFullIDL,0,&sfi, sizeof(SHFILEINFO),SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON); lpNameSpaceObject->nImageSmall=sfi.iIcon; // Request icon index for selected state SHGetFileInfo((LPCTSTR)lpNameSpaceObject->lpFullIDL,0,&sfi, sizeof(SHFILEINFO),SHGFI_PIDL|SHGFI_SYSICONINDEX| SHGFI_SMALLICON|SHGFI_OPENICON); lpNameSpaceObject->nImageSelectedSmall=sfi.iIcon; // Request icon index for normal state SHGetFileInfo((LPCTSTR)lpNameSpaceObject->lpFullIDL,0,&sfi, sizeof(SHFILEINFO),SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_LARGEICON); lpNameSpaceObject->nImage=sfi.iIcon; // Request icon index for selected state SHGetFileInfo((LPCTSTR)lpNameSpaceObject->lpFullIDL,0,&sfi, sizeof(SHFILEINFO),SHGFI_PIDL|SHGFI_SYSICONINDEX| SHGFI_LARGEICON|SHGFI_OPENICON); lpNameSpaceObject->nImageSelected=sfi.iIcon; return lpNameSpaceObject; } HMENU COXShellNamespaceNavigator:: GetObjectContextMenu(const LPSHELLFOLDER lpParentFolder, const LPITEMIDLIST lpRelativeIDL, DWORD dwFlags) const { // We can request IContextMenu interface using // IShellFolder::GetUIObjectOf method. // /* HRESULT GetUIObjectOf( HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, LPVOID *ppvOut ); Retrieves an OLE interface that can be used to carry out actions on the specified file objects or folders. Returns NOERROR if successful, E_NOINTERFACE if the interface is not supported, or an OLE-defined error value otherwise. hwndOwner - Handle to the owner window that the client should specify if it displays a dialog box or message box. cidl - Number of file objects or subfolders specified in the apidl parameter. apidl - Address of an array of pointers to ITEMIDLIST structures, each of which uniquely identifies a file object or subfolder relative to the parent folder. Each item identifier list must contain exactly one SHITEMID structure followed by a terminating zero. riid - Identifier of the COM interface object to return. This can be any valid interface identifier that can be created for an item. The most common identifiers used by the shell are listed in the comments at the end of this reference. prgfInOut - Reserved. ppvOut - Address that receives the interface pointer. If an error occurs, a NULL pointer is returned in this address. If cidl is greater than one, the GetUIObjectOf implementation should only succeed if it can create one object for all items specified in apidl. If the implementation cannot create one object for all items, this method should fail. The following are the most common interface identifiers the shell uses when requesting an interface from this method. The list also indicates if cidl can be greater than one for the requested interface. Interface Identifier Allowed cidl Value IContextMenu The cidl parameter can be greater than or equal to one. IContextMenu2 The cidl parameter can be greater than or equal to one. IDataObject The cidl parameter can be greater than or equal to one. IDropTarget The cidl parameter can only be one. IExtractIcon The cidl parameter can only be one. IQueryInfo The cidl parameter can only be one. */ LPCONTEXTMENU lpcm; HRESULT hResult=lpParentFolder-> GetUIObjectOf(m_pOwnerWnd!=NULL ? m_pOwnerWnd->GetSafeHwnd() : NULL, 1, (const struct _ITEMIDLIST**)&(lpRelativeIDL),IID_IContextMenu,0, (LPVOID*)&lpcm); if(FAILED(hResult)) return NULL; // Use IContextMenu::QueryContextMenu method to get handle to popup menu. // /* HRESULT QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ); Adds menu items to the specified menu. The menu items should be inserted in the menu at the position specified by indexMenu, and their menu item identifiers must be between the idCmdFirst and idCmdLast parameter values. Returns an HRESULT structure in which, if the method is successful, the code member contains the menu identifier of the last menu item added plus one. hmenu - Handle to the menu. The handler should specify this handle when adding menu items. indexMenu - Zero-based position at which to insert the first menu item. idCmdFirst - Minimum value that the handler can specify for a menu item identifier. idCmdLast - Maximum value that the handler can specify for a menu item identifier. uFlags - Optional flags specifying how the context menu can be changed. Can be any combination of the following values: CMF_CANRENAME - This flag is set if the calling application supports renaming of items. A context menu extension or drag-and-drop handler should ignore this flag. A namespace extension should add a rename item to the menu if applicable. CMF_DEFAULTONLY - This flag is set when the user is activating the default action, typically by double-clicking. This flag provides a hint for the context menu extension to add nothing if it does not modify the default item in the menu. A context menu extension or drag-and-drop handler should not add any menu items if this value is specified. A namespace extension should add only the default item (if any). CMF_EXPLORE - This flag is set when Windows Explorer's tree window is present. Context menu handlers should ignore this value. CMF_INCLUDESTATIC - This flag is set when a static menu is being constructed. Only the browser should use this flag. All other context menu extensions should ignore this flag. CMF_NODEFAULT - This flag is set if no item in the menu should be the default item. A context menu extension or drag-and-drop handler should ignore this flag. A namespace extension should not set any of the menu items to the default. CMF_NORMAL - Indicates normal operation. A context menu extension, namespace extension, or drag-and-drop handler can add all menu items. CMF_NOVERBS - This flag is set for items displayed in the "Send To:" menu. Context menu handlers should ignore this value. CMF_VERBSONLY - This flag is set if the context menu is for a shortcut object. Context menu handlers should ignore this value. The remaining bits of the low-order word are reserved by the system. The high-order word may be used for context-specific communications. The CMF_RESERVED value can be used to mask out the low-order word. The actual identifier of each menu item should be idCmdFirst plus a menu identifier offset in the range zero through (idCmdLast ... idCmdFirst). */ // // Create popup menu and populate it using above described method HMENU hMenu=::CreatePopupMenu(); if(hMenu==NULL) { TRACE(_T("COXShellNamespaceNavigator::GetObjectContextMenu: CreatePopupMenu() failed\n")); return NULL; } hResult=lpcm->QueryContextMenu(hMenu,0,1,0x7fff,dwFlags); if(FAILED(hResult)) { TRACE(_T("COXShellNamespaceNavigator::GetObjectContextMenu: QueryContextMenu() failed\n")); return NULL; } lpcm->Release(); return hMenu; } BOOL COXShellNamespaceNavigator::InvokeCommand(const LPSHELLFOLDER lpParentFolder, const LPITEMIDLIST lpRelativeIDL, UINT nCmdID, DWORD dwFlags) const { // If any command from popup menu was selected then it should be // applied to corresponding shell object. // So we have to fill CMINVOKECOMMANDINFO structure CMINVOKECOMMANDINFO cmici={ sizeof(CMINVOKECOMMANDINFO) }; cmici.fMask=0; cmici.hwnd=m_pOwnerWnd!=NULL ? m_pOwnerWnd->GetSafeHwnd() : NULL; cmici.lpVerb=LPCSTR((UINT_PTR)nCmdID-1); cmici.lpParameters=NULL; cmici.lpDirectory=NULL; cmici.nShow=SW_SHOWNORMAL; cmici.dwHotKey=0; cmici.hIcon=NULL; // We can request IContextMenu interface using // IShellFolder::GetUIObjectOf method. // /* HRESULT GetUIObjectOf( HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, LPVOID *ppvOut ); Retrieves an OLE interface that can be used to carry out actions on the specified file objects or folders. Returns NOERROR if successful, E_NOINTERFACE if the interface is not supported, or an OLE-defined error value otherwise. hwndOwner - Handle to the owner window that the client should specify if it displays a dialog box or message box. cidl - Number of file objects or subfolders specified in the apidl parameter. apidl - Address of an array of pointers to ITEMIDLIST structures, each of which uniquely identifies a file object or subfolder relative to the parent folder. Each item identifier list must contain exactly one SHITEMID structure followed by a terminating zero. riid - Identifier of the COM interface object to return. This can be any valid interface identifier that can be created for an item. The most common identifiers used by the shell are listed in the comments at the end of this reference. prgfInOut - Reserved. ppvOut - Address that receives the interface pointer. If an error occurs, a NULL pointer is returned in this address. If cidl is greater than one, the GetUIObjectOf implementation should only succeed if it can create one object for all items specified in apidl. If the implementation cannot create one object for all items, this method should fail. The following are the most common interface identifiers the shell uses when requesting an interface from this method. The list also indicates if cidl can be greater than one for the requested interface. Interface Identifier Allowed cidl Value IContextMenu The cidl parameter can be greater than or equal to one. IContextMenu2 The cidl parameter can be greater than or equal to one. IDataObject The cidl parameter can be greater than or equal to one. IDropTarget The cidl parameter can only be one. IExtractIcon The cidl parameter can only be one. IQueryInfo The cidl parameter can only be one. */ LPCONTEXTMENU lpcm; HRESULT hResult=lpParentFolder-> GetUIObjectOf(m_pOwnerWnd!=NULL ? m_pOwnerWnd->GetSafeHwnd() : NULL, 1, (const struct _ITEMIDLIST**)&(lpRelativeIDL), IID_IContextMenu, 0, (LPVOID*)&lpcm); if(FAILED(hResult)) return FALSE; // In order to apply specified command we use // IContextMenu::InvokeCommand() method. /* HRESULT InvokeCommand( LPCMINVOKECOMMANDINFO lpici ); Carries out the command associated with a context menu item. Returns NOERROR if successful, or an OLE-defined error code otherwise. lpici - Address of a CMINVOKECOMMANDINFO structure containing information about the command. typedef struct _CMInvokeCommandInfo{ DWORD cbSize; DWORD fMask; HWND hwnd; LPCSTR lpVerb; LPCSTR lpParameters; LPCSTR lpDirectory; int nShow; DWORD dwHotKey; HANDLE hIcon; } CMINVOKECOMMANDINFO, *LPCMINVOKECOMMANDINFO; Contains information about a context menu command. cbSize - Contains the size of this structure, in bytes. fMask - Zero, or one or more of the following flags: CMIC_MASK_HOTKEY - The dwHotKey member is valid. CMIC_MASK_ICON - The hIcon member is valid. CMIC_MASK_FLAG_NO_UI- The system is prevented from displaying user interface elements (for example, error messages) while carrying out a command. hwnd - Handle of the window that is the owner of the context menu. An extension can also use this handle as the owner of any message boxes or dialog boxes it displays. lpVerb - A 32-bit value that contains zero in the high-order word and a menu-identifier offset of the command to carry out in the low-order word. The shell specifies this value (using the MAKEINTRESOURCE macro) when the user chooses a menu command. If the high-order word is not zero, this member is the address of a null-terminated string specifying the language-independent name of the command to carry out. This member is typically a string when a command is being activated by an application. The system provides predefined constant values for the following command strings. Value String CMDSTR_NEWFOLDER "NewFolder" CMDSTR_VIEWDETAILS "ViewDetails" CMDSTR_VIEWLIST "ViewList" lpParameters - Optional parameters. This member is always NULL for menu items inserted by a shell extension. lpDirectory - Optional working directory name. This member is always NULL for menu items inserted by a shell extension. nShow - Set of SW_ values to pass to the ShowWindow function if the command displays a window or starts an application. dwHotKey - Optional hot key to assign to any application activated by the command. If the fMask parameter does not specify CMIC_MASK_HOTKEY, this member is ignored. hIcon - Icon to use for any application activated by the command. If the fMask member does not specify CMIC_MASK_ICON, this member is ignored. The shell calls this method when the user chooses a command that the handler added to a context menu. This method may also be called by an application without any corresponding user action. */ HMENU hMenu=::CreatePopupMenu(); if(hMenu==NULL) { TRACE(_T("COXShellNamespaceNavigator::GetObjectContextMenu: CreatePopupMenu() failed\n")); return NULL; } hResult=lpcm->QueryContextMenu(hMenu,0,1,0x7fff,dwFlags); if(FAILED(hResult)) { TRACE(_T("COXShellNamespaceNavigator::GetObjectContextMenu: QueryContextMenu() failed\n")); ::DestroyMenu(hMenu); return NULL; } hResult=lpcm->InvokeCommand(&cmici); ::DestroyMenu(hMenu); lpcm->Release(); if(FAILED(hResult)) { TRACE(_T("COXShellNamespaceNavigator::InvokeCommand: InvokeCommand() failed\n")); return FALSE; } return TRUE; } BOOL COXShellNamespaceNavigator::InvokeDefaultCommand(const LPSHELLFOLDER lpParentFolder, const LPITEMIDLIST lpRelativeIDL) const { // retrieve the default command ID HMENU hMenu=GetObjectContextMenu(lpParentFolder,lpRelativeIDL,CMF_DEFAULTONLY); if(hMenu==NULL) { TRACE(_T("COXShellNamespaceNavigator::InvokeDefaultCommand: GetObjectContextMenu() failed\n")); return FALSE; } CMenu menuPopup; VERIFY(menuPopup.Attach(hMenu)); if(menuPopup.GetMenuItemCount()==0) { TRACE(_T("COXShellNamespaceNavigator::InvokeDefaultCommand: there is no context menu for the specified object\n")); return FALSE; } int nDefaultCmdID=-1; #if _MFC_VER > 0x0421 nDefaultCmdID=menuPopup.GetDefaultItem(GMDI_GOINTOPOPUPS,FALSE); #else nDefaultCmdID=::GetMenuDefaultItem(menuPopup.GetSafeHmenu(), FALSE,GMDI_GOINTOPOPUPS); #endif if(nDefaultCmdID==-1) { TRACE(_T("COXShellNamespaceNavigator::InvokeDefaultCommand: there is no default menu item for the specified object\n")); return FALSE; } VERIFY(menuPopup.DestroyMenu()); return InvokeCommand(lpParentFolder,lpRelativeIDL,nDefaultCmdID,CMF_DEFAULTONLY); } CString COXShellNamespaceNavigator::GetDisplayName(const LPSHELLFOLDER lpParentFolder, const LPITEMIDLIST lpRelativeIDL) const { ASSERT(lpParentFolder!=NULL); // To get the display name we // can use IShellFolder::GetDisplayNameOf method. /* HRESULT GetDisplayNameOf( LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName ); Retrieves the display name for the specified file object or subfolder, returning it in a STRRET structure. Returns NOERROR if successful, or an OLE-defined error value otherwise. pidl - Address of an ITEMIDLIST structure that uniquely identifies the file object or subfolder relative to the parent folder. uFlags - Flags indicating the type of display name to return. This enumeration consists of two sets of values. The first set is contained in bits 0 through 7 and specifies if the name is relative to this folder or if the name is a full name. The following are valid values in this set: SHGDN_NORMAL - Name is a full name. That is, the name is relative to the desktop and not to any specific folder. SHGDN_INFOLDER - Name is relative to the folder that is processing the name. The second set of values is contained in bits 8 through 15. These values are modifiers to the first set and specify the name retrieval options. The following are valid values in this set: SHGDN_NORMAL - Name will be used for generic display. SHGDN_FORADDRESSBAR - Name will be used for display in the address bar combo box. SHGDN_FORPARSING - Name will be used for parsing. That is, it can be passed to ParseDisplayName. SHGDN_INCLUDE_NONFILESYS- If this flag is set, the calling application is interested in names of any types of items. If this flag is not set, the calling application is only interested in items that are part of the file system. If this flag is not set and the item is not part of the file system, this method should fail. lpName - Address of a STRRET structure in which to return the display name. The string returned in this structure depends on the type of display name requested. If the identifier contains the display name (in the local character set), it returns the offset to the name. If not, it returns a pointer to the display name string (UNICODE) allocated by the task allocator, or it fills in a buffer. The type of string returned depends on the type of display specified. */ // // Note that result display name could be in different // character set so we have to convert it. STRRET strRet; HRESULT hResult=lpParentFolder-> GetDisplayNameOf(lpRelativeIDL,SHGDN_NORMAL,&strRet); if(FAILED(hResult)) { return _T(""); } TCHAR szDisplayName[MAX_PATH]; // buffer for getting display name of shell object switch (strRet.uType) { #ifndef _UNICODE case STRRET_WSTR: { WideCharToMultiByte(CP_ACP,0,strRet.pOleStr,-1, szDisplayName,MAX_PATH,NULL,NULL); break; } case STRRET_OFFSET: { lstrcpy(szDisplayName,(char*)lpRelativeIDL+ strRet.uOffset); break; } case STRRET_CSTR: { lstrcpy(szDisplayName,strRet.cStr); break; } #else case STRRET_WSTR: { lstrcpyn(szDisplayName,strRet.pOleStr,MAX_PATH); break; } case STRRET_OFFSET: { _mbstowcsz(szDisplayName,(char*)lpRelativeIDL+ strRet.uOffset,MAX_PATH); break; } case STRRET_CSTR: { _mbstowcsz(szDisplayName,strRet.cStr,MAX_PATH); break; } #endif default: { ASSERT(FALSE); break; } } CString sResult=szDisplayName; return sResult; } //////////////////////////////////////////////////////////////////////////// /* LPITEMIDLIST COXShellNamespaceNavigator::ConcatenatePIDLs(const LPCITEMIDLIST pidl1, const LPCITEMIDLIST pidl2) const { LPITEMIDLIST pidlNew; UINT cb1, cb2; cb1=cb2=0; //are both of these NULL? if(!pidl1 && !pidl2) return NULL; //if pidl1 is NULL, just return a copy of pidl2 if(!pidl1) { pidlNew=CopyPIDL(pidl2); return pidlNew; } //if pidl2 is NULL, just return a copy of pidl1 if(!pidl2) { pidlNew=CopyPIDL(pidl1); return pidlNew; } cb1=GetIDLSize(pidl1)-sizeof(ITEMIDLIST); cb2=GetIDLSize(pidl2); //create the new PIDL pidlNew=(LPITEMIDLIST)m_pMalloc->Alloc(cb1+cb2); if(pidlNew) { //copy the first PIDL CopyMemory(pidlNew,pidl1,cb1); //copy the second PIDL CopyMemory(((LPBYTE)pidlNew)+cb1,pidl2,cb2); } return pidlNew; } UINT COXShellNamespaceNavigator::GetIDLSize(const LPCITEMIDLIST pidl) const { UINT cbTotal=0; LPITEMIDLIST pidlTemp=(LPITEMIDLIST)pidl; while(pidlTemp) { cbTotal+=pidlTemp->mkid.cb; pidlTemp=GetNextIDLItem(pidlTemp); } //add the size of the NULL terminating ITEMIDLIST cbTotal+=sizeof(ITEMIDLIST); return (cbTotal); } */ LPITEMIDLIST COXShellNamespaceNavigator::ConcatenatePIDLs(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) const { LPITEMIDLIST pidlNew; UINT cb1, cb2; cb1=cb2=0; //are both of these NULL? if(!pidl1 && !pidl2) return NULL; //if pidl1 is NULL, just return a copy of pidl2 if(!pidl1) { pidlNew=CopyPIDL(pidl2); return pidlNew; } //if pidl2 is NULL, just return a copy of pidl1 if(!pidl2) { pidlNew=CopyPIDL(pidl1); return pidlNew; } cb1=GetIDLSize(pidl1)-sizeof(pidl1->mkid.cb); cb2=GetIDLSize(pidl2); //create the new PIDL pidlNew=(LPITEMIDLIST)m_pMalloc->Alloc(cb1+cb2); if(pidlNew) { //copy the first PIDL CopyMemory(pidlNew,pidl1,cb1); //copy the second PIDL CopyMemory(((LPBYTE)pidlNew)+cb1,pidl2,cb2); } return pidlNew; } UINT COXShellNamespaceNavigator::GetIDLSize(LPCITEMIDLIST pidl) const { UINT cbTotal=0; LPITEMIDLIST pidlTemp=(LPITEMIDLIST)pidl; if(pidlTemp!=NULL) { // add the size of the NULL terminating mkid.cb - !!!!2 bytes!!!! cbTotal += sizeof(pidlTemp->mkid.cb); while(pidlTemp && pidlTemp->mkid.cb) { cbTotal += pidlTemp->mkid.cb; pidlTemp = GetNextIDLItem(pidlTemp); } } return cbTotal; } LPITEMIDLIST COXShellNamespaceNavigator::CopyPIDL(LPCITEMIDLIST pidlSource) const { LPITEMIDLIST pidlTarget=NULL; UINT cbSource=0; if(NULL==pidlSource) return (NULL); // Allocate the new pidl cbSource=GetIDLSize(pidlSource); pidlTarget=(LPITEMIDLIST)m_pMalloc->Alloc(cbSource); if(!pidlTarget) return (NULL); // Copy the source to the target CopyMemory(pidlTarget,pidlSource,cbSource); return pidlTarget; } LPITEMIDLIST COXShellNamespaceNavigator::GetNextIDLItem(LPCITEMIDLIST pidl) const { if(pidl && pidl->mkid.cb!=0) { LPITEMIDLIST pidlNext=(LPITEMIDLIST)(LPBYTE)(((LPBYTE)pidl) + pidl->mkid.cb); if(pidlNext->mkid.cb==0) return NULL; else return pidlNext; } else return (NULL); } void COXShellNamespaceNavigator::FreeShellObject(void* pVoid) const { ASSERT(m_pMalloc!=NULL); m_pMalloc->Free(pVoid); } CString COXShellNamespaceNavigator::GetSpecialFolderPath(int nFolder, CWnd* pWndOwner/*=NULL*/) const { CString sPath(_T("")); LPITEMIDLIST lpFullIDL; if(FAILED(SHGetSpecialFolderLocation( pWndOwner==NULL ? NULL : pWndOwner->GetSafeHwnd(),nFolder,&lpFullIDL))) { return sPath; } TCHAR buffer[MAX_PATH]; if(SHGetPathFromIDList(lpFullIDL,buffer)) { sPath=(LPCTSTR)buffer; } m_pMalloc->Free(lpFullIDL); return sPath; } BOOL COXShellNamespaceNavigator::RenameShellObject(const LPSHELLFOLDER lpParentFolder, const LPITEMIDLIST lpRelativeIDL, LPITEMIDLIST* lppNewRelativeIDL, LPCTSTR lpszNewName) const { ASSERT(lpParentFolder!=NULL); ASSERT(lpRelativeIDL!=NULL); ASSERT(lppNewRelativeIDL!=NULL); OLECHAR unicodeNewName[MAX_PATH]; #ifndef _UNICODE UTBStr::mbstowcs(unicodeNewName,MAX_PATH,lpszNewName,MAX_PATH); #else UTBStr::tcscpy(unicodeNewName, MAX_PATH, lpszNewName); #endif if(FAILED(lpParentFolder-> SetNameOf((m_pOwnerWnd==NULL ? NULL : m_pOwnerWnd->GetSafeHwnd()), lpRelativeIDL,unicodeNewName,SHGDN_INFOLDER/*|SHGDN_INCLUDE_NONFILESYS*/, lppNewRelativeIDL))) { TRACE(_T("COXShellNamespaceNavigator::RenameShellObject: failed to set new name for the specified object\n")); return FALSE; } ASSERT(*lppNewRelativeIDL!=NULL); return TRUE; } LPSHELLFOLDER COXShellNamespaceNavigator:: GetParentShellFolder(LPCITEMIDLIST pidlFull, LPITEMIDLIST* lppRelativeIDL) const { ASSERT(lppRelativeIDL!=NULL); if(pidlFull==NULL) { // Retrieve the Desktop's IShellFolder interface. LPSHELLFOLDER lpDesktopFolder; if(FAILED(SHGetDesktopFolder(&lpDesktopFolder))) return NULL; ASSERT(lpDesktopFolder!=NULL); if(lpDesktopFolder!=NULL) m_mapIShellFolderToRelease.SetAt((DWORD_PTR)lpDesktopFolder,NULL); return lpDesktopFolder; } UINT cbTotal=0; LPITEMIDLIST pidlTemp=(LPITEMIDLIST)pidlFull; // add the size of the NULL terminating mkid.cb - !!!!2 bytes!!!! cbTotal+=sizeof(pidlTemp->mkid.cb); while(pidlTemp && pidlTemp->mkid.cb) { LPITEMIDLIST pidlLast=pidlTemp; UINT cb=pidlTemp->mkid.cb; cbTotal+=cb; pidlTemp=GetNextIDLItem(pidlTemp); if(pidlTemp==NULL) { cbTotal-=cb; *lppRelativeIDL=CopyPIDL(pidlLast); } } LPITEMIDLIST pidlTarget=(LPITEMIDLIST)m_pMalloc->Alloc(cbTotal); if(!pidlTarget) return (NULL); ::memset(pidlTarget,0,cbTotal); // Copy the source to the target CopyMemory(pidlTarget,(LPITEMIDLIST)pidlFull,cbTotal-sizeof(pidlTemp->mkid.cb)); LPSHELLFOLDER pShellFolder=GetShellFolder(pidlTarget); m_pMalloc->Free(pidlTarget); return pShellFolder; }