1 //*******************************************************************************
4 // You may use this source code, compile or redistribute it as part of your application
5 // for free. You cannot redistribute it as a part of a software development
6 // library without the agreement of the author. If the sources are
7 // distributed along with the application, you should leave the original
8 // copyright notes in the source code without any changes.
9 // This code can be used WITHOUT ANY WARRANTIES at your own risk.
11 // For the latest updates to this code, check this site:
12 // http://www.masmex.com
15 // Copyright(C) 2000 Philip Oldaker <email: philip@masmex.com>
16 //*******************************************************************************
18 // ShellPidl.cpp: implementation of the CShellPidl class.
20 //////////////////////////////////////////////////////////////////////
23 #include "ShellPidl.h"
24 #include "UIMessages.h"
28 #include "ShellContextMenu.h"
29 #include "UICoolMenu.h"
30 #include "cbformats.h"
32 #define SF_DRAGDROP_FLAGS SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK;
33 UINT CF_IDLIST = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
34 UINT CF_SHELLURL = RegisterClipboardFormat(CFSTR_SHELLURL);
36 #define TPM_FLAGS (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD)
38 //////////////////////////////////////////////////////////////////////
39 // Construction/Destruction
40 //////////////////////////////////////////////////////////////////////
42 CShellPidl::CShellPidl()
44 SHGetMalloc(&m_pMalloc);
45 SHGetDesktopFolder(&m_psfDesktop);
46 ZeroMemory(&m_EmptyPidl,sizeof(ITEMIDLIST));
49 CShellPidl::~CShellPidl()
54 m_psfDesktop->Release();
57 IMalloc *CShellPidl::GetMalloc()
62 DWORD CShellPidl::GetDragDropAttributes(LPLVITEMDATA plvid)
64 return GetDragDropAttributes(plvid->lpsfParent,plvid->lpi);
67 DWORD CShellPidl::GetDragDropAttributes(LPTVITEMDATA ptvid)
69 return GetDragDropAttributes(ptvid->lpsfParent,ptvid->lpi);
72 DWORD CShellPidl::GetDragDropAttributes(LPSHELLFOLDER pFolder,LPCITEMIDLIST pidl)
75 pFolder = m_psfDesktop;
76 DWORD dwAttrs=SF_DRAGDROP_FLAGS;
77 HRESULT hr = pFolder->GetAttributesOf(1,(LPCITEMIDLIST*)&pidl,&dwAttrs);
79 dwAttrs = SF_DRAGDROP_FLAGS;
83 DWORD CShellPidl::GetDragDropAttributes(COleDataObject *pDataObject)
85 IDataObject *pDataObj = pDataObject->m_lpDataObject;
88 ZeroMemory(&stgm, sizeof(stgm));
91 fetc.cfFormat = CF_IDLIST;
93 fetc.dwAspect = DVASPECT_CONTENT;
95 fetc.tymed = TYMED_HGLOBAL;
97 DWORD dwAttrs=DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
98 HRESULT hr = pDataObj->QueryGetData(&fetc);
101 hr = pDataObj->GetData(&fetc, &stgm);
104 DWORD pData = (DWORD)GlobalLock(stgm.hGlobal);
105 LPIDA pIDList = (LPIDA)pData;
106 UINT nFolderOffset = pIDList->aoffset[0];
107 TRACE2("PIDL(%u) offfset=%u\n",0,nFolderOffset);
108 DWORD pFolder = pData+nFolderOffset;
109 LPITEMIDLIST pidl = (LPITEMIDLIST)pFolder;
110 LPSHELLFOLDER psfParent = GetFolder(pidl);
113 // get attributes for the children
114 TRACE1("PIDL count=%u\n",pIDList->cidl);
115 for(UINT i=1;i < (pIDList->cidl+1);i++)
117 UINT nListOffset = pIDList->aoffset[i];
118 TRACE2("PIDL(%u) offfset=%u\n",i,nListOffset);
119 ULONG ulAttrs = SF_DRAGDROP_FLAGS;
120 DWORD dwPidl = pData+nListOffset;
121 LPITEMIDLIST pidlist = (LPITEMIDLIST)dwPidl;
122 psfParent->GetAttributesOf(1,(LPCITEMIDLIST*)&pidlist,&ulAttrs);
125 SHPidlToPathEx(pidlist,sPath,psfParent);
126 TRACE2("Drag drop source path=%s Attributes=%u\n",sPath,ulAttrs);
129 dwAttrs = dwAttrs & ulAttrs;
131 psfParent->Release();
133 GlobalUnlock(stgm.hGlobal);
134 ReleaseStgMedium(&stgm);
138 LPCITEMIDLIST CShellPidl::GetEmptyPidl()
143 bool CShellPidl::IsDesktopFolder(LPSHELLFOLDER psFolder)
145 return psFolder == NULL || psFolder == m_psfDesktop;
148 LPSHELLFOLDER CShellPidl::GetDesktopFolder()
153 LPSHELLFOLDER CShellPidl::GetFolder(LPITEMIDLIST pidl)
155 if (pidl == NULL || pidl->mkid.cb == 0)
157 LPSHELLFOLDER pFolder=NULL;
158 if (FAILED(m_psfDesktop->BindToObject(pidl, 0, IID_IShellFolder,(LPVOID*)&pFolder)))
163 // CopyItemID - creates an item identifier list containing the first
164 // item identifier in the specified list.
165 // Returns a PIDL if successful, or NULL if out of memory.
166 LPITEMIDLIST CShellPidl::CopyItemID(LPITEMIDLIST pidl,int n)
168 // Get the size of the specified item identifier.
172 int cb = pidl->mkid.cb;
173 int nSize = cb + sizeof(pidl->mkid.cb);
174 // Allocate a new item identifier list.
175 LPITEMIDLIST pidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(nSize);
176 ZeroMemory(pidlNew,nSize);
179 // Copy the specified item identifier.
180 CopyMemory(pidlNew, pidl, nSize-sizeof(pidl->mkid.cb));
185 LPITEMIDLIST pidl_index=NULL;
186 for(int i=0;i < n && pidl->mkid.cb;i++)
191 return pidl_index ? CopyItemID(pidl_index,0) : NULL;
196 // Returns a PIDL if successful, or NULL if out of memory.
197 LPITEMIDLIST CShellPidl::CopyLastItemID(LPITEMIDLIST pidl)
199 // Get the size of the specified item identifier.
203 LPITEMIDLIST last_pidl=pidl;
204 while (pidl->mkid.cb)
209 if (last_pidl == NULL)
211 return CopyItemID(last_pidl);
214 // copies the absolute pidl up till n
215 LPITEMIDLIST CShellPidl::CopyAbsItemID(LPITEMIDLIST pidl,int n)
217 // Get the size of the specified item identifier.
221 LPITEMIDLIST first_pidl=NULL;
222 LPITEMIDLIST abs_pidl=NULL;
223 LPITEMIDLIST new_abs_pidl=NULL;
224 for(int i=0;i < n && pidl && pidl->mkid.cb;i++)
226 first_pidl = CopyItemID(pidl);
227 new_abs_pidl = ConcatPidl(abs_pidl,first_pidl);
230 m_pMalloc->Free(abs_pidl);
232 abs_pidl = new_abs_pidl;
235 m_pMalloc->Free(first_pidl);
242 // Makes a copy of an ITEMIDLIST
243 LPITEMIDLIST CShellPidl::CopyItemIDList(LPITEMIDLIST pidl)
245 // Allocate a new item identifier list.
246 int nSize = GetSize(pidl);
247 LPITEMIDLIST pidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(nSize);
248 ZeroMemory(pidlNew,nSize);
251 // Copy the specified item identifier.
252 CopyMemory(pidlNew, pidl, nSize);
257 bool CShellPidl::CompareMemPidls(LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)
259 // Allocate a new item identifier list.
260 if (pidl1 == NULL || pidl2 == NULL)
262 return memcmp(pidl1,pidl2,(size_t)GetSize(pidl1)) == 0;
265 // Returns true if lists are the same
266 bool CShellPidl::ComparePidls(LPSHELLFOLDER pFolder,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)
268 // Allocate a new item identifier list.
270 pFolder = GetDesktopFolder();
271 if (pidl1 == NULL || pidl2 == NULL)
273 return (short)pFolder->CompareIDs(0,pidl1,pidl2) == 0;
276 void CShellPidl::Free(void *pv)
282 void CShellPidl::FreePidl(LPITEMIDLIST pidl)
285 m_pMalloc->Free(pidl);
288 UINT CShellPidl::GetCount(LPCITEMIDLIST pidl)
293 while (pidl->mkid.cb)
302 UINT CShellPidl::GetSize(LPCITEMIDLIST pidl)
307 cbTotal += sizeof(pidl->mkid.cb); // Null terminator
308 while (pidl->mkid.cb)
310 cbTotal += pidl->mkid.cb;
317 LPITEMIDLIST CShellPidl::Next(LPCITEMIDLIST pidl)
319 LPSTR lpMem=(LPSTR)pidl;
321 lpMem+=pidl->mkid.cb;
323 return (LPITEMIDLIST)lpMem;
326 LPITEMIDLIST CShellPidl::ConcatPidl(LPITEMIDLIST pidlDest,LPITEMIDLIST pidlSrc)
328 // Get the size of the specified item identifier.
331 if (pidlDest) //May be NULL
332 cbDest = GetSize(pidlDest) - sizeof(pidlDest->mkid.cb);
333 cbSrc = GetSize(pidlSrc);
335 // Allocate a new item identifier list.
336 LPITEMIDLIST pidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(cbSrc+cbDest);
339 ZeroMemory(pidlNew,cbSrc+cbDest);
340 // Copy the specified item identifier.
342 CopyMemory(pidlNew, pidlDest, cbDest);
343 CopyMemory(((USHORT*)(((LPBYTE)pidlNew)+cbDest)), pidlSrc, cbSrc);
348 int CShellPidl::GetIcon(LPITEMIDLIST lpi, UINT uFlags)
351 ZeroMemory(&sfi,sizeof(sfi));
353 uFlags |= (SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
354 uFlags |= SHGFI_PIDL;
355 SHGetFileInfo((LPCTSTR)lpi, 0, &sfi, sizeof(SHFILEINFO), uFlags);
360 STDMETHODIMP CShellPidl::SHPidlToPathEx(LPCITEMIDLIST pidl, CString &sPath, LPSHELLFOLDER pFolder, DWORD dwFlags)
362 STRRET StrRetFilePath;
363 LPTSTR pszFilePath = NULL;
366 pFolder = GetDesktopFolder();
369 hr = pFolder->GetDisplayNameOf(pidl, dwFlags, &StrRetFilePath);
372 StrRetToStr(StrRetFilePath, &pszFilePath, (LPITEMIDLIST)pidl);
376 m_pMalloc->Free(pszFilePath);
380 STDMETHODIMP CShellPidl::SHPathToPidlEx(LPCTSTR szPath, LPITEMIDLIST* ppidl, LPSHELLFOLDER pFolder)
382 OLECHAR wszPath[MAX_PATH] = {0};
383 ULONG nCharsParsed = 0;
384 LPSHELLFOLDER pShellFolder = NULL;
385 BOOL bFreeOnExit = FALSE;
387 lstrcpy(wszPath,szPath);
389 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1, wszPath, MAX_PATH);
391 // Use the desktop's IShellFolder by default
394 SHGetDesktopFolder(&pShellFolder);
398 pShellFolder = pFolder;
400 HRESULT hr = pShellFolder->ParseDisplayName(NULL, NULL, wszPath, &nCharsParsed, ppidl, NULL);
403 pShellFolder->Release();
408 void CShellPidl::GetTypeName(LPITEMIDLIST lpi,CString &sTypeName)
411 ZeroMemory(&sfi,sizeof(sfi));
412 UINT uFlags = SHGFI_PIDL | SHGFI_TYPENAME;
413 SHGetFileInfo((LPCTSTR)lpi, 0, &sfi, sizeof(SHFILEINFO), uFlags);
414 sTypeName = sfi.szTypeName;
417 void CShellPidl::GetDisplayName(LPITEMIDLIST lpifq,CString &sDisplayName)
420 ZeroMemory(&sfi,sizeof(sfi));
421 UINT uFlags = SHGFI_PIDL | SHGFI_DISPLAYNAME;
422 SHGetFileInfo((LPCTSTR)lpifq, 0, &sfi, sizeof(SHFILEINFO), uFlags);
423 sDisplayName = sfi.szDisplayName;
426 void CShellPidl::GetNormalAndSelectedIcons (
427 LPITEMIDLIST lpifq, int &iImage, int &iSelectedImage)
429 // Don't check the return value here.
430 // If IGetIcon() fails, you're in big trouble.
431 iImage = GetIcon (lpifq,
432 SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
434 iSelectedImage = GetIcon (lpifq,
435 SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON);
440 BOOL CShellPidl::HandleMenuMsg(HWND hwnd, LPSHELLFOLDER lpsfParent,
441 LPITEMIDLIST lpi, UINT uMsg, WPARAM wParam, LPARAM lParam)
443 LPCONTEXTMENU lpcm=NULL;
444 HRESULT hr=lpsfParent->GetUIObjectOf(hwnd,
445 1, // get attributes for this many objects
446 (const struct _ITEMIDLIST **)&lpi,
452 LPCONTEXTMENU2 lpcm2=NULL;
453 hr = lpcm->QueryInterface(IID_IContextMenu2,(LPVOID*)&lpcm2);
457 lpcm2->HandleMenuMsg(uMsg,wParam,lParam);
465 BOOL CShellPidl::PopupTheMenu(HWND hwnd, LPSHELLFOLDER lpsfParent,
466 LPITEMIDLIST *plpi, UINT cidl, LPPOINT lppt)
469 menu.CreatePopupMenu();
470 g_CoolMenuManager.Install(CWnd::FromHandle(hwnd));
472 if (lpsfParent == NULL)
473 lpsfParent = GetDesktopFolder();
474 SHPidlToPathEx(*plpi,sPath,lpsfParent);
475 CShellContextMenu shell_menu(hwnd,sPath,plpi,cidl,lpsfParent);
476 shell_menu.SetMenu(&menu);
477 int idCmd = menu.TrackPopupMenu(TPM_FLAGS, lppt->x, lppt->y, CWnd::FromHandle(hwnd));
478 shell_menu.InvokeCommand(idCmd);
479 g_CoolMenuManager.Uninstall();
483 /****************************************************************************
485 * FUNCTION: GetName(LPSHELLFOLDER lpsf,LPITEMIDLIST lpi,DWORD dwFlags,
486 * LPSTR lpFriendlyName)
488 * PURPOSE: Gets the friendly name for the folder
490 ****************************************************************************/
491 BOOL CShellPidl::GetName (LPSHELLFOLDER lpsf, LPITEMIDLIST lpi,
492 DWORD dwFlags, CString &sFriendlyName)
497 if (NOERROR==lpsf->GetDisplayNameOf(lpi,dwFlags, &str))
503 _tcscpy(sFriendlyName.GetBuffer(MAX_PATH),str.pOleStr);
504 sFriendlyName.ReleaseBuffer();
509 str.pOleStr, // lpWideCharStr
510 -1, // cchWideCharStr
511 sFriendlyName.GetBuffer(_MAX_PATH), // lpMultiByteStr
512 _MAX_PATH, // cchMultiByte
513 NULL, // lpDefaultChar
514 NULL); // lpUsedDefaultChar
516 sFriendlyName.ReleaseBuffer();
520 sFriendlyName = (LPTSTR)lpi+str.uOffset;
524 sFriendlyName = (LPTSTR)str.cStr;
539 // ResolveChannel: Resolves a Channel Shortcut to its URL
541 STDMETHODIMP CShellPidl::ResolveChannel(IShellFolder* pFolder, LPCITEMIDLIST pidl, LPTSTR* lpszURL)
543 IShellLink* pShellLink;
545 *lpszURL = NULL; // Assume failure
547 // Get a pointer to the IShellLink interface from the given folder
548 HRESULT hr = pFolder->GetUIObjectOf(NULL, 1, &pidl, IID_IShellLink, NULL, (LPVOID*)&pShellLink);
551 LPITEMIDLIST pidlChannel;
553 // Convert the IShellLink pointer to a PIDL.
554 hr = pShellLink->GetIDList(&pidlChannel);
557 IShellFolder* psfDesktop;
559 hr = SHGetDesktopFolder(&psfDesktop);
564 hr = psfDesktop->GetDisplayNameOf(pidlChannel, 0, &strret);
566 StrRetToStr(strret, lpszURL, pidlChannel);
568 psfDesktop->Release();
572 pShellLink->Release();
578 STDMETHODIMP CShellPidl::ResolveHistoryShortcut(LPSHELLFOLDER pFolder,LPCITEMIDLIST *ppidl,CString &sURL)
581 IDataObject *pObj=NULL;
582 hr = pFolder->GetUIObjectOf(NULL, 1, ppidl, IID_IDataObject, NULL, (LPVOID*)&pObj);
585 hr = GetURL(pObj,sURL);
591 STDMETHODIMP CShellPidl::GetURL(IDataObject *pDataObj,CString &sURL)
595 ZeroMemory(&stgm, sizeof(stgm));
598 fetc.cfFormat = CF_SHELLURL;
600 fetc.dwAspect = DVASPECT_CONTENT;
602 fetc.tymed = TYMED_HGLOBAL;
604 HRESULT hr = pDataObj->QueryGetData(&fetc);
607 hr = pDataObj->GetData(&fetc, &stgm);
610 LPCTSTR pData = (LPCTSTR)GlobalLock(stgm.hGlobal);
612 GlobalUnlock(stgm.hGlobal);
613 ReleaseStgMedium(&stgm);
618 // ResolveInternetShortcut: Resolves an Internet Shortcut to its URL
620 STDMETHODIMP CShellPidl::ResolveInternetShortcut(LPCTSTR lpszLinkFile, LPTSTR* lpszURL)
622 IUniformResourceLocator* pUrlLink = NULL;
626 HRESULT hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
627 IID_IUniformResourceLocator, (LPVOID*)&pUrlLink);
631 IPersistFile* pPersistFile = NULL;
632 hr = pUrlLink->QueryInterface(IID_IPersistFile, (LPVOID*)&pPersistFile);
635 // Ensure that the string is Unicode.
638 _tcscpy(wsz,lpszLinkFile);
640 MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, wsz, MAX_PATH);
642 // Load the Internet Shortcut from persistent storage.
643 hr = pPersistFile->Load(wsz, STGM_READ);
646 hr = pUrlLink->GetURL(lpszURL);
648 pPersistFile->Release();
656 // ResolveLink: Resolves a Shell Link to its actual folder location
658 STDMETHODIMP CShellPidl::ResolveLink(HWND hWnd,LPCTSTR lpszLinkFile, LPTSTR* lpszURL)
660 IShellLink* pShellLink = NULL;
662 *lpszURL = NULL; // Assume failure
664 HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
665 IID_IShellLink, (LPVOID*)&pShellLink);
668 IPersistFile* pPersistFile = NULL;
670 hr = pShellLink->QueryInterface(IID_IPersistFile, (LPVOID*)&pPersistFile);
673 // Ensure that the string is Unicode.
676 _tcscpy(wsz,lpszLinkFile);
678 MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, wsz, MAX_PATH);
681 // Load the shortcut.from persistent storage
682 hr = pPersistFile->Load(wsz, STGM_READ);
687 hr = pShellLink->Resolve(hWnd, SLR_NO_UI);
690 // Get the path to the link target.
691 *lpszURL = (LPTSTR)m_pMalloc->Alloc(MAX_PATH); // Must remember to Free later
693 hr = pShellLink->GetPath(*lpszURL, MAX_PATH - 1, (WIN32_FIND_DATA*)&wfd, SLGP_UNCPRIORITY);
697 pPersistFile->Release();
700 pShellLink->Release();
707 // This method converts a STRRET structure to a LPTSTR
710 STDMETHODIMP CShellPidl::StrRetToStr(STRRET StrRet, LPTSTR* str, LPITEMIDLIST pidl)
716 *str = NULL; // Assume failure
718 switch (StrRet.uType)
721 cch = wcslen(StrRet.pOleStr) + 1; // NULL terminator
722 *str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));
725 lstrcpyn(*str, StrRet.pOleStr, cch);
731 strOffset = (((char *) pidl) + StrRet.uOffset);
733 cch = MultiByteToWideChar(CP_OEMCP, 0, strOffset, -1, NULL, 0);
734 *str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));
737 MultiByteToWideChar(CP_OEMCP, 0, strOffset, -1, *str, cch);
743 cch = MultiByteToWideChar(CP_OEMCP, 0, StrRet.cStr, -1, NULL, 0);
744 *str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));
747 MultiByteToWideChar(CP_OEMCP, 0, StrRet.cStr, -1, *str, cch);
756 #else // UNICODE not defined
757 STDMETHODIMP CShellPidl::StrRetToStr(STRRET StrRet, LPTSTR* str, LPITEMIDLIST pidl)
764 *str = NULL; // Assume failure
766 switch (StrRet.uType)
769 cch = WideCharToMultiByte(CP_ACP, 0, StrRet.pOleStr, -1, NULL, 0, NULL, NULL);
770 *str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));
773 WideCharToMultiByte(CP_ACP, 0, StrRet.pOleStr, -1, *str, cch, NULL, NULL);
779 strOffset = (((char *) pidl) + StrRet.uOffset);
781 cch = strlen(strOffset) + 1; // NULL terminator
782 *str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));
785 strcpy(*str, strOffset);
791 cch = strlen(StrRet.cStr) + 1; // NULL terminator
792 *str = (LPTSTR)m_pMalloc->Alloc(cch * sizeof(TCHAR));
795 strcpy(*str, StrRet.cStr);