54c06b492f6af6d49391b0a1932e8301cd9f11a9
[reactos.git] / reactos / dll / win32 / shell32 / folders / CFSFolder.cpp
1
2 /*
3 * file system folder
4 *
5 * Copyright 1997 Marcus Meissner
6 * Copyright 1998, 1999, 2002 Juergen Schmied
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include <precomp.h>
24
25 WINE_DEFAULT_DEBUG_CHANNEL (shell);
26
27 /*
28 CFileSysEnum should do an initial FindFirstFile and do a FindNextFile as each file is
29 returned by Next. When the enumerator is created, it can do numerous additional operations
30 including formatting a drive, reconnecting a network share drive, and requesting a disk
31 be inserted in a removable drive.
32 */
33
34 /***********************************************************************
35 * IShellFolder implementation
36 */
37
38 class CFileSysEnum :
39 public CEnumIDListBase
40 {
41 private:
42 public:
43 CFileSysEnum();
44 ~CFileSysEnum();
45 HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags);
46
47 BEGIN_COM_MAP(CFileSysEnum)
48 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
49 END_COM_MAP()
50 };
51
52 CFileSysEnum::CFileSysEnum()
53 {
54 }
55
56 CFileSysEnum::~CFileSysEnum()
57 {
58 }
59
60 HRESULT WINAPI CFileSysEnum::Initialize(LPWSTR lpszPath, DWORD dwFlags)
61 {
62 WIN32_FIND_DATAW stffile;
63 HANDLE hFile;
64 WCHAR szPath[MAX_PATH];
65 BOOL succeeded = TRUE;
66 static const WCHAR stars[] = { '*','.','*',0 };
67 static const WCHAR dot[] = { '.',0 };
68 static const WCHAR dotdot[] = { '.','.',0 };
69
70 TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(lpszPath), dwFlags);
71
72 if(!lpszPath || !lpszPath[0]) return FALSE;
73
74 wcscpy(szPath, lpszPath);
75 PathAddBackslashW(szPath);
76 wcscat(szPath,stars);
77
78 hFile = FindFirstFileW(szPath,&stffile);
79 if ( hFile != INVALID_HANDLE_VALUE )
80 {
81 BOOL findFinished = FALSE;
82
83 do
84 {
85 if ( !(stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
86 || (dwFlags & SHCONTF_INCLUDEHIDDEN) )
87 {
88 LPITEMIDLIST pidl = NULL;
89
90 if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
91 dwFlags & SHCONTF_FOLDERS &&
92 strcmpW(stffile.cFileName, dot) && strcmpW(stffile.cFileName, dotdot))
93 {
94 pidl = _ILCreateFromFindDataW(&stffile);
95 succeeded = succeeded && AddToEnumList(pidl);
96 }
97 else if (!(stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
98 && dwFlags & SHCONTF_NONFOLDERS)
99 {
100 pidl = _ILCreateFromFindDataW(&stffile);
101 succeeded = succeeded && AddToEnumList(pidl);
102 }
103 }
104 if (succeeded)
105 {
106 if (!FindNextFileW(hFile, &stffile))
107 {
108 if (GetLastError() == ERROR_NO_MORE_FILES)
109 findFinished = TRUE;
110 else
111 succeeded = FALSE;
112 }
113 }
114 } while (succeeded && !findFinished);
115 FindClose(hFile);
116 }
117
118 return succeeded;
119 }
120
121 CFSFolder::CFSFolder()
122 {
123 pclsid = (CLSID *)&CLSID_ShellFSFolder;
124 sPathTarget = NULL;
125 pidlRoot = NULL;
126 m_bGroupPolicyActive = 0;
127 }
128
129 CFSFolder::~CFSFolder()
130 {
131 TRACE("-- destroying IShellFolder(%p)\n", this);
132
133 SHFree(pidlRoot);
134 SHFree(sPathTarget);
135 }
136
137
138 static const shvheader GenericSFHeader[] = {
139 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
140 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 0},
141 {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
142 {IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
143 {IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 12},
144 {IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10}
145 };
146
147 #define GENERICSHELLVIEWCOLUMNS 6
148
149 /**************************************************************************
150 * SHELL32_CreatePidlFromBindCtx [internal]
151 *
152 * If the caller bound File System Bind Data, assume it is the
153 * find data for the path.
154 * This allows binding of paths that don't exist.
155 */
156 LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
157 {
158 IFileSystemBindData *fsbd = NULL;
159 LPITEMIDLIST pidl = NULL;
160 IUnknown *param = NULL;
161 WIN32_FIND_DATAW wfd;
162 HRESULT r;
163
164 TRACE("%p %s\n", pbc, debugstr_w(path));
165
166 if (!pbc)
167 return NULL;
168
169 /* see if the caller bound File System Bind Data */
170 r = pbc->GetObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, &param);
171 if (FAILED(r))
172 return NULL;
173
174 r = param->QueryInterface(IID_PPV_ARG(IFileSystemBindData,&fsbd));
175 if (SUCCEEDED(r))
176 {
177 r = fsbd->GetFindData(&wfd);
178 if (SUCCEEDED(r))
179 {
180 lstrcpynW(&wfd.cFileName[0], path, MAX_PATH);
181 pidl = _ILCreateFromFindDataW(&wfd);
182 }
183 fsbd->Release();
184 }
185
186 return pidl;
187 }
188
189 void SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, CLSID* pclsidFolder)
190 {
191 WCHAR wszCLSIDValue[CHARS_IN_GUID];
192 WCHAR wszDesktopIni[MAX_PATH];
193 StringCchCopyW(wszDesktopIni, MAX_PATH, pwszDir);
194 StringCchCatW(wszDesktopIni, MAX_PATH, L"\\desktop.ini");
195
196 if (GetPrivateProfileStringW(L".ShellClassInfo",
197 L"CLSID",
198 L"",
199 wszCLSIDValue,
200 CHARS_IN_GUID,
201 wszDesktopIni))
202 {
203 CLSIDFromString (wszCLSIDValue, pclsidFolder);
204 }
205 }
206
207
208 static const DWORD dwSupportedAttr=
209 SFGAO_CANCOPY | /*0x00000001 */
210 SFGAO_CANMOVE | /*0x00000002 */
211 SFGAO_CANLINK | /*0x00000004 */
212 SFGAO_CANRENAME | /*0x00000010 */
213 SFGAO_CANDELETE | /*0x00000020 */
214 SFGAO_HASPROPSHEET | /*0x00000040 */
215 SFGAO_DROPTARGET | /*0x00000100 */
216 SFGAO_LINK | /*0x00010000 */
217 SFGAO_READONLY | /*0x00040000 */
218 SFGAO_HIDDEN | /*0x00080000 */
219 SFGAO_FILESYSANCESTOR | /*0x10000000 */
220 SFGAO_FOLDER | /*0x20000000 */
221 SFGAO_FILESYSTEM | /*0x40000000 */
222 SFGAO_HASSUBFOLDER; /*0x80000000 */
223
224 HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
225 {
226 DWORD dwFileAttributes, dwShellAttributes;
227
228 if (!_ILIsFolder(pidl) && !_ILIsValue(pidl))
229 {
230 ERR("Got wrong type of pidl!\n");
231 *pdwAttributes &= SFGAO_CANLINK;
232 return S_OK;
233 }
234
235 if (*pdwAttributes & ~dwSupportedAttr)
236 {
237 WARN ("attributes 0x%08x not implemented\n", (*pdwAttributes & ~dwSupportedAttr));
238 *pdwAttributes &= dwSupportedAttr;
239 }
240
241 dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0);
242
243 /* Set common attributes */
244 dwShellAttributes = *pdwAttributes;
245 dwShellAttributes |= SFGAO_FILESYSTEM | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANDELETE |
246 SFGAO_CANRENAME | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANCOPY;
247
248 if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
249 {
250 dwShellAttributes |= (SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
251 }
252 else
253 dwShellAttributes &= ~(SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
254
255 if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
256 dwShellAttributes |= SFGAO_HIDDEN;
257 else
258 dwShellAttributes &= ~SFGAO_HIDDEN;
259
260 if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
261 dwShellAttributes |= SFGAO_READONLY;
262 else
263 dwShellAttributes &= ~SFGAO_READONLY;
264
265 if (SFGAO_LINK & *pdwAttributes)
266 {
267 char ext[MAX_PATH];
268
269 if (!_ILGetExtension(pidl, ext, MAX_PATH) || lstrcmpiA(ext, "lnk"))
270 dwShellAttributes &= ~SFGAO_LINK;
271 }
272
273 if (SFGAO_HASSUBFOLDER & *pdwAttributes)
274 {
275 CComPtr<IShellFolder> psf2;
276 if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
277 {
278 CComPtr<IEnumIDList> pEnumIL;
279 if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL)))
280 {
281 if (pEnumIL->Skip(1) != S_OK)
282 dwShellAttributes &= ~SFGAO_HASSUBFOLDER;
283 }
284 }
285 }
286
287 *pdwAttributes &= dwShellAttributes;
288
289 TRACE ("-- 0x%08x\n", *pdwAttributes);
290 return S_OK;
291 }
292
293 /**************************************************************************
294 * CFSFolder::ParseDisplayName {SHELL32}
295 *
296 * Parse a display name.
297 *
298 * PARAMS
299 * hwndOwner [in] Parent window for any message's
300 * pbc [in] optional FileSystemBindData context
301 * lpszDisplayName [in] Unicode displayname.
302 * pchEaten [out] (unicode) characters processed
303 * ppidl [out] complex pidl to item
304 * pdwAttributes [out] items attributes
305 *
306 * NOTES
307 * Every folder tries to parse only its own (the leftmost) pidl and creates a
308 * subfolder to evaluate the remaining parts.
309 * Now we can parse into namespaces implemented by shell extensions
310 *
311 * Behaviour on win98: lpszDisplayName=NULL -> crash
312 * lpszDisplayName="" -> returns mycoputer-pidl
313 *
314 * FIXME
315 * pdwAttributes is not set
316 * pchEaten is not set like in windows
317 */
318 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
319 LPBC pbc,
320 LPOLESTR lpszDisplayName,
321 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
322 DWORD *pdwAttributes)
323 {
324 HRESULT hr = E_INVALIDARG;
325 LPCWSTR szNext = NULL;
326 WCHAR szElement[MAX_PATH];
327 WCHAR szPath[MAX_PATH];
328 LPITEMIDLIST pidlTemp = NULL;
329 DWORD len;
330
331 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
332 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
333 pchEaten, ppidl, pdwAttributes);
334
335 if (!ppidl)
336 return E_INVALIDARG;
337
338 if (!lpszDisplayName)
339 {
340 *ppidl = NULL;
341 return E_INVALIDARG;
342 }
343
344 *ppidl = NULL;
345
346 if (pchEaten)
347 *pchEaten = 0; /* strange but like the original */
348
349 if (*lpszDisplayName)
350 {
351 /* get the next element */
352 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
353
354 pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, szElement);
355 if (pidlTemp != NULL)
356 {
357 /* We are creating an id list without ensuring that the items exist.
358 If we have a remaining path, this must be a folder.
359 We have to do it now because it is set as a file by default */
360 if (szNext)
361 {
362 pidlTemp->mkid.abID[0] = PT_FOLDER;
363 }
364 hr = S_OK;
365 }
366 else
367 {
368 /* build the full pathname to the element */
369 lstrcpynW(szPath, sPathTarget, MAX_PATH - 1);
370 PathAddBackslashW(szPath);
371 len = wcslen(szPath);
372 lstrcpynW(szPath + len, szElement, MAX_PATH - len);
373
374 /* get the pidl */
375 hr = _ILCreateFromPathW(szPath, &pidlTemp);
376 }
377
378 if (SUCCEEDED(hr))
379 {
380 if (szNext && *szNext)
381 {
382 /* try to analyse the next element */
383 hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
384 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
385 }
386 else
387 {
388 /* it's the last element */
389 if (pdwAttributes && *pdwAttributes)
390 hr = SHELL32_GetFSItemAttributes(this, pidlTemp, pdwAttributes);
391 }
392 }
393 }
394
395 if (SUCCEEDED(hr))
396 *ppidl = pidlTemp;
397 else
398 *ppidl = NULL;
399
400 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
401
402 return hr;
403 }
404
405 /**************************************************************************
406 * CFSFolder::EnumObjects
407 * PARAMETERS
408 * HWND hwndOwner, //[in ] Parent Window
409 * DWORD grfFlags, //[in ] SHCONTF enumeration mask
410 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface
411 */
412 HRESULT WINAPI CFSFolder::EnumObjects(
413 HWND hwndOwner,
414 DWORD dwFlags,
415 LPENUMIDLIST *ppEnumIDList)
416 {
417 return ShellObjectCreatorInit<CFileSysEnum>(sPathTarget, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
418 }
419
420 /**************************************************************************
421 * CFSFolder::BindToObject
422 * PARAMETERS
423 * LPCITEMIDLIST pidl, //[in ] relative pidl to open
424 * LPBC pbc, //[in ] optional FileSystemBindData context
425 * REFIID riid, //[in ] Initial Interface
426 * LPVOID* ppvObject //[out] Interface*
427 */
428 HRESULT WINAPI CFSFolder::BindToObject(
429 PCUIDLIST_RELATIVE pidl,
430 LPBC pbc,
431 REFIID riid,
432 LPVOID * ppvOut)
433 {
434 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
435 shdebugstr_guid(&riid), ppvOut);
436
437 CComPtr<IShellFolder> pSF;
438 HRESULT hr;
439
440 if (!pidlRoot || !ppvOut || !pidl || !pidl->mkid.cb)
441 return E_INVALIDARG;
442
443 if (_ILIsValue(pidl))
444 {
445 ERR("Binding to file is unimplemented\n");
446 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
447 }
448 if (!_ILIsFolder(pidl))
449 {
450 ERR("Got an unknown type of pidl!\n");
451 return E_FAIL;
452 }
453
454 *ppvOut = NULL;
455
456 /* Get the pidl data */
457 FileStruct* pData = &_ILGetDataPointer(pidl)->u.file;
458 FileStructW* pDataW = _ILGetFileStructW(pidl);
459
460 /* Create the target folder info */
461 PERSIST_FOLDER_TARGET_INFO pfti = {0};
462 pfti.dwAttributes = -1;
463 pfti.csidl = -1;
464 PathCombineW(pfti.szTargetParsingName, sPathTarget, pDataW->wszName);
465
466 /* Get the CLSID to bind to */
467 CLSID clsidFolder = CLSID_ShellFSFolder;
468 if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
469 SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, &clsidFolder);
470
471 hr = SHELL32_BindToSF(pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
472 if (FAILED_UNEXPECTEDLY(hr))
473 return hr;
474
475 TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
476
477 return S_OK;
478
479 }
480
481 /**************************************************************************
482 * CFSFolder::BindToStorage
483 * PARAMETERS
484 * LPCITEMIDLIST pidl, //[in ] complex pidl to store
485 * LPBC pbc, //[in ] reserved
486 * REFIID riid, //[in ] Initial storage interface
487 * LPVOID* ppvObject //[out] Interface* returned
488 */
489 HRESULT WINAPI CFSFolder::BindToStorage(
490 PCUIDLIST_RELATIVE pidl,
491 LPBC pbcReserved,
492 REFIID riid,
493 LPVOID *ppvOut)
494 {
495 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
496 shdebugstr_guid (&riid), ppvOut);
497
498 *ppvOut = NULL;
499 return E_NOTIMPL;
500 }
501
502 /**************************************************************************
503 * CFSFolder::CompareIDs
504 */
505
506 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
507 PCUIDLIST_RELATIVE pidl1,
508 PCUIDLIST_RELATIVE pidl2)
509 {
510 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
511 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
512 FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
513 FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
514 BOOL bIsFolder1 = _ILIsFolder(pidl1);
515 BOOL bIsFolder2 = _ILIsFolder(pidl2);
516 LPWSTR pExtension1, pExtension2;
517
518 if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
519 return E_INVALIDARG;
520
521 /* When sorting between a File and a Folder, the Folder gets sorted first */
522 if (bIsFolder1 != bIsFolder2)
523 {
524 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
525 }
526
527 int result;
528 switch (LOWORD(lParam))
529 {
530 case 0: /* Name */
531 result = wcsicmp(pDataW1->wszName, pDataW2->wszName);
532 break;
533 case 1: /* Comments */
534 result = 0;
535 break;
536 case 2: /* Type */
537 pExtension1 = PathFindExtensionW(pDataW1->wszName);
538 pExtension2 = PathFindExtensionW(pDataW2->wszName);
539 result = wcsicmp(pExtension1, pExtension2);
540 break;
541 case 3: /* Size */
542 result = pData1->u.file.dwFileSize - pData2->u.file.dwFileSize;
543 break;
544 case 4: /* Modified */
545 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
546 if (result == 0)
547 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
548 break;
549 case 5: /* Attributes */
550 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
551 }
552
553 if (result == 0)
554 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
555
556 return MAKE_COMPARE_HRESULT(result);
557 }
558
559 /**************************************************************************
560 * CFSFolder::CreateViewObject
561 */
562 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
563 REFIID riid, LPVOID * ppvOut)
564 {
565 CComPtr<IShellView> pShellView;
566 HRESULT hr = E_INVALIDARG;
567
568 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
569 ppvOut);
570
571 if (ppvOut)
572 {
573 *ppvOut = NULL;
574
575 if (IsEqualIID (riid, IID_IDropTarget))
576 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
577 else if (IsEqualIID (riid, IID_IContextMenu))
578 {
579 HKEY hKeys[16];
580 UINT cKeys = 0;
581 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
582
583 DEFCONTEXTMENU dcm;
584 dcm.hwnd = hwndOwner;
585 dcm.pcmcb = this;
586 dcm.pidlFolder = pidlRoot;
587 dcm.psf = this;
588 dcm.cidl = 0;
589 dcm.apidl = NULL;
590 dcm.cKeys = cKeys;
591 dcm.aKeys = hKeys;
592 dcm.punkAssociationInfo = NULL;
593 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
594 }
595 else if (IsEqualIID (riid, IID_IShellView))
596 {
597 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
598 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
599 }
600 }
601 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
602 return hr;
603 }
604
605 /**************************************************************************
606 * CFSFolder::GetAttributesOf
607 *
608 * PARAMETERS
609 * UINT cidl, //[in ] num elements in pidl array
610 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
611 * ULONG* rgfInOut) //[out] result array
612 *
613 */
614 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
615 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
616 {
617 HRESULT hr = S_OK;
618
619 if (!rgfInOut)
620 return E_INVALIDARG;
621 if (cidl && !apidl)
622 return E_INVALIDARG;
623
624 if (*rgfInOut == 0)
625 *rgfInOut = ~0;
626
627 if(cidl == 0)
628 {
629 LPCITEMIDLIST rpidl = ILFindLastID(pidlRoot);
630
631 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
632 {
633 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
634 }
635 else if (_ILIsDrive(rpidl))
636 {
637 IShellFolder *psfParent = NULL;
638 hr = SHBindToParent(pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
639 if(SUCCEEDED(hr))
640 {
641 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
642 psfParent->Release();
643 }
644 }
645 else
646 {
647 ERR("Got and unknown pidl!\n");
648 }
649 }
650 else
651 {
652 while (cidl > 0 && *apidl)
653 {
654 pdump(*apidl);
655 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
656 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
657 else
658 ERR("Got an unknown type of pidl!!!\n");
659 apidl++;
660 cidl--;
661 }
662 }
663 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
664 *rgfInOut &= ~SFGAO_VALIDATE;
665
666 TRACE("-- result=0x%08x\n", *rgfInOut);
667
668 return hr;
669 }
670
671 /**************************************************************************
672 * CFSFolder::GetUIObjectOf
673 *
674 * PARAMETERS
675 * HWND hwndOwner, //[in ] Parent window for any output
676 * UINT cidl, //[in ] array size
677 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
678 * REFIID riid, //[in ] Requested Interface
679 * UINT* prgfInOut, //[ ] reserved
680 * LPVOID* ppvObject) //[out] Resulting Interface
681 *
682 * NOTES
683 * This function gets asked to return "view objects" for one or more (multiple
684 * select) items:
685 * The viewobject typically is an COM object with one of the following
686 * interfaces:
687 * IExtractIcon,IDataObject,IContextMenu
688 * In order to support icon positions in the default Listview your DataObject
689 * must implement the SetData method (in addition to GetData :) - the shell
690 * passes a barely documented "Icon positions" structure to SetData when the
691 * drag starts, and GetData's it if the drop is in another explorer window that
692 * needs the positions.
693 */
694 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
695 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
696 REFIID riid, UINT * prgfInOut,
697 LPVOID * ppvOut)
698 {
699 LPVOID pObj = NULL;
700 HRESULT hr = E_INVALIDARG;
701
702 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
703 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
704
705 if (ppvOut)
706 {
707 *ppvOut = NULL;
708
709 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
710 {
711 HKEY hKeys[16];
712 UINT cKeys = 0;
713 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
714
715 DEFCONTEXTMENU dcm;
716 dcm.hwnd = hwndOwner;
717 dcm.pcmcb = this;
718 dcm.pidlFolder = pidlRoot;
719 dcm.psf = this;
720 dcm.cidl = cidl;
721 dcm.apidl = apidl;
722 dcm.cKeys = cKeys;
723 dcm.aKeys = hKeys;
724 dcm.punkAssociationInfo = NULL;
725 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
726 }
727 else if (IsEqualIID (riid, IID_IDataObject))
728 {
729 if (cidl >= 1)
730 {
731 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
732 }
733 else
734 {
735 hr = E_INVALIDARG;
736 }
737 }
738 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
739 {
740 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
741 }
742 else if (IsEqualIID (riid, IID_IDropTarget))
743 {
744 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
745 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
746 {
747 IDropTarget * pDt = NULL;
748 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
749 pObj = pDt;
750 }
751 }
752 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
753 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
754 {
755 hr = IShellLink_ConstructFromFile(this, apidl[0], riid, &pObj);
756 }
757 else
758 hr = E_NOINTERFACE;
759
760 if (SUCCEEDED(hr) && !pObj)
761 hr = E_OUTOFMEMORY;
762
763 *ppvOut = pObj;
764 }
765 TRACE("(%p)->hr=0x%08x\n", this, hr);
766 return hr;
767 }
768
769 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
770 static const WCHAR HideFileExtW[] = L"HideFileExt";
771 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
772
773 /******************************************************************************
774 * SHELL_FS_HideExtension [Internal]
775 *
776 * Query the registry if the filename extension of a given path should be
777 * hidden.
778 *
779 * PARAMS
780 * szPath [I] Relative or absolute path of a file
781 *
782 * RETURNS
783 * TRUE, if the filename's extension should be hidden
784 * FALSE, otherwise.
785 */
786 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
787 {
788 HKEY hKey;
789 DWORD dwData;
790 DWORD dwDataSize = sizeof (DWORD);
791 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
792
793 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
794 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
795 doHide = dwData;
796 RegCloseKey (hKey);
797 }
798
799 if (!doHide) {
800 LPWSTR ext = PathFindExtensionW(szPath);
801
802 if (*ext != '\0') {
803 WCHAR classname[MAX_PATH];
804 LONG classlen = sizeof(classname);
805
806 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
807 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
808 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
809 doHide = TRUE;
810 RegCloseKey(hKey);
811 }
812 }
813 }
814 return doHide;
815 }
816
817 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
818 {
819 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
820 if (!(dwFlags & SHGDN_FORPARSING) &&
821 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
822 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
823 PathRemoveExtensionW(szPath);
824 }
825 }
826
827 /**************************************************************************
828 * CFSFolder::GetDisplayNameOf
829 * Retrieves the display name for the specified file object or subfolder
830 *
831 * PARAMETERS
832 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
833 * DWORD dwFlags, //[in ] SHGNO formatting flags
834 * LPSTRRET lpName) //[out] Returned display name
835 *
836 * FIXME
837 * if the name is in the pidl the ret value should be a STRRET_OFFSET
838 */
839
840 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
841 DWORD dwFlags, LPSTRRET strRet)
842 {
843 if (!strRet)
844 return E_INVALIDARG;
845
846 /* If it is a complex pidl, let the child handle it */
847 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
848 {
849 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
850 }
851 else if (pidl && !pidl->mkid.cb) /* empty pidl */
852 {
853 /* If it is an empty pidl return only the path of the folder */
854 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
855 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
856 sPathTarget)
857 {
858 return SHSetStrRet(strRet, sPathTarget);
859 }
860 return E_INVALIDARG;
861 }
862
863 int len = 0;
864 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
865 if (!pszPath)
866 return E_OUTOFMEMORY;
867
868 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
869 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
870 sPathTarget)
871 {
872 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
873 PathAddBackslashW(pszPath);
874 len = wcslen(pszPath);
875 }
876 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
877 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
878
879 strRet->uType = STRRET_WSTR;
880 strRet->pOleStr = pszPath;
881
882 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
883 return S_OK;
884 }
885
886 /**************************************************************************
887 * CFSFolder::SetNameOf
888 * Changes the name of a file object or subfolder, possibly changing its item
889 * identifier in the process.
890 *
891 * PARAMETERS
892 * HWND hwndOwner, //[in ] Owner window for output
893 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
894 * LPCOLESTR lpszName, //[in ] the items new display name
895 * DWORD dwFlags, //[in ] SHGNO formatting flags
896 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
897 */
898 HRESULT WINAPI CFSFolder::SetNameOf(
899 HWND hwndOwner,
900 PCUITEMID_CHILD pidl,
901 LPCOLESTR lpName,
902 DWORD dwFlags,
903 PITEMID_CHILD *pPidlOut)
904 {
905 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
906 LPWSTR ptr;
907 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
908
909 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
910 debugstr_w (lpName), dwFlags, pPidlOut);
911
912 /* build source path */
913 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
914 ptr = PathAddBackslashW (szSrc);
915 if (ptr)
916 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
917
918 /* build destination path */
919 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
920 lstrcpynW(szDest, sPathTarget, MAX_PATH);
921 ptr = PathAddBackslashW (szDest);
922 if (ptr)
923 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
924 } else
925 lstrcpynW(szDest, lpName, MAX_PATH);
926
927 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
928 WCHAR *ext = PathFindExtensionW(szSrc);
929 if(*ext != '\0') {
930 INT len = wcslen(szDest);
931 lstrcpynW(szDest + len, ext, MAX_PATH - len);
932 }
933 }
934
935 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
936 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
937 {
938 /* src and destination is the same */
939 HRESULT hr = S_OK;
940 if (pPidlOut)
941 hr = _ILCreateFromPathW(szDest, pPidlOut);
942
943 return hr;
944 }
945
946
947 if (MoveFileW (szSrc, szDest))
948 {
949 HRESULT hr = S_OK;
950
951 if (pPidlOut)
952 hr = _ILCreateFromPathW(szDest, pPidlOut);
953
954 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
955 SHCNF_PATHW, szSrc, szDest);
956
957 return hr;
958 }
959
960 return E_FAIL;
961 }
962
963 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
964 {
965 FIXME ("(%p)\n", this);
966 return E_NOTIMPL;
967 }
968
969 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
970 {
971 FIXME ("(%p)\n", this);
972 return E_NOTIMPL;
973 }
974
975 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
976 ULONG * pSort, ULONG * pDisplay)
977 {
978 TRACE ("(%p)\n", this);
979
980 if (pSort)
981 *pSort = 0;
982 if (pDisplay)
983 *pDisplay = 0;
984
985 return S_OK;
986 }
987
988 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
989 DWORD * pcsFlags)
990 {
991 TRACE ("(%p)\n", this);
992
993 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
994 return E_INVALIDARG;
995
996 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
997
998 return S_OK;
999 }
1000
1001 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
1002 const SHCOLUMNID * pscid, VARIANT * pv)
1003 {
1004 FIXME ("(%p)\n", this);
1005
1006 return E_NOTIMPL;
1007 }
1008
1009 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
1010 UINT iColumn, SHELLDETAILS * psd)
1011 {
1012 HRESULT hr = E_FAIL;
1013
1014 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1015
1016 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
1017 return E_INVALIDARG;
1018
1019 if (!pidl)
1020 {
1021 /* the header titles */
1022 psd->fmt = GenericSFHeader[iColumn].fmt;
1023 psd->cxChar = GenericSFHeader[iColumn].cxChar;
1024 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid);
1025 }
1026 else
1027 {
1028 hr = S_OK;
1029 psd->str.uType = STRRET_CSTR;
1030 /* the data from the pidl */
1031 switch (iColumn)
1032 {
1033 case 0: /* name */
1034 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1035 break;
1036 case 1: /* FIXME: comments */
1037 psd->str.cStr[0] = 0;
1038 break;
1039 case 2: /* type */
1040 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
1041 break;
1042 case 3: /* size */
1043 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
1044 break;
1045 case 4: /* date */
1046 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
1047 break;
1048 case 5: /* attributes */
1049 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
1050 break;
1051 }
1052 }
1053
1054 return hr;
1055 }
1056
1057 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
1058 SHCOLUMNID * pscid)
1059 {
1060 FIXME ("(%p)\n", this);
1061 return E_NOTIMPL;
1062 }
1063
1064 /************************************************************************
1065 * CFSFolder::GetClassID
1066 */
1067 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1068 {
1069 TRACE ("(%p)\n", this);
1070
1071 if (!lpClassId)
1072 return E_POINTER;
1073
1074 *lpClassId = *pclsid;
1075
1076 return S_OK;
1077 }
1078
1079 /************************************************************************
1080 * CFSFolder::Initialize
1081 *
1082 * NOTES
1083 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
1084 */
1085 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
1086 {
1087 WCHAR wszTemp[MAX_PATH];
1088
1089 TRACE ("(%p)->(%p)\n", this, pidl);
1090
1091 SHFree (pidlRoot); /* free the old pidl */
1092 pidlRoot = ILClone (pidl); /* set my pidl */
1093
1094 SHFree (sPathTarget);
1095 sPathTarget = NULL;
1096
1097 /* set my path */
1098 if (SHGetPathFromIDListW (pidl, wszTemp))
1099 {
1100 int len = wcslen(wszTemp);
1101 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1102 if (!sPathTarget)
1103 return E_OUTOFMEMORY;
1104 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1105 }
1106
1107 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
1108 return S_OK;
1109 }
1110
1111 /**************************************************************************
1112 * CFSFolder::GetCurFolder
1113 */
1114 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
1115 {
1116 TRACE ("(%p)->(%p)\n", this, pidl);
1117
1118 if (!pidl)
1119 return E_POINTER;
1120
1121 *pidl = ILClone(pidlRoot);
1122 return S_OK;
1123 }
1124
1125 /**************************************************************************
1126 * CFSFolder::InitializeEx
1127 *
1128 * FIXME: error handling
1129 */
1130 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1131 const PERSIST_FOLDER_TARGET_INFO * ppfti)
1132 {
1133 WCHAR wszTemp[MAX_PATH];
1134
1135 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1136 if (ppfti)
1137 TRACE("--%p %s %s 0x%08x 0x%08x\n",
1138 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1139 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1140 ppfti->csidl);
1141
1142 pdump (pidlRootx);
1143 if (ppfti && ppfti->pidlTargetFolder)
1144 pdump(ppfti->pidlTargetFolder);
1145
1146 if (pidlRoot)
1147 __SHFreeAndNil(&pidlRoot); /* free the old */
1148 if (sPathTarget)
1149 __SHFreeAndNil(&sPathTarget);
1150
1151 /*
1152 * Root path and pidl
1153 */
1154 pidlRoot = ILClone(pidlRootx);
1155
1156 /*
1157 * the target folder is spezified in csidl OR pidlTargetFolder OR
1158 * szTargetParsingName
1159 */
1160 if (ppfti)
1161 {
1162 if (ppfti->csidl != -1)
1163 {
1164 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1165 ppfti->csidl & CSIDL_FLAG_CREATE)) {
1166 int len = wcslen(wszTemp);
1167 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1168 if (!sPathTarget)
1169 return E_OUTOFMEMORY;
1170 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1171 }
1172 }
1173 else if (ppfti->szTargetParsingName[0])
1174 {
1175 int len = wcslen(ppfti->szTargetParsingName);
1176 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1177 if (!sPathTarget)
1178 return E_OUTOFMEMORY;
1179 memcpy(sPathTarget, ppfti->szTargetParsingName,
1180 (len + 1) * sizeof(WCHAR));
1181 }
1182 else if (ppfti->pidlTargetFolder)
1183 {
1184 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1185 {
1186 int len = wcslen(wszTemp);
1187 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1188 if (!sPathTarget)
1189 return E_OUTOFMEMORY;
1190 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1191 }
1192 }
1193 }
1194
1195 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
1196 pdump(pidlRoot);
1197 return (sPathTarget) ? S_OK : E_FAIL;
1198 }
1199
1200 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1201 {
1202 FIXME("(%p)->(%p)\n", this, ppfti);
1203 ZeroMemory(ppfti, sizeof (*ppfti));
1204 return E_NOTIMPL;
1205 }
1206
1207 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1208 HKEY hKey;
1209 HRESULT hr;
1210
1211 TRACE("CFSFolder::_GetDropTarget entered\n");
1212
1213 if (_ILIsFolder (pidl))
1214 {
1215 CComPtr<IShellFolder> psfChild;
1216 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1217 if (FAILED_UNEXPECTEDLY(hr))
1218 return hr;
1219
1220 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1221 }
1222
1223 STRRET strFile;
1224 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1225 if (hr == S_OK)
1226 {
1227 WCHAR wszPath[MAX_PATH];
1228 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1229
1230 if (hr == S_OK)
1231 {
1232 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1233 if (pwszExt[0])
1234 {
1235 /* enumerate dynamic/static for a given file class */
1236 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1237 {
1238 /* load dynamic extensions from file extension key, for example .jpg */
1239 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1240 RegCloseKey(hKey);
1241 }
1242
1243 WCHAR wszTemp[40];
1244 DWORD dwSize = sizeof(wszTemp);
1245 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1246 {
1247 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1248 {
1249 /* load dynamic extensions from progid key, for example jpegfile */
1250 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1251 RegCloseKey(hKey);
1252 }
1253 }
1254 }
1255 }
1256 }
1257 else
1258 ERR("GetDisplayNameOf failed: %x\n", hr);
1259
1260 return hr;
1261 }
1262
1263 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1264 {
1265 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1266
1267 WCHAR wszName[MAX_PATH];
1268 DWORD dwSize = sizeof(wszName);
1269 HRESULT hr;
1270 LRESULT res;
1271
1272 res = RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize);
1273 if (res != ERROR_SUCCESS)
1274 return S_FALSE;
1275
1276 CLSID clsid;
1277 hr = CLSIDFromString(wszName, &clsid);
1278 if (FAILED_UNEXPECTEDLY(hr))
1279 return hr;
1280
1281 if (m_bGroupPolicyActive)
1282 {
1283 res = RegGetValueW(HKEY_LOCAL_MACHINE,
1284 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1285 wszName,
1286 RRF_RT_REG_SZ,
1287 NULL,
1288 NULL,
1289 NULL);
1290 if (res != ERROR_SUCCESS)
1291 {
1292 ERR("DropHandler extension %S not approved\n", wszName);
1293 return E_FAIL;
1294 }
1295 }
1296
1297 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1298 if (FAILED_UNEXPECTEDLY(hr))
1299 return hr;
1300
1301 return S_OK;
1302 }
1303
1304 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1305 {
1306 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1307 HRESULT hr;
1308
1309 CComPtr<IPersistFile> pp;
1310 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1311 if (hr != S_OK)
1312 {
1313 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1314 }
1315 pp->Load(pwcsname, 0);
1316
1317 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1318 if (hr != S_OK)
1319 {
1320 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1321 return hr;
1322 }
1323 return hr;
1324 }
1325
1326 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1327 {
1328 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1329 return S_OK;
1330
1331 /* no data object means no selection */
1332 if (!pdtobj)
1333 {
1334 if (uMsg == DFM_INVOKECOMMAND && wParam == DFM_CMD_PROPERTIES)
1335 {
1336 PUITEMID_CHILD pidlChild = ILClone(ILFindLastID(pidlRoot));
1337 LPITEMIDLIST pidlParent = ILClone(pidlRoot);
1338 ILRemoveLastID(pidlParent);
1339 HRESULT hr = SH_ShowPropertiesDialog(sPathTarget, pidlParent, &pidlChild);
1340 if (FAILED(hr))
1341 ERR("SH_ShowPropertiesDialog failed\n");
1342 ILFree(pidlChild);
1343 ILFree(pidlParent);
1344 }
1345 else if (uMsg == DFM_MERGECONTEXTMENU)
1346 {
1347 QCMINFO *pqcminfo = (QCMINFO *)lParam;
1348 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
1349 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1350 }
1351
1352 return S_OK;
1353 }
1354
1355 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1356 return S_OK;
1357
1358 PIDLIST_ABSOLUTE pidlFolder;
1359 PUITEMID_CHILD *apidl;
1360 UINT cidl;
1361 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
1362 if (FAILED_UNEXPECTEDLY(hr))
1363 return hr;
1364
1365 if (cidl > 1)
1366 ERR("SHMultiFileProperties is not yet implemented\n");
1367
1368 STRRET strFile;
1369 hr = GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strFile);
1370 if (SUCCEEDED(hr))
1371 {
1372 hr = SH_ShowPropertiesDialog(strFile.pOleStr, pidlFolder, apidl);
1373 if (FAILED(hr))
1374 ERR("SH_ShowPropertiesDialog failed\n");
1375 }
1376 else
1377 {
1378 ERR("Failed to get display name\n");
1379 }
1380
1381 SHFree(pidlFolder);
1382 _ILFreeaPidl(apidl, cidl);
1383
1384 return hr;
1385 }