4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
7 * IShellFolder2 and related interfaces
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
28 /***************************************************************************
29 * GetNextElement (internal function)
31 * Gets a part of a string till the first backslash.
34 * pszNext [IN] string to get the element from
35 * pszOut [IN] pointer to buffer which receives string
36 * dwOut [IN] length of pszOut
39 * LPSTR pointer to first, not yet parsed char
42 LPCWSTR
GetNextElementW (LPCWSTR pszNext
, LPWSTR pszOut
, DWORD dwOut
)
44 LPCWSTR pszTail
= pszNext
;
47 TRACE ("(%s %p 0x%08x)\n", debugstr_w (pszNext
), pszOut
, dwOut
);
51 if (!pszNext
|| !*pszNext
)
54 while (*pszTail
&& (*pszTail
!= (WCHAR
) '\\'))
57 dwCopy
= pszTail
- pszNext
+ 1;
58 lstrcpynW (pszOut
, pszNext
, (dwOut
< dwCopy
) ? dwOut
: dwCopy
);
65 TRACE ("--(%s %s 0x%08x %p)\n", debugstr_w (pszNext
), debugstr_w (pszOut
), dwOut
, pszTail
);
69 HRESULT
SHELL32_ParseNextElement (IShellFolder2
* psf
, HWND hwndOwner
, LPBC pbc
,
70 LPITEMIDLIST
* pidlInOut
, LPOLESTR szNext
, DWORD
* pEaten
, DWORD
* pdwAttributes
)
72 HRESULT hr
= E_INVALIDARG
;
73 LPITEMIDLIST pidlIn
= pidlInOut
? *pidlInOut
: NULL
;
74 LPITEMIDLIST pidlOut
= NULL
;
75 LPITEMIDLIST pidlTemp
= NULL
;
76 CComPtr
<IShellFolder
> psfChild
;
78 TRACE ("(%p, %p, %p, %s)\n", psf
, pbc
, pidlIn
, debugstr_w (szNext
));
80 /* get the shellfolder for the child pidl and let it analyse further */
81 hr
= psf
->BindToObject(pidlIn
, pbc
, IID_PPV_ARG(IShellFolder
, &psfChild
));
85 hr
= psfChild
->ParseDisplayName(hwndOwner
, pbc
, szNext
, pEaten
, &pidlOut
, pdwAttributes
);
89 pidlTemp
= ILCombine (pidlIn
, pidlOut
);
104 *pidlInOut
= pidlTemp
;
106 TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut
? *pidlInOut
: NULL
, hr
);
110 /***********************************************************************
111 * SHELL32_CoCreateInitSF
113 * Creates a shell folder and initializes it with a pidl and a root folder
114 * via IPersistFolder3 or IPersistFolder.
117 * pathRoot can be NULL for Folders being a drive.
118 * In this case the absolute path is built from pidlChild (eg. C:)
120 HRESULT
SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot
, PERSIST_FOLDER_TARGET_INFO
* ppfti
,
121 LPCITEMIDLIST pidlChild
, const GUID
* clsid
, REFIID riid
, LPVOID
*ppvOut
)
124 CComPtr
<IShellFolder
> pShellFolder
;
126 hr
= SHCoCreateInstance(NULL
, clsid
, NULL
, IID_PPV_ARG(IShellFolder
, &pShellFolder
));
130 LPITEMIDLIST pidlAbsolute
= ILCombine (pidlRoot
, pidlChild
);
131 CComPtr
<IPersistFolder
> ppf
;
132 CComPtr
<IPersistFolder3
> ppf3
;
134 if (ppfti
&& SUCCEEDED(pShellFolder
->QueryInterface(IID_PPV_ARG(IPersistFolder3
, &ppf3
))))
136 ppf3
->InitializeEx(NULL
, pidlAbsolute
, ppfti
);
138 else if (SUCCEEDED(pShellFolder
->QueryInterface(IID_PPV_ARG(IPersistFolder
, &ppf
))))
140 ppf
->Initialize(pidlAbsolute
);
142 ILFree (pidlAbsolute
);
144 return pShellFolder
->QueryInterface(riid
, ppvOut
);
147 HRESULT
SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot
, const GUID
* clsid
,
148 int csidl
, REFIID riid
, LPVOID
*ppvOut
)
150 /* fill the PERSIST_FOLDER_TARGET_INFO */
151 PERSIST_FOLDER_TARGET_INFO pfti
= {0};
152 pfti
.dwAttributes
= -1;
155 return SHELL32_CoCreateInitSF(pidlRoot
, &pfti
, NULL
, clsid
, riid
, ppvOut
);
158 HRESULT
SHELL32_BindToSF (LPCITEMIDLIST pidlRoot
, PERSIST_FOLDER_TARGET_INFO
* ppfti
,
159 LPCITEMIDLIST pidl
, const GUID
* clsid
, REFIID riid
, LPVOID
*ppvOut
)
161 PITEMID_CHILD pidlChild
= ILCloneFirst (pidl
);
165 CComPtr
<IShellFolder
> psf
;
166 HRESULT hr
= SHELL32_CoCreateInitSF(pidlRoot
,
170 IID_PPV_ARG(IShellFolder
, &psf
));
173 if (FAILED_UNEXPECTEDLY(hr
))
176 if (_ILIsPidlSimple (pidl
))
177 return psf
->QueryInterface(riid
, ppvOut
);
179 return psf
->BindToObject(ILGetNext (pidl
), NULL
, riid
, ppvOut
);
182 /***********************************************************************
183 * SHELL32_GetDisplayNameOfChild
185 * Retrieves the display name of a child object of a shellfolder.
187 * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
188 * - it binds to the child shellfolder [subpidl1]
189 * - asks it for the displayname of [subpidl2][subpidl3]
191 * Is possible the pidl is a simple pidl. In this case it asks the
192 * subfolder for the displayname of an empty pidl. The subfolder
193 * returns the own displayname eg. "::{guid}". This is used for
194 * virtual folders with the registry key WantsFORPARSING set.
196 HRESULT
SHELL32_GetDisplayNameOfChild (IShellFolder2
* psf
,
197 LPCITEMIDLIST pidl
, DWORD dwFlags
, LPSTRRET strRet
)
199 LPITEMIDLIST pidlFirst
= ILCloneFirst(pidl
);
201 return E_OUTOFMEMORY
;
203 CComPtr
<IShellFolder
> psfChild
;
204 HRESULT hr
= psf
->BindToObject(pidlFirst
, NULL
, IID_PPV_ARG(IShellFolder
, &psfChild
));
207 hr
= psfChild
->GetDisplayNameOf(ILGetNext (pidl
), dwFlags
, strRet
);
214 HRESULT
SHELL32_CompareChildren(IShellFolder2
* psf
, LPARAM lParam
, LPCITEMIDLIST pidl1
, LPCITEMIDLIST pidl2
)
216 PUIDLIST_RELATIVE nextpidl1
= ILGetNext (pidl1
);
217 PUIDLIST_RELATIVE nextpidl2
= ILGetNext (pidl2
);
219 bool isEmpty1
= _ILIsDesktop(nextpidl1
);
220 bool isEmpty2
= _ILIsDesktop(nextpidl2
);
221 if (isEmpty1
|| isEmpty2
)
222 return MAKE_COMPARE_HRESULT(isEmpty2
- isEmpty1
);
224 PITEMID_CHILD firstpidl
= ILCloneFirst (pidl1
);
226 return E_OUTOFMEMORY
;
228 CComPtr
<IShellFolder
> psf2
;
229 HRESULT hr
= psf
->BindToObject(firstpidl
, 0, IID_PPV_ARG(IShellFolder
, &psf2
));
232 return MAKE_COMPARE_HRESULT(0);
234 return psf2
->CompareIDs(lParam
, nextpidl1
, nextpidl2
);
237 HRESULT
SHELL32_CompareDetails(IShellFolder2
* isf
, LPARAM lParam
, LPCITEMIDLIST pidl1
, LPCITEMIDLIST pidl2
)
240 WCHAR wszItem1
[MAX_PATH
], wszItem2
[MAX_PATH
];
243 hres
= isf
->GetDetailsOf(pidl1
, lParam
, &sd
);
245 return MAKE_COMPARE_HRESULT(1);
247 hres
= StrRetToBufW(&sd
.str
, pidl1
, wszItem1
, MAX_PATH
);
249 return MAKE_COMPARE_HRESULT(1);
251 hres
= isf
->GetDetailsOf(pidl2
, lParam
, &sd
);
253 return MAKE_COMPARE_HRESULT(1);
255 hres
= StrRetToBufW(&sd
.str
, pidl2
, wszItem2
, MAX_PATH
);
257 return MAKE_COMPARE_HRESULT(1);
259 int ret
= wcsicmp(wszItem1
, wszItem2
);
261 return SHELL32_CompareChildren(isf
, lParam
, pidl1
, pidl2
);
263 return MAKE_COMPARE_HRESULT(ret
);
266 void AddClassKeyToArray(const WCHAR
* szClass
, HKEY
* array
, UINT
* cKeys
)
272 LSTATUS result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, szClass
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hkey
);
273 if (result
!= ERROR_SUCCESS
)
276 array
[*cKeys
] = hkey
;
280 void AddFSClassKeysToArray(PCUITEMID_CHILD pidl
, HKEY
* array
, UINT
* cKeys
)
282 if (_ILIsValue(pidl
))
284 FileStructW
* pFileData
= _ILGetFileStructW(pidl
);
285 LPWSTR extension
= PathFindExtension(pFileData
->wszName
);
289 AddClassKeyToArray(extension
, array
, cKeys
);
291 WCHAR wszClass
[MAX_PATH
], wszClass2
[MAX_PATH
];
292 DWORD dwSize
= sizeof(wszClass
);
293 if (RegGetValueW(HKEY_CLASSES_ROOT
, extension
, NULL
, RRF_RT_REG_SZ
, NULL
, wszClass
, &dwSize
) == ERROR_SUCCESS
)
295 swprintf(wszClass2
, L
"%s//%s", extension
, wszClass
);
297 AddClassKeyToArray(wszClass
, array
, cKeys
);
298 AddClassKeyToArray(wszClass2
, array
, cKeys
);
301 swprintf(wszClass2
, L
"SystemFileAssociations//%s", extension
);
302 AddClassKeyToArray(wszClass2
, array
, cKeys
);
304 if (RegGetValueW(HKEY_CLASSES_ROOT
, extension
, L
"PerceivedType ", RRF_RT_REG_SZ
, NULL
, wszClass
, &dwSize
) == ERROR_SUCCESS
)
306 swprintf(wszClass2
, L
"SystemFileAssociations//%s", wszClass
);
307 AddClassKeyToArray(wszClass2
, array
, cKeys
);
311 AddClassKeyToArray(L
"AllFilesystemObjects", array
, cKeys
);
312 AddClassKeyToArray(L
"*", array
, cKeys
);
314 else if (_ILIsFolder(pidl
))
316 AddClassKeyToArray(L
"AllFilesystemObjects", array
, cKeys
);
317 AddClassKeyToArray(L
"Directory", array
, cKeys
);
318 AddClassKeyToArray(L
"Folder", array
, cKeys
);
322 ERR("Got non FS pidl\n");
326 HRESULT
SH_GetApidlFromDataObject(IDataObject
*pDataObject
, PIDLIST_ABSOLUTE
* ppidlfolder
, PUITEMID_CHILD
**apidlItems
, UINT
*pcidl
)
328 UINT cfShellIDList
= RegisterClipboardFormatW(CFSTR_SHELLIDLIST
);
333 InitFormatEtc (fmt
, cfShellIDList
, TYMED_HGLOBAL
);
335 HRESULT hr
= pDataObject
->QueryGetData(&fmt
);
336 if (FAILED_UNEXPECTEDLY(hr
))
340 hr
= pDataObject
->GetData(&fmt
, &medium
);
341 if (FAILED_UNEXPECTEDLY(hr
))
344 /* lock the handle */
345 LPIDA lpcida
= (LPIDA
)GlobalLock(medium
.hGlobal
);
348 ReleaseStgMedium(&medium
);
352 /* convert the data into pidl */
354 LPITEMIDLIST
*apidl
= _ILCopyCidaToaPidl(&pidl
, lpcida
);
357 ReleaseStgMedium(&medium
);
358 return E_OUTOFMEMORY
;
363 *pcidl
= lpcida
->cidl
;
365 ReleaseStgMedium(&medium
);
369 /***********************************************************************
374 HRESULT WINAPI
SHCreateLinks( HWND hWnd
, LPCSTR lpszDir
, IDataObject
* lpDataObject
,
375 UINT uFlags
, LPITEMIDLIST
*lppidlLinks
)
377 FIXME("%p %s %p %08x %p\n", hWnd
, lpszDir
, lpDataObject
, uFlags
, lppidlLinks
);
381 /***********************************************************************
382 * SHOpenFolderAndSelectItems
388 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder
,
390 PCUITEMID_CHILD_ARRAY apidl
,
393 ERR("SHOpenFolderAndSelectItems() is hackplemented\n");
394 PCIDLIST_ABSOLUTE pidlItem
;
397 /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */
398 if (ILGetNext(apidl
[0]) != NULL
)
404 pidlItem
= ILCombine(pidlFolder
, apidl
[0]);
409 pidlItem
= pidlFolder
;
412 CComPtr
<IShellFolder
> psfDesktop
;
414 HRESULT hr
= SHGetDesktopFolder(&psfDesktop
);
415 if (FAILED_UNEXPECTEDLY(hr
))
419 hr
= psfDesktop
->GetDisplayNameOf(pidlItem
, SHGDN_FORPARSING
, &strret
);
420 if (FAILED_UNEXPECTEDLY(hr
))
423 WCHAR wszBuf
[MAX_PATH
];
424 hr
= StrRetToBufW(&strret
, pidlItem
, wszBuf
, _countof(wszBuf
));
425 if (FAILED_UNEXPECTEDLY(hr
))
428 WCHAR wszParams
[MAX_PATH
];
429 wcscpy(wszParams
, L
"/select,");
430 wcscat(wszParams
, wszBuf
);
432 SHELLEXECUTEINFOW sei
;
433 memset(&sei
, 0, sizeof sei
);
434 sei
.cbSize
= sizeof sei
;
435 sei
.fMask
= SEE_MASK_WAITFORINPUTIDLE
;
436 sei
.lpFile
= L
"explorer.exe";
437 sei
.lpParameters
= wszParams
;
439 if (ShellExecuteExW(&sei
))