[SHELL32]
[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 LPITEMIDLIST pidl;
491 IUnknown *pObj = NULL;
492 HRESULT hr = E_INVALIDARG;
493
494 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
495 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
496
497 if (ppvOut)
498 {
499 *ppvOut = NULL;
500
501 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
502 {
503 IContextMenu * pCm = NULL;
504 hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), NULL, 0, NULL, &pCm);
505 pObj = pCm;
506 }
507 else if (IsEqualIID (riid, IID_IDataObject))
508 {
509 if (cidl >= 1)
510 {
511 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
512 }
513 else
514 {
515 hr = IDataObject_Constructor (hwndOwner, pidlRoot, (LPCITEMIDLIST*)&pidlRoot, 1, (IDataObject **)&pObj);
516 }
517 }
518 else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
519 {
520 pidl = ILCombine (pidlRoot, apidl[0]);
521 pObj = IExtractIconA_Constructor (pidl);
522 SHFree (pidl);
523 hr = S_OK;
524 }
525 else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
526 {
527 pidl = ILCombine (pidlRoot, apidl[0]);
528 pObj = IExtractIconW_Constructor (pidl);
529 SHFree (pidl);
530 hr = S_OK;
531 }
532 else if (IsEqualIID (riid, IID_IDropTarget))
533 {
534 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
535 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
536 {
537 IDropTarget * pDt = NULL;
538 hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
539 pObj = pDt;
540 }
541 }
542 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
543 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
544 {
545 pidl = ILCombine (pidlRoot, apidl[0]);
546 hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj);
547 SHFree (pidl);
548 }
549 else
550 hr = E_NOINTERFACE;
551
552 if (SUCCEEDED(hr) && !pObj)
553 hr = E_OUTOFMEMORY;
554
555 *ppvOut = pObj;
556 }
557 TRACE("(%p)->hr=0x%08x\n", this, hr);
558 return hr;
559 }
560
561 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
562 static const WCHAR HideFileExtW[] = L"HideFileExt";
563 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
564
565 /******************************************************************************
566 * SHELL_FS_HideExtension [Internal]
567 *
568 * Query the registry if the filename extension of a given path should be
569 * hidden.
570 *
571 * PARAMS
572 * szPath [I] Relative or absolute path of a file
573 *
574 * RETURNS
575 * TRUE, if the filename's extension should be hidden
576 * FALSE, otherwise.
577 */
578 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
579 {
580 HKEY hKey;
581 DWORD dwData;
582 DWORD dwDataSize = sizeof (DWORD);
583 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
584
585 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
586 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
587 doHide = dwData;
588 RegCloseKey (hKey);
589 }
590
591 if (!doHide) {
592 LPWSTR ext = PathFindExtensionW(szPath);
593
594 if (*ext != '\0') {
595 WCHAR classname[MAX_PATH];
596 LONG classlen = sizeof(classname);
597
598 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
599 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
600 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
601 doHide = TRUE;
602 RegCloseKey(hKey);
603 }
604 }
605 }
606 return doHide;
607 }
608
609 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
610 {
611 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
612 if (!(dwFlags & SHGDN_FORPARSING) &&
613 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
614 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
615 PathRemoveExtensionW(szPath);
616 }
617 }
618
619 /**************************************************************************
620 * CFSFolder::GetDisplayNameOf
621 * Retrieves the display name for the specified file object or subfolder
622 *
623 * PARAMETERS
624 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
625 * DWORD dwFlags, //[in ] SHGNO formatting flags
626 * LPSTRRET lpName) //[out] Returned display name
627 *
628 * FIXME
629 * if the name is in the pidl the ret value should be a STRRET_OFFSET
630 */
631
632 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
633 DWORD dwFlags, LPSTRRET strRet)
634 {
635 if (!pidl || !strRet)
636 return E_INVALIDARG;
637
638 /* If it is a complex pidl, let the child handle it */
639 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
640 {
641 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
642 }
643 else if (!pidl->mkid.cb) /* empty pidl */
644 {
645 /* If it is an empty pidl return only the path of the folder */
646 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
647 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
648 sPathTarget)
649 {
650 return SHSetStrRet(strRet, sPathTarget);
651 }
652 return E_INVALIDARG;
653 }
654
655 int len = 0;
656 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
657 if (!pszPath)
658 return E_OUTOFMEMORY;
659
660 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
661 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
662 sPathTarget)
663 {
664 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
665 PathAddBackslashW(pszPath);
666 len = wcslen(pszPath);
667 }
668 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
669 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
670
671 strRet->uType = STRRET_WSTR;
672 strRet->pOleStr = pszPath;
673
674 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
675 return S_OK;
676 }
677
678 /**************************************************************************
679 * CFSFolder::SetNameOf
680 * Changes the name of a file object or subfolder, possibly changing its item
681 * identifier in the process.
682 *
683 * PARAMETERS
684 * HWND hwndOwner, //[in ] Owner window for output
685 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
686 * LPCOLESTR lpszName, //[in ] the items new display name
687 * DWORD dwFlags, //[in ] SHGNO formatting flags
688 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
689 */
690 HRESULT WINAPI CFSFolder::SetNameOf(
691 HWND hwndOwner,
692 PCUITEMID_CHILD pidl,
693 LPCOLESTR lpName,
694 DWORD dwFlags,
695 PITEMID_CHILD *pPidlOut)
696 {
697 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
698 LPWSTR ptr;
699 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
700
701 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
702 debugstr_w (lpName), dwFlags, pPidlOut);
703
704 /* build source path */
705 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
706 ptr = PathAddBackslashW (szSrc);
707 if (ptr)
708 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
709
710 /* build destination path */
711 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
712 lstrcpynW(szDest, sPathTarget, MAX_PATH);
713 ptr = PathAddBackslashW (szDest);
714 if (ptr)
715 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
716 } else
717 lstrcpynW(szDest, lpName, MAX_PATH);
718
719 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
720 WCHAR *ext = PathFindExtensionW(szSrc);
721 if(*ext != '\0') {
722 INT len = wcslen(szDest);
723 lstrcpynW(szDest + len, ext, MAX_PATH - len);
724 }
725 }
726
727 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
728 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
729 {
730 /* src and destination is the same */
731 HRESULT hr = S_OK;
732 if (pPidlOut)
733 hr = _ILCreateFromPathW(szDest, pPidlOut);
734
735 return hr;
736 }
737
738
739 if (MoveFileW (szSrc, szDest))
740 {
741 HRESULT hr = S_OK;
742
743 if (pPidlOut)
744 hr = _ILCreateFromPathW(szDest, pPidlOut);
745
746 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
747 SHCNF_PATHW, szSrc, szDest);
748
749 return hr;
750 }
751
752 return E_FAIL;
753 }
754
755 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
756 {
757 FIXME ("(%p)\n", this);
758 return E_NOTIMPL;
759 }
760
761 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
762 {
763 FIXME ("(%p)\n", this);
764 return E_NOTIMPL;
765 }
766
767 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
768 ULONG * pSort, ULONG * pDisplay)
769 {
770 TRACE ("(%p)\n", this);
771
772 if (pSort)
773 *pSort = 0;
774 if (pDisplay)
775 *pDisplay = 0;
776
777 return S_OK;
778 }
779
780 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
781 DWORD * pcsFlags)
782 {
783 TRACE ("(%p)\n", this);
784
785 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
786 return E_INVALIDARG;
787
788 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
789
790 return S_OK;
791 }
792
793 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
794 const SHCOLUMNID * pscid, VARIANT * pv)
795 {
796 FIXME ("(%p)\n", this);
797
798 return E_NOTIMPL;
799 }
800
801 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
802 UINT iColumn, SHELLDETAILS * psd)
803 {
804 HRESULT hr = E_FAIL;
805
806 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
807
808 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
809 return E_INVALIDARG;
810
811 if (!pidl)
812 {
813 /* the header titles */
814 psd->fmt = GenericSFHeader[iColumn].fmt;
815 psd->cxChar = GenericSFHeader[iColumn].cxChar;
816 psd->str.uType = STRRET_CSTR;
817 LoadStringA(shell32_hInstance, GenericSFHeader[iColumn].colnameid,
818 psd->str.cStr, MAX_PATH);
819 return S_OK;
820 }
821 else
822 {
823 hr = S_OK;
824 psd->str.uType = STRRET_CSTR;
825 /* the data from the pidl */
826 switch (iColumn)
827 {
828 case 0: /* name */
829 hr = GetDisplayNameOf (pidl,
830 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
831 break;
832 case 1: /* size */
833 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
834 break;
835 case 2: /* type */
836 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
837 break;
838 case 3: /* date */
839 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
840 break;
841 case 4: /* attributes */
842 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
843 break;
844 }
845 }
846
847 return hr;
848 }
849
850 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
851 SHCOLUMNID * pscid)
852 {
853 FIXME ("(%p)\n", this);
854 return E_NOTIMPL;
855 }
856
857 /****************************************************************************
858 * BuildPathsList
859 *
860 * Builds a list of paths like the one used in SHFileOperation from a table of
861 * PIDLs relative to the given base folder
862 */
863 WCHAR *
864 BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls)
865 {
866 WCHAR *pwszPathsList;
867 WCHAR *pwszListPos;
868 int iPathLen, i;
869
870 iPathLen = wcslen(wszBasePath);
871 pwszPathsList = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) * cidl + 1);
872 pwszListPos = pwszPathsList;
873
874 for (i = 0; i < cidl; i++)
875 {
876 if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i]))
877 continue;
878
879 wcscpy(pwszListPos, wszBasePath);
880 pwszListPos += iPathLen;
881 /* FIXME: abort if path too long */
882 _ILSimpleGetTextW(pidls[i], pwszListPos, MAX_PATH - iPathLen);
883 pwszListPos += wcslen(pwszListPos) + 1;
884 }
885 *pwszListPos = 0;
886 return pwszPathsList;
887 }
888
889 /****************************************************************************
890 * CFSFolder::CopyItems
891 *
892 * copies items to this folder
893 */
894 HRESULT WINAPI CFSFolder::CopyItems(IShellFolder * pSFFrom, UINT cidl,
895 LPCITEMIDLIST * apidl, BOOL bCopy)
896 {
897 CComPtr<IPersistFolder2> ppf2 = NULL;
898 WCHAR szSrcPath[MAX_PATH];
899 WCHAR szTargetPath[MAX_PATH];
900 SHFILEOPSTRUCTW op;
901 LPITEMIDLIST pidl;
902 LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName;
903 int res, length;
904 HRESULT hr;
905
906 TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl);
907
908 hr = pSFFrom->QueryInterface (IID_PPV_ARG(IPersistFolder2, &ppf2));
909 if (SUCCEEDED(hr))
910 {
911 hr = ppf2->GetCurFolder(&pidl);
912 if (FAILED(hr))
913 {
914 return hr;
915 }
916
917 hr = SHGetPathFromIDListW(pidl, szSrcPath);
918 SHFree(pidl);
919
920 if (FAILED(hr))
921 return hr;
922
923 pszSrc = PathAddBackslashW(szSrcPath);
924
925 wcscpy(szTargetPath, sPathTarget);
926 pszTarget = PathAddBackslashW(szTargetPath);
927
928 pszSrcList = BuildPathsList(szSrcPath, cidl, apidl);
929 pszTargetList = BuildPathsList(szTargetPath, cidl, apidl);
930
931 if (!pszSrcList || !pszTargetList)
932 {
933 if (pszSrcList)
934 HeapFree(GetProcessHeap(), 0, pszSrcList);
935
936 if (pszTargetList)
937 HeapFree(GetProcessHeap(), 0, pszTargetList);
938
939 SHFree(pidl);
940 return E_OUTOFMEMORY;
941 }
942
943 ZeroMemory(&op, sizeof(op));
944 if (!pszSrcList[0])
945 {
946 /* remove trailing backslash */
947 pszSrc--;
948 pszSrc[0] = L'\0';
949 op.pFrom = szSrcPath;
950 }
951 else
952 {
953 op.pFrom = pszSrcList;
954 }
955
956 if (!pszTargetList[0])
957 {
958 /* remove trailing backslash */
959 if (pszTarget - szTargetPath > 3)
960 {
961 pszTarget--;
962 pszTarget[0] = L'\0';
963 }
964 else
965 {
966 pszTarget[1] = L'\0';
967 }
968
969 op.pTo = szTargetPath;
970 op.fFlags = 0;
971 }
972 else
973 {
974 op.pTo = pszTargetList;
975 op.fFlags = FOF_MULTIDESTFILES;
976 }
977 op.hwnd = GetActiveWindow();
978 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
979 op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
980
981 res = SHFileOperationW(&op);
982
983 if (res == DE_SAMEFILE)
984 {
985 length = wcslen(szTargetPath);
986
987 pszFileName = wcsrchr(pszSrcList, '\\');
988 pszFileName++;
989
990 if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length))
991 {
992 wcscat(szTargetPath, L" ");
993 }
994
995 wcscat(szTargetPath, pszFileName);
996 op.pTo = szTargetPath;
997
998 res = SHFileOperationW(&op);
999 }
1000
1001 HeapFree(GetProcessHeap(), 0, pszSrcList);
1002 HeapFree(GetProcessHeap(), 0, pszTargetList);
1003
1004 if (res)
1005 return E_FAIL;
1006 else
1007 return S_OK;
1008 }
1009 return E_FAIL;
1010 }
1011
1012 /************************************************************************
1013 * CFSFolder::GetClassID
1014 */
1015 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1016 {
1017 TRACE ("(%p)\n", this);
1018
1019 if (!lpClassId)
1020 return E_POINTER;
1021
1022 *lpClassId = *pclsid;
1023
1024 return S_OK;
1025 }
1026
1027 /************************************************************************
1028 * CFSFolder::Initialize
1029 *
1030 * NOTES
1031 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
1032 */
1033 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
1034 {
1035 WCHAR wszTemp[MAX_PATH];
1036
1037 TRACE ("(%p)->(%p)\n", this, pidl);
1038
1039 SHFree (pidlRoot); /* free the old pidl */
1040 pidlRoot = ILClone (pidl); /* set my pidl */
1041
1042 SHFree (sPathTarget);
1043 sPathTarget = NULL;
1044
1045 /* set my path */
1046 if (SHGetPathFromIDListW (pidl, wszTemp))
1047 {
1048 int len = wcslen(wszTemp);
1049 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1050 if (!sPathTarget)
1051 return E_OUTOFMEMORY;
1052 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1053 }
1054
1055 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
1056 return S_OK;
1057 }
1058
1059 /**************************************************************************
1060 * CFSFolder::GetCurFolder
1061 */
1062 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
1063 {
1064 TRACE ("(%p)->(%p)\n", this, pidl);
1065
1066 if (!pidl)
1067 return E_POINTER;
1068
1069 *pidl = ILClone(pidlRoot);
1070 return S_OK;
1071 }
1072
1073 /**************************************************************************
1074 * CFSFolder::InitializeEx
1075 *
1076 * FIXME: error handling
1077 */
1078 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1079 const PERSIST_FOLDER_TARGET_INFO * ppfti)
1080 {
1081 WCHAR wszTemp[MAX_PATH];
1082
1083 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1084 if (ppfti)
1085 TRACE("--%p %s %s 0x%08x 0x%08x\n",
1086 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1087 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1088 ppfti->csidl);
1089
1090 pdump (pidlRootx);
1091 if (ppfti && ppfti->pidlTargetFolder)
1092 pdump(ppfti->pidlTargetFolder);
1093
1094 if (pidlRoot)
1095 __SHFreeAndNil(&pidlRoot); /* free the old */
1096 if (sPathTarget)
1097 __SHFreeAndNil(&sPathTarget);
1098
1099 /*
1100 * Root path and pidl
1101 */
1102 pidlRoot = ILClone(pidlRootx);
1103
1104 /*
1105 * the target folder is spezified in csidl OR pidlTargetFolder OR
1106 * szTargetParsingName
1107 */
1108 if (ppfti)
1109 {
1110 if (ppfti->csidl != -1)
1111 {
1112 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1113 ppfti->csidl & CSIDL_FLAG_CREATE)) {
1114 int len = wcslen(wszTemp);
1115 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1116 if (!sPathTarget)
1117 return E_OUTOFMEMORY;
1118 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1119 }
1120 }
1121 else if (ppfti->szTargetParsingName[0])
1122 {
1123 int len = wcslen(ppfti->szTargetParsingName);
1124 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1125 if (!sPathTarget)
1126 return E_OUTOFMEMORY;
1127 memcpy(sPathTarget, ppfti->szTargetParsingName,
1128 (len + 1) * sizeof(WCHAR));
1129 }
1130 else if (ppfti->pidlTargetFolder)
1131 {
1132 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1133 {
1134 int len = wcslen(wszTemp);
1135 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1136 if (!sPathTarget)
1137 return E_OUTOFMEMORY;
1138 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1139 }
1140 }
1141 }
1142
1143 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
1144 pdump(pidlRoot);
1145 return (sPathTarget) ? S_OK : E_FAIL;
1146 }
1147
1148 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1149 {
1150 FIXME("(%p)->(%p)\n", this, ppfti);
1151 ZeroMemory(ppfti, sizeof (*ppfti));
1152 return E_NOTIMPL;
1153 }
1154
1155 BOOL
1156 CFSFolder::GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut)
1157 {
1158 WCHAR wszLink[40];
1159
1160 if (!bShortcut)
1161 {
1162 if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink)))
1163 wszLink[0] = L'\0';
1164 }
1165
1166 if (!bShortcut)
1167 swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt);
1168 else
1169 swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt);
1170
1171 for (UINT i = 2; PathFileExistsW(pwszTarget); ++i)
1172 {
1173 if (!bShortcut)
1174 swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt);
1175 else
1176 swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt);
1177 }
1178
1179 return TRUE;
1180 }
1181
1182 /****************************************************************************
1183 * IDropTarget implementation
1184 */
1185 BOOL CFSFolder::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1186 {
1187 /* TODO Windows does different drop effects if dragging across drives.
1188 i.e., it will copy instead of move if the directories are on different disks. */
1189
1190 DWORD dwEffect = DROPEFFECT_MOVE;
1191
1192 *pdwEffect = DROPEFFECT_NONE;
1193
1194 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1195 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1196
1197 if (*pdwEffect == DROPEFFECT_NONE)
1198 *pdwEffect = dwEffect;
1199
1200 /* ... matches the desired effect ? */
1201 if (dwEffect & *pdwEffect) {
1202 return TRUE;
1203 }
1204 }
1205 return FALSE;
1206 }
1207
1208 HRESULT WINAPI CFSFolder::DragEnter(IDataObject *pDataObject,
1209 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1210 {
1211 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1212 FORMATETC fmt;
1213 FORMATETC fmt2;
1214 fAcceptFmt = FALSE;
1215
1216 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1217 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1218
1219 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1220 fAcceptFmt = TRUE;
1221 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1222 fAcceptFmt = TRUE;
1223
1224 QueryDrop(dwKeyState, pdwEffect);
1225 return S_OK;
1226 }
1227
1228 HRESULT WINAPI CFSFolder::DragOver(DWORD dwKeyState, POINTL pt,
1229 DWORD *pdwEffect)
1230 {
1231 TRACE("(%p)\n", this);
1232
1233 if (!pdwEffect)
1234 return E_INVALIDARG;
1235
1236 QueryDrop(dwKeyState, pdwEffect);
1237
1238 return S_OK;
1239 }
1240
1241 HRESULT WINAPI CFSFolder::DragLeave()
1242 {
1243 TRACE("(%p)\n", this);
1244
1245 fAcceptFmt = FALSE;
1246
1247 return S_OK;
1248 }
1249
1250 HRESULT WINAPI CFSFolder::Drop(IDataObject *pDataObject,
1251 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1252 {
1253 TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
1254
1255 BOOL fIsOpAsync = FALSE;
1256 CComPtr<IAsyncOperation> pAsyncOperation;
1257
1258 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
1259 {
1260 if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync)
1261 {
1262 _DoDropData *data = static_cast<_DoDropData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData)));
1263 data->This = this;
1264 // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder).
1265 pDataObject->AddRef();
1266 pAsyncOperation->StartOperation(NULL);
1267 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream);
1268 this->AddRef();
1269 data->dwKeyState = dwKeyState;
1270 data->pt = pt;
1271 // Need to dereference as pdweffect gets freed.
1272 data->pdwEffect = *pdwEffect;
1273 SHCreateThread(CFSFolder::_DoDropThreadProc, data, NULL, NULL);
1274 return S_OK;
1275 }
1276 }
1277 return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect);
1278 }
1279
1280 HRESULT WINAPI CFSFolder::_DoDrop(IDataObject *pDataObject,
1281 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1282 {
1283 TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect);
1284 FORMATETC fmt;
1285 FORMATETC fmt2;
1286 STGMEDIUM medium;
1287
1288 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1289 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1290
1291 HRESULT hr;
1292 bool bCopy = TRUE;
1293 bool bLinking = FALSE;
1294
1295 /* Figure out what drop operation we're doing */
1296 if (pdwEffect)
1297 {
1298 TRACE("Current drop effect flag %i\n", *pdwEffect);
1299 if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
1300 bCopy = FALSE;
1301 if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK)
1302 bLinking = TRUE;
1303 }
1304
1305 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1306 {
1307 hr = pDataObject->GetData(&fmt, &medium);
1308 TRACE("CFSTR_SHELLIDLIST.\n");
1309
1310 /* lock the handle */
1311 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1312 if (!lpcida)
1313 {
1314 ReleaseStgMedium(&medium);
1315 return E_FAIL;
1316 }
1317
1318 /* convert the data into pidl */
1319 LPITEMIDLIST pidl;
1320 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1321 if (!apidl)
1322 {
1323 ReleaseStgMedium(&medium);
1324 return E_FAIL;
1325 }
1326
1327 CComPtr<IShellFolder> psfDesktop;
1328 CComPtr<IShellFolder> psfFrom = NULL;
1329 CComPtr<IShellFolder> psfTarget = NULL;
1330
1331 hr = this->QueryInterface(IID_PPV_ARG(IShellFolder, &psfTarget));
1332 if (FAILED(hr))
1333 {
1334 ERR("psfTarget setting failed\n");
1335 SHFree(pidl);
1336 _ILFreeaPidl(apidl, lpcida->cidl);
1337 ReleaseStgMedium(&medium);
1338 return E_FAIL;
1339 }
1340
1341 /* Grab the desktop shell folder */
1342 hr = SHGetDesktopFolder(&psfDesktop);
1343 if (FAILED(hr))
1344 {
1345 ERR("SHGetDesktopFolder failed\n");
1346 SHFree(pidl);
1347 _ILFreeaPidl(apidl, lpcida->cidl);
1348 ReleaseStgMedium(&medium);
1349 return E_FAIL;
1350 }
1351
1352 /* Find source folder, this is where the clipboard data was copied from */
1353 if (_ILIsDesktop(pidl))
1354 {
1355 /* use desktop shell folder */
1356 psfFrom = psfDesktop;
1357 }
1358 else
1359 {
1360 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
1361 if (FAILED(hr))
1362 {
1363 ERR("no IShellFolder\n");
1364 SHFree(pidl);
1365 _ILFreeaPidl(apidl, lpcida->cidl);
1366 ReleaseStgMedium(&medium);
1367 return E_FAIL;
1368 }
1369 }
1370
1371 if (bLinking)
1372 {
1373 CComPtr<IPersistFolder2> ppf2 = NULL;
1374 STRRET strFile;
1375 WCHAR wszTargetPath[MAX_PATH];
1376 LPITEMIDLIST targetpidl;
1377 WCHAR wszPath[MAX_PATH];
1378 WCHAR wszTarget[MAX_PATH];
1379
1380 hr = this->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1381 if (SUCCEEDED(hr))
1382 {
1383 hr = ppf2->GetCurFolder(&targetpidl);
1384 if (SUCCEEDED(hr))
1385 {
1386 hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
1387 ILFree(targetpidl);
1388 if (SUCCEEDED(hr))
1389 {
1390 hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
1391 }
1392 }
1393 }
1394
1395 if (FAILED(hr))
1396 {
1397 ERR("Error obtaining target path");
1398 }
1399
1400 TRACE("target path = %s", debugstr_w(wszTargetPath));
1401
1402 /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
1403 for (UINT i = 0; i < lpcida->cidl; i++)
1404 {
1405 //Find out which file we're copying
1406 STRRET strFile;
1407 hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
1408 if (FAILED(hr))
1409 {
1410 ERR("Error source obtaining path");
1411 break;
1412 }
1413
1414 hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
1415 if (FAILED(hr))
1416 {
1417 ERR("Error putting source path into buffer");
1418 break;
1419 }
1420 TRACE("source path = %s", debugstr_w(wszPath));
1421
1422 // Creating a buffer to hold the combined path
1423 WCHAR buffer_1[MAX_PATH] = L"";
1424 WCHAR *lpStr1;
1425 lpStr1 = buffer_1;
1426
1427 LPWSTR pwszFileName = PathFindFileNameW(wszPath);
1428 LPWSTR pwszExt = PathFindExtensionW(wszPath);
1429 LPWSTR placementPath = PathCombineW(lpStr1, wszTargetPath, pwszFileName);
1430 CComPtr<IPersistFile> ppf;
1431
1432 //Check to see if it's already a link.
1433 if (!wcsicmp(pwszExt, L".lnk"))
1434 {
1435 //It's a link so, we create a new one which copies the old.
1436 if(!GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE))
1437 {
1438 ERR("Error getting unique file name");
1439 hr = E_FAIL;
1440 break;
1441 }
1442 hr = IShellLink_ConstructFromFile(NULL, IID_IPersistFile, ILCombine(pidl, apidl[i]), (LPVOID*)&ppf);
1443 if (FAILED(hr)) {
1444 ERR("Error constructing link from file");
1445 break;
1446 }
1447
1448 hr = ppf->Save(wszTarget, FALSE);
1449 if (FAILED(hr))
1450 break;
1451 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
1452 }
1453 else
1454 {
1455 //It's not a link, so build a new link using the creator class and fill it in.
1456 //Create a file name for the link
1457 if (!GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE))
1458 {
1459 ERR("Error creating unique file name");
1460 hr = E_FAIL;
1461 break;
1462 }
1463
1464 CComPtr<IShellLinkW> pLink;
1465 hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
1466 if (FAILED(hr)) {
1467 ERR("Error instantiating IShellLinkW");
1468 break;
1469 }
1470
1471 WCHAR szDirPath[MAX_PATH], *pwszFile;
1472 GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
1473 if (pwszFile) pwszFile[0] = 0;
1474
1475 hr = pLink->SetPath(wszPath);
1476 if(FAILED(hr))
1477 break;
1478
1479 hr = pLink->SetWorkingDirectory(szDirPath);
1480 if(FAILED(hr))
1481 break;
1482
1483 hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
1484 if(FAILED(hr))
1485 break;
1486
1487 hr = ppf->Save(wszTarget, TRUE);
1488 if (FAILED(hr))
1489 break;
1490 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
1491 }
1492 }
1493 }
1494 else
1495 {
1496 hr = this->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
1497 }
1498
1499 SHFree(pidl);
1500 _ILFreeaPidl(apidl, lpcida->cidl);
1501 ReleaseStgMedium(&medium);
1502 }
1503 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1504 {
1505 FORMATETC fmt2;
1506 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1507 if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/)
1508 {
1509 CComPtr<IPersistFolder2> ppf2 = NULL;
1510 STRRET strFile;
1511 WCHAR wszTargetPath[MAX_PATH + 1];
1512 LPWSTR pszSrcList;
1513 LPITEMIDLIST targetpidl;
1514 CComPtr<IShellFolder> psfDesktop = NULL;
1515 hr = SHGetDesktopFolder(&psfDesktop);
1516 if (FAILED(hr))
1517 {
1518 ERR("SHGetDesktopFolder failed\n");
1519 return E_FAIL;
1520 }
1521
1522 hr = this->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1523 if (SUCCEEDED(hr))
1524 {
1525 hr = ppf2->GetCurFolder(&targetpidl);
1526 if (SUCCEEDED(hr))
1527 {
1528 hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
1529 ILFree(targetpidl);
1530 if (SUCCEEDED(hr))
1531 {
1532 hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
1533 //Double NULL terminate.
1534 wszTargetPath[wcslen(wszTargetPath) + 1] = '\0';
1535 }
1536 }
1537 }
1538 if (FAILED(hr))
1539 {
1540 ERR("Error obtaining target path");
1541 return E_FAIL;
1542 }
1543
1544 LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
1545 if (!lpdf)
1546 {
1547 ERR("Error locking global\n");
1548 return E_FAIL;
1549 }
1550 pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);
1551 TRACE("Source file (just the first) = %s\n", debugstr_w(pszSrcList));
1552 TRACE("Target path = %s\n", debugstr_w(wszTargetPath));
1553
1554 SHFILEOPSTRUCTW op;
1555 ZeroMemory(&op, sizeof(op));
1556 op.pFrom = pszSrcList;
1557 op.pTo = wszTargetPath;
1558 op.hwnd = GetActiveWindow();
1559 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
1560 op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
1561 hr = SHFileOperationW(&op);
1562 return hr;
1563 }
1564 ERR("Error calling GetData\n");
1565 hr = E_FAIL;
1566 }
1567 else
1568 {
1569 ERR("No viable drop format.\n");
1570 hr = E_FAIL;
1571 }
1572 return hr;
1573 }
1574
1575 DWORD WINAPI CFSFolder::_DoDropThreadProc(LPVOID lpParameter) {
1576 CoInitialize(NULL);
1577 _DoDropData *data = static_cast<_DoDropData*>(lpParameter);
1578 CComPtr<IDataObject> pDataObject;
1579 HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject));
1580
1581 if (SUCCEEDED(hr))
1582 {
1583 CComPtr<IAsyncOperation> pAsyncOperation;
1584 hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect);
1585 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
1586 {
1587 pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect);
1588 }
1589 }
1590 //Release the CFSFolder and data object holds in the copying thread.
1591 data->This->Release();
1592 //Release the parameter from the heap.
1593 HeapFree(GetProcessHeap(), 0, data);
1594 CoUninitialize();
1595 return 0;
1596 }
1597
1598 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1599 HKEY hKey;
1600 HRESULT hr;
1601
1602 TRACE("CFSFolder::_GetDropTarget entered\n");
1603
1604 if (_ILGetGUIDPointer (pidl) || _ILIsFolder (pidl))
1605 return this->BindToObject(pidl, NULL, IID_IDropTarget, ppvOut);
1606
1607 STRRET strFile;
1608 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1609 if (hr == S_OK)
1610 {
1611 WCHAR wszPath[MAX_PATH];
1612 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1613
1614 if (hr == S_OK)
1615 {
1616 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1617 if (pwszExt[0])
1618 {
1619 /* enumerate dynamic/static for a given file class */
1620 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1621 {
1622 /* load dynamic extensions from file extension key, for example .jpg */
1623 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1624 RegCloseKey(hKey);
1625 }
1626
1627 WCHAR wszTemp[40];
1628 DWORD dwSize = sizeof(wszTemp);
1629 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1630 {
1631 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1632 {
1633 /* load dynamic extensions from progid key, for example jpegfile */
1634 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1635 RegCloseKey(hKey);
1636 }
1637 }
1638 }
1639 }
1640 }
1641 else
1642 ERR("GetDisplayNameOf failed: %x\n", hr);
1643
1644 return hr;
1645 }
1646
1647 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1648 {
1649 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1650
1651 WCHAR wszName[MAX_PATH], *pwszClsid;
1652 DWORD dwSize = sizeof(wszName);
1653 HRESULT hr;
1654
1655 if (RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize) == ERROR_SUCCESS)
1656 {
1657 CLSID clsid;
1658 hr = CLSIDFromString(wszName, &clsid);
1659 if (hr == S_OK)
1660 pwszClsid = wszName;
1661
1662 if (m_bGroupPolicyActive)
1663 {
1664 if (RegGetValueW(HKEY_LOCAL_MACHINE,
1665 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1666 pwszClsid,
1667 RRF_RT_REG_SZ,
1668 NULL,
1669 NULL,
1670 NULL) == ERROR_SUCCESS)
1671 {
1672 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1673 }
1674 }
1675 else
1676 {
1677 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1678 }
1679 }
1680 else
1681 return E_FAIL;
1682 return hr;
1683 }
1684
1685 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1686 {
1687 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1688 HRESULT hr;
1689
1690 CComPtr<IPersistFile> pp;
1691 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1692 if (hr != S_OK)
1693 {
1694 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1695 }
1696 pp->Load(pwcsname, 0);
1697
1698 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1699 if (hr != S_OK)
1700 {
1701 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1702 return hr;
1703 }
1704 return hr;
1705 }