// only one full qualified PIDL has been passed void CShellContextMenu::SetObjects(LPITEMIDLIST pidl) { // free all allocated datas if (m_psfFolder && bDelete) m_psfFolder->Release (); m_psfFolder = NULL; FreePIDLArray (m_pidlArray); m_pidlArray = NULL; // full qualified PIDL is passed so we need // its parent IShellFolder interface and its relative PIDL to that LPITEMIDLIST pidlItem = NULL; SHBindToParent ((LPCITEMIDLIST) pidl, IID_IShellFolder, (void **) &m_psfFolder, (LPCITEMIDLIST *) &pidlItem); m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST)); // allocate ony for one elemnt m_pidlArray[0] = CopyPIDL (pidlItem); // now free pidlItem via IMalloc interface (but not m_psfFolder, that we need later LPMALLOC lpMalloc = NULL; SHGetMalloc (&lpMalloc); lpMalloc->Free (pidlItem); lpMalloc->Release(); nItems = 1; bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu }
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; }
// this is workaround function for the Shell API Function SHBindToParent // SHBindToParent is not available under Win95/98 HRESULT CShellContextMenu::SHBindToParentEx (LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast) { HRESULT hr = 0; if (!pidl || !ppv) return E_POINTER; int nCount = GetPIDLCount (pidl); if (nCount == 0) // desktop pidl of invalid pidl return E_POINTER; IShellFolder * psfDesktop = NULL; SHGetDesktopFolder (&psfDesktop); if (nCount == 1) // desktop pidl { if ((hr = psfDesktop->QueryInterface(riid, ppv)) == S_OK) { if (ppidlLast) *ppidlLast = CopyPIDL (pidl); } psfDesktop->Release (); return hr; } LPBYTE pRel = GetPIDLPos (pidl, nCount - 1); LPITEMIDLIST pidlParent = NULL; pidlParent = CopyPIDL (pidl, pRel - (LPBYTE) pidl); IShellFolder * psfFolder = NULL; if ((hr = psfDesktop->BindToObject (pidlParent, NULL, __uuidof (psfFolder), (void **) &psfFolder)) != S_OK) { free (pidlParent); psfDesktop->Release (); return hr; } if ((hr = psfFolder->QueryInterface (riid, ppv)) == S_OK) { if (ppidlLast) *ppidlLast = CopyPIDL ((LPCITEMIDLIST) pRel); } free (pidlParent); psfFolder->Release (); psfDesktop->Release (); return hr; }
void CShellContextMenu::SetObjects(const QStringList &strList) { // free all allocated datas if (m_psfFolder && bDelete) m_psfFolder->Release (); m_psfFolder = NULL; FreePIDLArray (m_pidlArray); m_pidlArray = NULL; // get IShellFolder interface of Desktop (root of shell namespace) IShellFolder * psfDesktop = NULL; SHGetDesktopFolder (&psfDesktop); // needed to obtain full qualified pidl // ParseDisplayName creates a PIDL from a file system path relative to the IShellFolder interface // but since we use the Desktop as our interface and the Desktop is the namespace root // that means that it's a fully qualified PIDL, which is what we need LPITEMIDLIST pidl = NULL; psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[0].utf16(), NULL, &pidl, NULL); // now we need the parent IShellFolder interface of pidl, and the relative PIDL to that interface LPITEMIDLIST pidlItem = NULL; // relative pidl SHBindToParentEx (pidl, IID_IShellFolder, (void **) &m_psfFolder, NULL); free (pidlItem); // get interface to IMalloc (need to free the PIDLs allocated by the shell functions) LPMALLOC lpMalloc = NULL; SHGetMalloc (&lpMalloc); lpMalloc->Free (pidl); // now we have the IShellFolder interface to the parent folder specified in the first element in strArray // since we assume that all objects are in the same folder (as it's stated in the MSDN) // we now have the IShellFolder interface to every objects parent folder IShellFolder * psfFolder = NULL; nItems = strList.size (); for (int i = 0; i < nItems; i++) { pidl=0; psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[i].utf16(), NULL, &pidl, NULL); if (pidl) { m_pidlArray = (LPITEMIDLIST *) realloc (m_pidlArray, (i + 1) * sizeof (LPITEMIDLIST)); // get relative pidl via SHBindToParent SHBindToParentEx (pidl, IID_IShellFolder, (void **) &psfFolder, (LPCITEMIDLIST *) &pidlItem); m_pidlArray[i] = CopyPIDL (pidlItem); // copy relative pidl to pidlArray free (pidlItem); lpMalloc->Free (pidl); // free pidl allocated by ParseDisplayName psfFolder->Release (); } } lpMalloc->Release (); psfDesktop->Release (); bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu }
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; }
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; }
HRESULT CContextMenuHelper::SHGetContextMenu(std::vector<LPCTSTR> files) { HRESULT hr; IMalloc *pm = NULL; IShellFolder *pDesktop = NULL; IShellFolder *psf = NULL; LPITEMIDLIST pidl = NULL; WCHAR fwname[MAX_PATH + 1]; if (SUCCEEDED(hr = SHGetMalloc(&pm))) { if (SUCCEEDED(hr = SHGetDesktopFolder(&pDesktop))) { std::vector<LPITEMIDLIST> pidls; IShellFolder* psfFolder = NULL; for (UINT i = 0; SUCCEEDED(hr) && i < files.size(); i++) { LPCTSTR lpszFilePath = files[i]; ULONG cch; ULONG attrs; // Convert to Unicode memset(fwname, L'\0', (MAX_PATH + 1) * sizeof(WCHAR)); MultiByteToWideChar(CP_THREAD_ACP, 0, lpszFilePath, -1, fwname, MAX_PATH); if (SUCCEEDED(hr = pDesktop->ParseDisplayName(m_hWnd, NULL, fwname, &cch, &pidl, &attrs))) { LPITEMIDLIST pidlItem = NULL; if (SUCCEEDED(hr = SHBindToParentEx((LPCITEMIDLIST)pidl, IID_IShellFolder, (void **)&psf, (LPCITEMIDLIST *) &pidlItem, pDesktop, pm))) { pidls.push_back(CopyPIDL(pidlItem, pm)); pm->Free(pidlItem); if (psfFolder == NULL) { // Remember first folder and we wiil show menu for this one // All other folder will be ignored psfFolder = psf; } else { psf->Release(); } } pm->Free(pidl); } } if (SUCCEEDED(hr) && psfFolder != NULL) { hr = psfFolder->GetUIObjectOf(m_hWnd, pidls.size(), const_cast<LPCITEMIDLIST*>(pidls.begin()), IID_IContextMenu, NULL, (void**)&m_lpcm); psfFolder->Release(); } FreeItemIDList(pidls, pm); pDesktop->Release(); } pm->Release(); } return hr; }
// IShellFolder interface with a relative pidl has been passed void CShellContextMenu::SetObjects(IShellFolder *psfFolder, LPITEMIDLIST pidlItem) { // free all allocated datas if (m_psfFolder && bDelete) m_psfFolder->Release (); m_psfFolder = NULL; FreePIDLArray (m_pidlArray); m_pidlArray = NULL; m_psfFolder = psfFolder; m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST)); m_pidlArray[0] = CopyPIDL (pidlItem); nItems = 1; bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu }
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; }
void CShellContextMenu::SetObjects(IShellFolder * psfFolder, LPITEMIDLIST *pidlArray, int nItemCount) { // free all allocated datas if (m_psfFolder && bDelete) m_psfFolder->Release (); m_psfFolder = NULL; FreePIDLArray (m_pidlArray); m_pidlArray = NULL; m_psfFolder = psfFolder; m_pidlArray = (LPITEMIDLIST *) malloc (nItemCount * sizeof (LPITEMIDLIST)); for (int i = 0; i < nItemCount; i++) m_pidlArray[i] = CopyPIDL (pidlArray[i]); nItems = nItemCount; bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu }
// this is workaround function for the Shell API Function SHBindToParent // SHBindToParent is not available under Win95/98 HRESULT CContextMenuHelper::SHBindToParentEx(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv, LPCITEMIDLIST *ppidlLast, IShellFolder *psfDesktop, IMalloc *pm) { HRESULT hr = 0; if (pidl == NULL || ppv == NULL) { return E_POINTER; } IShellFolder *psfFolder = NULL; if (SUCCEEDED(hr == psfDesktop->QueryInterface(riid, (LPVOID*)&psfFolder))) { IShellFolder *psfParent = NULL; // For each pidl component, bind to folder LPITEMIDLIST pidlNext, pidlLast; pidlNext = NEXTPIDL(pidl); pidlLast = (LPITEMIDLIST)pidl; while (pidlNext->mkid.cb != 0) { UINT uSave = pidlNext->mkid.cb; //stop the chain temporarily pidlNext->mkid.cb = 0; //so we can bind to the next folder 1 deeper if (!SUCCEEDED(hr = psfFolder->BindToObject(pidlLast, NULL, riid, (LPVOID*)&psfParent))) { return hr; } pidlNext->mkid.cb = uSave; //restore the chain psfFolder->Release(); //and set up to work with the next-level folder psfFolder = psfParent; pidlLast = pidlNext; pidlNext = NEXTPIDL(pidlNext); //advance to next pidl } if (ppidlLast != NULL) { *ppidlLast = CopyPIDL((LPCITEMIDLIST)pidlLast, pm); } *ppv = psfFolder; } return hr; }
void CShellContextMenu::SetObjects(CString &strArray) { // free all allocated datas if (m_psfFolder && bDelete) m_psfFolder->Release (); m_psfFolder = NULL; FreePIDLArray (m_pidlArray); m_pidlArray = NULL; // get IShellFolder interface of Desktop (root of shell namespace) IShellFolder * psfDesktop = NULL; SHGetDesktopFolder (&psfDesktop); // needed to obtain full qualified pidl // ParseDisplayName creates a PIDL from a file system path relative to the IShellFolder interface // but since we use the Desktop as our interface and the Desktop is the namespace root // that means that it's a fully qualified PIDL, which is what we need LPITEMIDLIST pidl = NULL; #ifndef _UNICODE OLECHAR * olePath = (OLECHAR *) calloc (strArray.GetLength () + 1, sizeof (OLECHAR)); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)strArray, -1, olePath, strArray.GetLength () + 1); psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL); free (olePath); #else psfDesktop->ParseDisplayName (NULL, 0, strArray.GetBuffer (0), NULL, &pidl, NULL); #endif // now we need the parent IShellFolder interface of pidl, and the relative PIDL to that interface LPITEMIDLIST pidlItem = NULL; // relative pidl SHBindToParentEx (pidl, IID_IShellFolder, (void **) &m_psfFolder, NULL); free (pidlItem); // get interface to IMalloc (need to free the PIDLs allocated by the shell functions) LPMALLOC lpMalloc = NULL; SHGetMalloc (&lpMalloc); lpMalloc->Free (pidl); // now we have the IShellFolder interface to the parent folder specified in the first element in strArray // since we assume that all objects are in the same folder (as it's stated in the MSDN) // we now have the IShellFolder interface to every objects parent folder IShellFolder * psfFolder = NULL; nItems = 1; for (int i = 0; i < nItems; i++) { #ifndef _UNICODE olePath = (OLECHAR *) calloc (strArray.GetLength () + 1, sizeof (OLECHAR)); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (strArray), -1, olePath, strArray.GetLength () + 1); psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL); free (olePath); #else psfDesktop->ParseDisplayName (NULL, 0, strArray.GetBuffer (0), NULL, &pidl, NULL); #endif m_pidlArray = (LPITEMIDLIST *) realloc (m_pidlArray, (i + 1) * sizeof (LPITEMIDLIST)); // get relative pidl via SHBindToParent SHBindToParentEx (pidl, IID_IShellFolder, (void **) &psfFolder, (LPCITEMIDLIST *) &pidlItem); m_pidlArray[i] = CopyPIDL (pidlItem); // copy relative pidl to pidlArray free (pidlItem); lpMalloc->Free (pidl); // free pidl allocated by ParseDisplayName psfFolder->Release (); } lpMalloc->Release (); psfDesktop->Release (); bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu }
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; }