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