2 * Virtual Workplace folder
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2009 Andrew Hill
7 * Copyright 2017-2019 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
27 WINE_DEFAULT_DEBUG_CHANNEL (shell
);
30 CDrivesFolder should create a CRegFolder to represent the virtual items that exist only in
31 the registry. The CRegFolder is aggregated by the CDrivesFolder.
32 The CDrivesFolderEnum class should enumerate only drives on the system. Since the CRegFolder
33 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
34 CDrivesFolderEnum is only responsible for returning the physical items.
36 2. At least on my XP system, the drive pidls returned are of type PT_DRIVE1, not PT_DRIVE
37 3. The parsing name returned for my computer is incorrect. It should be "My Computer"
40 static int iDriveIconIds
[7] = { IDI_SHELL_DRIVE
, /* DRIVE_UNKNOWN */
41 IDI_SHELL_CDROM
, /* DRIVE_NO_ROOT_DIR*/
42 IDI_SHELL_3_14_FLOPPY
, /* DRIVE_REMOVABLE*/
43 IDI_SHELL_DRIVE
, /* DRIVE_FIXED*/
44 IDI_SHELL_NETDRIVE
, /* DRIVE_REMOTE*/
45 IDI_SHELL_CDROM
, /* DRIVE_CDROM*/
46 IDI_SHELL_RAMDISK
/* DRIVE_RAMDISK*/
49 static int iDriveTypeIds
[7] = { IDS_DRIVE_FIXED
, /* DRIVE_UNKNOWN */
50 IDS_DRIVE_FIXED
, /* DRIVE_NO_ROOT_DIR*/
51 IDS_DRIVE_FLOPPY
, /* DRIVE_REMOVABLE*/
52 IDS_DRIVE_FIXED
, /* DRIVE_FIXED*/
53 IDS_DRIVE_NETWORK
, /* DRIVE_REMOTE*/
54 IDS_DRIVE_CDROM
, /* DRIVE_CDROM*/
55 IDS_DRIVE_FIXED
/* DRIVE_RAMDISK*/
58 /***********************************************************************
59 * IShellFolder implementation
63 #define RETRY_SLEEP 250
64 static BOOL
TryToLockOrUnlockDrive(HANDLE hDrive
, BOOL bLock
)
66 DWORD dwError
, dwBytesReturned
;
67 DWORD dwCode
= (bLock
? FSCTL_LOCK_VOLUME
: FSCTL_UNLOCK_VOLUME
);
68 for (DWORD i
= 0; i
< RETRY_COUNT
; ++i
)
70 if (DeviceIoControl(hDrive
, dwCode
, NULL
, 0, NULL
, 0, &dwBytesReturned
, NULL
))
73 dwError
= GetLastError();
74 if (dwError
== ERROR_INVALID_FUNCTION
)
75 break; /* don't sleep if function is not implemented */
79 SetLastError(dwError
);
83 // NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp
84 static BOOL
DoEjectDrive(const WCHAR
*physical
, UINT nDriveType
, INT
*pnStringID
)
86 /* GENERIC_WRITE isn't needed for umount */
87 DWORD dwAccessMode
= GENERIC_READ
;
88 DWORD dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
90 HANDLE hDrive
= CreateFile(physical
, dwAccessMode
, dwShareMode
, 0, OPEN_EXISTING
, 0, NULL
);
91 if (hDrive
== INVALID_HANDLE_VALUE
)
94 BOOL bResult
, bNeedUnlock
= FALSE
;
95 DWORD dwBytesReturned
, dwError
= NO_ERROR
;
96 PREVENT_MEDIA_REMOVAL removal
;
99 bResult
= TryToLockOrUnlockDrive(hDrive
, TRUE
);
102 dwError
= GetLastError();
103 *pnStringID
= IDS_CANTLOCKVOLUME
; /* Unable to lock volume */
106 bResult
= DeviceIoControl(hDrive
, FSCTL_DISMOUNT_VOLUME
, NULL
, 0, NULL
, 0, &dwBytesReturned
, NULL
);
109 dwError
= GetLastError();
110 *pnStringID
= IDS_CANTDISMOUNTVOLUME
; /* Unable to dismount volume */
114 removal
.PreventMediaRemoval
= FALSE
;
115 bResult
= DeviceIoControl(hDrive
, IOCTL_STORAGE_MEDIA_REMOVAL
, &removal
, sizeof(removal
), NULL
,
116 0, &dwBytesReturned
, NULL
);
119 *pnStringID
= IDS_CANTEJECTMEDIA
; /* Unable to eject media */
120 dwError
= GetLastError();
124 bResult
= DeviceIoControl(hDrive
, IOCTL_STORAGE_EJECT_MEDIA
, NULL
, 0, NULL
, 0, &dwBytesReturned
, NULL
);
127 *pnStringID
= IDS_CANTEJECTMEDIA
; /* Unable to eject media */
128 dwError
= GetLastError();
136 TryToLockOrUnlockDrive(hDrive
, FALSE
);
141 SetLastError(dwError
);
145 // A callback function for finding the stub windows.
147 EnumStubProc(HWND hwnd
, LPARAM lParam
)
149 CSimpleArray
<HWND
> *pStubs
= reinterpret_cast<CSimpleArray
<HWND
> *>(lParam
);
152 GetClassNameW(hwnd
, szClass
, _countof(szClass
));
154 if (lstrcmpiW(szClass
, L
"StubWindow32") == 0)
162 // Another callback function to find the owned window of the stub window.
164 EnumStubProc2(HWND hwnd
, LPARAM lParam
)
166 HWND
*phwnd
= reinterpret_cast<HWND
*>(lParam
);
168 if (phwnd
[0] == GetWindow(hwnd
, GW_OWNER
))
177 // Parameters for format_drive_thread function below.
183 static unsigned __stdcall
format_drive_thread(void *args
)
185 THREAD_PARAMS
*params
= (THREAD_PARAMS
*)args
;
186 UINT nDriveNumber
= params
->nDriveNumber
;
187 LONG_PTR nProp
= nDriveNumber
| 0x7F00;
189 // Search the stub windows that already exist.
190 CSimpleArray
<HWND
> old_stubs
;
191 EnumWindows(EnumStubProc
, (LPARAM
)&old_stubs
);
193 for (INT n
= 0; n
< old_stubs
.GetSize(); ++n
)
195 HWND hwndStub
= old_stubs
[n
];
197 // The target stub window has the prop.
198 if (GetPropW(hwndStub
, L
"DriveNumber") == (HANDLE
)nProp
)
204 EnumWindows(EnumStubProc2
, (LPARAM
)ahwnd
);
207 BringWindowToTop(ahwnd
[1]);
214 // Create a stub window.
215 DWORD style
= WS_DISABLED
| WS_CLIPSIBLINGS
| WS_CAPTION
;
216 DWORD exstyle
= WS_EX_WINDOWEDGE
| WS_EX_APPWINDOW
;
218 if (!stub
.Create(NULL
, NULL
, NULL
, style
, exstyle
))
220 ERR("StubWindow32 creation failed\n");
225 // Add prop to the target stub window.
226 SetPropW(stub
, L
"DriveNumber", (HANDLE
)nProp
);
229 SHFormatDrive(stub
, nDriveNumber
, SHFMT_ID_DEFAULT
, 0);
232 RemovePropW(stub
, L
"DriveNumber");
233 stub
.DestroyWindow();
239 static HRESULT
DoFormatDrive(HWND hwnd
, UINT nDriveNumber
)
241 THREAD_PARAMS
*params
= new THREAD_PARAMS
;
242 params
->nDriveNumber
= nDriveNumber
;
244 // Create thread to avoid locked.
246 HANDLE hThread
= (HANDLE
)_beginthreadex(NULL
, 0, format_drive_thread
, params
, 0, &tid
);
253 CloseHandle(hThread
);
258 HRESULT CALLBACK
DrivesContextMenuCallback(IShellFolder
*psf
,
265 if (uMsg
!= DFM_MERGECONTEXTMENU
&& uMsg
!= DFM_INVOKECOMMAND
)
268 PIDLIST_ABSOLUTE pidlFolder
;
269 PUITEMID_CHILD
*apidl
;
273 HRESULT hr
= SH_GetApidlFromDataObject(pdtobj
, &pidlFolder
, &apidl
, &cidl
);
274 if (FAILED_UNEXPECTEDLY(hr
))
277 char szDrive
[8] = {0};
278 if (!_ILGetDrive(apidl
[0], szDrive
, sizeof(szDrive
)))
280 ERR("pidl is not a drive\n");
282 _ILFreeaPidl(apidl
, cidl
);
285 nDriveType
= GetDriveTypeA(szDrive
);
286 GetVolumeInformationA(szDrive
, NULL
, 0, NULL
, NULL
, &dwFlags
, NULL
, 0);
288 // custom command IDs
289 #define CMDID_FORMAT 1
290 #define CMDID_EJECT 2
291 #define CMDID_DISCONNECT 3
293 if (uMsg
== DFM_MERGECONTEXTMENU
)
295 QCMINFO
*pqcminfo
= (QCMINFO
*)lParam
;
297 UINT idCmdFirst
= pqcminfo
->idCmdFirst
;
298 if (!(dwFlags
& FILE_READ_ONLY_VOLUME
) && nDriveType
!= DRIVE_REMOTE
)
300 /* add separator and Format */
301 UINT idCmd
= idCmdFirst
+ CMDID_FORMAT
;
302 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
303 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, idCmd
, MFT_STRING
, MAKEINTRESOURCEW(IDS_FORMATDRIVE
), MFS_ENABLED
);
305 if (nDriveType
== DRIVE_REMOVABLE
|| nDriveType
== DRIVE_CDROM
)
307 /* add separator and Eject */
308 UINT idCmd
= idCmdFirst
+ CMDID_EJECT
;
309 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
310 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, idCmd
, MFT_STRING
, MAKEINTRESOURCEW(IDS_EJECT
), MFS_ENABLED
);
312 if (nDriveType
== DRIVE_REMOTE
)
314 /* add separator and Disconnect */
315 UINT idCmd
= idCmdFirst
+ CMDID_DISCONNECT
;
316 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, 0, MFT_SEPARATOR
, NULL
, 0);
317 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, idCmd
, MFT_STRING
, MAKEINTRESOURCEW(IDS_DISCONNECT
), MFS_ENABLED
);
320 pqcminfo
->idCmdFirst
+= 3;
322 else if (uMsg
== DFM_INVOKECOMMAND
)
324 WCHAR wszBuf
[4] = L
"A:\\";
325 wszBuf
[0] = (WCHAR
)szDrive
[0];
328 DWORD dwError
= NO_ERROR
;
330 if (wParam
== DFM_CMD_PROPERTIES
)
332 hr
= SH_ShowDriveProperties(wszBuf
, pidlFolder
, apidl
);
335 dwError
= ERROR_CAN_NOT_COMPLETE
;
336 nStringID
= IDS_CANTSHOWPROPERTIES
;
341 if (wParam
== CMDID_FORMAT
)
343 hr
= DoFormatDrive(hwnd
, szDrive
[0] - 'A');
345 else if (wParam
== CMDID_EJECT
)
349 wsprintfW(physical
, _T("\\\\.\\%c:"), szDrive
[0]);
351 if (DoEjectDrive(physical
, nDriveType
, &nStringID
))
353 SHChangeNotify(SHCNE_MEDIAREMOVED
, SHCNF_PATHW
| SHCNF_FLUSHNOWAIT
, wszBuf
, NULL
);
357 dwError
= GetLastError();
360 else if (wParam
== CMDID_DISCONNECT
)
363 wszBuf
[2] = UNICODE_NULL
;
364 dwError
= WNetCancelConnection2W(wszBuf
, 0, FALSE
);
365 if (dwError
== NO_ERROR
)
367 SHChangeNotify(SHCNE_DRIVEREMOVED
, SHCNF_PATHW
| SHCNF_FLUSHNOWAIT
, wszBuf
, NULL
);
371 nStringID
= IDS_CANTDISCONNECT
;
378 /* show error message */
379 WCHAR szFormat
[128], szMessage
[128];
380 LoadStringW(shell32_hInstance
, nStringID
, szFormat
, _countof(szFormat
));
381 wsprintfW(szMessage
, szFormat
, dwError
);
382 MessageBoxW(hwnd
, szMessage
, NULL
, MB_ICONERROR
);
387 _ILFreeaPidl(apidl
, cidl
);
392 HRESULT
CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder
,
395 PCUITEMID_CHILD_ARRAY apidl
,
401 AddClassKeyToArray(L
"Drive", hKeys
, &cKeys
);
402 AddClassKeyToArray(L
"Folder", hKeys
, &cKeys
);
404 return CDefFolderMenu_Create2(pidlFolder
, hwnd
, cidl
, apidl
, psf
, DrivesContextMenuCallback
, cKeys
, hKeys
, ppcm
);
408 getIconLocationForDrive(IShellFolder
*psf
, PCITEMID_CHILD pidl
, UINT uFlags
,
409 LPWSTR szIconFile
, UINT cchMax
, int *piIndex
, UINT
*pwFlags
)
411 WCHAR wszPath
[MAX_PATH
];
412 WCHAR wszAutoRunInfPath
[MAX_PATH
];
413 WCHAR wszValue
[MAX_PATH
], wszTemp
[MAX_PATH
];
414 static const WCHAR wszAutoRunInf
[] = { 'a','u','t','o','r','u','n','.','i','n','f',0 };
415 static const WCHAR wszAutoRun
[] = { 'a','u','t','o','r','u','n',0 };
418 if (!ILGetDisplayNameExW(psf
, pidl
, wszPath
, 0))
420 if (!PathIsDirectoryW(wszPath
))
423 // build the full path of autorun.inf
424 StringCchCopyW(wszAutoRunInfPath
, _countof(wszAutoRunInfPath
), wszPath
);
425 PathAppendW(wszAutoRunInfPath
, wszAutoRunInf
);
427 // autorun.inf --> wszValue
428 if (GetPrivateProfileStringW(wszAutoRun
, L
"icon", NULL
, wszValue
, _countof(wszValue
),
429 wszAutoRunInfPath
) && wszValue
[0] != 0)
431 // wszValue --> wszTemp
432 ExpandEnvironmentStringsW(wszValue
, wszTemp
, _countof(wszTemp
));
434 // parse the icon location
435 *piIndex
= PathParseIconLocationW(wszTemp
);
437 // wszPath + wszTemp --> wszPath
438 if (PathIsRelativeW(wszTemp
))
439 PathAppendW(wszPath
, wszTemp
);
441 StringCchCopyW(wszPath
, _countof(wszPath
), wszTemp
);
443 // wszPath --> szIconFile
444 GetFullPathNameW(wszPath
, cchMax
, szIconFile
, NULL
);
452 BOOL
IsDriveFloppyA(LPCSTR pszDriveRoot
);
454 HRESULT
CDrivesExtractIcon_CreateInstance(IShellFolder
* psf
, LPCITEMIDLIST pidl
, REFIID riid
, LPVOID
* ppvOut
)
456 CComPtr
<IDefaultExtractIconInit
> initIcon
;
457 HRESULT hr
= SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit
, &initIcon
));
458 if (FAILED_UNEXPECTEDLY(hr
))
461 CHAR
* pszDrive
= _ILGetDataPointer(pidl
)->u
.drive
.szDriveName
;
462 UINT DriveType
= GetDriveTypeA(pszDrive
);
463 if (DriveType
> DRIVE_RAMDISK
)
464 DriveType
= DRIVE_FIXED
;
466 WCHAR wTemp
[MAX_PATH
];
469 if ((DriveType
== DRIVE_FIXED
|| DriveType
== DRIVE_UNKNOWN
) &&
470 (HCR_GetIconW(L
"Drive", wTemp
, NULL
, MAX_PATH
, &icon_idx
)))
472 initIcon
->SetNormalIcon(wTemp
, icon_idx
);
474 else if (SUCCEEDED(getIconLocationForDrive(psf
, pidl
, 0, wTemp
, _countof(wTemp
),
477 initIcon
->SetNormalIcon(wTemp
, icon_idx
);
481 if (DriveType
== DRIVE_REMOVABLE
&& !IsDriveFloppyA(pszDrive
))
483 icon_idx
= IDI_SHELL_REMOVEABLE
;
487 icon_idx
= iDriveIconIds
[DriveType
];
489 initIcon
->SetNormalIcon(swShell32Name
, -icon_idx
);
492 return initIcon
->QueryInterface(riid
, ppvOut
);
495 class CDrivesFolderEnum
:
496 public CEnumIDListBase
499 HRESULT WINAPI
Initialize(HWND hwndOwner
, DWORD dwFlags
, IEnumIDList
* pRegEnumerator
)
501 /* enumerate the folders */
502 if (dwFlags
& SHCONTF_FOLDERS
)
504 WCHAR wszDriveName
[] = {'A', ':', '\\', '\0'};
505 DWORD dwDrivemap
= GetLogicalDrives();
507 while (wszDriveName
[0] <= 'Z')
509 if(dwDrivemap
& 0x00000001L
)
510 AddToEnumList(_ILCreateDrive(wszDriveName
));
512 dwDrivemap
= dwDrivemap
>> 1;
516 /* Enumerate the items of the reg folder */
517 AppendItemsFromEnumerator(pRegEnumerator
);
522 BEGIN_COM_MAP(CDrivesFolderEnum
)
523 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
527 /***********************************************************************
528 * IShellFolder [MyComputer] implementation
531 static const shvheader MyComputerSFHeader
[] = {
532 {IDS_SHV_COLUMN_NAME
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 15},
533 {IDS_SHV_COLUMN_COMMENTS
, SHCOLSTATE_TYPE_STR
, LVCFMT_LEFT
, 10},
534 {IDS_SHV_COLUMN_TYPE
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 10},
535 {IDS_SHV_COLUMN_DISK_CAPACITY
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 10},
536 {IDS_SHV_COLUMN_DISK_AVAILABLE
, SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 10},
539 #define MYCOMPUTERSHELLVIEWCOLUMNS 5
541 static const DWORD dwComputerAttributes
=
542 SFGAO_CANRENAME
| SFGAO_CANDELETE
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
|
543 SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
;
544 static const DWORD dwControlPanelAttributes
=
545 SFGAO_HASSUBFOLDER
| SFGAO_FOLDER
| SFGAO_CANLINK
;
546 static const DWORD dwDriveAttributes
=
547 SFGAO_HASSUBFOLDER
| SFGAO_FILESYSTEM
| SFGAO_FOLDER
| SFGAO_FILESYSANCESTOR
|
548 SFGAO_DROPTARGET
| SFGAO_HASPROPSHEET
| SFGAO_CANRENAME
| SFGAO_CANLINK
;
550 CDrivesFolder::CDrivesFolder()
555 CDrivesFolder::~CDrivesFolder()
557 TRACE ("-- destroying IShellFolder(%p)\n", this);
561 HRESULT WINAPI
CDrivesFolder::FinalConstruct()
563 pidlRoot
= _ILCreateMyComputer(); /* my qualified pidl */
564 if (pidlRoot
== NULL
)
565 return E_OUTOFMEMORY
;
567 HRESULT hr
= CRegFolder_CreateInstance(&CLSID_MyComputer
,
569 L
"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
571 IID_PPV_ARG(IShellFolder2
, &m_regFolder
));
576 /**************************************************************************
577 * CDrivesFolder::ParseDisplayName
579 HRESULT WINAPI
CDrivesFolder::ParseDisplayName(HWND hwndOwner
, LPBC pbc
, LPOLESTR lpszDisplayName
,
580 DWORD
* pchEaten
, PIDLIST_RELATIVE
* ppidl
, DWORD
* pdwAttributes
)
582 HRESULT hr
= E_INVALIDARG
;
583 LPCWSTR szNext
= NULL
;
584 LPITEMIDLIST pidlTemp
= NULL
;
587 TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
588 hwndOwner
, pbc
, lpszDisplayName
, debugstr_w (lpszDisplayName
),
589 pchEaten
, ppidl
, pdwAttributes
);
593 *pchEaten
= 0; /* strange but like the original */
595 /* handle CLSID paths */
596 if (lpszDisplayName
[0] == ':' && lpszDisplayName
[1] == ':')
597 return m_regFolder
->ParseDisplayName(hwndOwner
, pbc
, lpszDisplayName
, pchEaten
, ppidl
, pdwAttributes
);
599 nDriveNumber
= PathGetDriveNumberW(lpszDisplayName
);
600 if (nDriveNumber
< 0)
603 /* check if this drive actually exists */
604 if ((::GetLogicalDrives() & (1 << nDriveNumber
)) == 0)
606 return HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE
);
609 pidlTemp
= _ILCreateDrive(lpszDisplayName
);
611 return E_OUTOFMEMORY
;
613 if (lpszDisplayName
[2] == L
'\\')
615 szNext
= &lpszDisplayName
[3];
618 if (szNext
&& *szNext
)
620 hr
= SHELL32_ParseNextElement (this, hwndOwner
, pbc
, &pidlTemp
,
621 (LPOLESTR
) szNext
, pchEaten
, pdwAttributes
);
626 if (pdwAttributes
&& *pdwAttributes
)
628 if (_ILIsDrive(pidlTemp
))
629 *pdwAttributes
&= dwDriveAttributes
;
630 else if (_ILIsSpecialFolder(pidlTemp
))
631 m_regFolder
->GetAttributesOf(1, &pidlTemp
, pdwAttributes
);
633 ERR("Got an unkown pidl here!\n");
639 TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr
);
644 /**************************************************************************
645 * CDrivesFolder::EnumObjects
647 HRESULT WINAPI
CDrivesFolder::EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
)
649 CComPtr
<IEnumIDList
> pRegEnumerator
;
650 m_regFolder
->EnumObjects(hwndOwner
, dwFlags
, &pRegEnumerator
);
652 return ShellObjectCreatorInit
<CDrivesFolderEnum
>(hwndOwner
, dwFlags
, pRegEnumerator
, IID_PPV_ARG(IEnumIDList
, ppEnumIDList
));
655 /**************************************************************************
656 * CDrivesFolder::BindToObject
658 HRESULT WINAPI
CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
660 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
661 pidl
, pbcReserved
, shdebugstr_guid(&riid
), ppvOut
);
666 if (_ILIsSpecialFolder(pidl
))
667 return m_regFolder
->BindToObject(pidl
, pbcReserved
, riid
, ppvOut
);
669 CHAR
* pchDrive
= _ILGetDataPointer(pidl
)->u
.drive
.szDriveName
;
671 PERSIST_FOLDER_TARGET_INFO pfti
= {0};
672 pfti
.dwAttributes
= -1;
674 pfti
.szTargetParsingName
[0] = *pchDrive
;
675 pfti
.szTargetParsingName
[1] = L
':';
676 pfti
.szTargetParsingName
[2] = L
'\\';
678 HRESULT hr
= SHELL32_BindToSF(pidlRoot
,
681 &CLSID_ShellFSFolder
,
684 if (FAILED_UNEXPECTEDLY(hr
))
690 /**************************************************************************
691 * CDrivesFolder::BindToStorage
693 HRESULT WINAPI
CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
695 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
696 pidl
, pbcReserved
, shdebugstr_guid (&riid
), ppvOut
);
702 /**************************************************************************
703 * CDrivesFolder::CompareIDs
706 HRESULT WINAPI
CDrivesFolder::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
710 if (!pidl1
|| !pidl2
)
712 ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam
, pidl1
, pidl2
);
716 if (_ILIsSpecialFolder(pidl1
) || _ILIsSpecialFolder(pidl2
))
717 return m_regFolder
->CompareIDs(lParam
, pidl1
, pidl2
);
719 if (!_ILIsDrive(pidl1
) || !_ILIsDrive(pidl2
) || LOWORD(lParam
) >= MYCOMPUTERSHELLVIEWCOLUMNS
)
722 CHAR
* pszDrive1
= _ILGetDataPointer(pidl1
)->u
.drive
.szDriveName
;
723 CHAR
* pszDrive2
= _ILGetDataPointer(pidl2
)->u
.drive
.szDriveName
;
726 switch(LOWORD(lParam
))
730 result
= stricmp(pszDrive1
, pszDrive2
);
731 hres
= MAKE_COMPARE_HRESULT(result
);
734 case 1: /* comments */
735 hres
= MAKE_COMPARE_HRESULT(0);
739 /* We want to return immediately because SHELL32_CompareDetails also compares children. */
740 return SHELL32_CompareDetails(this, lParam
, pidl1
, pidl2
);
743 case 4: /* Size Available */
745 ULARGE_INTEGER Drive1Available
, Drive1Total
, Drive2Available
, Drive2Total
;
747 if (GetVolumeInformationA(pszDrive1
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, 0))
748 GetDiskFreeSpaceExA(pszDrive1
, &Drive1Available
, &Drive1Total
, NULL
);
750 Drive1Available
.QuadPart
= Drive1Total
.QuadPart
= 0;
752 if (GetVolumeInformationA(pszDrive2
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, 0))
753 GetDiskFreeSpaceExA(pszDrive2
, &Drive2Available
, &Drive2Total
, NULL
);
755 Drive2Available
.QuadPart
= Drive2Total
.QuadPart
= 0;
758 if (lParam
== 3) /* Size */
759 Diff
.QuadPart
= Drive1Total
.QuadPart
- Drive2Total
.QuadPart
;
760 else /* Size available */
761 Diff
.QuadPart
= Drive1Available
.QuadPart
- Drive2Available
.QuadPart
;
763 hres
= MAKE_COMPARE_HRESULT(Diff
.QuadPart
);
770 if (HRESULT_CODE(hres
) == 0)
771 return SHELL32_CompareChildren(this, lParam
, pidl1
, pidl2
);
776 /**************************************************************************
777 * CDrivesFolder::CreateViewObject
779 HRESULT WINAPI
CDrivesFolder::CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
* ppvOut
)
781 CComPtr
<IShellView
> pShellView
;
782 HRESULT hr
= E_INVALIDARG
;
784 TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
785 hwndOwner
, shdebugstr_guid (&riid
), ppvOut
);
792 if (IsEqualIID(riid
, IID_IDropTarget
))
794 WARN("IDropTarget not implemented\n");
797 else if (IsEqualIID(riid
, IID_IContextMenu
))
801 AddClassKeyToArray(L
"Directory\\Background", hKeys
, &cKeys
);
804 dcm
.hwnd
= hwndOwner
;
806 dcm
.pidlFolder
= pidlRoot
;
812 dcm
.punkAssociationInfo
= NULL
;
813 hr
= SHCreateDefaultContextMenu(&dcm
, riid
, ppvOut
);
815 else if (IsEqualIID(riid
, IID_IShellView
))
817 SFV_CREATE sfvparams
= {sizeof(SFV_CREATE
), this};
818 hr
= SHCreateShellFolderView(&sfvparams
, (IShellView
**)ppvOut
);
820 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut
);
824 static BOOL
_ILIsControlPanel(LPCITEMIDLIST pidl
)
826 GUID
*guid
= _ILGetGUIDPointer(pidl
);
828 TRACE("(%p)\n", pidl
);
831 return IsEqualIID(*guid
, CLSID_ControlPanel
);
835 /**************************************************************************
836 * CDrivesFolder::GetAttributesOf
838 HRESULT WINAPI
CDrivesFolder::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
* rgfInOut
)
840 TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
841 this, cidl
, apidl
, rgfInOut
, rgfInOut
? *rgfInOut
: 0);
849 /* FIXME: always add SFGAO_CANLINK */
851 *rgfInOut
&= dwComputerAttributes
;
854 for (UINT i
= 0; i
< cidl
; ++i
)
856 if (_ILIsDrive(apidl
[i
]))
857 *rgfInOut
&= dwDriveAttributes
;
858 else if (_ILIsControlPanel(apidl
[i
]))
859 *rgfInOut
&= dwControlPanelAttributes
;
860 else if (_ILIsSpecialFolder(*apidl
))
861 m_regFolder
->GetAttributesOf(1, &apidl
[i
], rgfInOut
);
863 ERR("Got unknown pidl type!\n");
867 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
868 *rgfInOut
&= ~SFGAO_VALIDATE
;
870 TRACE ("-- result=0x%08x\n", *rgfInOut
);
874 /**************************************************************************
875 * CDrivesFolder::GetUIObjectOf
878 * hwndOwner [in] Parent window for any output
879 * cidl [in] array size
880 * apidl [in] simple pidl array
881 * riid [in] Requested Interface
882 * prgfInOut [ ] reserved
883 * ppvObject [out] Resulting Interface
886 HRESULT WINAPI
CDrivesFolder::GetUIObjectOf(HWND hwndOwner
,
887 UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
,
888 REFIID riid
, UINT
*prgfInOut
, LPVOID
*ppvOut
)
891 HRESULT hr
= E_INVALIDARG
;
893 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
894 hwndOwner
, cidl
, apidl
, shdebugstr_guid (&riid
), prgfInOut
, ppvOut
);
901 if (IsEqualIID (riid
, IID_IContextMenu
) && (cidl
>= 1))
903 if (_ILIsDrive(apidl
[0]))
904 hr
= CDrivesContextMenu_CreateInstance(pidlRoot
, hwndOwner
, cidl
, apidl
, static_cast<IShellFolder
*>(this), (IContextMenu
**)&pObj
);
906 hr
= m_regFolder
->GetUIObjectOf(hwndOwner
, cidl
, apidl
, riid
, prgfInOut
, &pObj
);
908 else if (IsEqualIID (riid
, IID_IDataObject
) && (cidl
>= 1))
910 hr
= IDataObject_Constructor (hwndOwner
,
911 pidlRoot
, apidl
, cidl
, TRUE
, (IDataObject
**)&pObj
);
913 else if ((IsEqualIID (riid
, IID_IExtractIconA
) || IsEqualIID (riid
, IID_IExtractIconW
)) && (cidl
== 1))
915 if (_ILIsDrive(apidl
[0]))
916 hr
= CDrivesExtractIcon_CreateInstance(this, apidl
[0], riid
, &pObj
);
918 hr
= m_regFolder
->GetUIObjectOf(hwndOwner
, cidl
, apidl
, riid
, prgfInOut
, &pObj
);
920 else if (IsEqualIID (riid
, IID_IDropTarget
) && (cidl
== 1))
922 CComPtr
<IShellFolder
> psfChild
;
923 hr
= this->BindToObject(apidl
[0], NULL
, IID_PPV_ARG(IShellFolder
, &psfChild
));
924 if (FAILED_UNEXPECTEDLY(hr
))
927 return psfChild
->CreateViewObject(NULL
, riid
, ppvOut
);
932 if (SUCCEEDED(hr
) && !pObj
)
936 TRACE ("(%p)->hr=0x%08x\n", this, hr
);
940 /**************************************************************************
941 * CDrivesFolder::GetDisplayNameOf
943 HRESULT WINAPI
CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET strRet
)
948 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl
, dwFlags
, strRet
);
954 if (!_ILIsPidlSimple (pidl
))
956 return SHELL32_GetDisplayNameOfChild(this, pidl
, dwFlags
, strRet
);
958 else if (_ILIsSpecialFolder(pidl
))
960 return m_regFolder
->GetDisplayNameOf(pidl
, dwFlags
, strRet
);
962 else if (!_ILIsDrive(pidl
))
964 ERR("Wrong pidl type\n");
968 pszPath
= (LPWSTR
)CoTaskMemAlloc((MAX_PATH
+ 1) * sizeof(WCHAR
));
970 return E_OUTOFMEMORY
;
974 _ILSimpleGetTextW(pidl
, pszPath
, MAX_PATH
); /* append my own path */
975 /* long view "lw_name (C:)" */
976 if (!(dwFlags
& SHGDN_FORPARSING
))
978 WCHAR wszDrive
[18] = {0};
979 DWORD dwVolumeSerialNumber
, dwMaximumComponentLength
, dwFileSystemFlags
;
980 static const WCHAR wszOpenBracket
[] = {' ', '(', 0};
981 static const WCHAR wszCloseBracket
[] = {')', 0};
983 lstrcpynW(wszDrive
, pszPath
, 4);
985 GetVolumeInformationW(wszDrive
, pszPath
,
987 &dwVolumeSerialNumber
,
988 &dwMaximumComponentLength
, &dwFileSystemFlags
, NULL
, 0);
989 pszPath
[MAX_PATH
-1] = L
'\0';
990 if (!wcslen(pszPath
))
992 UINT DriveType
, ResourceId
;
993 DriveType
= GetDriveTypeW(wszDrive
);
997 ResourceId
= IDS_DRIVE_FIXED
;
1000 ResourceId
= IDS_DRIVE_NETWORK
;
1003 ResourceId
= IDS_DRIVE_CDROM
;
1010 dwFileSystemFlags
= LoadStringW(shell32_hInstance
, ResourceId
, pszPath
, MAX_PATH
);
1011 if (dwFileSystemFlags
> MAX_PATH
- 7)
1012 pszPath
[MAX_PATH
-7] = L
'\0';
1015 wcscat (pszPath
, wszOpenBracket
);
1016 wszDrive
[2] = L
'\0';
1017 wcscat (pszPath
, wszDrive
);
1018 wcscat (pszPath
, wszCloseBracket
);
1023 strRet
->uType
= STRRET_WSTR
;
1024 strRet
->pOleStr
= pszPath
;
1027 CoTaskMemFree(pszPath
);
1029 TRACE("-- (%p)->(%s)\n", this, strRet
->uType
== STRRET_CSTR
? strRet
->cStr
: debugstr_w(strRet
->pOleStr
));
1033 /**************************************************************************
1034 * CDrivesFolder::SetNameOf
1035 * Changes the name of a file object or subfolder, possibly changing its item
1036 * identifier in the process.
1039 * hwndOwner [in] Owner window for output
1040 * pidl [in] simple pidl of item to change
1041 * lpszName [in] the items new display name
1042 * dwFlags [in] SHGNO formatting flags
1043 * ppidlOut [out] simple pidl returned
1045 HRESULT WINAPI
CDrivesFolder::SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
,
1046 LPCOLESTR lpName
, DWORD dwFlags
, PITEMID_CHILD
*pPidlOut
)
1050 if (_ILIsDrive(pidl
))
1052 if (_ILSimpleGetTextW(pidl
, szName
, _countof(szName
)))
1053 SetVolumeLabelW(szName
, lpName
);
1055 *pPidlOut
= _ILCreateDrive(szName
);
1059 return m_regFolder
->SetNameOf(hwndOwner
, pidl
, lpName
, dwFlags
, pPidlOut
);
1062 HRESULT WINAPI
CDrivesFolder::GetDefaultSearchGUID(GUID
* pguid
)
1064 FIXME ("(%p)\n", this);
1068 HRESULT WINAPI
CDrivesFolder::EnumSearches(IEnumExtraSearch
** ppenum
)
1070 FIXME ("(%p)\n", this);
1074 HRESULT WINAPI
CDrivesFolder::GetDefaultColumn (DWORD dwRes
, ULONG
*pSort
, ULONG
*pDisplay
)
1076 TRACE ("(%p)\n", this);
1085 HRESULT WINAPI
CDrivesFolder::GetDefaultColumnState(UINT iColumn
, DWORD
* pcsFlags
)
1087 TRACE ("(%p)\n", this);
1089 if (!pcsFlags
|| iColumn
>= MYCOMPUTERSHELLVIEWCOLUMNS
)
1090 return E_INVALIDARG
;
1091 *pcsFlags
= MyComputerSFHeader
[iColumn
].pcsFlags
;
1095 HRESULT WINAPI
CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
* pscid
, VARIANT
* pv
)
1097 FIXME ("(%p)\n", this);
1101 HRESULT WINAPI
CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*psd
)
1105 TRACE ("(%p)->(%p %i %p)\n", this, pidl
, iColumn
, psd
);
1107 if (!psd
|| iColumn
>= MYCOMPUTERSHELLVIEWCOLUMNS
)
1108 return E_INVALIDARG
;
1112 psd
->fmt
= MyComputerSFHeader
[iColumn
].fmt
;
1113 psd
->cxChar
= MyComputerSFHeader
[iColumn
].cxChar
;
1114 return SHSetStrRet(&psd
->str
, MyComputerSFHeader
[iColumn
].colnameid
);
1116 else if (!_ILIsDrive(pidl
))
1118 return m_regFolder
->GetDetailsOf(pidl
, iColumn
, psd
);
1122 ULARGE_INTEGER ulTotalBytes
, ulFreeBytes
;
1123 CHAR
* pszDrive
= _ILGetDataPointer(pidl
)->u
.drive
.szDriveName
;
1124 UINT DriveType
= GetDriveTypeA(pszDrive
);
1125 if (DriveType
> DRIVE_RAMDISK
)
1126 DriveType
= DRIVE_FIXED
;
1131 hr
= GetDisplayNameOf(pidl
, SHGDN_NORMAL
| SHGDN_INFOLDER
, &psd
->str
);
1133 case 1: /* FIXME: comments */
1134 hr
= SHSetStrRet(&psd
->str
, "");
1137 if (DriveType
== DRIVE_REMOVABLE
&& !IsDriveFloppyA(pszDrive
))
1138 hr
= SHSetStrRet(&psd
->str
, IDS_DRIVE_REMOVABLE
);
1140 hr
= SHSetStrRet(&psd
->str
, iDriveTypeIds
[DriveType
]);
1142 case 3: /* total size */
1143 case 4: /* free size */
1144 psd
->str
.cStr
[0] = 0x00;
1145 psd
->str
.uType
= STRRET_CSTR
;
1146 if (GetVolumeInformationA(pszDrive
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, 0))
1148 GetDiskFreeSpaceExA(pszDrive
, &ulFreeBytes
, &ulTotalBytes
, NULL
);
1150 StrFormatByteSize64A(ulTotalBytes
.QuadPart
, psd
->str
.cStr
, MAX_PATH
);
1152 StrFormatByteSize64A(ulFreeBytes
.QuadPart
, psd
->str
.cStr
, MAX_PATH
);
1162 HRESULT WINAPI
CDrivesFolder::MapColumnToSCID(UINT column
, SHCOLUMNID
* pscid
)
1164 FIXME("(%p)\n", this);
1168 /************************************************************************
1169 * CDrivesFolder::GetClassID
1171 HRESULT WINAPI
CDrivesFolder::GetClassID(CLSID
*lpClassId
)
1173 TRACE ("(%p)\n", this);
1178 *lpClassId
= CLSID_MyComputer
;
1182 /************************************************************************
1183 * CDrivesFolder::Initialize
1185 * NOTES: it makes no sense to change the pidl
1187 HRESULT WINAPI
CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl
)
1192 /**************************************************************************
1193 * CDrivesFolder::GetCurFolder
1195 HRESULT WINAPI
CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE
*pidl
)
1197 TRACE("(%p)->(%p)\n", this, pidl
);
1200 return E_INVALIDARG
; /* xp doesn't have this check and crashes on NULL */
1202 *pidl
= ILClone(pidlRoot
);
1206 /************************************************************************/
1207 /* IContextMenuCB interface */
1209 HRESULT WINAPI
CDrivesFolder::CallBack(IShellFolder
*psf
, HWND hwndOwner
, IDataObject
*pdtobj
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1211 if (uMsg
!= DFM_MERGECONTEXTMENU
&& uMsg
!= DFM_INVOKECOMMAND
)
1214 /* no data object means no selection */
1217 if (uMsg
== DFM_INVOKECOMMAND
&& wParam
== 1) // #1
1219 // "System" properties
1220 ShellExecuteW(hwndOwner
,
1223 L
"shell32.dll,Control_RunDLL sysdm.cpl",
1227 else if (uMsg
== DFM_MERGECONTEXTMENU
)
1229 QCMINFO
*pqcminfo
= (QCMINFO
*)lParam
;
1230 HMENU hpopup
= CreatePopupMenu();
1231 _InsertMenuItemW(hpopup
, 0, TRUE
, 0, MFT_SEPARATOR
, NULL
, MFS_ENABLED
); // #0
1232 _InsertMenuItemW(hpopup
, 1, TRUE
, 1, MFT_STRING
, MAKEINTRESOURCEW(IDS_PROPERTIES
), MFS_ENABLED
); // #1
1233 Shell_MergeMenus(pqcminfo
->hmenu
, hpopup
, pqcminfo
->indexMenu
++, pqcminfo
->idCmdFirst
, pqcminfo
->idCmdLast
, MM_ADDSEPARATOR
);
1234 DestroyMenu(hpopup
);
1240 if (uMsg
!= DFM_INVOKECOMMAND
|| wParam
!= DFM_CMD_PROPERTIES
)
1243 return Shell_DefaultContextMenuCallBack(this, pdtobj
);