2 * Virtual Workplace folder
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2009 Andrew Hill
7 * Copyright 2017-2018 Katayama Hirofumi MZ
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 WINE_DEFAULT_DEBUG_CHANNEL (shell
);
29 CDrivesFolder should create a CRegFolder to represent the virtual items that exist only in
30 the registry. The CRegFolder is aggregated by the CDrivesFolder.
31 The CDrivesFolderEnum class should enumerate only drives on the system. Since the CRegFolder
32 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
33 CDrivesFolderEnum is only responsible for returning the physical items.
35 2. At least on my XP system, the drive pidls returned are of type PT_DRIVE1, not PT_DRIVE
36 3. The parsing name returned for my computer is incorrect. It should be "My Computer"
39 static int iDriveIconIds
[7] = { IDI_SHELL_DRIVE
, /* DRIVE_UNKNOWN */
40 IDI_SHELL_CDROM
, /* DRIVE_NO_ROOT_DIR*/
41 IDI_SHELL_3_14_FLOPPY
, /* DRIVE_REMOVABLE*/
42 IDI_SHELL_DRIVE
, /* DRIVE_FIXED*/
43 IDI_SHELL_NETDRIVE
, /* DRIVE_REMOTE*/
44 IDI_SHELL_CDROM
, /* DRIVE_CDROM*/
45 IDI_SHELL_RAMDISK
/* DRIVE_RAMDISK*/
48 static int iDriveTypeIds
[7] = { IDS_DRIVE_FIXED
, /* DRIVE_UNKNOWN */
49 IDS_DRIVE_FIXED
, /* DRIVE_NO_ROOT_DIR*/
50 IDS_DRIVE_FLOPPY
, /* DRIVE_REMOVABLE*/
51 IDS_DRIVE_FIXED
, /* DRIVE_FIXED*/
52 IDS_DRIVE_NETWORK
, /* DRIVE_REMOTE*/
53 IDS_DRIVE_CDROM
, /* DRIVE_CDROM*/
54 IDS_DRIVE_FIXED
/* DRIVE_RAMDISK*/
57 /***********************************************************************
58 * IShellFolder implementation
62 #define RETRY_SLEEP 250
63 static BOOL
TryToLockOrUnlockDrive(HANDLE hDrive
, BOOL bLock
)
65 DWORD dwError
, dwBytesReturned
;
66 DWORD dwCode
= (bLock
? FSCTL_LOCK_VOLUME
: FSCTL_UNLOCK_VOLUME
);
67 for (DWORD i
= 0; i
< RETRY_COUNT
; ++i
)
69 if (DeviceIoControl(hDrive
, dwCode
, NULL
, 0, NULL
, 0, &dwBytesReturned
, NULL
))
72 dwError
= GetLastError();
73 if (dwError
== ERROR_INVALID_FUNCTION
)
74 break; /* don't sleep if function is not implemented */
78 SetLastError(dwError
);
82 // NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp
83 static BOOL
DoEjectDrive(const WCHAR
*physical
, UINT nDriveType
, INT
*pnStringID
)
85 /* GENERIC_WRITE isn't needed for umount */
86 DWORD dwAccessMode
= GENERIC_READ
;
87 DWORD dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
89 HANDLE hDrive
= CreateFile(physical
, dwAccessMode
, dwShareMode
, 0, OPEN_EXISTING
, 0, NULL
);
90 if (hDrive
== INVALID_HANDLE_VALUE
)
93 BOOL bResult
, bNeedUnlock
= FALSE
;
94 DWORD dwBytesReturned
, dwError
= NO_ERROR
;
95 PREVENT_MEDIA_REMOVAL removal
;
98 bResult
= TryToLockOrUnlockDrive(hDrive
, TRUE
);
101 dwError
= GetLastError();
102 *pnStringID
= IDS_CANTLOCKVOLUME
; /* Unable to lock volume */
105 bResult
= DeviceIoControl(hDrive
, FSCTL_DISMOUNT_VOLUME
, NULL
, 0, NULL
, 0, &dwBytesReturned
, NULL
);
108 dwError
= GetLastError();
109 *pnStringID
= IDS_CANTDISMOUNTVOLUME
; /* Unable to dismount volume */
113 removal
.PreventMediaRemoval
= FALSE
;
114 bResult
= DeviceIoControl(hDrive
, IOCTL_STORAGE_MEDIA_REMOVAL
, &removal
, sizeof(removal
), NULL
,
115 0, &dwBytesReturned
, NULL
);
118 *pnStringID
= IDS_CANTEJECTMEDIA
; /* Unable to eject media */
119 dwError
= GetLastError();
123 bResult
= DeviceIoControl(hDrive
, IOCTL_STORAGE_EJECT_MEDIA
, NULL
, 0, NULL
, 0, &dwBytesReturned
, NULL
);
126 *pnStringID
= IDS_CANTEJECTMEDIA
; /* Unable to eject media */
127 dwError
= GetLastError();
135 TryToLockOrUnlockDrive(hDrive
, FALSE
);
140 SetLastError(dwError
);
144 HRESULT CALLBACK
DrivesContextMenuCallback(IShellFolder
*psf
,
151 if (uMsg
!= DFM_MERGECONTEXTMENU
&& uMsg
!= DFM_INVOKECOMMAND
)
154 PIDLIST_ABSOLUTE pidlFolder
;
155 PUITEMID_CHILD
*apidl
;
159 HRESULT hr
= SH_GetApidlFromDataObject(pdtobj
, &pidlFolder
, &apidl
, &cidl
);
160 if (FAILED_UNEXPECTEDLY(hr
))
163 char szDrive
[8] = {0};
164 if (!_ILGetDrive(apidl
[0], szDrive
, sizeof(szDrive
)))
166 ERR("pidl is not a drive\n");
168 _ILFreeaPidl(apidl
, cidl
);
171 nDriveType
= GetDriveTypeA(szDrive
);
172 GetVolumeInformationA(szDrive
, NULL
, 0, NULL
, NULL
, &dwFlags
, NULL
, 0);
174 // custom command IDs
175 #define CMDID_FORMAT 1
176 #define CMDID_EJECT 2
177 #define CMDID_DISCONNECT 3
179 if (uMsg
== DFM_MERGECONTEXTMENU
)
181 QCMINFO
*pqcminfo
= (QCMINFO
*)lParam
;
183 UINT idCmdFirst
= pqcminfo
->idCmdFirst
;
184 if (!(dwFlags
& FILE_READ_ONLY_VOLUME
) && nDriveType
!= DRIVE_REMOTE
)
186 /* add separator and Format */
187 UINT idCmd
= idCmdFirst
+ CMDID_FORMAT
;
188 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
189 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, idCmd
, MFT_STRING
, MAKEINTRESOURCEW(IDS_FORMATDRIVE
), MFS_ENABLED
);
191 if (nDriveType
== DRIVE_REMOVABLE
|| nDriveType
== DRIVE_CDROM
)
193 /* add separator and Eject */
194 UINT idCmd
= idCmdFirst
+ CMDID_EJECT
;
195 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
196 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, idCmd
, MFT_STRING
, MAKEINTRESOURCEW(IDS_EJECT
), MFS_ENABLED
);
198 if (nDriveType
== DRIVE_REMOTE
)
200 /* add separator and Disconnect */
201 UINT idCmd
= idCmdFirst
+ CMDID_DISCONNECT
;
202 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
203 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, idCmd
, MFT_STRING
, MAKEINTRESOURCEW(IDS_DISCONNECT
), MFS_ENABLED
);
206 pqcminfo
->idCmdFirst
+= 3;
208 else if (uMsg
== DFM_INVOKECOMMAND
)
210 WCHAR wszBuf
[4] = L
"A:\\";
211 wszBuf
[0] = (WCHAR
)szDrive
[0];
214 DWORD dwError
= NO_ERROR
;
216 if (wParam
== DFM_CMD_PROPERTIES
)
218 hr
= SH_ShowDriveProperties(wszBuf
, pidlFolder
, apidl
);
221 dwError
= ERROR_CAN_NOT_COMPLETE
;
222 nStringID
= IDS_CANTSHOWPROPERTIES
;
227 if (wParam
== CMDID_FORMAT
)
230 DWORD dwRet
= SHFormatDrive(hwnd
, szDrive
[0] - 'A', SHFMT_ID_DEFAULT
, 0);
233 case SHFMT_ERROR
: case SHFMT_CANCEL
: case SHFMT_NOFORMAT
:
238 else if (wParam
== CMDID_EJECT
)
242 wsprintfW(physical
, _T("\\\\.\\%c:"), szDrive
[0]);
244 if (DoEjectDrive(physical
, nDriveType
, &nStringID
))
246 SHChangeNotify(SHCNE_MEDIAREMOVED
, SHCNF_PATHW
| SHCNF_FLUSHNOWAIT
, wszBuf
, NULL
);
250 dwError
= GetLastError();
253 else if (wParam
== CMDID_DISCONNECT
)
256 wszBuf
[2] = UNICODE_NULL
;
257 dwError
= WNetCancelConnection2W(wszBuf
, 0, FALSE
);
258 if (dwError
== NO_ERROR
)
260 SHChangeNotify(SHCNE_DRIVEREMOVED
, SHCNF_PATHW
| SHCNF_FLUSHNOWAIT
, wszBuf
, NULL
);
264 nStringID
= IDS_CANTDISCONNECT
;
271 /* show error message */
272 WCHAR szFormat
[128], szMessage
[128];
273 LoadStringW(shell32_hInstance
, nStringID
, szFormat
, _countof(szFormat
));
274 wsprintfW(szMessage
, szFormat
, dwError
);
275 MessageBoxW(hwnd
, szMessage
, NULL
, MB_ICONERROR
);
280 _ILFreeaPidl(apidl
, cidl
);
285 HRESULT
CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder
,
288 PCUITEMID_CHILD_ARRAY apidl
,
294 AddClassKeyToArray(L
"Drive", hKeys
, &cKeys
);
295 AddClassKeyToArray(L
"Folder", hKeys
, &cKeys
);
297 return CDefFolderMenu_Create2(pidlFolder
, hwnd
, cidl
, apidl
, psf
, DrivesContextMenuCallback
, cKeys
, hKeys
, ppcm
);
301 getIconLocationForDrive(IShellFolder
*psf
, PCITEMID_CHILD pidl
, UINT uFlags
,
302 LPWSTR szIconFile
, UINT cchMax
, int *piIndex
, UINT
*pwFlags
)
304 WCHAR wszPath
[MAX_PATH
];
305 WCHAR wszAutoRunInfPath
[MAX_PATH
];
306 WCHAR wszValue
[MAX_PATH
], wszTemp
[MAX_PATH
];
307 static const WCHAR wszAutoRunInf
[] = { 'a','u','t','o','r','u','n','.','i','n','f',0 };
308 static const WCHAR wszAutoRun
[] = { 'a','u','t','o','r','u','n',0 };
311 if (!ILGetDisplayNameExW(psf
, pidl
, wszPath
, 0))
313 if (!PathIsDirectoryW(wszPath
))
316 // build the full path of autorun.inf
317 StringCchCopyW(wszAutoRunInfPath
, _countof(wszAutoRunInfPath
), wszPath
);
318 PathAppendW(wszAutoRunInfPath
, wszAutoRunInf
);
320 // autorun.inf --> wszValue
321 if (GetPrivateProfileStringW(wszAutoRun
, L
"icon", NULL
, wszValue
, _countof(wszValue
),
322 wszAutoRunInfPath
) && wszValue
[0] != 0)
324 // wszValue --> wszTemp
325 ExpandEnvironmentStringsW(wszValue
, wszTemp
, _countof(wszTemp
));
327 // parse the icon location
328 *piIndex
= PathParseIconLocationW(wszTemp
);
330 // wszPath + wszTemp --> wszPath
331 if (PathIsRelativeW(wszTemp
))
332 PathAppendW(wszPath
, wszTemp
);
334 StringCchCopyW(wszPath
, _countof(wszPath
), wszTemp
);
336 // wszPath --> szIconFile
337 GetFullPathNameW(wszPath
, cchMax
, szIconFile
, NULL
);
345 HRESULT
CDrivesExtractIcon_CreateInstance(IShellFolder
* psf
, LPCITEMIDLIST pidl
, REFIID riid
, LPVOID
* ppvOut
)
347 CComPtr
<IDefaultExtractIconInit
> initIcon
;
348 HRESULT hr
= SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit
, &initIcon
));
349 if (FAILED_UNEXPECTEDLY(hr
))
352 CHAR
* pszDrive
= _ILGetDataPointer(pidl
)->u
.drive
.szDriveName
;
353 UINT DriveType
= GetDriveTypeA(pszDrive
);
354 if (DriveType
> DRIVE_RAMDISK
)
355 DriveType
= DRIVE_FIXED
;
357 WCHAR wTemp
[MAX_PATH
];
360 if ((DriveType
== DRIVE_FIXED
|| DriveType
== DRIVE_UNKNOWN
) &&
361 (HCR_GetIconW(L
"Drive", wTemp
, NULL
, MAX_PATH
, &icon_idx
)))
363 initIcon
->SetNormalIcon(wTemp
, icon_idx
);
365 else if (SUCCEEDED(getIconLocationForDrive(psf
, pidl
, 0, wTemp
, _countof(wTemp
),
368 initIcon
->SetNormalIcon(wTemp
, icon_idx
);
372 icon_idx
= iDriveIconIds
[DriveType
];
373 initIcon
->SetNormalIcon(swShell32Name
, -icon_idx
);
376 return initIcon
->QueryInterface(riid
, ppvOut
);
379 class CDrivesFolderEnum
:
380 public CEnumIDListBase
383 HRESULT WINAPI
Initialize(HWND hwndOwner
, DWORD dwFlags
, IEnumIDList
* pRegEnumerator
)
385 /* enumerate the folders */
386 if (dwFlags
& SHCONTF_FOLDERS
)
388 WCHAR wszDriveName
[] = {'A', ':', '\\', '\0'};
389 DWORD dwDrivemap
= GetLogicalDrives();
391 while (wszDriveName
[0] <= 'Z')
393 if(dwDrivemap
& 0x00000001L
)
394 AddToEnumList(_ILCreateDrive(wszDriveName
));
396 dwDrivemap
= dwDrivemap
>> 1;
400 /* Enumerate the items of the reg folder */
401 AppendItemsFromEnumerator(pRegEnumerator
);
406 BEGIN_COM_MAP(CDrivesFolderEnum
)
407 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
411 /***********************************************************************
412 * IShellFolder [MyComputer] implementation
415 static const shvheader MyComputerSFHeader
[] = {
416 {IDS_SHV_COLUMN_NAME
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 15},
417 {IDS_SHV_COLUMN_COMMENTS
, SHCOLSTATE_TYPE_STR
, LVCFMT_LEFT
, 10},
418 {IDS_SHV_COLUMN_TYPE
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 10},
419 {IDS_SHV_COLUMN_DISK_CAPACITY
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 10},
420 {IDS_SHV_COLUMN_DISK_AVAILABLE
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 10},
423 #define MYCOMPUTERSHELLVIEWCOLUMNS 5
425 static const DWORD dwComputerAttributes
=
426 SFGAO_CANRENAME
| SFGAO_CANDELETE
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
|
427 SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
;
428 static const DWORD dwControlPanelAttributes
=
429 SFGAO_HASSUBFOLDER
| SFGAO_FOLDER
| SFGAO_CANLINK
;
430 static const DWORD dwDriveAttributes
=
431 SFGAO_HASSUBFOLDER
| SFGAO_FILESYSTEM
| SFGAO_FOLDER
| SFGAO_FILESYSANCESTOR
|
432 SFGAO_DROPTARGET
| SFGAO_HASPROPSHEET
| SFGAO_CANRENAME
| SFGAO_CANLINK
;
434 CDrivesFolder::CDrivesFolder()
439 CDrivesFolder::~CDrivesFolder()
441 TRACE ("-- destroying IShellFolder(%p)\n", this);
445 HRESULT WINAPI
CDrivesFolder::FinalConstruct()
447 pidlRoot
= _ILCreateMyComputer(); /* my qualified pidl */
448 if (pidlRoot
== NULL
)
449 return E_OUTOFMEMORY
;
451 HRESULT hr
= CRegFolder_CreateInstance(&CLSID_MyComputer
,
453 L
"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
455 IID_PPV_ARG(IShellFolder2
, &m_regFolder
));
460 /**************************************************************************
461 * CDrivesFolder::ParseDisplayName
463 HRESULT WINAPI
CDrivesFolder::ParseDisplayName(HWND hwndOwner
, LPBC pbc
, LPOLESTR lpszDisplayName
,
464 DWORD
* pchEaten
, PIDLIST_RELATIVE
* ppidl
, DWORD
* pdwAttributes
)
466 HRESULT hr
= E_INVALIDARG
;
467 LPCWSTR szNext
= NULL
;
468 LPITEMIDLIST pidlTemp
= NULL
;
470 TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
471 hwndOwner
, pbc
, lpszDisplayName
, debugstr_w (lpszDisplayName
),
472 pchEaten
, ppidl
, pdwAttributes
);
476 *pchEaten
= 0; /* strange but like the original */
478 /* handle CLSID paths */
479 if (lpszDisplayName
[0] == ':' && lpszDisplayName
[1] == ':')
480 return m_regFolder
->ParseDisplayName(hwndOwner
, pbc
, lpszDisplayName
, pchEaten
, ppidl
, pdwAttributes
);
482 if (PathGetDriveNumberW(lpszDisplayName
) < 0)
485 pidlTemp
= _ILCreateDrive(lpszDisplayName
);
487 return E_OUTOFMEMORY
;
489 if (lpszDisplayName
[2] == L
'\\')
491 szNext
= &lpszDisplayName
[3];
494 if (szNext
&& *szNext
)
496 hr
= SHELL32_ParseNextElement (this, hwndOwner
, pbc
, &pidlTemp
,
497 (LPOLESTR
) szNext
, pchEaten
, pdwAttributes
);
502 if (pdwAttributes
&& *pdwAttributes
)
504 if (_ILIsDrive(pidlTemp
))
505 *pdwAttributes
&= dwDriveAttributes
;
506 else if (_ILIsSpecialFolder(pidlTemp
))
507 m_regFolder
->GetAttributesOf(1, &pidlTemp
, pdwAttributes
);
509 ERR("Got an unkown pidl here!\n");
515 TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr
);
520 /**************************************************************************
521 * CDrivesFolder::EnumObjects
523 HRESULT WINAPI
CDrivesFolder::EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
)
525 CComPtr
<IEnumIDList
> pRegEnumerator
;
526 m_regFolder
->EnumObjects(hwndOwner
, dwFlags
, &pRegEnumerator
);
528 return ShellObjectCreatorInit
<CDrivesFolderEnum
>(hwndOwner
, dwFlags
, pRegEnumerator
, IID_PPV_ARG(IEnumIDList
, ppEnumIDList
));
531 /**************************************************************************
532 * CDrivesFolder::BindToObject
534 HRESULT WINAPI
CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
536 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
537 pidl
, pbcReserved
, shdebugstr_guid(&riid
), ppvOut
);
539 if (_ILIsSpecialFolder(pidl
))
540 return m_regFolder
->BindToObject(pidl
, pbcReserved
, riid
, ppvOut
);
542 CHAR
* pchDrive
= _ILGetDataPointer(pidl
)->u
.drive
.szDriveName
;
544 PERSIST_FOLDER_TARGET_INFO pfti
= {0};
545 pfti
.dwAttributes
= -1;
547 pfti
.szTargetParsingName
[0] = *pchDrive
;
548 pfti
.szTargetParsingName
[1] = L
':';
549 pfti
.szTargetParsingName
[2] = L
'\\';
551 HRESULT hr
= SHELL32_BindToSF(pidlRoot
,
554 &CLSID_ShellFSFolder
,
557 if (FAILED_UNEXPECTEDLY(hr
))
563 /**************************************************************************
564 * CDrivesFolder::BindToStorage
566 HRESULT WINAPI
CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
568 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
569 pidl
, pbcReserved
, shdebugstr_guid (&riid
), ppvOut
);
575 /**************************************************************************
576 * CDrivesFolder::CompareIDs
579 HRESULT WINAPI
CDrivesFolder::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
583 if (!pidl1
|| !pidl2
)
585 ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam
, pidl1
, pidl2
);
589 if (_ILIsSpecialFolder(pidl1
) || _ILIsSpecialFolder(pidl2
))
590 return m_regFolder
->CompareIDs(lParam
, pidl1
, pidl2
);
592 if (!_ILIsDrive(pidl1
) || !_ILIsDrive(pidl2
) || LOWORD(lParam
) >= MYCOMPUTERSHELLVIEWCOLUMNS
)
595 CHAR
* pszDrive1
= _ILGetDataPointer(pidl1
)->u
.drive
.szDriveName
;
596 CHAR
* pszDrive2
= _ILGetDataPointer(pidl2
)->u
.drive
.szDriveName
;
599 switch(LOWORD(lParam
))
603 result
= stricmp(pszDrive1
, pszDrive2
);
604 hres
= MAKE_COMPARE_HRESULT(result
);
607 case 1: /* comments */
608 hres
= MAKE_COMPARE_HRESULT(0);
612 /* We want to return immediately because SHELL32_CompareDetails also compares children. */
613 return SHELL32_CompareDetails(this, lParam
, pidl1
, pidl2
);
616 case 4: /* Size Available */
618 ULARGE_INTEGER Drive1Available
, Drive1Total
, Drive2Available
, Drive2Total
;
620 if (GetVolumeInformationA(pszDrive1
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, 0))
621 GetDiskFreeSpaceExA(pszDrive1
, &Drive1Available
, &Drive1Total
, NULL
);
623 Drive1Available
.QuadPart
= Drive1Total
.QuadPart
= 0;
625 if (GetVolumeInformationA(pszDrive2
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, 0))
626 GetDiskFreeSpaceExA(pszDrive2
, &Drive2Available
, &Drive2Total
, NULL
);
628 Drive2Available
.QuadPart
= Drive2Total
.QuadPart
= 0;
631 if (lParam
== 3) /* Size */
632 Diff
.QuadPart
= Drive1Total
.QuadPart
- Drive2Total
.QuadPart
;
633 else /* Size available */
634 Diff
.QuadPart
= Drive1Available
.QuadPart
- Drive2Available
.QuadPart
;
636 hres
= MAKE_COMPARE_HRESULT(Diff
.QuadPart
);
643 if (HRESULT_CODE(hres
) == 0)
644 return SHELL32_CompareChildren(this, lParam
, pidl1
, pidl2
);
649 /**************************************************************************
650 * CDrivesFolder::CreateViewObject
652 HRESULT WINAPI
CDrivesFolder::CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
* ppvOut
)
654 CComPtr
<IShellView
> pShellView
;
655 HRESULT hr
= E_INVALIDARG
;
657 TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
658 hwndOwner
, shdebugstr_guid (&riid
), ppvOut
);
665 if (IsEqualIID(riid
, IID_IDropTarget
))
667 WARN("IDropTarget not implemented\n");
670 else if (IsEqualIID(riid
, IID_IContextMenu
))
674 AddClassKeyToArray(L
"Directory\\Background", hKeys
, &cKeys
);
677 dcm
.hwnd
= hwndOwner
;
679 dcm
.pidlFolder
= pidlRoot
;
685 dcm
.punkAssociationInfo
= NULL
;
686 hr
= SHCreateDefaultContextMenu(&dcm
, riid
, ppvOut
);
688 else if (IsEqualIID(riid
, IID_IShellView
))
690 SFV_CREATE sfvparams
= {sizeof(SFV_CREATE
), this};
691 hr
= SHCreateShellFolderView(&sfvparams
, (IShellView
**)ppvOut
);
693 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut
);
697 static BOOL
_ILIsControlPanel(LPCITEMIDLIST pidl
)
699 GUID
*guid
= _ILGetGUIDPointer(pidl
);
701 TRACE("(%p)\n", pidl
);
704 return IsEqualIID(*guid
, CLSID_ControlPanel
);
708 /**************************************************************************
709 * CDrivesFolder::GetAttributesOf
711 HRESULT WINAPI
CDrivesFolder::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
* rgfInOut
)
713 TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
714 this, cidl
, apidl
, rgfInOut
, rgfInOut
? *rgfInOut
: 0);
722 /* FIXME: always add SFGAO_CANLINK */
724 *rgfInOut
&= dwComputerAttributes
;
727 for (UINT i
= 0; i
< cidl
; ++i
)
729 if (_ILIsDrive(apidl
[i
]))
730 *rgfInOut
&= dwDriveAttributes
;
731 else if (_ILIsControlPanel(apidl
[i
]))
732 *rgfInOut
&= dwControlPanelAttributes
;
733 else if (_ILIsSpecialFolder(*apidl
))
734 m_regFolder
->GetAttributesOf(1, &apidl
[i
], rgfInOut
);
736 ERR("Got unknown pidl type!\n");
740 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
741 *rgfInOut
&= ~SFGAO_VALIDATE
;
743 TRACE ("-- result=0x%08x\n", *rgfInOut
);
747 /**************************************************************************
748 * CDrivesFolder::GetUIObjectOf
751 * hwndOwner [in] Parent window for any output
752 * cidl [in] array size
753 * apidl [in] simple pidl array
754 * riid [in] Requested Interface
755 * prgfInOut [ ] reserved
756 * ppvObject [out] Resulting Interface
759 HRESULT WINAPI
CDrivesFolder::GetUIObjectOf(HWND hwndOwner
,
760 UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
,
761 REFIID riid
, UINT
*prgfInOut
, LPVOID
*ppvOut
)
764 HRESULT hr
= E_INVALIDARG
;
766 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
767 hwndOwner
, cidl
, apidl
, shdebugstr_guid (&riid
), prgfInOut
, ppvOut
);
774 if (IsEqualIID (riid
, IID_IContextMenu
) && (cidl
>= 1))
776 if (_ILIsDrive(apidl
[0]))
777 hr
= CDrivesContextMenu_CreateInstance(pidlRoot
, hwndOwner
, cidl
, apidl
, static_cast<IShellFolder
*>(this), (IContextMenu
**)&pObj
);
779 hr
= m_regFolder
->GetUIObjectOf(hwndOwner
, cidl
, apidl
, riid
, prgfInOut
, &pObj
);
781 else if (IsEqualIID (riid
, IID_IDataObject
) && (cidl
>= 1))
783 hr
= IDataObject_Constructor (hwndOwner
,
784 pidlRoot
, apidl
, cidl
, (IDataObject
**)&pObj
);
786 else if ((IsEqualIID (riid
, IID_IExtractIconA
) || IsEqualIID (riid
, IID_IExtractIconW
)) && (cidl
== 1))
788 if (_ILIsDrive(apidl
[0]))
789 hr
= CDrivesExtractIcon_CreateInstance(this, apidl
[0], riid
, &pObj
);
791 hr
= m_regFolder
->GetUIObjectOf(hwndOwner
, cidl
, apidl
, riid
, prgfInOut
, &pObj
);
793 else if (IsEqualIID (riid
, IID_IDropTarget
) && (cidl
== 1))
795 CComPtr
<IShellFolder
> psfChild
;
796 hr
= this->BindToObject(apidl
[0], NULL
, IID_PPV_ARG(IShellFolder
, &psfChild
));
797 if (FAILED_UNEXPECTEDLY(hr
))
800 return psfChild
->CreateViewObject(NULL
, riid
, ppvOut
);
805 if (SUCCEEDED(hr
) && !pObj
)
809 TRACE ("(%p)->hr=0x%08x\n", this, hr
);
813 /**************************************************************************
814 * CDrivesFolder::GetDisplayNameOf
816 HRESULT WINAPI
CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET strRet
)
821 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl
, dwFlags
, strRet
);
827 if (!_ILIsPidlSimple (pidl
))
829 return SHELL32_GetDisplayNameOfChild(this, pidl
, dwFlags
, strRet
);
831 else if (_ILIsSpecialFolder(pidl
))
833 return m_regFolder
->GetDisplayNameOf(pidl
, dwFlags
, strRet
);
835 else if (!_ILIsDrive(pidl
))
837 ERR("Wrong pidl type\n");
841 pszPath
= (LPWSTR
)CoTaskMemAlloc((MAX_PATH
+ 1) * sizeof(WCHAR
));
843 return E_OUTOFMEMORY
;
847 _ILSimpleGetTextW(pidl
, pszPath
, MAX_PATH
); /* append my own path */
848 /* long view "lw_name (C:)" */
849 if (!(dwFlags
& SHGDN_FORPARSING
))
851 WCHAR wszDrive
[18] = {0};
852 DWORD dwVolumeSerialNumber
, dwMaximumComponentLength
, dwFileSystemFlags
;
853 static const WCHAR wszOpenBracket
[] = {' ', '(', 0};
854 static const WCHAR wszCloseBracket
[] = {')', 0};
856 lstrcpynW(wszDrive
, pszPath
, 4);
858 GetVolumeInformationW(wszDrive
, pszPath
,
860 &dwVolumeSerialNumber
,
861 &dwMaximumComponentLength
, &dwFileSystemFlags
, NULL
, 0);
862 pszPath
[MAX_PATH
-1] = L
'\0';
863 if (!wcslen(pszPath
))
865 UINT DriveType
, ResourceId
;
866 DriveType
= GetDriveTypeW(wszDrive
);
870 ResourceId
= IDS_DRIVE_FIXED
;
873 ResourceId
= IDS_DRIVE_NETWORK
;
876 ResourceId
= IDS_DRIVE_CDROM
;
883 dwFileSystemFlags
= LoadStringW(shell32_hInstance
, ResourceId
, pszPath
, MAX_PATH
);
884 if (dwFileSystemFlags
> MAX_PATH
- 7)
885 pszPath
[MAX_PATH
-7] = L
'\0';
888 wcscat (pszPath
, wszOpenBracket
);
890 wcscat (pszPath
, wszDrive
);
891 wcscat (pszPath
, wszCloseBracket
);
896 strRet
->uType
= STRRET_WSTR
;
897 strRet
->pOleStr
= pszPath
;
900 CoTaskMemFree(pszPath
);
902 TRACE("-- (%p)->(%s)\n", this, strRet
->uType
== STRRET_CSTR
? strRet
->cStr
: debugstr_w(strRet
->pOleStr
));
906 /**************************************************************************
907 * CDrivesFolder::SetNameOf
908 * Changes the name of a file object or subfolder, possibly changing its item
909 * identifier in the process.
912 * hwndOwner [in] Owner window for output
913 * pidl [in] simple pidl of item to change
914 * lpszName [in] the items new display name
915 * dwFlags [in] SHGNO formatting flags
916 * ppidlOut [out] simple pidl returned
918 HRESULT WINAPI
CDrivesFolder::SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
,
919 LPCOLESTR lpName
, DWORD dwFlags
, PITEMID_CHILD
*pPidlOut
)
923 if (_ILIsDrive(pidl
))
925 if (_ILSimpleGetTextW(pidl
, szName
, _countof(szName
)))
926 SetVolumeLabelW(szName
, lpName
);
928 *pPidlOut
= _ILCreateDrive(szName
);
932 return m_regFolder
->SetNameOf(hwndOwner
, pidl
, lpName
, dwFlags
, pPidlOut
);
935 HRESULT WINAPI
CDrivesFolder::GetDefaultSearchGUID(GUID
* pguid
)
937 FIXME ("(%p)\n", this);
941 HRESULT WINAPI
CDrivesFolder::EnumSearches(IEnumExtraSearch
** ppenum
)
943 FIXME ("(%p)\n", this);
947 HRESULT WINAPI
CDrivesFolder::GetDefaultColumn (DWORD dwRes
, ULONG
*pSort
, ULONG
*pDisplay
)
949 TRACE ("(%p)\n", this);
958 HRESULT WINAPI
CDrivesFolder::GetDefaultColumnState(UINT iColumn
, DWORD
* pcsFlags
)
960 TRACE ("(%p)\n", this);
962 if (!pcsFlags
|| iColumn
>= MYCOMPUTERSHELLVIEWCOLUMNS
)
964 *pcsFlags
= MyComputerSFHeader
[iColumn
].pcsFlags
;
968 HRESULT WINAPI
CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
* pscid
, VARIANT
* pv
)
970 FIXME ("(%p)\n", this);
974 HRESULT WINAPI
CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*psd
)
978 TRACE ("(%p)->(%p %i %p)\n", this, pidl
, iColumn
, psd
);
980 if (!psd
|| iColumn
>= MYCOMPUTERSHELLVIEWCOLUMNS
)
985 psd
->fmt
= MyComputerSFHeader
[iColumn
].fmt
;
986 psd
->cxChar
= MyComputerSFHeader
[iColumn
].cxChar
;
987 return SHSetStrRet(&psd
->str
, MyComputerSFHeader
[iColumn
].colnameid
);
989 else if (!_ILIsDrive(pidl
))
991 return m_regFolder
->GetDetailsOf(pidl
, iColumn
, psd
);
995 ULARGE_INTEGER ulTotalBytes
, ulFreeBytes
;
996 CHAR
* pszDrive
= _ILGetDataPointer(pidl
)->u
.drive
.szDriveName
;
997 UINT DriveType
= GetDriveTypeA(pszDrive
);
998 if (DriveType
> DRIVE_RAMDISK
)
999 DriveType
= DRIVE_FIXED
;
1004 hr
= GetDisplayNameOf(pidl
, SHGDN_NORMAL
| SHGDN_INFOLDER
, &psd
->str
);
1006 case 1: /* FIXME: comments */
1007 hr
= SHSetStrRet(&psd
->str
, "");
1010 hr
= SHSetStrRet(&psd
->str
, iDriveTypeIds
[DriveType
]);
1012 case 3: /* total size */
1013 case 4: /* free size */
1014 psd
->str
.cStr
[0] = 0x00;
1015 psd
->str
.uType
= STRRET_CSTR
;
1016 if (GetVolumeInformationA(pszDrive
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, 0))
1018 GetDiskFreeSpaceExA(pszDrive
, &ulFreeBytes
, &ulTotalBytes
, NULL
);
1020 StrFormatByteSize64A(ulTotalBytes
.QuadPart
, psd
->str
.cStr
, MAX_PATH
);
1022 StrFormatByteSize64A(ulFreeBytes
.QuadPart
, psd
->str
.cStr
, MAX_PATH
);
1032 HRESULT WINAPI
CDrivesFolder::MapColumnToSCID(UINT column
, SHCOLUMNID
* pscid
)
1034 FIXME("(%p)\n", this);
1038 /************************************************************************
1039 * CDrivesFolder::GetClassID
1041 HRESULT WINAPI
CDrivesFolder::GetClassID(CLSID
*lpClassId
)
1043 TRACE ("(%p)\n", this);
1048 *lpClassId
= CLSID_MyComputer
;
1052 /************************************************************************
1053 * CDrivesFolder::Initialize
1055 * NOTES: it makes no sense to change the pidl
1057 HRESULT WINAPI
CDrivesFolder::Initialize(LPCITEMIDLIST pidl
)
1062 /**************************************************************************
1063 * CDrivesFolder::GetCurFolder
1065 HRESULT WINAPI
CDrivesFolder::GetCurFolder(LPITEMIDLIST
*pidl
)
1067 TRACE("(%p)->(%p)\n", this, pidl
);
1070 return E_INVALIDARG
; /* xp doesn't have this check and crashes on NULL */
1072 *pidl
= ILClone(pidlRoot
);
1076 /************************************************************************/
1077 /* IContextMenuCB interface */
1079 HRESULT WINAPI
CDrivesFolder::CallBack(IShellFolder
*psf
, HWND hwndOwner
, IDataObject
*pdtobj
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1081 if (uMsg
!= DFM_MERGECONTEXTMENU
&& uMsg
!= DFM_INVOKECOMMAND
)
1084 /* no data object means no selection */
1087 if (uMsg
== DFM_INVOKECOMMAND
&& wParam
== 1) // #1
1089 // "System" properties
1090 ShellExecuteW(hwndOwner
, NULL
, L
"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL
, NULL
, SW_SHOWNORMAL
);
1092 else if (uMsg
== DFM_MERGECONTEXTMENU
)
1094 QCMINFO
*pqcminfo
= (QCMINFO
*)lParam
;
1095 HMENU hpopup
= CreatePopupMenu();
1096 _InsertMenuItemW(hpopup
, 0, TRUE
, 0, MFT_SEPARATOR
, NULL
, MFS_ENABLED
); // #0
1097 _InsertMenuItemW(hpopup
, 1, TRUE
, 1, MFT_STRING
, MAKEINTRESOURCEW(IDS_PROPERTIES
), MFS_ENABLED
); // #1
1098 Shell_MergeMenus(pqcminfo
->hmenu
, hpopup
, pqcminfo
->indexMenu
++, pqcminfo
->idCmdFirst
, pqcminfo
->idCmdLast
, MM_ADDSEPARATOR
);
1099 DestroyMenu(hpopup
);
1105 if (uMsg
!= DFM_INVOKECOMMAND
|| wParam
!= DFM_CMD_PROPERTIES
)
1108 return Shell_DefaultContextMenuCallBack(this, pdtobj
);