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