4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2018 Katayama Hirofumi MZ
8 * IShellFolder2 and related interfaces
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
29 /***************************************************************************
30 * GetNextElement (internal function)
32 * Gets a part of a string till the first backslash.
35 * pszNext [IN] string to get the element from
36 * pszOut [IN] pointer to buffer which receives string
37 * dwOut [IN] length of pszOut
40 * LPSTR pointer to first, not yet parsed char
43 LPCWSTR
GetNextElementW (LPCWSTR pszNext
, LPWSTR pszOut
, DWORD dwOut
)
45 LPCWSTR pszTail
= pszNext
;
48 TRACE ("(%s %p 0x%08x)\n", debugstr_w (pszNext
), pszOut
, dwOut
);
52 if (!pszNext
|| !*pszNext
)
55 while (*pszTail
&& (*pszTail
!= (WCHAR
) '\\'))
58 dwCopy
= pszTail
- pszNext
+ 1;
59 lstrcpynW (pszOut
, pszNext
, (dwOut
< dwCopy
) ? dwOut
: dwCopy
);
66 TRACE ("--(%s %s 0x%08x %p)\n", debugstr_w (pszNext
), debugstr_w (pszOut
), dwOut
, pszTail
);
70 HRESULT
SHELL32_ParseNextElement (IShellFolder2
* psf
, HWND hwndOwner
, LPBC pbc
,
71 LPITEMIDLIST
* pidlInOut
, LPOLESTR szNext
, DWORD
* pEaten
, DWORD
* pdwAttributes
)
73 HRESULT hr
= E_INVALIDARG
;
74 LPITEMIDLIST pidlIn
= pidlInOut
? *pidlInOut
: NULL
;
75 LPITEMIDLIST pidlOut
= NULL
;
76 LPITEMIDLIST pidlTemp
= NULL
;
77 CComPtr
<IShellFolder
> psfChild
;
79 TRACE ("(%p, %p, %p, %s)\n", psf
, pbc
, pidlIn
, debugstr_w (szNext
));
81 /* get the shellfolder for the child pidl and let it analyse further */
82 hr
= psf
->BindToObject(pidlIn
, pbc
, IID_PPV_ARG(IShellFolder
, &psfChild
));
86 hr
= psfChild
->ParseDisplayName(hwndOwner
, pbc
, szNext
, pEaten
, &pidlOut
, pdwAttributes
);
90 pidlTemp
= ILCombine (pidlIn
, pidlOut
);
105 *pidlInOut
= pidlTemp
;
107 TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut
? *pidlInOut
: NULL
, hr
);
111 /***********************************************************************
112 * SHELL32_CoCreateInitSF
114 * Creates a shell folder and initializes it with a pidl and a root folder
115 * via IPersistFolder3 or IPersistFolder.
118 * pathRoot can be NULL for Folders being a drive.
119 * In this case the absolute path is built from pidlChild (eg. C:)
121 HRESULT
SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot
, PERSIST_FOLDER_TARGET_INFO
* ppfti
,
122 LPCITEMIDLIST pidlChild
, const GUID
* clsid
, REFIID riid
, LPVOID
*ppvOut
)
125 CComPtr
<IShellFolder
> pShellFolder
;
127 hr
= SHCoCreateInstance(NULL
, clsid
, NULL
, IID_PPV_ARG(IShellFolder
, &pShellFolder
));
131 LPITEMIDLIST pidlAbsolute
= ILCombine (pidlRoot
, pidlChild
);
132 CComPtr
<IPersistFolder
> ppf
;
133 CComPtr
<IPersistFolder3
> ppf3
;
135 if (ppfti
&& SUCCEEDED(pShellFolder
->QueryInterface(IID_PPV_ARG(IPersistFolder3
, &ppf3
))))
137 ppf3
->InitializeEx(NULL
, pidlAbsolute
, ppfti
);
139 else if (SUCCEEDED(pShellFolder
->QueryInterface(IID_PPV_ARG(IPersistFolder
, &ppf
))))
141 ppf
->Initialize(pidlAbsolute
);
143 ILFree (pidlAbsolute
);
145 return pShellFolder
->QueryInterface(riid
, ppvOut
);
148 HRESULT
SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot
, const GUID
* clsid
,
149 int csidl
, REFIID riid
, LPVOID
*ppvOut
)
151 /* fill the PERSIST_FOLDER_TARGET_INFO */
152 PERSIST_FOLDER_TARGET_INFO pfti
= {0};
153 pfti
.dwAttributes
= -1;
156 return SHELL32_CoCreateInitSF(pidlRoot
, &pfti
, NULL
, clsid
, riid
, ppvOut
);
159 HRESULT
SHELL32_BindToSF (LPCITEMIDLIST pidlRoot
, PERSIST_FOLDER_TARGET_INFO
* ppfti
,
160 LPCITEMIDLIST pidl
, const GUID
* clsid
, REFIID riid
, LPVOID
*ppvOut
)
162 PITEMID_CHILD pidlChild
= ILCloneFirst (pidl
);
166 CComPtr
<IShellFolder
> psf
;
167 HRESULT hr
= SHELL32_CoCreateInitSF(pidlRoot
,
171 IID_PPV_ARG(IShellFolder
, &psf
));
174 if (FAILED_UNEXPECTEDLY(hr
))
177 if (_ILIsPidlSimple (pidl
))
178 return psf
->QueryInterface(riid
, ppvOut
);
180 return psf
->BindToObject(ILGetNext (pidl
), NULL
, riid
, ppvOut
);
183 /***********************************************************************
184 * SHELL32_GetDisplayNameOfChild
186 * Retrieves the display name of a child object of a shellfolder.
188 * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
189 * - it binds to the child shellfolder [subpidl1]
190 * - asks it for the displayname of [subpidl2][subpidl3]
192 * Is possible the pidl is a simple pidl. In this case it asks the
193 * subfolder for the displayname of an empty pidl. The subfolder
194 * returns the own displayname eg. "::{guid}". This is used for
195 * virtual folders with the registry key WantsFORPARSING set.
197 HRESULT
SHELL32_GetDisplayNameOfChild (IShellFolder2
* psf
,
198 LPCITEMIDLIST pidl
, DWORD dwFlags
, LPSTRRET strRet
)
200 LPITEMIDLIST pidlFirst
= ILCloneFirst(pidl
);
202 return E_OUTOFMEMORY
;
204 CComPtr
<IShellFolder
> psfChild
;
205 HRESULT hr
= psf
->BindToObject(pidlFirst
, NULL
, IID_PPV_ARG(IShellFolder
, &psfChild
));
208 hr
= psfChild
->GetDisplayNameOf(ILGetNext (pidl
), dwFlags
, strRet
);
215 HRESULT
SHELL32_CompareChildren(IShellFolder2
* psf
, LPARAM lParam
, LPCITEMIDLIST pidl1
, LPCITEMIDLIST pidl2
)
217 PUIDLIST_RELATIVE nextpidl1
= ILGetNext (pidl1
);
218 PUIDLIST_RELATIVE nextpidl2
= ILGetNext (pidl2
);
220 bool isEmpty1
= _ILIsDesktop(nextpidl1
);
221 bool isEmpty2
= _ILIsDesktop(nextpidl2
);
222 if (isEmpty1
|| isEmpty2
)
223 return MAKE_COMPARE_HRESULT(isEmpty2
- isEmpty1
);
225 PITEMID_CHILD firstpidl
= ILCloneFirst (pidl1
);
227 return E_OUTOFMEMORY
;
229 CComPtr
<IShellFolder
> psf2
;
230 HRESULT hr
= psf
->BindToObject(firstpidl
, 0, IID_PPV_ARG(IShellFolder
, &psf2
));
233 return MAKE_COMPARE_HRESULT(0);
235 return psf2
->CompareIDs(lParam
, nextpidl1
, nextpidl2
);
238 HRESULT
SHELL32_CompareDetails(IShellFolder2
* isf
, LPARAM lParam
, LPCITEMIDLIST pidl1
, LPCITEMIDLIST pidl2
)
241 WCHAR wszItem1
[MAX_PATH
], wszItem2
[MAX_PATH
];
244 hres
= isf
->GetDetailsOf(pidl1
, lParam
, &sd
);
246 return MAKE_COMPARE_HRESULT(1);
248 hres
= StrRetToBufW(&sd
.str
, pidl1
, wszItem1
, MAX_PATH
);
250 return MAKE_COMPARE_HRESULT(1);
252 hres
= isf
->GetDetailsOf(pidl2
, lParam
, &sd
);
254 return MAKE_COMPARE_HRESULT(1);
256 hres
= StrRetToBufW(&sd
.str
, pidl2
, wszItem2
, MAX_PATH
);
258 return MAKE_COMPARE_HRESULT(1);
260 int ret
= wcsicmp(wszItem1
, wszItem2
);
262 return SHELL32_CompareChildren(isf
, lParam
, pidl1
, pidl2
);
264 return MAKE_COMPARE_HRESULT(ret
);
267 void AddClassKeyToArray(const WCHAR
* szClass
, HKEY
* array
, UINT
* cKeys
)
273 LSTATUS result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, szClass
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hkey
);
274 if (result
!= ERROR_SUCCESS
)
277 array
[*cKeys
] = hkey
;
281 void AddFSClassKeysToArray(PCUITEMID_CHILD pidl
, HKEY
* array
, UINT
* cKeys
)
283 if (_ILIsValue(pidl
))
285 FileStructW
* pFileData
= _ILGetFileStructW(pidl
);
286 LPWSTR extension
= PathFindExtension(pFileData
->wszName
);
290 AddClassKeyToArray(extension
, array
, cKeys
);
292 WCHAR wszClass
[MAX_PATH
], wszClass2
[MAX_PATH
];
293 DWORD dwSize
= sizeof(wszClass
);
294 if (RegGetValueW(HKEY_CLASSES_ROOT
, extension
, NULL
, RRF_RT_REG_SZ
, NULL
, wszClass
, &dwSize
) == ERROR_SUCCESS
)
296 swprintf(wszClass2
, L
"%s//%s", extension
, wszClass
);
298 AddClassKeyToArray(wszClass
, array
, cKeys
);
299 AddClassKeyToArray(wszClass2
, array
, cKeys
);
302 swprintf(wszClass2
, L
"SystemFileAssociations//%s", extension
);
303 AddClassKeyToArray(wszClass2
, array
, cKeys
);
305 if (RegGetValueW(HKEY_CLASSES_ROOT
, extension
, L
"PerceivedType ", RRF_RT_REG_SZ
, NULL
, wszClass
, &dwSize
) == ERROR_SUCCESS
)
307 swprintf(wszClass2
, L
"SystemFileAssociations//%s", wszClass
);
308 AddClassKeyToArray(wszClass2
, array
, cKeys
);
312 AddClassKeyToArray(L
"AllFilesystemObjects", array
, cKeys
);
313 AddClassKeyToArray(L
"*", array
, cKeys
);
315 else if (_ILIsFolder(pidl
))
317 AddClassKeyToArray(L
"AllFilesystemObjects", array
, cKeys
);
318 AddClassKeyToArray(L
"Directory", array
, cKeys
);
319 AddClassKeyToArray(L
"Folder", array
, cKeys
);
323 ERR("Got non FS pidl\n");
327 HRESULT
SH_GetApidlFromDataObject(IDataObject
*pDataObject
, PIDLIST_ABSOLUTE
* ppidlfolder
, PUITEMID_CHILD
**apidlItems
, UINT
*pcidl
)
329 UINT cfShellIDList
= RegisterClipboardFormatW(CFSTR_SHELLIDLIST
);
334 InitFormatEtc (fmt
, cfShellIDList
, TYMED_HGLOBAL
);
336 HRESULT hr
= pDataObject
->QueryGetData(&fmt
);
337 if (FAILED_UNEXPECTEDLY(hr
))
341 hr
= pDataObject
->GetData(&fmt
, &medium
);
342 if (FAILED_UNEXPECTEDLY(hr
))
345 /* lock the handle */
346 LPIDA lpcida
= (LPIDA
)GlobalLock(medium
.hGlobal
);
349 ReleaseStgMedium(&medium
);
353 /* convert the data into pidl */
355 LPITEMIDLIST
*apidl
= _ILCopyCidaToaPidl(&pidl
, lpcida
);
358 ReleaseStgMedium(&medium
);
359 return E_OUTOFMEMORY
;
364 *pcidl
= lpcida
->cidl
;
366 ReleaseStgMedium(&medium
);
370 /***********************************************************************
375 HRESULT WINAPI
SHCreateLinks( HWND hWnd
, LPCSTR lpszDir
, IDataObject
* lpDataObject
,
376 UINT uFlags
, LPITEMIDLIST
*lppidlLinks
)
378 FIXME("%p %s %p %08x %p\n", hWnd
, lpszDir
, lpDataObject
, uFlags
, lppidlLinks
);
382 /***********************************************************************
383 * SHOpenFolderAndSelectItems
389 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder
,
391 PCUITEMID_CHILD_ARRAY apidl
,
394 ERR("SHOpenFolderAndSelectItems() is hackplemented\n");
395 PCIDLIST_ABSOLUTE pidlItem
;
398 /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */
399 if (ILGetNext(apidl
[0]) != NULL
)
405 pidlItem
= ILCombine(pidlFolder
, apidl
[0]);
410 pidlItem
= pidlFolder
;
413 CComPtr
<IShellFolder
> psfDesktop
;
415 HRESULT hr
= SHGetDesktopFolder(&psfDesktop
);
416 if (FAILED_UNEXPECTEDLY(hr
))
420 hr
= psfDesktop
->GetDisplayNameOf(pidlItem
, SHGDN_FORPARSING
, &strret
);
421 if (FAILED_UNEXPECTEDLY(hr
))
424 WCHAR wszBuf
[MAX_PATH
];
425 hr
= StrRetToBufW(&strret
, pidlItem
, wszBuf
, _countof(wszBuf
));
426 if (FAILED_UNEXPECTEDLY(hr
))
429 WCHAR wszParams
[MAX_PATH
];
430 wcscpy(wszParams
, L
"/select,");
431 wcscat(wszParams
, wszBuf
);
433 SHELLEXECUTEINFOW sei
;
434 memset(&sei
, 0, sizeof sei
);
435 sei
.cbSize
= sizeof sei
;
436 sei
.fMask
= SEE_MASK_WAITFORINPUTIDLE
;
437 sei
.lpFile
= L
"explorer.exe";
438 sei
.lpParameters
= wszParams
;
440 if (ShellExecuteExW(&sei
))
450 Shell_DefaultContextMenuCallBack(IShellFolder
*psf
, IDataObject
*pdtobj
)
452 PIDLIST_ABSOLUTE pidlFolder
;
453 PUITEMID_CHILD
*apidl
;
455 HRESULT hr
= SH_GetApidlFromDataObject(pdtobj
, &pidlFolder
, &apidl
, &cidl
);
456 if (FAILED_UNEXPECTEDLY(hr
))
461 ERR("SHMultiFileProperties is not yet implemented\n");
463 _ILFreeaPidl(apidl
, cidl
);
468 hr
= psf
->GetDisplayNameOf(apidl
[0], SHGDN_FORPARSING
, &strFile
);
471 hr
= SH_ShowPropertiesDialog(strFile
.pOleStr
, pidlFolder
, apidl
);
473 ERR("SH_ShowPropertiesDialog failed\n");
477 ERR("Failed to get display name\n");
481 _ILFreeaPidl(apidl
, cidl
);