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