2 * Virtual Desktop Folder
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2009 Andrew Hill
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.
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.
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
25 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
28 CDesktopFolder should create two file system folders internally, one representing the
29 user's desktop folder, and the other representing the common desktop folder. It should
30 also create a CRegFolder to represent the virtual items that exist only in the registry.
31 The CRegFolder is aggregated by the CDesktopFolder, and queries for the CLSID_IShellFolder,
32 CLSID_IShellFolder2, or CLSID_IShellIconOverlay interfaces prefer the CRegFolder
34 The CDesktopFolderEnum class should create two enumerators, one for each of the file
35 system folders, and enumerate the contents of each folder. Since the CRegFolder
36 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
37 CDesktopFolderEnum is only responsible for returning the physical items.
38 CDesktopFolderEnum is incorrect where it filters My Computer from the enumeration
39 if the new start menu is used. The CDesktopViewCallback is responsible for filtering
40 it from the view by handling the IncludeObject query to return S_FALSE. The enumerator
41 always shows My Computer.
44 /* Undocumented functions from shdocvw */
45 extern "C" HRESULT WINAPI
IEParseDisplayNameWithBCW(DWORD codepage
, LPCWSTR lpszDisplayName
, LPBC pbc
, LPITEMIDLIST
*ppidl
);
47 /***********************************************************************
48 * Desktopfolder implementation
53 class CDesktopFolderEnum
:
54 public IEnumIDListImpl
57 // CComPtr fDesktopEnumerator;
58 // CComPtr fCommonDesktopEnumerator;
61 ~CDesktopFolderEnum();
62 HRESULT WINAPI
Initialize(CDesktopFolder
*desktopFolder
, HWND hwndOwner
, DWORD dwFlags
);
64 BEGIN_COM_MAP(CDesktopFolderEnum
)
65 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
69 int SHELL_ConfirmMsgBox(HWND hWnd
, LPWSTR lpszText
, LPWSTR lpszCaption
, HICON hIcon
, BOOL bYesToAll
);
71 static const shvheader DesktopSFHeader
[] = {
72 {IDS_SHV_COLUMN1
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 15},
73 {IDS_SHV_COLUMN2
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 10},
74 {IDS_SHV_COLUMN3
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 10},
75 {IDS_SHV_COLUMN4
, SHCOLSTATE_TYPE_DATE
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 12},
76 {IDS_SHV_COLUMN5
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 5}
79 #define DESKTOPSHELLVIEWCOLUMNS 5
81 CDesktopFolderEnum::CDesktopFolderEnum()
85 CDesktopFolderEnum::~CDesktopFolderEnum()
89 static const WCHAR ClassicStartMenuW
[] = L
"SOFTWARE\\Microsoft\\Windows\\"
90 L
"CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu";
93 IsNamespaceExtensionHidden(const WCHAR
*iid
)
95 DWORD Result
, dwResult
;
96 dwResult
= sizeof(DWORD
);
98 if (RegGetValueW(HKEY_CURRENT_USER
, /* FIXME use NewStartPanel when activated */
104 &dwResult
) != ERROR_SUCCESS
)
113 SetNamespaceExtensionVisibleStatus(const WCHAR
* iid
, DWORD dwStatus
)
117 if (RegOpenKeyExW(HKEY_CURRENT_USER
, ClassicStartMenuW
, 0, KEY_WRITE
, &hKey
) == ERROR_SUCCESS
)
119 RegSetValueExW(hKey
, iid
, 0, REG_DWORD
, (LPBYTE
)&dwStatus
, sizeof(DWORD
));
124 /**************************************************************************
125 * CreateDesktopEnumList()
128 HRESULT WINAPI
CDesktopFolderEnum::Initialize(CDesktopFolder
*desktopFolder
, HWND hwndOwner
, DWORD dwFlags
)
131 WCHAR szPath
[MAX_PATH
];
133 static const WCHAR MyDocumentsClassString
[] = L
"{450D8FBA-AD25-11D0-98A8-0800361B1103}";
134 static const WCHAR Desktop_NameSpaceW
[] = L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\Namespace";
136 TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags
);
138 /* enumerate the root folders */
139 if (dwFlags
& SHCONTF_FOLDERS
)
145 /* create the pidl for This item */
146 if (IsNamespaceExtensionHidden(MyDocumentsClassString
) < 1)
148 ret
= AddToEnumList(_ILCreateMyDocuments());
150 ret
= AddToEnumList(_ILCreateMyComputer());
152 for (i
= 0; i
< 2; i
++)
155 dwResult
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, Desktop_NameSpaceW
, 0, KEY_READ
, &hkey
);
157 dwResult
= RegOpenKeyExW(HKEY_CURRENT_USER
, Desktop_NameSpaceW
, 0, KEY_READ
, &hkey
);
159 if (dwResult
== ERROR_SUCCESS
)
170 size
= sizeof (iid
) / sizeof (iid
[0]);
171 r
= RegEnumKeyExW(hkey
, i
, iid
, &size
, 0, NULL
, NULL
, NULL
);
172 if (ERROR_SUCCESS
== r
)
174 if (IsNamespaceExtensionHidden(iid
) < 1)
176 pidl
= _ILCreateGuidFromStrW(iid
);
179 if (!HasItemWithCLSID(pidl
))
181 ret
= AddToEnumList(pidl
);
190 else if (ERROR_NO_MORE_ITEMS
== r
)
199 for (i
= 0; i
< 2; i
++)
202 dwResult
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, ClassicStartMenuW
, 0, KEY_READ
, &hkey
);
204 dwResult
= RegOpenKeyExW(HKEY_CURRENT_USER
, ClassicStartMenuW
, 0, KEY_READ
, &hkey
);
206 if (dwResult
== ERROR_SUCCESS
)
208 DWORD j
= 0, dwVal
, Val
, dwType
, dwIID
;
215 dwIID
= sizeof(iid
) / sizeof(WCHAR
);
217 r
= RegEnumValueW(hkey
, j
++, iid
, &dwIID
, NULL
, &dwType
, (LPBYTE
)&Val
, &dwVal
);
218 if (r
== ERROR_SUCCESS
)
220 if (Val
== 0 && dwType
== REG_DWORD
)
222 LPITEMIDLIST pidl
= _ILCreateGuidFromStrW(iid
);
225 if (!HasItemWithCLSID(pidl
))
236 else if (ERROR_NO_MORE_ITEMS
== r
)
247 /* enumerate the elements in %windir%\desktop */
248 ret
= ret
&& SHGetSpecialFolderPathW(0, szPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
249 ret
= ret
&& CreateFolderEnumList(szPath
, dwFlags
);
251 ret
= ret
&& SHGetSpecialFolderPathW(0, szPath
, CSIDL_COMMON_DESKTOPDIRECTORY
, FALSE
);
252 ret
= ret
&& CreateFolderEnumList(szPath
, dwFlags
);
254 return ret
? S_OK
: E_FAIL
;
257 void CDesktopFolder::SF_RegisterClipFmt()
259 TRACE ("(%p)\n", this);
262 cfShellIDList
= RegisterClipboardFormatW(CFSTR_SHELLIDLIST
);
265 CDesktopFolder::CDesktopFolder()
270 SF_RegisterClipFmt();
274 CDesktopFolder::~CDesktopFolder()
278 HRESULT WINAPI
CDesktopFolder::FinalConstruct()
280 WCHAR szMyPath
[MAX_PATH
];
282 if (!SHGetSpecialFolderPathW( 0, szMyPath
, CSIDL_DESKTOPDIRECTORY
, TRUE
))
285 pidlRoot
= _ILCreateDesktop(); /* my qualified pidl */
286 sPathTarget
= (LPWSTR
)SHAlloc((wcslen(szMyPath
) + 1) * sizeof(WCHAR
));
287 wcscpy(sPathTarget
, szMyPath
);
291 /**************************************************************************
292 * CDesktopFolder::ParseDisplayName
295 * "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" and "" binds
298 HRESULT WINAPI
CDesktopFolder::ParseDisplayName(
301 LPOLESTR lpszDisplayName
,
304 DWORD
*pdwAttributes
)
306 WCHAR szElement
[MAX_PATH
];
307 LPCWSTR szNext
= NULL
;
308 LPITEMIDLIST pidlTemp
= NULL
;
313 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
314 this, hwndOwner
, pbc
, lpszDisplayName
, debugstr_w(lpszDisplayName
),
315 pchEaten
, ppidl
, pdwAttributes
);
320 if (!lpszDisplayName
)
329 *pchEaten
= 0; /* strange but like the original */
331 urldata
.cbSize
= sizeof(urldata
);
333 if (lpszDisplayName
[0] == ':' && lpszDisplayName
[1] == ':')
335 szNext
= GetNextElementW (lpszDisplayName
, szElement
, MAX_PATH
);
336 TRACE ("-- element: %s\n", debugstr_w (szElement
));
337 CLSIDFromString (szElement
+ 2, &clsid
);
338 pidlTemp
= _ILCreateGuid (PT_GUID
, clsid
);
340 else if (PathGetDriveNumberW (lpszDisplayName
) >= 0)
342 /* it's a filesystem path with a drive. Let MyComputer/UnixDosFolder parse it */
343 pidlTemp
= _ILCreateMyComputer ();
344 szNext
= lpszDisplayName
;
346 else if (PathIsUNCW(lpszDisplayName
))
348 pidlTemp
= _ILCreateNetwork();
349 szNext
= lpszDisplayName
;
351 else if( (pidlTemp
= SHELL32_CreatePidlFromBindCtx(pbc
, lpszDisplayName
)) )
356 else if (SUCCEEDED(ParseURLW(lpszDisplayName
, &urldata
)))
358 if (urldata
.nScheme
== URL_SCHEME_SHELL
) /* handle shell: urls */
360 TRACE ("-- shell url: %s\n", debugstr_w(urldata
.pszSuffix
));
361 SHCLSIDFromStringW (urldata
.pszSuffix
+ 2, &clsid
);
362 pidlTemp
= _ILCreateGuid (PT_GUID
, clsid
);
365 return IEParseDisplayNameWithBCW(CP_ACP
, lpszDisplayName
, pbc
, ppidl
);
369 /* it's a filesystem path on the desktop. Let a FSFolder parse it */
371 if (*lpszDisplayName
)
373 WCHAR szPath
[MAX_PATH
];
376 /* build a complete path to create a simple pidl */
377 lstrcpynW(szPath
, sPathTarget
, MAX_PATH
);
378 pathPtr
= PathAddBackslashW(szPath
);
381 lstrcpynW(pathPtr
, lpszDisplayName
, MAX_PATH
- (pathPtr
- szPath
));
382 hr
= _ILCreateFromPathW(szPath
, &pidlTemp
);
386 /* should never reach here, but for completeness */
387 hr
= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
391 pidlTemp
= _ILCreateMyComputer();
396 if (SUCCEEDED(hr
) && pidlTemp
)
398 if (szNext
&& *szNext
)
400 hr
= SHELL32_ParseNextElement(this, hwndOwner
, pbc
,
401 &pidlTemp
, (LPOLESTR
) szNext
, pchEaten
, pdwAttributes
);
405 if (pdwAttributes
&& *pdwAttributes
)
406 hr
= SHELL32_GetItemAttributes((IShellFolder
*)this,
407 pidlTemp
, pdwAttributes
);
416 TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr
);
421 /**************************************************************************
422 * CDesktopFolder::EnumObjects
424 HRESULT WINAPI
CDesktopFolder::EnumObjects(
427 LPENUMIDLIST
*ppEnumIDList
)
429 CComObject
<CDesktopFolderEnum
> *theEnumerator
;
430 CComPtr
<IEnumIDList
> result
;
433 TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", this, hwndOwner
, dwFlags
, ppEnumIDList
);
435 if (ppEnumIDList
== NULL
)
437 *ppEnumIDList
= NULL
;
439 ATLTRY (theEnumerator
= new CComObject
<CDesktopFolderEnum
>);
441 if (theEnumerator
== NULL
)
442 return E_OUTOFMEMORY
;
444 hResult
= theEnumerator
->QueryInterface(IID_PPV_ARG(IEnumIDList
, &result
));
445 if (FAILED (hResult
))
447 delete theEnumerator
;
451 hResult
= theEnumerator
->Initialize (this, hwndOwner
, dwFlags
);
452 if (FAILED (hResult
))
454 *ppEnumIDList
= result
.Detach ();
456 TRACE ("-- (%p)->(new ID List: %p)\n", this, *ppEnumIDList
);
461 /**************************************************************************
462 * CDesktopFolder::BindToObject
464 HRESULT WINAPI
CDesktopFolder::BindToObject(
470 TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n",
471 this, pidl
, pbcReserved
, shdebugstr_guid (&riid
), ppvOut
);
473 return SHELL32_BindToChild( pidlRoot
, sPathTarget
, pidl
, riid
, ppvOut
);
476 /**************************************************************************
477 * CDesktopFolder::BindToStorage
479 HRESULT WINAPI
CDesktopFolder::BindToStorage(
485 FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
486 this, pidl
, pbcReserved
, shdebugstr_guid (&riid
), ppvOut
);
492 /**************************************************************************
493 * CDesktopFolder::CompareIDs
495 HRESULT WINAPI
CDesktopFolder::CompareIDs(LPARAM lParam
, LPCITEMIDLIST pidl1
, LPCITEMIDLIST pidl2
)
499 TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", this, lParam
, pidl1
, pidl2
);
500 nReturn
= SHELL32_CompareIDs ((IShellFolder
*)this, lParam
, pidl1
, pidl2
);
501 TRACE ("-- %i\n", nReturn
);
505 /**************************************************************************
506 * CDesktopFolder::CreateViewObject
508 HRESULT WINAPI
CDesktopFolder::CreateViewObject(
513 CComPtr
<IShellView
> pShellView
;
514 HRESULT hr
= E_INVALIDARG
;
516 TRACE ("(%p)->(hwnd=%p,%s,%p)\n",
517 this, hwndOwner
, shdebugstr_guid (&riid
), ppvOut
);
524 if (IsEqualIID (riid
, IID_IDropTarget
))
526 hr
= this->QueryInterface (IID_IDropTarget
, ppvOut
);
528 else if (IsEqualIID (riid
, IID_IContextMenu
))
530 WARN ("IContextMenu not implemented\n");
533 else if (IsEqualIID (riid
, IID_IShellView
))
535 hr
= IShellView_Constructor((IShellFolder
*)this, &pShellView
);
537 hr
= pShellView
->QueryInterface(riid
, ppvOut
);
539 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut
);
543 /**************************************************************************
544 * CDesktopFolder::GetAttributesOf
546 HRESULT WINAPI
CDesktopFolder::GetAttributesOf(
548 LPCITEMIDLIST
*apidl
,
552 static const DWORD dwDesktopAttributes
=
553 SFGAO_HASSUBFOLDER
| SFGAO_FILESYSTEM
| SFGAO_FOLDER
| SFGAO_FILESYSANCESTOR
|
554 SFGAO_STORAGEANCESTOR
| SFGAO_HASPROPSHEET
| SFGAO_STORAGE
| SFGAO_CANLINK
;
555 static const DWORD dwMyComputerAttributes
=
556 SFGAO_CANRENAME
| SFGAO_CANDELETE
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
|
557 SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
| SFGAO_CANLINK
;
558 static DWORD dwMyNetPlacesAttributes
=
559 SFGAO_CANRENAME
| SFGAO_CANDELETE
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
|
560 SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
| SFGAO_CANLINK
;
562 TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
563 this, cidl
, apidl
, rgfInOut
, rgfInOut
? *rgfInOut
: 0);
572 *rgfInOut
&= dwDesktopAttributes
;
575 /* TODO: always add SFGAO_CANLINK */
576 for (UINT i
= 0; i
< cidl
; ++i
)
579 if (_ILIsDesktop(*apidl
))
580 *rgfInOut
&= dwDesktopAttributes
;
581 else if (_ILIsMyComputer(apidl
[i
]))
582 *rgfInOut
&= dwMyComputerAttributes
;
583 else if (_ILIsNetHood(apidl
[i
]))
584 *rgfInOut
&= dwMyNetPlacesAttributes
;
586 SHELL32_GetItemAttributes((IShellFolder
*)this, apidl
[i
], rgfInOut
);
589 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
590 *rgfInOut
&= ~SFGAO_VALIDATE
;
592 TRACE("-- result=0x%08x\n", *rgfInOut
);
597 /**************************************************************************
598 * CDesktopFolder::GetUIObjectOf
601 * HWND hwndOwner, //[in ] Parent window for any output
602 * UINT cidl, //[in ] array size
603 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
604 * REFIID riid, //[in ] Requested Interface
605 * UINT* prgfInOut, //[ ] reserved
606 * LPVOID* ppvObject) //[out] Resulting Interface
609 HRESULT WINAPI
CDesktopFolder::GetUIObjectOf(
612 LPCITEMIDLIST
*apidl
,
618 IUnknown
*pObj
= NULL
;
619 HRESULT hr
= E_INVALIDARG
;
621 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
622 this, hwndOwner
, cidl
, apidl
, shdebugstr_guid (&riid
), prgfInOut
, ppvOut
);
629 if (IsEqualIID (riid
, IID_IContextMenu
))
631 hr
= CDefFolderMenu_Create2(pidlRoot
, hwndOwner
, cidl
, apidl
, (IShellFolder
*)this, NULL
, 0, NULL
, (IContextMenu
**)&pObj
);
633 else if (IsEqualIID (riid
, IID_IDataObject
) && (cidl
>= 1))
635 hr
= IDataObject_Constructor( hwndOwner
, pidlRoot
, apidl
, cidl
, (IDataObject
**)&pObj
);
637 else if (IsEqualIID (riid
, IID_IExtractIconA
) && (cidl
== 1))
639 pidl
= ILCombine (pidlRoot
, apidl
[0]);
640 pObj
= (LPUNKNOWN
) IExtractIconA_Constructor (pidl
);
644 else if (IsEqualIID (riid
, IID_IExtractIconW
) && (cidl
== 1))
646 pidl
= ILCombine (pidlRoot
, apidl
[0]);
647 pObj
= (LPUNKNOWN
) IExtractIconW_Constructor (pidl
);
651 else if (IsEqualIID (riid
, IID_IDropTarget
))
653 /* only interested in attempting to bind to shell folders, not files, semicolon intentionate */
654 if (cidl
== 1 && SUCCEEDED(this->BindToObject(apidl
[0], NULL
, IID_IDropTarget
, (LPVOID
*)&pObj
)));
656 hr
= this->QueryInterface(IID_IDropTarget
, (LPVOID
*)&pObj
);
658 else if ((IsEqualIID(riid
, IID_IShellLinkW
) ||
659 IsEqualIID(riid
, IID_IShellLinkA
)) && (cidl
== 1))
661 pidl
= ILCombine (pidlRoot
, apidl
[0]);
662 hr
= IShellLink_ConstructFromFile(NULL
, riid
, pidl
, (LPVOID
*)&pObj
);
668 if (SUCCEEDED(hr
) && !pObj
)
672 TRACE ("(%p)->hr=0x%08x\n", this, hr
);
676 /**************************************************************************
677 * CDesktopFolder::GetDisplayNameOf
680 * special case: pidl = null gives desktop-name back
682 HRESULT WINAPI
CDesktopFolder::GetDisplayNameOf(LPCITEMIDLIST pidl
, DWORD dwFlags
, LPSTRRET strRet
)
687 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl
, dwFlags
, strRet
);
693 pszPath
= (LPWSTR
)CoTaskMemAlloc((MAX_PATH
+ 1) * sizeof(WCHAR
));
695 return E_OUTOFMEMORY
;
697 if (_ILIsDesktop (pidl
))
699 if ((GET_SHGDN_RELATION (dwFlags
) == SHGDN_NORMAL
) &&
700 (GET_SHGDN_FOR (dwFlags
) & SHGDN_FORPARSING
))
701 wcscpy(pszPath
, sPathTarget
);
703 HCR_GetClassNameW(CLSID_ShellDesktop
, pszPath
, MAX_PATH
);
705 else if (_ILIsPidlSimple (pidl
))
709 if ((clsid
= _ILGetGUIDPointer (pidl
)))
711 if (GET_SHGDN_FOR (dwFlags
) & SHGDN_FORPARSING
)
713 int bWantsForParsing
;
716 * We can only get a filesystem path from a shellfolder if the
717 * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
719 * Exception: The MyComputer folder doesn't have this key,
720 * but any other filesystem backed folder it needs it.
722 if (IsEqualIID (*clsid
, CLSID_MyComputer
))
724 bWantsForParsing
= TRUE
;
728 /* get the "WantsFORPARSING" flag from the registry */
729 static const WCHAR clsidW
[] =
730 { 'C', 'L', 'S', 'I', 'D', '\\', 0 };
731 static const WCHAR shellfolderW
[] =
732 { '\\', 's', 'h', 'e', 'l', 'l', 'f', 'o', 'l', 'd', 'e', 'r', 0 };
733 static const WCHAR wantsForParsingW
[] =
734 { 'W', 'a', 'n', 't', 's', 'F', 'o', 'r', 'P', 'a', 'r', 's', 'i', 'n',
737 WCHAR szRegPath
[100];
740 wcscpy (szRegPath
, clsidW
);
741 SHELL32_GUIDToStringW (*clsid
, &szRegPath
[6]);
742 wcscat (szRegPath
, shellfolderW
);
743 r
= SHGetValueW(HKEY_CLASSES_ROOT
, szRegPath
,
744 wantsForParsingW
, NULL
, NULL
, NULL
);
745 if (r
== ERROR_SUCCESS
)
746 bWantsForParsing
= TRUE
;
748 bWantsForParsing
= FALSE
;
751 if ((GET_SHGDN_RELATION (dwFlags
) == SHGDN_NORMAL
) &&
755 * we need the filesystem path to the destination folder.
756 * Only the folder itself can know it
758 hr
= SHELL32_GetDisplayNameOfChild (this, pidl
, dwFlags
,
764 /* parsing name like ::{...} */
767 SHELL32_GUIDToStringW (*clsid
, &pszPath
[2]);
772 /* user friendly name */
773 HCR_GetClassNameW (*clsid
, pszPath
, MAX_PATH
);
780 /* file system folder or file rooted at the desktop */
781 if ((GET_SHGDN_FOR(dwFlags
) == SHGDN_FORPARSING
) &&
782 (GET_SHGDN_RELATION(dwFlags
) != SHGDN_INFOLDER
))
784 lstrcpynW(pszPath
, sPathTarget
, MAX_PATH
- 1);
785 PathAddBackslashW(pszPath
);
786 cLen
= wcslen(pszPath
);
789 _ILSimpleGetTextW(pidl
, pszPath
+ cLen
, MAX_PATH
- cLen
);
790 if (!_ILIsFolder(pidl
))
791 SHELL_FS_ProcessDisplayFilename(pszPath
, dwFlags
);
793 if (GetFileAttributes(pszPath
) == INVALID_FILE_ATTRIBUTES
)
795 /* file system folder or file rooted at the AllUsers desktop */
796 if ((GET_SHGDN_FOR(dwFlags
) == SHGDN_FORPARSING
) &&
797 (GET_SHGDN_RELATION(dwFlags
) != SHGDN_INFOLDER
))
799 SHGetSpecialFolderPathW(0, pszPath
, CSIDL_COMMON_DESKTOPDIRECTORY
, FALSE
);
800 PathAddBackslashW(pszPath
);
801 cLen
= wcslen(pszPath
);
804 _ILSimpleGetTextW(pidl
, pszPath
+ cLen
, MAX_PATH
- cLen
);
805 if (!_ILIsFolder(pidl
))
806 SHELL_FS_ProcessDisplayFilename(pszPath
, dwFlags
);
812 /* a complex pidl, let the subfolder do the work */
813 hr
= SHELL32_GetDisplayNameOfChild (this, pidl
, dwFlags
,
819 /* Win9x always returns ANSI strings, NT always returns Unicode strings */
820 if (GetVersion() & 0x80000000)
822 strRet
->uType
= STRRET_CSTR
;
823 if (!WideCharToMultiByte(CP_ACP
, 0, pszPath
, -1, strRet
->cStr
, MAX_PATH
,
825 strRet
->cStr
[0] = '\0';
826 CoTaskMemFree(pszPath
);
830 strRet
->uType
= STRRET_WSTR
;
831 strRet
->pOleStr
= pszPath
;
835 CoTaskMemFree(pszPath
);
837 TRACE ("-- (%p)->(%s,0x%08x)\n", this,
838 strRet
->uType
== STRRET_CSTR
? strRet
->cStr
:
839 debugstr_w(strRet
->pOleStr
), hr
);
843 /**************************************************************************
844 * CDesktopFolder::SetNameOf
845 * Changes the name of a file object or subfolder, possibly changing its item
846 * identifier in the process.
849 * HWND hwndOwner, //[in ] Owner window for output
850 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
851 * LPCOLESTR lpszName, //[in ] the items new display name
852 * DWORD dwFlags, //[in ] SHGNO formatting flags
853 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
855 HRESULT WINAPI
CDesktopFolder::SetNameOf(
857 LPCITEMIDLIST pidl
, /* simple pidl */
860 LPITEMIDLIST
*pPidlOut
)
862 CComPtr
<IShellFolder2
> psf
;
864 WCHAR szSrc
[MAX_PATH
+ 1], szDest
[MAX_PATH
+ 1];
866 BOOL bIsFolder
= _ILIsFolder (ILFindLastID (pidl
));
868 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner
, pidl
,
869 debugstr_w (lpName
), dwFlags
, pPidlOut
);
871 if (_ILGetGUIDPointer(pidl
))
873 if (SUCCEEDED(BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder2
, &psf
))))
875 hr
= psf
->SetNameOf(hwndOwner
, pidl
, lpName
, dwFlags
, pPidlOut
);
880 /* build source path */
881 lstrcpynW(szSrc
, sPathTarget
, MAX_PATH
);
882 ptr
= PathAddBackslashW (szSrc
);
884 _ILSimpleGetTextW (pidl
, ptr
, MAX_PATH
+ 1 - (ptr
- szSrc
));
886 /* build destination path */
887 if (dwFlags
== SHGDN_NORMAL
|| dwFlags
& SHGDN_INFOLDER
) {
888 lstrcpynW(szDest
, sPathTarget
, MAX_PATH
);
889 ptr
= PathAddBackslashW (szDest
);
891 lstrcpynW(ptr
, lpName
, MAX_PATH
+ 1 - (ptr
- szDest
));
893 lstrcpynW(szDest
, lpName
, MAX_PATH
);
895 if(!(dwFlags
& SHGDN_FORPARSING
) && SHELL_FS_HideExtension(szSrc
)) {
896 WCHAR
*ext
= PathFindExtensionW(szSrc
);
898 INT len
= wcslen(szDest
);
899 lstrcpynW(szDest
+ len
, ext
, MAX_PATH
- len
);
903 if (!memcmp(szSrc
, szDest
, (wcslen(szDest
) + 1) * sizeof(WCHAR
)))
905 /* src and destination is the same */
908 hr
= _ILCreateFromPathW(szDest
, pPidlOut
);
913 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc
), debugstr_w(szDest
));
914 if (MoveFileW (szSrc
, szDest
))
919 hr
= _ILCreateFromPathW(szDest
, pPidlOut
);
921 SHChangeNotify (bIsFolder
? SHCNE_RENAMEFOLDER
: SHCNE_RENAMEITEM
,
922 SHCNF_PATHW
, szSrc
, szDest
);
929 HRESULT WINAPI
CDesktopFolder::GetDefaultSearchGUID(GUID
*pguid
)
931 FIXME ("(%p)\n", this);
935 HRESULT WINAPI
CDesktopFolder::EnumSearches(IEnumExtraSearch
**ppenum
)
937 FIXME ("(%p)\n", this);
941 HRESULT WINAPI
CDesktopFolder::GetDefaultColumn(DWORD dwRes
, ULONG
*pSort
, ULONG
*pDisplay
)
943 TRACE ("(%p)\n", this);
953 HRESULT WINAPI
CDesktopFolder::GetDefaultColumnState(UINT iColumn
, DWORD
*pcsFlags
)
955 TRACE ("(%p)\n", this);
957 if (!pcsFlags
|| iColumn
>= DESKTOPSHELLVIEWCOLUMNS
)
960 *pcsFlags
= DesktopSFHeader
[iColumn
].pcsFlags
;
965 HRESULT WINAPI
CDesktopFolder::GetDetailsEx(
967 const SHCOLUMNID
*pscid
,
970 FIXME ("(%p)\n", this);
975 HRESULT WINAPI
CDesktopFolder::GetDetailsOf(
982 TRACE ("(%p)->(%p %i %p)\n", this, pidl
, iColumn
, psd
);
984 if (!psd
|| iColumn
>= DESKTOPSHELLVIEWCOLUMNS
)
989 psd
->fmt
= DesktopSFHeader
[iColumn
].fmt
;
990 psd
->cxChar
= DesktopSFHeader
[iColumn
].cxChar
;
991 psd
->str
.uType
= STRRET_CSTR
;
992 LoadStringA (shell32_hInstance
, DesktopSFHeader
[iColumn
].colnameid
,
993 psd
->str
.cStr
, MAX_PATH
);
997 /* the data from the pidl */
998 psd
->str
.uType
= STRRET_CSTR
;
1002 hr
= GetDisplayNameOf(pidl
,
1003 SHGDN_NORMAL
| SHGDN_INFOLDER
, &psd
->str
);
1006 _ILGetFileSize (pidl
, psd
->str
.cStr
, MAX_PATH
);
1009 _ILGetFileType (pidl
, psd
->str
.cStr
, MAX_PATH
);
1012 _ILGetFileDate (pidl
, psd
->str
.cStr
, MAX_PATH
);
1014 case 4: /* attributes */
1015 _ILGetFileAttributes (pidl
, psd
->str
.cStr
, MAX_PATH
);
1022 HRESULT WINAPI
CDesktopFolder::MapColumnToSCID(UINT column
, SHCOLUMNID
*pscid
)
1024 FIXME ("(%p)\n", this);
1028 HRESULT WINAPI
CDesktopFolder::GetClassID(CLSID
*lpClassId
)
1030 TRACE ("(%p)\n", this);
1035 *lpClassId
= CLSID_ShellDesktop
;
1040 HRESULT WINAPI
CDesktopFolder::Initialize(LPCITEMIDLIST pidl
)
1042 TRACE ("(%p)->(%p)\n", this, pidl
);
1047 HRESULT WINAPI
CDesktopFolder::GetCurFolder(LPITEMIDLIST
* pidl
)
1049 TRACE ("(%p)->(%p)\n", this, pidl
);
1051 if (!pidl
) return E_POINTER
;
1052 *pidl
= ILClone (pidlRoot
);
1056 HRESULT WINAPI
CDesktopFolder::GetUniqueName(LPWSTR pwszName
, UINT uLen
)
1058 CComPtr
<IEnumIDList
> penum
;
1060 WCHAR wszText
[MAX_PATH
];
1061 WCHAR wszNewFolder
[25];
1062 const WCHAR wszFormat
[] = {'%', 's', ' ', '%', 'd', 0 };
1064 LoadStringW(shell32_hInstance
, IDS_NEWFOLDER
, wszNewFolder
, sizeof(wszNewFolder
) / sizeof(WCHAR
));
1066 TRACE ("(%p)(%p %u)\n", this, pwszName
, uLen
);
1068 if (uLen
< sizeof(wszNewFolder
) / sizeof(WCHAR
) + 3)
1071 lstrcpynW (pwszName
, wszNewFolder
, uLen
);
1074 SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
| SHCONTF_INCLUDEHIDDEN
, &penum
);
1082 while (S_OK
== penum
->Next(1, &pidl
, &dwFetched
) &&
1084 _ILSimpleGetTextW (pidl
, wszText
, MAX_PATH
);
1085 if (0 == lstrcmpiW (wszText
, pwszName
)) {
1086 _snwprintf (pwszName
, uLen
, wszFormat
, wszNewFolder
, i
++);
1099 HRESULT WINAPI
CDesktopFolder::AddFolder(HWND hwnd
, LPCWSTR pwszName
, LPITEMIDLIST
*ppidlOut
)
1101 WCHAR wszNewDir
[MAX_PATH
];
1103 HRESULT hres
= E_FAIL
;
1105 TRACE ("(%p)(%s %p)\n", this, debugstr_w(pwszName
), ppidlOut
);
1109 lstrcpynW(wszNewDir
, sPathTarget
, MAX_PATH
);
1110 PathAppendW(wszNewDir
, pwszName
);
1111 bRes
= CreateDirectoryW (wszNewDir
, NULL
);
1114 SHChangeNotify (SHCNE_MKDIR
, SHCNF_PATHW
, wszNewDir
, NULL
);
1117 hres
= _ILCreateFromPathW(wszNewDir
, ppidlOut
);
1123 HRESULT WINAPI
CDesktopFolder::DeleteItems(UINT cidl
, LPCITEMIDLIST
*apidl
)
1127 WCHAR wszPath
[MAX_PATH
];
1128 WCHAR wszCaption
[50];
1129 WCHAR
*wszPathsList
;
1131 WCHAR
*wszCurrentPath
;
1132 UINT bRestoreWithDeskCpl
= FALSE
;
1135 TRACE ("(%p)(%u %p)\n", this, cidl
, apidl
);
1136 if (cidl
== 0) return S_OK
;
1138 for(i
= 0; i
< cidl
; i
++)
1140 if (_ILIsMyComputer(apidl
[i
]))
1141 bRestoreWithDeskCpl
++;
1142 else if (_ILIsNetHood(apidl
[i
]))
1143 bRestoreWithDeskCpl
++;
1144 else if (_ILIsMyDocuments(apidl
[i
]))
1145 bRestoreWithDeskCpl
++;
1148 if (bRestoreWithDeskCpl
)
1150 /* FIXME use FormatMessage
1151 * use a similar message resource as in windows
1153 LoadStringW(shell32_hInstance
, IDS_DELETEMULTIPLE_TEXT
, wszPath
, sizeof(wszPath
) / sizeof(WCHAR
));
1154 wszPath
[(sizeof(wszPath
)/sizeof(WCHAR
))-1] = 0;
1156 LoadStringW(shell32_hInstance
, IDS_DELETEITEM_CAPTION
, wszCaption
, sizeof(wszCaption
) / sizeof(WCHAR
));
1157 wszCaption
[(sizeof(wszCaption
)/sizeof(WCHAR
))-1] = 0;
1159 res
= SHELL_ConfirmMsgBox(GetActiveWindow(), wszPath
, wszCaption
, NULL
, cidl
> 1);
1160 if (res
== IDC_YESTOALL
|| res
== IDYES
)
1162 for(i
= 0; i
< cidl
; i
++)
1164 if (_ILIsMyComputer(apidl
[i
]))
1165 SetNamespaceExtensionVisibleStatus(L
"{20D04FE0-3AEA-1069-A2D8-08002B30309D}", 0x1);
1166 else if (_ILIsNetHood(apidl
[i
]))
1167 SetNamespaceExtensionVisibleStatus(L
"{208D2C60-3AEA-1069-A2D7-08002B30309D}", 0x1);
1168 else if (_ILIsMyDocuments(apidl
[i
]))
1169 SetNamespaceExtensionVisibleStatus(L
"{450D8FBA-AD25-11D0-98A8-0800361B1103}", 0x1);
1174 lstrcpynW(wszPath
, sPathTarget
, MAX_PATH
);
1178 PathAddBackslashW(wszPath
);
1179 wszPathsList
= BuildPathsList(wszPath
, cidl
, apidl
);
1181 ZeroMemory(&op
, sizeof(op
));
1182 op
.hwnd
= GetActiveWindow();
1183 op
.wFunc
= FO_DELETE
;
1184 op
.pFrom
= wszPathsList
;
1185 op
.fFlags
= FOF_ALLOWUNDO
;
1186 if (SHFileOperationW(&op
))
1188 WARN("SHFileOperation failed\n");
1194 /* we currently need to manually send the notifies */
1195 wszCurrentPath
= wszPathsList
;
1196 for (i
= 0; i
< cidl
; i
++)
1200 if (_ILIsFolder(apidl
[i
]))
1201 wEventId
= SHCNE_RMDIR
;
1202 else if (_ILIsValue(apidl
[i
]))
1203 wEventId
= SHCNE_DELETE
;
1207 /* check if file exists */
1208 if (GetFileAttributesW(wszCurrentPath
) == INVALID_FILE_ATTRIBUTES
)
1210 LPITEMIDLIST pidl
= ILCombine(pidlRoot
, apidl
[i
]);
1211 SHChangeNotify(wEventId
, SHCNF_IDLIST
, pidl
, NULL
);
1215 wszCurrentPath
+= wcslen(wszCurrentPath
) + 1;
1217 HeapFree(GetProcessHeap(), 0, wszPathsList
);
1221 HRESULT WINAPI
CDesktopFolder::CopyItems(IShellFolder
*pSFFrom
, UINT cidl
, LPCITEMIDLIST
*apidl
, bool bCopy
)
1223 CComPtr
<IPersistFolder2
> ppf2
;
1224 WCHAR szSrcPath
[MAX_PATH
];
1225 WCHAR szTargetPath
[MAX_PATH
];
1228 LPWSTR pszSrc
, pszTarget
, pszSrcList
, pszTargetList
, pszFileName
;
1232 TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom
, cidl
, apidl
);
1234 pSFFrom
->QueryInterface(IID_PPV_ARG(IPersistFolder2
, &ppf2
));
1237 if (FAILED(ppf2
->GetCurFolder(&pidl
)))
1240 if (FAILED(pSFFrom
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
, &strRet
)))
1246 if (FAILED(StrRetToBufW(&strRet
, pidl
, szSrcPath
, MAX_PATH
)))
1253 pszSrc
= PathAddBackslashW (szSrcPath
);
1255 wcscpy(szTargetPath
, sPathTarget
);
1256 pszTarget
= PathAddBackslashW (szTargetPath
);
1258 pszSrcList
= BuildPathsList(szSrcPath
, cidl
, apidl
);
1259 pszTargetList
= BuildPathsList(szTargetPath
, cidl
, apidl
);
1261 if (!pszSrcList
|| !pszTargetList
)
1264 HeapFree(GetProcessHeap(), 0, pszSrcList
);
1267 HeapFree(GetProcessHeap(), 0, pszTargetList
);
1270 return E_OUTOFMEMORY
;
1272 ZeroMemory(&op
, sizeof(op
));
1275 /* remove trailing backslash */
1278 op
.pFrom
= szSrcPath
;
1282 op
.pFrom
= pszSrcList
;
1285 if (!pszTargetList
[0])
1287 /* remove trailing backslash */
1288 if (pszTarget
- szTargetPath
> 3)
1291 pszTarget
[0] = L
'\0';
1295 pszTarget
[1] = L
'\0';
1298 op
.pTo
= szTargetPath
;
1303 op
.pTo
= pszTargetList
;
1304 op
.fFlags
= FOF_MULTIDESTFILES
;
1306 op
.hwnd
= GetActiveWindow();
1307 op
.wFunc
= bCopy
? FO_COPY
: FO_MOVE
;
1308 op
.fFlags
|= FOF_ALLOWUNDO
| FOF_NOCONFIRMMKDIR
;
1310 res
= SHFileOperationW(&op
);
1312 if (res
== DE_SAMEFILE
)
1314 length
= wcslen(szTargetPath
);
1317 pszFileName
= wcsrchr(pszSrcList
, '\\');
1320 if (LoadStringW(shell32_hInstance
, IDS_COPY_OF
, pszTarget
, MAX_PATH
- length
))
1322 wcscat(szTargetPath
, L
" ");
1325 wcscat(szTargetPath
, pszFileName
);
1326 op
.pTo
= szTargetPath
;
1328 res
= SHFileOperationW(&op
);
1332 HeapFree(GetProcessHeap(), 0, pszSrcList
);
1333 HeapFree(GetProcessHeap(), 0, pszTargetList
);
1343 /****************************************************************************
1344 * IDropTarget implementation
1346 * This should allow two somewhat separate things, copying files to the users directory,
1347 * as well as allowing icons to be moved anywhere and updating the registry to save.
1349 * The first thing I think is best done using fs.cpp to prevent WET code. So we'll simulate
1350 * a drop to the user's home directory. The second will look at the pointer location and
1351 * set sensible places for the icons to live.
1354 BOOL
CDesktopFolder::QueryDrop(DWORD dwKeyState
, LPDWORD pdwEffect
)
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. */
1359 DWORD dwEffect
= DROPEFFECT_MOVE
;
1361 *pdwEffect
= DROPEFFECT_NONE
;
1363 if (fAcceptFmt
) { /* Does our interpretation of the keystate ... */
1364 *pdwEffect
= KeyStateToDropEffect (dwKeyState
);
1366 if (*pdwEffect
== DROPEFFECT_NONE
)
1367 *pdwEffect
= dwEffect
;
1369 /* ... matches the desired effect ? */
1370 if (dwEffect
& *pdwEffect
) {
1377 HRESULT WINAPI
CDesktopFolder::DragEnter(IDataObject
*pDataObject
,
1378 DWORD dwKeyState
, POINTL pt
, DWORD
*pdwEffect
)
1380 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject
);
1385 InitFormatEtc (fmt
, cfShellIDList
, TYMED_HGLOBAL
);
1386 InitFormatEtc (fmt2
, CF_HDROP
, TYMED_HGLOBAL
);
1388 if (SUCCEEDED(pDataObject
->QueryGetData(&fmt
)))
1390 else if (SUCCEEDED(pDataObject
->QueryGetData(&fmt2
)))
1393 QueryDrop(dwKeyState
, pdwEffect
);
1397 HRESULT WINAPI
CDesktopFolder::DragOver(DWORD dwKeyState
, POINTL pt
,
1400 TRACE("(%p)\n", this);
1403 return E_INVALIDARG
;
1405 QueryDrop(dwKeyState
, pdwEffect
);
1410 HRESULT WINAPI
CDesktopFolder::DragLeave()
1412 TRACE("(%p)\n", this);
1417 HRESULT WINAPI
CDesktopFolder::Drop(IDataObject
*pDataObject
,
1418 DWORD dwKeyState
, POINTL pt
, DWORD
*pdwEffect
)
1420 TRACE("(%p) object dropped desktop\n", this);
1423 bool passthroughtofs
= FALSE
;
1424 FORMATETC formatetc
;
1425 InitFormatEtc(formatetc
, RegisterClipboardFormatW(CFSTR_SHELLIDLIST
), TYMED_HGLOBAL
);
1427 HRESULT hr
= pDataObject
->GetData(&formatetc
, &medium
);
1430 /* lock the handle */
1431 LPIDA lpcida
= (LPIDA
)GlobalLock(medium
.hGlobal
);
1434 ReleaseStgMedium(&medium
);
1438 /* convert the clipboard data into pidl (pointer to id list) */
1440 LPITEMIDLIST
*apidl
= _ILCopyCidaToaPidl(&pidl
, lpcida
);
1443 ReleaseStgMedium(&medium
);
1446 passthroughtofs
= !_ILIsDesktop(pidl
) || (dwKeyState
& MK_CONTROL
);
1448 _ILFreeaPidl(apidl
, lpcida
->cidl
);
1449 ReleaseStgMedium(&medium
);
1453 InitFormatEtc (formatetc
, CF_HDROP
, TYMED_HGLOBAL
);
1454 if SUCCEEDED(pDataObject
->QueryGetData(&formatetc
));
1456 passthroughtofs
= TRUE
;
1459 /* We only want to really move files around if they don't already
1460 come from the desktop, or we're linking or copying */
1461 if (passthroughtofs
)
1463 LPITEMIDLIST pidl
= NULL
;
1465 WCHAR szPath
[MAX_PATH
];
1468 /* build a complete path to create a simple pidl */
1469 lstrcpynW(szPath
, sPathTarget
, MAX_PATH
);
1470 /*pathPtr = */PathAddBackslashW(szPath
);
1471 //hr = _ILCreateFromPathW(szPath, &pidl);
1472 hr
= this->ParseDisplayName(NULL
, NULL
, szPath
, NULL
, &pidl
, NULL
);
1477 hr
= this->BindToObject(pidl
, NULL
, IID_IDropTarget
, (LPVOID
*)&pDT
);
1478 CoTaskMemFree(pidl
);
1480 SHSimulateDrop(pDT
, pDataObject
, dwKeyState
, NULL
, pdwEffect
);
1482 ERR("Error Binding");
1485 ERR("Error creating from %s\n", debugstr_w(szPath
));
1488 /* Todo, rewrite the registry such that the icons are well placed.
1489 Blocked by no bags implementation. */