867f7da2bcc1c0a0c64d7896ffc9fee79e8aaf35
[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 sPathTarget, DWORD dwFlags)
61 {
62 return CreateFolderEnumList(sPathTarget, dwFlags);
63 }
64
65 /**************************************************************************
66 * registers clipboardformat once
67 */
68 void CFSFolder::SF_RegisterClipFmt()
69 {
70 TRACE ("(%p)\n", this);
71
72 if (!cfShellIDList)
73 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
74 }
75
76 CFSFolder::CFSFolder()
77 {
78 pclsid = (CLSID *)&CLSID_ShellFSFolder;
79 sPathTarget = NULL;
80 pidlRoot = NULL;
81 cfShellIDList = 0;
82 SF_RegisterClipFmt();
83 fAcceptFmt = FALSE;
84 m_bGroupPolicyActive = 0;
85 }
86
87 CFSFolder::~CFSFolder()
88 {
89 TRACE("-- destroying IShellFolder(%p)\n", this);
90
91 SHFree(pidlRoot);
92 SHFree(sPathTarget);
93 }
94
95
96 static const shvheader GenericSFHeader[] = {
97 {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
98 {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
99 {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
100 {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 12},
101 {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10}
102 };
103
104 #define GENERICSHELLVIEWCOLUMNS 5
105
106 /**************************************************************************
107 * SHELL32_CreatePidlFromBindCtx [internal]
108 *
109 * If the caller bound File System Bind Data, assume it is the
110 * find data for the path.
111 * This allows binding of paths that don't exist.
112 */
113 LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
114 {
115 IFileSystemBindData *fsbd = NULL;
116 LPITEMIDLIST pidl = NULL;
117 IUnknown *param = NULL;
118 WIN32_FIND_DATAW wfd;
119 HRESULT r;
120
121 TRACE("%p %s\n", pbc, debugstr_w(path));
122
123 if (!pbc)
124 return NULL;
125
126 /* see if the caller bound File System Bind Data */
127 r = pbc->GetObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, &param);
128 if (FAILED(r))
129 return NULL;
130
131 r = param->QueryInterface(IID_PPV_ARG(IFileSystemBindData,&fsbd));
132 if (SUCCEEDED(r))
133 {
134 r = fsbd->GetFindData(&wfd);
135 if (SUCCEEDED(r))
136 {
137 lstrcpynW(&wfd.cFileName[0], path, MAX_PATH);
138 pidl = _ILCreateFromFindDataW(&wfd);
139 }
140 fsbd->Release();
141 }
142
143 return pidl;
144 }
145
146 /**************************************************************************
147 * CFSFolder::ParseDisplayName {SHELL32}
148 *
149 * Parse a display name.
150 *
151 * PARAMS
152 * hwndOwner [in] Parent window for any message's
153 * pbc [in] optional FileSystemBindData context
154 * lpszDisplayName [in] Unicode displayname.
155 * pchEaten [out] (unicode) characters processed
156 * ppidl [out] complex pidl to item
157 * pdwAttributes [out] items attributes
158 *
159 * NOTES
160 * Every folder tries to parse only its own (the leftmost) pidl and creates a
161 * subfolder to evaluate the remaining parts.
162 * Now we can parse into namespaces implemented by shell extensions
163 *
164 * Behaviour on win98: lpszDisplayName=NULL -> crash
165 * lpszDisplayName="" -> returns mycoputer-pidl
166 *
167 * FIXME
168 * pdwAttributes is not set
169 * pchEaten is not set like in windows
170 */
171 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
172 LPBC pbc,
173 LPOLESTR lpszDisplayName,
174 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
175 DWORD *pdwAttributes)
176 {
177 HRESULT hr = E_INVALIDARG;
178 LPCWSTR szNext = NULL;
179 WCHAR szElement[MAX_PATH];
180 WCHAR szPath[MAX_PATH];
181 LPITEMIDLIST pidlTemp = NULL;
182 DWORD len;
183
184 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
185 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
186 pchEaten, ppidl, pdwAttributes);
187
188 if (!ppidl)
189 return E_INVALIDARG;
190
191 if (!lpszDisplayName)
192 {
193 *ppidl = NULL;
194 return E_INVALIDARG;
195 }
196
197 *ppidl = NULL;
198
199 if (pchEaten)
200 *pchEaten = 0; /* strange but like the original */
201
202 if (*lpszDisplayName)
203 {
204 /* get the next element */
205 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
206
207 pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, szElement);
208 if (pidlTemp != NULL)
209 {
210 /* We are creating an id list without ensuring that the items exist.
211 If we have a remaining path, this must be a folder.
212 We have to do it now because it is set as a file by default */
213 if (szNext)
214 {
215 pidlTemp->mkid.abID[0] = PT_FOLDER;
216 }
217 hr = S_OK;
218 }
219 else
220 {
221 /* build the full pathname to the element */
222 lstrcpynW(szPath, sPathTarget, MAX_PATH - 1);
223 PathAddBackslashW(szPath);
224 len = wcslen(szPath);
225 lstrcpynW(szPath + len, szElement, MAX_PATH - len);
226
227 /* get the pidl */
228 hr = _ILCreateFromPathW(szPath, &pidlTemp);
229 }
230
231 if (SUCCEEDED(hr))
232 {
233 if (szNext && *szNext)
234 {
235 /* try to analyse the next element */
236 hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
237 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
238 }
239 else
240 {
241 /* it's the last element */
242 if (pdwAttributes && *pdwAttributes)
243 hr = SHELL32_GetFSItemAttributes(this, pidlTemp, pdwAttributes);
244 }
245 }
246 }
247
248 if (SUCCEEDED(hr))
249 *ppidl = pidlTemp;
250 else
251 *ppidl = NULL;
252
253 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
254
255 return hr;
256 }
257
258 /**************************************************************************
259 * CFSFolder::EnumObjects
260 * PARAMETERS
261 * HWND hwndOwner, //[in ] Parent Window
262 * DWORD grfFlags, //[in ] SHCONTF enumeration mask
263 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface
264 */
265 HRESULT WINAPI CFSFolder::EnumObjects(
266 HWND hwndOwner,
267 DWORD dwFlags,
268 LPENUMIDLIST *ppEnumIDList)
269 {
270 return ShellObjectCreatorInit<CFileSysEnum>(sPathTarget, dwFlags, IID_IEnumIDList, ppEnumIDList);
271 }
272
273 /**************************************************************************
274 * CFSFolder::BindToObject
275 * PARAMETERS
276 * LPCITEMIDLIST pidl, //[in ] relative pidl to open
277 * LPBC pbc, //[in ] optional FileSystemBindData context
278 * REFIID riid, //[in ] Initial Interface
279 * LPVOID* ppvObject //[out] Interface*
280 */
281 HRESULT WINAPI CFSFolder::BindToObject(
282 PCUIDLIST_RELATIVE pidl,
283 LPBC pbc,
284 REFIID riid,
285 LPVOID * ppvOut)
286 {
287 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
288 shdebugstr_guid(&riid), ppvOut);
289
290 return SHELL32_BindToFS(pidlRoot, sPathTarget, pidl, riid, ppvOut);
291 }
292
293 /**************************************************************************
294 * CFSFolder::BindToStorage
295 * PARAMETERS
296 * LPCITEMIDLIST pidl, //[in ] complex pidl to store
297 * LPBC pbc, //[in ] reserved
298 * REFIID riid, //[in ] Initial storage interface
299 * LPVOID* ppvObject //[out] Interface* returned
300 */
301 HRESULT WINAPI CFSFolder::BindToStorage(
302 PCUIDLIST_RELATIVE pidl,
303 LPBC pbcReserved,
304 REFIID riid,
305 LPVOID *ppvOut)
306 {
307 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
308 shdebugstr_guid (&riid), ppvOut);
309
310 *ppvOut = NULL;
311 return E_NOTIMPL;
312 }
313
314 /**************************************************************************
315 * CFSFolder::CompareIDs
316 */
317
318 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
319 PCUIDLIST_RELATIVE pidl1,
320 PCUIDLIST_RELATIVE pidl2)
321 {
322 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
323 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
324 FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
325 FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
326 BOOL bIsFolder1 = _ILIsFolder(pidl1);
327 BOOL bIsFolder2 = _ILIsFolder(pidl2);
328 LPWSTR pExtension1, pExtension2;
329
330 if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
331 return E_INVALIDARG;
332
333 /* When sorting between a File and a Folder, the Folder gets sorted first */
334 if (bIsFolder1 != bIsFolder2)
335 {
336 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
337 }
338
339 int result;
340 switch (LOWORD(lParam))
341 {
342 case 0: /* Name */
343 result = wcsicmp(pDataW1->wszName, pDataW2->wszName);
344 break;
345 case 2: /* Type */
346 pExtension1 = PathFindExtensionW(pDataW1->wszName);
347 pExtension2 = PathFindExtensionW(pDataW2->wszName);
348 result = wcsicmp(pExtension1, pExtension2);
349 break;
350 case 1: /* Size */
351 result = pData1->u.file.dwFileSize - pData2->u.file.dwFileSize;
352 break;
353 case 3: /* Modified */
354 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
355 if (result == 0)
356 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
357 break;
358 case 4: /* Attributes */
359 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
360 }
361 return MAKE_COMPARE_HRESULT(result);
362 }
363
364 /**************************************************************************
365 * CFSFolder::CreateViewObject
366 */
367 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
368 REFIID riid, LPVOID * ppvOut)
369 {
370 CComPtr<IShellView> pShellView;
371 HRESULT hr = E_INVALIDARG;
372
373 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
374 ppvOut);
375
376 if (ppvOut)
377 {
378 *ppvOut = NULL;
379
380 if (IsEqualIID (riid, IID_IDropTarget))
381 hr = this->QueryInterface (IID_IDropTarget, ppvOut);
382 else if (IsEqualIID (riid, IID_IContextMenu))
383 {
384 FIXME ("IContextMenu not implemented\n");
385 hr = E_NOTIMPL;
386 }
387 else if (IsEqualIID (riid, IID_IShellView))
388 {
389 hr = CDefView_Constructor(this, riid, ppvOut);
390 }
391 }
392 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
393 return hr;
394 }
395
396 /**************************************************************************
397 * CFSFolder::GetAttributesOf
398 *
399 * PARAMETERS
400 * UINT cidl, //[in ] num elements in pidl array
401 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
402 * ULONG* rgfInOut) //[out] result array
403 *
404 */
405 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
406 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
407 {
408 HRESULT hr = S_OK;
409
410 if (!rgfInOut)
411 return E_INVALIDARG;
412 if (cidl && !apidl)
413 return E_INVALIDARG;
414
415 if (*rgfInOut == 0)
416 *rgfInOut = ~0;
417
418 if(cidl == 0)
419 {
420 LPCITEMIDLIST rpidl = ILFindLastID(pidlRoot);
421
422 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
423 {
424 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
425 }
426 else if (_ILIsDrive(rpidl))
427 {
428 IShellFolder *psfParent = NULL;
429 hr = SHBindToParent(pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
430 if(SUCCEEDED(hr))
431 {
432 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
433 psfParent->Release();
434 }
435 }
436 else
437 {
438 ERR("Got and unknown pidl!\n");
439 }
440 }
441 else
442 {
443 while (cidl > 0 && *apidl)
444 {
445 pdump(*apidl);
446 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
447 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
448 else
449 ERR("Got an unknown type of pidl!!!\n");
450 apidl++;
451 cidl--;
452 }
453 }
454 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
455 *rgfInOut &= ~SFGAO_VALIDATE;
456
457 TRACE("-- result=0x%08x\n", *rgfInOut);
458
459 return hr;
460 }
461
462 /**************************************************************************
463 * CFSFolder::GetUIObjectOf
464 *
465 * PARAMETERS
466 * HWND hwndOwner, //[in ] Parent window for any output
467 * UINT cidl, //[in ] array size
468 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
469 * REFIID riid, //[in ] Requested Interface
470 * UINT* prgfInOut, //[ ] reserved
471 * LPVOID* ppvObject) //[out] Resulting Interface
472 *
473 * NOTES
474 * This function gets asked to return "view objects" for one or more (multiple
475 * select) items:
476 * The viewobject typically is an COM object with one of the following
477 * interfaces:
478 * IExtractIcon,IDataObject,IContextMenu
479 * In order to support icon positions in the default Listview your DataObject
480 * must implement the SetData method (in addition to GetData :) - the shell
481 * passes a barely documented "Icon positions" structure to SetData when the
482 * drag starts, and GetData's it if the drop is in another explorer window that
483 * needs the positions.
484 */
485 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
486 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
487 REFIID riid, UINT * prgfInOut,
488 LPVOID * ppvOut)
489 {
490 LPVOID pObj = NULL;
491 HRESULT hr = E_INVALIDARG;
492
493 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
494 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
495
496 if (ppvOut)
497 {
498 *ppvOut = NULL;
499
500 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
501 {
502 IContextMenu * pCm = NULL;
503 hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), NULL, 0, NULL, &pCm);
504 pObj = pCm;
505 }
506 else if (IsEqualIID (riid, IID_IDataObject))
507 {
508 if (cidl >= 1)
509 {
510 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
511 }
512 else
513 {
514 hr = IDataObject_Constructor (hwndOwner, pidlRoot, (LPCITEMIDLIST*)&pidlRoot, 1, (IDataObject **)&pObj);
515 }
516 }
517 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
518 {
519 hr = GenericExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
520 }
521 else if (IsEqualIID (riid, IID_IDropTarget))
522 {
523 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
524 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
525 {
526 IDropTarget * pDt = NULL;
527 hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
528 pObj = pDt;
529 }
530 }
531 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
532 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
533 {
534 hr = IShellLink_ConstructFromFile(this, apidl[0], riid, &pObj);
535 }
536 else
537 hr = E_NOINTERFACE;
538
539 if (SUCCEEDED(hr) && !pObj)
540 hr = E_OUTOFMEMORY;
541
542 *ppvOut = pObj;
543 }
544 TRACE("(%p)->hr=0x%08x\n", this, hr);
545 return hr;
546 }
547
548 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
549 static const WCHAR HideFileExtW[] = L"HideFileExt";
550 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
551
552 /******************************************************************************
553 * SHELL_FS_HideExtension [Internal]
554 *
555 * Query the registry if the filename extension of a given path should be
556 * hidden.
557 *
558 * PARAMS
559 * szPath [I] Relative or absolute path of a file
560 *
561 * RETURNS
562 * TRUE, if the filename's extension should be hidden
563 * FALSE, otherwise.
564 */
565 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
566 {
567 HKEY hKey;
568 DWORD dwData;
569 DWORD dwDataSize = sizeof (DWORD);
570 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
571
572 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
573 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
574 doHide = dwData;
575 RegCloseKey (hKey);
576 }
577
578 if (!doHide) {
579 LPWSTR ext = PathFindExtensionW(szPath);
580
581 if (*ext != '\0') {
582 WCHAR classname[MAX_PATH];
583 LONG classlen = sizeof(classname);
584
585 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
586 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
587 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
588 doHide = TRUE;
589 RegCloseKey(hKey);
590 }
591 }
592 }
593 return doHide;
594 }
595
596 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
597 {
598 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
599 if (!(dwFlags & SHGDN_FORPARSING) &&
600 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
601 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
602 PathRemoveExtensionW(szPath);
603 }
604 }
605
606 /**************************************************************************
607 * CFSFolder::GetDisplayNameOf
608 * Retrieves the display name for the specified file object or subfolder
609 *
610 * PARAMETERS
611 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
612 * DWORD dwFlags, //[in ] SHGNO formatting flags
613 * LPSTRRET lpName) //[out] Returned display name
614 *
615 * FIXME
616 * if the name is in the pidl the ret value should be a STRRET_OFFSET
617 */
618
619 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
620 DWORD dwFlags, LPSTRRET strRet)
621 {
622 if (!pidl || !strRet)
623 return E_INVALIDARG;
624
625 /* If it is a complex pidl, let the child handle it */
626 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
627 {
628 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
629 }
630 else if (!pidl->mkid.cb) /* empty pidl */
631 {
632 /* If it is an empty pidl return only the path of the folder */
633 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
634 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
635 sPathTarget)
636 {
637 return SHSetStrRet(strRet, sPathTarget);
638 }
639 return E_INVALIDARG;
640 }
641
642 int len = 0;
643 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
644 if (!pszPath)
645 return E_OUTOFMEMORY;
646
647 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
648 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
649 sPathTarget)
650 {
651 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
652 PathAddBackslashW(pszPath);
653 len = wcslen(pszPath);
654 }
655 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
656 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
657
658 strRet->uType = STRRET_WSTR;
659 strRet->pOleStr = pszPath;
660
661 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
662 return S_OK;
663 }
664
665 /**************************************************************************
666 * CFSFolder::SetNameOf
667 * Changes the name of a file object or subfolder, possibly changing its item
668 * identifier in the process.
669 *
670 * PARAMETERS
671 * HWND hwndOwner, //[in ] Owner window for output
672 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
673 * LPCOLESTR lpszName, //[in ] the items new display name
674 * DWORD dwFlags, //[in ] SHGNO formatting flags
675 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
676 */
677 HRESULT WINAPI CFSFolder::SetNameOf(
678 HWND hwndOwner,
679 PCUITEMID_CHILD pidl,
680 LPCOLESTR lpName,
681 DWORD dwFlags,
682 PITEMID_CHILD *pPidlOut)
683 {
684 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
685 LPWSTR ptr;
686 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
687
688 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
689 debugstr_w (lpName), dwFlags, pPidlOut);
690
691 /* build source path */
692 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
693 ptr = PathAddBackslashW (szSrc);
694 if (ptr)
695 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
696
697 /* build destination path */
698 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
699 lstrcpynW(szDest, sPathTarget, MAX_PATH);
700 ptr = PathAddBackslashW (szDest);
701 if (ptr)
702 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
703 } else
704 lstrcpynW(szDest, lpName, MAX_PATH);
705
706 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
707 WCHAR *ext = PathFindExtensionW(szSrc);
708 if(*ext != '\0') {
709 INT len = wcslen(szDest);
710 lstrcpynW(szDest + len, ext, MAX_PATH - len);
711 }
712 }
713
714 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
715 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
716 {
717 /* src and destination is the same */
718 HRESULT hr = S_OK;
719 if (pPidlOut)
720 hr = _ILCreateFromPathW(szDest, pPidlOut);
721
722 return hr;
723 }
724
725
726 if (MoveFileW (szSrc, szDest))
727 {
728 HRESULT hr = S_OK;
729
730 if (pPidlOut)
731 hr = _ILCreateFromPathW(szDest, pPidlOut);
732
733 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
734 SHCNF_PATHW, szSrc, szDest);
735
736 return hr;
737 }
738
739 return E_FAIL;
740 }
741
742 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
743 {
744 FIXME ("(%p)\n", this);
745 return E_NOTIMPL;
746 }
747
748 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
749 {
750 FIXME ("(%p)\n", this);
751 return E_NOTIMPL;
752 }
753
754 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
755 ULONG * pSort, ULONG * pDisplay)
756 {
757 TRACE ("(%p)\n", this);
758
759 if (pSort)
760 *pSort = 0;
761 if (pDisplay)
762 *pDisplay = 0;
763
764 return S_OK;
765 }
766
767 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
768 DWORD * pcsFlags)
769 {
770 TRACE ("(%p)\n", this);
771
772 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
773 return E_INVALIDARG;
774
775 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
776
777 return S_OK;
778 }
779
780 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
781 const SHCOLUMNID * pscid, VARIANT * pv)
782 {
783 FIXME ("(%p)\n", this);
784
785 return E_NOTIMPL;
786 }
787
788 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
789 UINT iColumn, SHELLDETAILS * psd)
790 {
791 HRESULT hr = E_FAIL;
792
793 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
794
795 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
796 return E_INVALIDARG;
797
798 if (!pidl)
799 {
800 /* the header titles */
801 psd->fmt = GenericSFHeader[iColumn].fmt;
802 psd->cxChar = GenericSFHeader[iColumn].cxChar;
803 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid);
804 }
805 else
806 {
807 hr = S_OK;
808 psd->str.uType = STRRET_CSTR;
809 /* the data from the pidl */
810 switch (iColumn)
811 {
812 case 0: /* name */
813 hr = GetDisplayNameOf (pidl,
814 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
815 break;
816 case 1: /* size */
817 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
818 break;
819 case 2: /* type */
820 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
821 break;
822 case 3: /* date */
823 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
824 break;
825 case 4: /* attributes */
826 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
827 break;
828 }
829 }
830
831 return hr;
832 }
833
834 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
835 SHCOLUMNID * pscid)
836 {
837 FIXME ("(%p)\n", this);
838 return E_NOTIMPL;
839 }
840
841 /****************************************************************************
842 * BuildPathsList
843 *
844 * Builds a list of paths like the one used in SHFileOperation from a table of
845 * PIDLs relative to the given base folder
846 */
847 WCHAR *
848 BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls)
849 {
850 WCHAR *pwszPathsList;
851 WCHAR *pwszListPos;
852 int iPathLen, i;
853
854 iPathLen = wcslen(wszBasePath);
855 pwszPathsList = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) * cidl + 1);
856 pwszListPos = pwszPathsList;
857
858 for (i = 0; i < cidl; i++)
859 {
860 if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i]))
861 continue;
862
863 wcscpy(pwszListPos, wszBasePath);
864 pwszListPos += iPathLen;
865 /* FIXME: abort if path too long */
866 _ILSimpleGetTextW(pidls[i], pwszListPos, MAX_PATH - iPathLen);
867 pwszListPos += wcslen(pwszListPos) + 1;
868 }
869 *pwszListPos = 0;
870 return pwszPathsList;
871 }
872
873 /****************************************************************************
874 * CFSFolder::CopyItems
875 *
876 * copies items to this folder
877 */
878 HRESULT WINAPI CFSFolder::CopyItems(IShellFolder * pSFFrom, UINT cidl,
879 LPCITEMIDLIST * apidl, BOOL bCopy)
880 {
881 CComPtr<IPersistFolder2> ppf2 = NULL;
882 WCHAR szSrcPath[MAX_PATH];
883 WCHAR szTargetPath[MAX_PATH];
884 SHFILEOPSTRUCTW op;
885 LPITEMIDLIST pidl;
886 LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName;
887 int res, length;
888 HRESULT hr;
889
890 TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl);
891
892 hr = pSFFrom->QueryInterface (IID_PPV_ARG(IPersistFolder2, &ppf2));
893 if (SUCCEEDED(hr))
894 {
895 hr = ppf2->GetCurFolder(&pidl);
896 if (FAILED(hr))
897 {
898 return hr;
899 }
900
901 hr = SHGetPathFromIDListW(pidl, szSrcPath);
902 SHFree(pidl);
903
904 if (FAILED(hr))
905 return hr;
906
907 pszSrc = PathAddBackslashW(szSrcPath);
908
909 wcscpy(szTargetPath, sPathTarget);
910 pszTarget = PathAddBackslashW(szTargetPath);
911
912 pszSrcList = BuildPathsList(szSrcPath, cidl, apidl);
913 pszTargetList = BuildPathsList(szTargetPath, cidl, apidl);
914
915 if (!pszSrcList || !pszTargetList)
916 {
917 if (pszSrcList)
918 HeapFree(GetProcessHeap(), 0, pszSrcList);
919
920 if (pszTargetList)
921 HeapFree(GetProcessHeap(), 0, pszTargetList);
922
923 SHFree(pidl);
924 return E_OUTOFMEMORY;
925 }
926
927 ZeroMemory(&op, sizeof(op));
928 if (!pszSrcList[0])
929 {
930 /* remove trailing backslash */
931 pszSrc--;
932 pszSrc[0] = L'\0';
933 op.pFrom = szSrcPath;
934 }
935 else
936 {
937 op.pFrom = pszSrcList;
938 }
939
940 if (!pszTargetList[0])
941 {
942 /* remove trailing backslash */
943 if (pszTarget - szTargetPath > 3)
944 {
945 pszTarget--;
946 pszTarget[0] = L'\0';
947 }
948 else
949 {
950 pszTarget[1] = L'\0';
951 }
952
953 op.pTo = szTargetPath;
954 op.fFlags = 0;
955 }
956 else
957 {
958 op.pTo = pszTargetList;
959 op.fFlags = FOF_MULTIDESTFILES;
960 }
961 op.hwnd = GetActiveWindow();
962 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
963 op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
964
965 res = SHFileOperationW(&op);
966
967 if (res == DE_SAMEFILE)
968 {
969 length = wcslen(szTargetPath);
970
971 pszFileName = wcsrchr(pszSrcList, '\\');
972 pszFileName++;
973
974 if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length))
975 {
976 wcscat(szTargetPath, L" ");
977 }
978
979 wcscat(szTargetPath, pszFileName);
980 op.pTo = szTargetPath;
981
982 res = SHFileOperationW(&op);
983 }
984
985 HeapFree(GetProcessHeap(), 0, pszSrcList);
986 HeapFree(GetProcessHeap(), 0, pszTargetList);
987
988 if (res)
989 return E_FAIL;
990 else
991 return S_OK;
992 }
993 return E_FAIL;
994 }
995
996 /************************************************************************
997 * CFSFolder::GetClassID
998 */
999 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1000 {
1001 TRACE ("(%p)\n", this);
1002
1003 if (!lpClassId)
1004 return E_POINTER;
1005
1006 *lpClassId = *pclsid;
1007
1008 return S_OK;
1009 }
1010
1011 /************************************************************************
1012 * CFSFolder::Initialize
1013 *
1014 * NOTES
1015 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
1016 */
1017 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
1018 {
1019 WCHAR wszTemp[MAX_PATH];
1020
1021 TRACE ("(%p)->(%p)\n", this, pidl);
1022
1023 SHFree (pidlRoot); /* free the old pidl */
1024 pidlRoot = ILClone (pidl); /* set my pidl */
1025
1026 SHFree (sPathTarget);
1027 sPathTarget = NULL;
1028
1029 /* set my path */
1030 if (SHGetPathFromIDListW (pidl, wszTemp))
1031 {
1032 int len = wcslen(wszTemp);
1033 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1034 if (!sPathTarget)
1035 return E_OUTOFMEMORY;
1036 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1037 }
1038
1039 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
1040 return S_OK;
1041 }
1042
1043 /**************************************************************************
1044 * CFSFolder::GetCurFolder
1045 */
1046 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
1047 {
1048 TRACE ("(%p)->(%p)\n", this, pidl);
1049
1050 if (!pidl)
1051 return E_POINTER;
1052
1053 *pidl = ILClone(pidlRoot);
1054 return S_OK;
1055 }
1056
1057 /**************************************************************************
1058 * CFSFolder::InitializeEx
1059 *
1060 * FIXME: error handling
1061 */
1062 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1063 const PERSIST_FOLDER_TARGET_INFO * ppfti)
1064 {
1065 WCHAR wszTemp[MAX_PATH];
1066
1067 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1068 if (ppfti)
1069 TRACE("--%p %s %s 0x%08x 0x%08x\n",
1070 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1071 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1072 ppfti->csidl);
1073
1074 pdump (pidlRootx);
1075 if (ppfti && ppfti->pidlTargetFolder)
1076 pdump(ppfti->pidlTargetFolder);
1077
1078 if (pidlRoot)
1079 __SHFreeAndNil(&pidlRoot); /* free the old */
1080 if (sPathTarget)
1081 __SHFreeAndNil(&sPathTarget);
1082
1083 /*
1084 * Root path and pidl
1085 */
1086 pidlRoot = ILClone(pidlRootx);
1087
1088 /*
1089 * the target folder is spezified in csidl OR pidlTargetFolder OR
1090 * szTargetParsingName
1091 */
1092 if (ppfti)
1093 {
1094 if (ppfti->csidl != -1)
1095 {
1096 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1097 ppfti->csidl & CSIDL_FLAG_CREATE)) {
1098 int len = wcslen(wszTemp);
1099 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1100 if (!sPathTarget)
1101 return E_OUTOFMEMORY;
1102 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1103 }
1104 }
1105 else if (ppfti->szTargetParsingName[0])
1106 {
1107 int len = wcslen(ppfti->szTargetParsingName);
1108 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1109 if (!sPathTarget)
1110 return E_OUTOFMEMORY;
1111 memcpy(sPathTarget, ppfti->szTargetParsingName,
1112 (len + 1) * sizeof(WCHAR));
1113 }
1114 else if (ppfti->pidlTargetFolder)
1115 {
1116 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1117 {
1118 int len = wcslen(wszTemp);
1119 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1120 if (!sPathTarget)
1121 return E_OUTOFMEMORY;
1122 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1123 }
1124 }
1125 }
1126
1127 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
1128 pdump(pidlRoot);
1129 return (sPathTarget) ? S_OK : E_FAIL;
1130 }
1131
1132 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1133 {
1134 FIXME("(%p)->(%p)\n", this, ppfti);
1135 ZeroMemory(ppfti, sizeof (*ppfti));
1136 return E_NOTIMPL;
1137 }
1138
1139 BOOL
1140 CFSFolder::GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut)
1141 {
1142 WCHAR wszLink[40];
1143
1144 if (!bShortcut)
1145 {
1146 if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink)))
1147 wszLink[0] = L'\0';
1148 }
1149
1150 if (!bShortcut)
1151 swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt);
1152 else
1153 swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt);
1154
1155 for (UINT i = 2; PathFileExistsW(pwszTarget); ++i)
1156 {
1157 if (!bShortcut)
1158 swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt);
1159 else
1160 swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt);
1161 }
1162
1163 return TRUE;
1164 }
1165
1166 /****************************************************************************
1167 * IDropTarget implementation
1168 */
1169 BOOL CFSFolder::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1170 {
1171 /* TODO Windows does different drop effects if dragging across drives.
1172 i.e., it will copy instead of move if the directories are on different disks. */
1173
1174 DWORD dwEffect = DROPEFFECT_MOVE;
1175
1176 *pdwEffect = DROPEFFECT_NONE;
1177
1178 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1179 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1180
1181 if (*pdwEffect == DROPEFFECT_NONE)
1182 *pdwEffect = dwEffect;
1183
1184 /* ... matches the desired effect ? */
1185 if (dwEffect & *pdwEffect) {
1186 return TRUE;
1187 }
1188 }
1189 return FALSE;
1190 }
1191
1192 HRESULT WINAPI CFSFolder::DragEnter(IDataObject *pDataObject,
1193 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1194 {
1195 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1196 FORMATETC fmt;
1197 FORMATETC fmt2;
1198 fAcceptFmt = FALSE;
1199
1200 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1201 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1202
1203 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1204 fAcceptFmt = TRUE;
1205 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1206 fAcceptFmt = TRUE;
1207
1208 QueryDrop(dwKeyState, pdwEffect);
1209 return S_OK;
1210 }
1211
1212 HRESULT WINAPI CFSFolder::DragOver(DWORD dwKeyState, POINTL pt,
1213 DWORD *pdwEffect)
1214 {
1215 TRACE("(%p)\n", this);
1216
1217 if (!pdwEffect)
1218 return E_INVALIDARG;
1219
1220 QueryDrop(dwKeyState, pdwEffect);
1221
1222 return S_OK;
1223 }
1224
1225 HRESULT WINAPI CFSFolder::DragLeave()
1226 {
1227 TRACE("(%p)\n", this);
1228
1229 fAcceptFmt = FALSE;
1230
1231 return S_OK;
1232 }
1233
1234 HRESULT WINAPI CFSFolder::Drop(IDataObject *pDataObject,
1235 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1236 {
1237 TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
1238
1239 BOOL fIsOpAsync = FALSE;
1240 CComPtr<IAsyncOperation> pAsyncOperation;
1241
1242 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
1243 {
1244 if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync)
1245 {
1246 _DoDropData *data = static_cast<_DoDropData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData)));
1247 data->This = this;
1248 // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder).
1249 pDataObject->AddRef();
1250 pAsyncOperation->StartOperation(NULL);
1251 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream);
1252 this->AddRef();
1253 data->dwKeyState = dwKeyState;
1254 data->pt = pt;
1255 // Need to dereference as pdweffect gets freed.
1256 data->pdwEffect = *pdwEffect;
1257 SHCreateThread(CFSFolder::_DoDropThreadProc, data, NULL, NULL);
1258 return S_OK;
1259 }
1260 }
1261 return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect);
1262 }
1263
1264 HRESULT WINAPI CFSFolder::_DoDrop(IDataObject *pDataObject,
1265 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1266 {
1267 TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect);
1268 FORMATETC fmt;
1269 FORMATETC fmt2;
1270 STGMEDIUM medium;
1271
1272 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1273 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1274
1275 HRESULT hr;
1276 bool bCopy = TRUE;
1277 bool bLinking = FALSE;
1278
1279 /* Figure out what drop operation we're doing */
1280 if (pdwEffect)
1281 {
1282 TRACE("Current drop effect flag %i\n", *pdwEffect);
1283 if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
1284 bCopy = FALSE;
1285 if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK)
1286 bLinking = TRUE;
1287 }
1288
1289 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1290 {
1291 hr = pDataObject->GetData(&fmt, &medium);
1292 TRACE("CFSTR_SHELLIDLIST.\n");
1293
1294 /* lock the handle */
1295 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1296 if (!lpcida)
1297 {
1298 ReleaseStgMedium(&medium);
1299 return E_FAIL;
1300 }
1301
1302 /* convert the data into pidl */
1303 LPITEMIDLIST pidl;
1304 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1305 if (!apidl)
1306 {
1307 ReleaseStgMedium(&medium);
1308 return E_FAIL;
1309 }
1310
1311 CComPtr<IShellFolder> psfDesktop;
1312 CComPtr<IShellFolder> psfFrom = NULL;
1313 CComPtr<IShellFolder> psfTarget = NULL;
1314
1315 hr = this->QueryInterface(IID_PPV_ARG(IShellFolder, &psfTarget));
1316 if (FAILED(hr))
1317 {
1318 ERR("psfTarget setting failed\n");
1319 SHFree(pidl);
1320 _ILFreeaPidl(apidl, lpcida->cidl);
1321 ReleaseStgMedium(&medium);
1322 return E_FAIL;
1323 }
1324
1325 /* Grab the desktop shell folder */
1326 hr = SHGetDesktopFolder(&psfDesktop);
1327 if (FAILED(hr))
1328 {
1329 ERR("SHGetDesktopFolder failed\n");
1330 SHFree(pidl);
1331 _ILFreeaPidl(apidl, lpcida->cidl);
1332 ReleaseStgMedium(&medium);
1333 return E_FAIL;
1334 }
1335
1336 /* Find source folder, this is where the clipboard data was copied from */
1337 if (_ILIsDesktop(pidl))
1338 {
1339 /* use desktop shell folder */
1340 psfFrom = psfDesktop;
1341 }
1342 else
1343 {
1344 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
1345 if (FAILED(hr))
1346 {
1347 ERR("no IShellFolder\n");
1348 SHFree(pidl);
1349 _ILFreeaPidl(apidl, lpcida->cidl);
1350 ReleaseStgMedium(&medium);
1351 return E_FAIL;
1352 }
1353 }
1354
1355 if (bLinking)
1356 {
1357 CComPtr<IPersistFolder2> ppf2 = NULL;
1358 STRRET strFile;
1359 WCHAR wszTargetPath[MAX_PATH];
1360 LPITEMIDLIST targetpidl;
1361 WCHAR wszPath[MAX_PATH];
1362 WCHAR wszTarget[MAX_PATH];
1363
1364 hr = this->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1365 if (SUCCEEDED(hr))
1366 {
1367 hr = ppf2->GetCurFolder(&targetpidl);
1368 if (SUCCEEDED(hr))
1369 {
1370 hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
1371 ILFree(targetpidl);
1372 if (SUCCEEDED(hr))
1373 {
1374 hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
1375 }
1376 }
1377 }
1378
1379 if (FAILED(hr))
1380 {
1381 ERR("Error obtaining target path");
1382 }
1383
1384 TRACE("target path = %s", debugstr_w(wszTargetPath));
1385
1386 /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
1387 for (UINT i = 0; i < lpcida->cidl; i++)
1388 {
1389 //Find out which file we're copying
1390 STRRET strFile;
1391 hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
1392 if (FAILED(hr))
1393 {
1394 ERR("Error source obtaining path");
1395 break;
1396 }
1397
1398 hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
1399 if (FAILED(hr))
1400 {
1401 ERR("Error putting source path into buffer");
1402 break;
1403 }
1404 TRACE("source path = %s", debugstr_w(wszPath));
1405
1406 // Creating a buffer to hold the combined path
1407 WCHAR buffer_1[MAX_PATH] = L"";
1408 WCHAR *lpStr1;
1409 lpStr1 = buffer_1;
1410
1411 LPWSTR pwszFileName = PathFindFileNameW(wszPath);
1412 LPWSTR pwszExt = PathFindExtensionW(wszPath);
1413 LPWSTR placementPath = PathCombineW(lpStr1, wszTargetPath, pwszFileName);
1414 CComPtr<IPersistFile> ppf;
1415
1416 //Check to see if it's already a link.
1417 if (!wcsicmp(pwszExt, L".lnk"))
1418 {
1419 //It's a link so, we create a new one which copies the old.
1420 if(!GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE))
1421 {
1422 ERR("Error getting unique file name");
1423 hr = E_FAIL;
1424 break;
1425 }
1426 hr = IShellLink_ConstructFromFile(this, apidl[i], IID_PPV_ARG(IPersistFile, &ppf));
1427 if (FAILED(hr)) {
1428 ERR("Error constructing link from file");
1429 break;
1430 }
1431
1432 hr = ppf->Save(wszTarget, FALSE);
1433 if (FAILED(hr))
1434 break;
1435 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
1436 }
1437 else
1438 {
1439 //It's not a link, so build a new link using the creator class and fill it in.
1440 //Create a file name for the link
1441 if (!GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE))
1442 {
1443 ERR("Error creating unique file name");
1444 hr = E_FAIL;
1445 break;
1446 }
1447
1448 CComPtr<IShellLinkW> pLink;
1449 hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
1450 if (FAILED(hr)) {
1451 ERR("Error instantiating IShellLinkW");
1452 break;
1453 }
1454
1455 WCHAR szDirPath[MAX_PATH], *pwszFile;
1456 GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
1457 if (pwszFile) pwszFile[0] = 0;
1458
1459 hr = pLink->SetPath(wszPath);
1460 if(FAILED(hr))
1461 break;
1462
1463 hr = pLink->SetWorkingDirectory(szDirPath);
1464 if(FAILED(hr))
1465 break;
1466
1467 hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
1468 if(FAILED(hr))
1469 break;
1470
1471 hr = ppf->Save(wszTarget, TRUE);
1472 if (FAILED(hr))
1473 break;
1474 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
1475 }
1476 }
1477 }
1478 else
1479 {
1480 hr = this->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
1481 }
1482
1483 SHFree(pidl);
1484 _ILFreeaPidl(apidl, lpcida->cidl);
1485 ReleaseStgMedium(&medium);
1486 }
1487 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1488 {
1489 FORMATETC fmt2;
1490 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1491 if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/)
1492 {
1493 CComPtr<IPersistFolder2> ppf2 = NULL;
1494 STRRET strFile;
1495 WCHAR wszTargetPath[MAX_PATH + 1];
1496 LPWSTR pszSrcList;
1497 LPITEMIDLIST targetpidl;
1498 CComPtr<IShellFolder> psfDesktop = NULL;
1499 hr = SHGetDesktopFolder(&psfDesktop);
1500 if (FAILED(hr))
1501 {
1502 ERR("SHGetDesktopFolder failed\n");
1503 return E_FAIL;
1504 }
1505
1506 hr = this->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1507 if (SUCCEEDED(hr))
1508 {
1509 hr = ppf2->GetCurFolder(&targetpidl);
1510 if (SUCCEEDED(hr))
1511 {
1512 hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
1513 ILFree(targetpidl);
1514 if (SUCCEEDED(hr))
1515 {
1516 hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
1517 //Double NULL terminate.
1518 wszTargetPath[wcslen(wszTargetPath) + 1] = '\0';
1519 }
1520 }
1521 }
1522 if (FAILED(hr))
1523 {
1524 ERR("Error obtaining target path");
1525 return E_FAIL;
1526 }
1527
1528 LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
1529 if (!lpdf)
1530 {
1531 ERR("Error locking global\n");
1532 return E_FAIL;
1533 }
1534 pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);
1535 TRACE("Source file (just the first) = %s\n", debugstr_w(pszSrcList));
1536 TRACE("Target path = %s\n", debugstr_w(wszTargetPath));
1537
1538 SHFILEOPSTRUCTW op;
1539 ZeroMemory(&op, sizeof(op));
1540 op.pFrom = pszSrcList;
1541 op.pTo = wszTargetPath;
1542 op.hwnd = GetActiveWindow();
1543 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
1544 op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
1545 hr = SHFileOperationW(&op);
1546 return hr;
1547 }
1548 ERR("Error calling GetData\n");
1549 hr = E_FAIL;
1550 }
1551 else
1552 {
1553 ERR("No viable drop format.\n");
1554 hr = E_FAIL;
1555 }
1556 return hr;
1557 }
1558
1559 DWORD WINAPI CFSFolder::_DoDropThreadProc(LPVOID lpParameter) {
1560 CoInitialize(NULL);
1561 _DoDropData *data = static_cast<_DoDropData*>(lpParameter);
1562 CComPtr<IDataObject> pDataObject;
1563 HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject));
1564
1565 if (SUCCEEDED(hr))
1566 {
1567 CComPtr<IAsyncOperation> pAsyncOperation;
1568 hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect);
1569 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
1570 {
1571 pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect);
1572 }
1573 }
1574 //Release the CFSFolder and data object holds in the copying thread.
1575 data->This->Release();
1576 //Release the parameter from the heap.
1577 HeapFree(GetProcessHeap(), 0, data);
1578 CoUninitialize();
1579 return 0;
1580 }
1581
1582 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1583 HKEY hKey;
1584 HRESULT hr;
1585
1586 TRACE("CFSFolder::_GetDropTarget entered\n");
1587
1588 if (_ILIsFolder (pidl))
1589 return this->BindToObject(pidl, NULL, IID_IDropTarget, ppvOut);
1590
1591 STRRET strFile;
1592 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1593 if (hr == S_OK)
1594 {
1595 WCHAR wszPath[MAX_PATH];
1596 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1597
1598 if (hr == S_OK)
1599 {
1600 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1601 if (pwszExt[0])
1602 {
1603 /* enumerate dynamic/static for a given file class */
1604 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1605 {
1606 /* load dynamic extensions from file extension key, for example .jpg */
1607 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1608 RegCloseKey(hKey);
1609 }
1610
1611 WCHAR wszTemp[40];
1612 DWORD dwSize = sizeof(wszTemp);
1613 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1614 {
1615 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1616 {
1617 /* load dynamic extensions from progid key, for example jpegfile */
1618 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1619 RegCloseKey(hKey);
1620 }
1621 }
1622 }
1623 }
1624 }
1625 else
1626 ERR("GetDisplayNameOf failed: %x\n", hr);
1627
1628 return hr;
1629 }
1630
1631 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1632 {
1633 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1634
1635 WCHAR wszName[MAX_PATH], *pwszClsid;
1636 DWORD dwSize = sizeof(wszName);
1637 HRESULT hr;
1638
1639 if (RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize) == ERROR_SUCCESS)
1640 {
1641 CLSID clsid;
1642 hr = CLSIDFromString(wszName, &clsid);
1643 if (hr == S_OK)
1644 pwszClsid = wszName;
1645
1646 if (m_bGroupPolicyActive)
1647 {
1648 if (RegGetValueW(HKEY_LOCAL_MACHINE,
1649 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1650 pwszClsid,
1651 RRF_RT_REG_SZ,
1652 NULL,
1653 NULL,
1654 NULL) == ERROR_SUCCESS)
1655 {
1656 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1657 }
1658 }
1659 else
1660 {
1661 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1662 }
1663 }
1664 else
1665 return E_FAIL;
1666 return hr;
1667 }
1668
1669 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1670 {
1671 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1672 HRESULT hr;
1673
1674 CComPtr<IPersistFile> pp;
1675 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1676 if (hr != S_OK)
1677 {
1678 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1679 }
1680 pp->Load(pwcsname, 0);
1681
1682 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1683 if (hr != S_OK)
1684 {
1685 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1686 return hr;
1687 }
1688 return hr;
1689 }