1 /*
2  *    Virtual Workplace folder
3  *
4  *    Copyright 1997                Marcus Meissner
5  *    Copyright 1998, 1999, 2002    Juergen Schmied
6  *    Copyright 2009                Andrew Hill
7  *    Copyright 2017-2024           Katayama Hirofumi MZ
8  *
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.
13  *
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.
18  *
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
22  */
23 
24 #include <precomp.h>
25 #include <process.h>
26 
27 WINE_DEFAULT_DEBUG_CHANNEL(shell);
28 
29 /*
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.
35 
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"
38 */
39 
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*/
47                                 };
48 
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*/
56                                 };
57 
58 static const REQUIREDREGITEM g_RequiredItems[] =
59 {
60     { CLSID_ControlPanel, 0, 0x50 },
61 };
62 static const REGFOLDERINFO g_RegFolderInfo =
63 {
64     PT_COMPUTER_REGITEM,
65     _countof(g_RequiredItems), g_RequiredItems,
66     CLSID_MyComputer,
67     L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
68     L"MyComputer",
69 };
70 
71 static const CLSID* IsRegItem(PCUITEMID_CHILD pidl)
72 {
73     if (pidl && pidl->mkid.cb == 2 + 2 + sizeof(CLSID))
74     {
75         if (pidl->mkid.abID[0] == PT_SHELLEXT || pidl->mkid.abID[0] == PT_GUID) // FIXME: Remove PT_GUID when CRegFolder is fixed
76             return (const CLSID*)(&pidl->mkid.abID[2]);
77     }
78     return NULL;
79 }
80 
81 BOOL _ILGetDriveType(LPCITEMIDLIST pidl)
82 {
83     WCHAR szDrive[8];
84     if (!_ILGetDrive(pidl, szDrive, _countof(szDrive)))
85     {
86         ERR("pidl %p is not a drive\n", pidl);
87         return DRIVE_UNKNOWN;
88     }
89     return ::GetDriveTypeW(szDrive);
90 }
91 
92 BOOL SHELL32_IsShellFolderNamespaceItemHidden(LPCWSTR SubKey, REFCLSID Clsid)
93 {
94     // If this function returns true, the item should be hidden in DefView but not in the Explorer folder tree.
95     WCHAR path[MAX_PATH], name[CHARS_IN_GUID];
96     wsprintfW(path, L"%s\\%s", REGSTR_PATH_EXPLORER, SubKey);
97     SHELL32_GUIDToStringW(Clsid, name);
98     DWORD data = 0, size = sizeof(data);
99     return !RegGetValueW(HKEY_CURRENT_USER, path, name, RRF_RT_DWORD, NULL, &data, &size) && data;
100 }
101 
102 /***********************************************************************
103 *   IShellFolder implementation
104 */
105 
106 #define RETRY_COUNT 3
107 #define RETRY_SLEEP 250
108 static BOOL TryToLockOrUnlockDrive(HANDLE hDrive, BOOL bLock)
109 {
110     DWORD dwError, dwBytesReturned;
111     DWORD dwCode = (bLock ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME);
112     for (DWORD i = 0; i < RETRY_COUNT; ++i)
113     {
114         if (DeviceIoControl(hDrive, dwCode, NULL, 0, NULL, 0, &dwBytesReturned, NULL))
115             return TRUE;
116 
117         dwError = GetLastError();
118         if (dwError == ERROR_INVALID_FUNCTION)
119             break; /* don't sleep if function is not implemented */
120 
121         Sleep(RETRY_SLEEP);
122     }
123     SetLastError(dwError);
124     return FALSE;
125 }
126 
127 // NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp
128 static BOOL DoEjectDrive(const WCHAR *physical, UINT nDriveType, INT *pnStringID)
129 {
130     /* GENERIC_WRITE isn't needed for umount */
131     DWORD dwAccessMode = GENERIC_READ;
132     DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
133 
134     HANDLE hDrive = CreateFile(physical, dwAccessMode, dwShareMode, 0, OPEN_EXISTING, 0, NULL);
135     if (hDrive == INVALID_HANDLE_VALUE)
136         return FALSE;
137 
138     BOOL bResult, bNeedUnlock = FALSE;
139     DWORD dwBytesReturned, dwError = NO_ERROR;
140     PREVENT_MEDIA_REMOVAL removal;
141     do
142     {
143         bResult = TryToLockOrUnlockDrive(hDrive, TRUE);
144         if (!bResult)
145         {
146             dwError = GetLastError();
147             *pnStringID = IDS_CANTLOCKVOLUME; /* Unable to lock volume */
148             break;
149         }
150         bResult = DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
151         if (!bResult)
152         {
153             dwError = GetLastError();
154             *pnStringID = IDS_CANTDISMOUNTVOLUME; /* Unable to dismount volume */
155             bNeedUnlock = TRUE;
156             break;
157         }
158         removal.PreventMediaRemoval = FALSE;
159         bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_MEDIA_REMOVAL, &removal, sizeof(removal), NULL,
160                                   0, &dwBytesReturned, NULL);
161         if (!bResult)
162         {
163             *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
164             dwError = GetLastError();
165             bNeedUnlock = TRUE;
166             break;
167         }
168         bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
169         if (!bResult)
170         {
171             *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
172             dwError = GetLastError();
173             bNeedUnlock = TRUE;
174             break;
175         }
176     } while (0);
177 
178     if (bNeedUnlock)
179     {
180         TryToLockOrUnlockDrive(hDrive, FALSE);
181     }
182 
183     CloseHandle(hDrive);
184 
185     SetLastError(dwError);
186     return bResult;
187 }
188 
189 static DWORD CALLBACK DoFormatDriveThread(LPVOID args)
190 {
191     UINT nDrive = PtrToUlong(args);
192     WCHAR szPath[] = { LOWORD(L'A' + nDrive), L'\0' }; // Arbitrary, just needs to include nDrive
193     CStubWindow32 stub;
194     HRESULT hr = stub.CreateStub(CStubWindow32::TYPE_FORMATDRIVE, szPath, NULL);
195     if (FAILED(hr))
196         return hr;
197     SHFormatDrive(stub, nDrive, SHFMT_ID_DEFAULT, 0);
198     return stub.DestroyWindow();
199 }
200 
201 static HRESULT DoFormatDriveAsync(HWND hwnd, UINT nDrive)
202 {
203     BOOL succ = SHCreateThread(DoFormatDriveThread, UlongToPtr(nDrive), CTF_PROCESS_REF, NULL);
204     return succ ? S_OK : E_FAIL;
205 }
206 
207 HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf,
208                                            HWND         hwnd,
209                                            IDataObject  *pdtobj,
210                                            UINT         uMsg,
211                                            WPARAM       wParam,
212                                            LPARAM       lParam)
213 {
214     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
215         return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
216 
217     PIDLIST_ABSOLUTE pidlFolder;
218     PUITEMID_CHILD *apidl;
219     UINT cidl;
220     UINT nDriveType;
221     DWORD dwFlags;
222     HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
223     if (FAILED_UNEXPECTEDLY(hr))
224         return hr;
225 
226     WCHAR szDrive[8] = {0};
227     if (!_ILGetDrive(apidl[0], szDrive, _countof(szDrive)))
228     {
229         ERR("pidl is not a drive\n");
230         SHFree(pidlFolder);
231         _ILFreeaPidl(apidl, cidl);
232         return E_FAIL;
233     }
234     nDriveType = GetDriveTypeW(szDrive);
235     GetVolumeInformationW(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0);
236 
237 // custom command IDs
238 #if 0 // Disabled until our menu building system is fixed
239 #define CMDID_FORMAT        0
240 #define CMDID_EJECT         1
241 #define CMDID_DISCONNECT    2
242 #else
243 /* FIXME: These IDs should start from 0, however there is difference
244  * between ours and Windows' menu building systems, which should be fixed. */
245 #define CMDID_FORMAT        1
246 #define CMDID_EJECT         2
247 #define CMDID_DISCONNECT    3
248 #endif
249 
250     if (uMsg == DFM_MERGECONTEXTMENU)
251     {
252         QCMINFO *pqcminfo = (QCMINFO *)lParam;
253 
254         UINT idCmdFirst = pqcminfo->idCmdFirst;
255         UINT idCmd = 0;
256         if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE)
257         {
258             /* add separator and Format */
259             idCmd = idCmdFirst + CMDID_FORMAT;
260             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
261             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
262         }
263         if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM)
264         {
265             /* add separator and Eject */
266             idCmd = idCmdFirst + CMDID_EJECT;
267             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
268             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED);
269         }
270         if (nDriveType == DRIVE_REMOTE)
271         {
272             /* add separator and Disconnect */
273             idCmd = idCmdFirst + CMDID_DISCONNECT;
274             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
275             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED);
276         }
277 
278         if (idCmd)
279 #if 0 // see FIXME above
280             pqcminfo->idCmdFirst = ++idCmd;
281 #else
282             pqcminfo->idCmdFirst = (idCmd + 2);
283 #endif
284         hr = S_OK;
285     }
286     else if (uMsg == DFM_INVOKECOMMAND)
287     {
288         WCHAR wszBuf[4] = L"A:\\";
289         wszBuf[0] = (WCHAR)szDrive[0];
290 
291         INT nStringID = 0;
292         DWORD dwError = NO_ERROR;
293 
294         if (wParam == DFM_CMD_PROPERTIES)
295         {
296             ATLASSERT(pdtobj);
297             hr = SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj);
298             // Not setting nStringID because SHOpenPropSheet already displayed an error box
299         }
300         else
301         {
302             if (wParam == CMDID_FORMAT)
303             {
304                 hr = DoFormatDriveAsync(hwnd, szDrive[0] - 'A');
305             }
306             else if (wParam == CMDID_EJECT)
307             {
308                 /* do eject */
309                 WCHAR physical[10];
310                 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
311 
312                 if (DoEjectDrive(physical, nDriveType, &nStringID))
313                 {
314                     SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
315                 }
316                 else
317                 {
318                     dwError = GetLastError();
319                 }
320             }
321             else if (wParam == CMDID_DISCONNECT)
322             {
323                 /* do disconnect */
324                 wszBuf[2] = UNICODE_NULL;
325                 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
326                 if (dwError == NO_ERROR)
327                 {
328                     SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
329                 }
330                 else
331                 {
332                     nStringID = IDS_CANTDISCONNECT;
333                 }
334             }
335         }
336 
337         if (nStringID != 0)
338         {
339             /* show error message */
340             WCHAR szFormat[128], szMessage[128];
341             LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
342             wsprintfW(szMessage, szFormat, dwError);
343             MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
344         }
345     }
346 
347     SHFree(pidlFolder);
348     _ILFreeaPidl(apidl, cidl);
349 
350     return hr;
351 }
352 
353 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
354                                           HWND hwnd,
355                                           UINT cidl,
356                                           PCUITEMID_CHILD_ARRAY apidl,
357                                           IShellFolder *psf,
358                                           IContextMenu **ppcm)
359 {
360     HKEY hKeys[2];
361     UINT cKeys = 0;
362     AddClassKeyToArray(L"Drive", hKeys, &cKeys);
363     AddClassKeyToArray(L"Folder", hKeys, &cKeys);
364 
365     return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
366 }
367 
368 static HRESULT
369 getIconLocationForDrive(IShellFolder *psf, PCITEMID_CHILD pidl, UINT uFlags,
370                         LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
371 {
372     WCHAR wszPath[MAX_PATH];
373     WCHAR wszAutoRunInfPath[MAX_PATH];
374     WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
375 
376     // get path
377     if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
378         return E_FAIL;
379     if (!PathIsDirectoryW(wszPath))
380         return E_FAIL;
381 
382     // build the full path of autorun.inf
383     StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
384     PathAppendW(wszAutoRunInfPath, L"autorun.inf");
385 
386     // autorun.inf --> wszValue
387     if (GetPrivateProfileStringW(L"autorun", L"icon", NULL, wszValue, _countof(wszValue),
388                                  wszAutoRunInfPath) && wszValue[0] != 0)
389     {
390         // wszValue --> wszTemp
391         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
392 
393         // parse the icon location
394         *piIndex = PathParseIconLocationW(wszTemp);
395 
396         // wszPath + wszTemp --> wszPath
397         if (PathIsRelativeW(wszTemp))
398             PathAppendW(wszPath, wszTemp);
399         else
400             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
401 
402         // wszPath --> szIconFile
403         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
404 
405         return S_OK;
406     }
407 
408     return E_FAIL;
409 }
410 
411 static HRESULT
412 getLabelForDrive(LPWSTR wszPath, LPWSTR wszLabel)
413 {
414     WCHAR wszAutoRunInfPath[MAX_PATH];
415     WCHAR wszTemp[MAX_PATH];
416 
417     if (!PathIsDirectoryW(wszPath))
418         return E_FAIL;
419 
420     StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
421     PathAppendW(wszAutoRunInfPath, L"autorun.inf");
422 
423     if (GetPrivateProfileStringW(L"autorun", L"label", NULL, wszTemp, _countof(wszTemp),
424                                  wszAutoRunInfPath) && wszTemp[0] != 0)
425     {
426         StringCchCopyW(wszLabel, _countof(wszTemp), wszTemp);
427         return S_OK;
428     }
429 
430     return E_FAIL;
431 }
432 
433 BOOL IsDriveFloppyA(LPCSTR pszDriveRoot);
434 
435 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
436 {
437     CComPtr<IDefaultExtractIconInit> initIcon;
438     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
439     if (FAILED_UNEXPECTEDLY(hr))
440         return hr;
441 
442     CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
443     UINT DriveType = GetDriveTypeA(pszDrive);
444     if (DriveType > DRIVE_RAMDISK)
445         DriveType = DRIVE_FIXED;
446 
447     WCHAR wTemp[MAX_PATH];
448     int icon_idx, reg_idx;
449     UINT flags = 0;
450 
451     switch (DriveType)
452     {
453         case DRIVE_FIXED:
454         case DRIVE_UNKNOWN:
455             reg_idx = IDI_SHELL_DRIVE;
456             break;
457         case DRIVE_CDROM:
458             reg_idx = IDI_SHELL_CDROM;
459             break;
460         case DRIVE_REMOTE:
461             reg_idx = IDI_SHELL_NETDRIVE;
462             break;
463         case DRIVE_REMOVABLE:
464             if (!IsDriveFloppyA(pszDrive))
465                 reg_idx = IDI_SHELL_REMOVEABLE;
466             else
467                 reg_idx = IDI_SHELL_3_14_FLOPPY;
468             break;
469         case DRIVE_RAMDISK:
470             reg_idx = IDI_SHELL_RAMDISK;
471             break;
472         case DRIVE_NO_ROOT_DIR:
473         default:
474             reg_idx = IDI_SHELL_DOCUMENT;
475             break;
476     }
477 
478     hr = getIconLocationForDrive(psf, pidl, 0, wTemp, _countof(wTemp),
479                                  &icon_idx, &flags);
480     if (SUCCEEDED(hr))
481     {
482         initIcon->SetNormalIcon(wTemp, icon_idx);
483     }
484     else if (HLM_GetIconW(reg_idx - 1, wTemp, _countof(wTemp), &icon_idx))
485     {
486         initIcon->SetNormalIcon(wTemp, icon_idx);
487     }
488     else if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
489              (HCR_GetIconW(L"Drive", wTemp, NULL, _countof(wTemp), &icon_idx)))
490     {
491         initIcon->SetNormalIcon(wTemp, icon_idx);
492     }
493     else
494     {
495         if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
496         {
497             icon_idx = IDI_SHELL_REMOVEABLE;
498         }
499         else
500         {
501             icon_idx = iDriveIconIds[DriveType];
502         }
503         initIcon->SetNormalIcon(swShell32Name, -icon_idx);
504     }
505 
506     return initIcon->QueryInterface(riid, ppvOut);
507 }
508 
509 class CDrivesFolderEnum :
510     public CEnumIDListBase
511 {
512     public:
513         HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
514         {
515             /* enumerate the folders */
516             if (dwFlags & SHCONTF_FOLDERS)
517             {
518                 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
519                 DWORD dwDrivemap = GetLogicalDrives();
520 
521                 while (wszDriveName[0] <= 'Z')
522                 {
523                     if(dwDrivemap & 0x00000001L)
524                         AddToEnumList(_ILCreateDrive(wszDriveName));
525                     wszDriveName[0]++;
526                     dwDrivemap = dwDrivemap >> 1;
527                 }
528             }
529 
530             /* Enumerate the items of the reg folder */
531             AppendItemsFromEnumerator(pRegEnumerator);
532 
533             return S_OK;
534         }
535 
536         BEGIN_COM_MAP(CDrivesFolderEnum)
537         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
538         END_COM_MAP()
539 };
540 
541 /***********************************************************************
542 *   IShellFolder [MyComputer] implementation
543 */
544 
545 static const shvheader MyComputerSFHeader[] = {
546     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
547     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
548     {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
549     {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
550     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
551 };
552 
553 static const DWORD dwComputerAttributes =
554     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
555     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
556 static const DWORD dwControlPanelAttributes =
557     SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
558 static const DWORD dwDriveAttributes =
559     SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
560     SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
561 
562 CDrivesFolder::CDrivesFolder()
563 {
564     pidlRoot = NULL;
565 }
566 
567 CDrivesFolder::~CDrivesFolder()
568 {
569     TRACE("-- destroying IShellFolder(%p)\n", this);
570     SHFree(pidlRoot);
571 }
572 
573 HRESULT WINAPI CDrivesFolder::FinalConstruct()
574 {
575     pidlRoot = _ILCreateMyComputer();    /* my qualified pidl */
576     if (pidlRoot == NULL)
577         return E_OUTOFMEMORY;
578 
579     REGFOLDERINITDATA RegInit = { static_cast<IShellFolder*>(this), &g_RegFolderInfo };
580     HRESULT hr = CRegFolder_CreateInstance(&RegInit,
581                                            pidlRoot,
582                                            IID_PPV_ARG(IShellFolder2, &m_regFolder));
583 
584     return hr;
585 }
586 
587 /**************************************************************************
588 *    CDrivesFolder::ParseDisplayName
589 */
590 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
591         DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes)
592 {
593     HRESULT hr = E_INVALIDARG;
594 
595     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
596           hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
597           pchEaten, ppidl, pdwAttributes);
598 
599     if (!ppidl)
600         return hr;
601 
602     *ppidl = NULL;
603 
604     if (!lpszDisplayName)
605         return hr;
606 
607     /* handle CLSID paths */
608     if (lpszDisplayName[0] == L':' && lpszDisplayName[1] == L':')
609     {
610         return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl,
611                                              pdwAttributes);
612     }
613 
614     if (lpszDisplayName[0] &&
615         ((L'A' <= lpszDisplayName[0] && lpszDisplayName[0] <= L'Z') ||
616          (L'a' <= lpszDisplayName[0] && lpszDisplayName[0] <= L'z')) &&
617         lpszDisplayName[1] == L':' && lpszDisplayName[2] == L'\\')
618     {
619         // "C:\..."
620         WCHAR szRoot[8];
621         PathBuildRootW(szRoot, ((*lpszDisplayName - 1) & 0x1F));
622 
623         if (SHIsFileSysBindCtx(pbc, NULL) != S_OK && !(BindCtx_GetMode(pbc, 0) & STGM_CREATE))
624         {
625             if (::GetDriveType(szRoot) == DRIVE_NO_ROOT_DIR)
626                 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
627         }
628 
629         CComHeapPtr<ITEMIDLIST> pidlTemp(_ILCreateDrive(szRoot));
630         if (!pidlTemp)
631             return E_OUTOFMEMORY;
632 
633         if (lpszDisplayName[3])
634         {
635             CComPtr<IShellFolder> pChildFolder;
636             hr = BindToObject(pidlTemp, pbc, IID_PPV_ARG(IShellFolder, &pChildFolder));
637             if (FAILED_UNEXPECTEDLY(hr))
638                 return hr;
639 
640             ULONG chEaten;
641             CComHeapPtr<ITEMIDLIST> pidlChild;
642             hr = pChildFolder->ParseDisplayName(hwndOwner, pbc, &lpszDisplayName[3], &chEaten,
643                                                 &pidlChild, pdwAttributes);
644             if (FAILED_UNEXPECTEDLY(hr))
645                 return hr;
646 
647             hr = SHILCombine(pidlTemp, pidlChild, ppidl);
648         }
649         else
650         {
651             *ppidl = pidlTemp.Detach();
652             if (pdwAttributes && *pdwAttributes)
653                 GetAttributesOf(1, (PCUITEMID_CHILD_ARRAY)ppidl, pdwAttributes);
654             hr = S_OK;
655         }
656     }
657 
658     TRACE("(%p)->(-- ret=0x%08x)\n", this, hr);
659 
660     return hr;
661 }
662 
663 /**************************************************************************
664 *        CDrivesFolder::EnumObjects
665 */
666 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
667 {
668     CComPtr<IEnumIDList> pRegEnumerator;
669     m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
670 
671     return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
672 }
673 
674 /**************************************************************************
675 *        CDrivesFolder::BindToObject
676 */
677 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
678 {
679     TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
680           pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
681 
682     if (!pidl)
683         return E_INVALIDARG;
684 
685     if (_ILIsSpecialFolder(pidl))
686         return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
687 
688     CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
689 
690     PERSIST_FOLDER_TARGET_INFO pfti = {0};
691     pfti.dwAttributes = -1;
692     pfti.csidl = -1;
693     pfti.szTargetParsingName[0] = *pchDrive;
694     pfti.szTargetParsingName[1] = L':';
695     pfti.szTargetParsingName[2] = L'\\';
696 
697     HRESULT hr = SHELL32_BindToSF(pidlRoot,
698                                   &pfti,
699                                   pidl,
700                                   &CLSID_ShellFSFolder,
701                                   riid,
702                                   ppvOut);
703     if (FAILED_UNEXPECTEDLY(hr))
704         return hr;
705 
706     return S_OK;
707 }
708 
709 /**************************************************************************
710 *    CDrivesFolder::BindToStorage
711 */
712 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
713 {
714     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
715           pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
716 
717     *ppvOut = NULL;
718     return E_NOTIMPL;
719 }
720 
721 /**************************************************************************
722 *     CDrivesFolder::CompareIDs
723 */
724 
725 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
726 {
727     HRESULT hres;
728 
729     if (!pidl1 || !pidl2)
730     {
731         ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
732         return E_INVALIDARG;
733     }
734 
735     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
736         return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
737 
738     UINT iColumn = LOWORD(lParam);
739     if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || iColumn >= _countof(MyComputerSFHeader))
740         return E_INVALIDARG;
741 
742     CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
743     CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
744 
745     int result;
746     switch (MyComputerSFHeader[iColumn].colnameid)
747     {
748         case IDS_SHV_COLUMN_NAME:
749         {
750             result = _stricmp(pszDrive1, pszDrive2);
751             hres = MAKE_COMPARE_HRESULT(result);
752             break;
753         }
754         case IDS_SHV_COLUMN_TYPE:
755         {
756             /* We want to return immediately because SHELL32_CompareDetails also compares children. */
757             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
758         }
759         case IDS_SHV_COLUMN_DISK_CAPACITY:
760         case IDS_SHV_COLUMN_DISK_AVAILABLE:
761         {
762             ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
763 
764             if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
765                 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
766             else
767                 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
768 
769             if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
770                 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
771             else
772                 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
773 
774             LARGE_INTEGER Diff;
775             if (lParam == 3) /* Size */
776                 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
777             else /* Size available */
778                 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
779 
780             hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
781             break;
782         }
783         case IDS_SHV_COLUMN_COMMENTS:
784             hres = MAKE_COMPARE_HRESULT(0);
785             break;
786         DEFAULT_UNREACHABLE;
787     }
788 
789     if (HRESULT_CODE(hres) == 0)
790         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
791 
792     return hres;
793 }
794 
795 /**************************************************************************
796 *    CDrivesFolder::CreateViewObject
797 */
798 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
799 {
800     CComPtr<IShellView> pShellView;
801     HRESULT hr = E_INVALIDARG;
802 
803     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
804           hwndOwner, shdebugstr_guid (&riid), ppvOut);
805 
806     if (!ppvOut)
807         return hr;
808 
809     *ppvOut = NULL;
810 
811     if (IsEqualIID(riid, IID_IDropTarget))
812     {
813         WARN("IDropTarget not implemented\n");
814         hr = E_NOTIMPL;
815     }
816     else if (IsEqualIID(riid, IID_IContextMenu))
817     {
818         DEFCONTEXTMENU dcm = { hwndOwner, this, pidlRoot, this };
819         hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
820     }
821     else if (IsEqualIID(riid, IID_IShellView))
822     {
823             SFV_CREATE sfvparams = { sizeof(SFV_CREATE), this, NULL, static_cast<IShellFolderViewCB*>(this) };
824             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
825     }
826     TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
827     return hr;
828 }
829 
830 /**************************************************************************
831 *  CDrivesFolder::GetAttributesOf
832 */
833 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
834 {
835     TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
836           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
837 
838     if (cidl && !apidl)
839         return E_INVALIDARG;
840 
841     if (*rgfInOut == 0)
842         *rgfInOut = ~0;
843 
844     /* FIXME: always add SFGAO_CANLINK */
845     if(cidl == 0)
846         *rgfInOut &= dwComputerAttributes;
847     else
848     {
849         for (UINT i = 0; i < cidl; ++i)
850         {
851             if (_ILIsDrive(apidl[i]))
852             {
853                 *rgfInOut &= dwDriveAttributes;
854 
855                 if (_ILGetDriveType(apidl[i]) == DRIVE_CDROM)
856                     *rgfInOut &= ~SFGAO_CANRENAME; // CD-ROM drive cannot rename
857             }
858             else if (_ILIsControlPanel(apidl[i]))
859             {
860                 *rgfInOut &= dwControlPanelAttributes;
861             }
862             else if (_ILIsSpecialFolder(*apidl))
863             {
864                 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
865             }
866             else
867             {
868                 ERR("Got unknown pidl type!\n");
869             }
870         }
871     }
872 
873     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
874     *rgfInOut &= ~SFGAO_VALIDATE;
875 
876     TRACE("-- result=0x%08x\n", *rgfInOut);
877     return S_OK;
878 }
879 
880 /**************************************************************************
881 *    CDrivesFolder::GetUIObjectOf
882 *
883 * PARAMETERS
884 *  hwndOwner [in]  Parent window for any output
885 *  cidl      [in]  array size
886 *  apidl     [in]  simple pidl array
887 *  riid      [in]  Requested Interface
888 *  prgfInOut [   ] reserved
889 *  ppvObject [out] Resulting Interface
890 *
891 */
892 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
893     UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
894     REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
895 {
896     LPVOID pObj = NULL;
897     HRESULT hr = E_INVALIDARG;
898 
899     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
900           hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
901 
902     if (!ppvOut)
903         return hr;
904 
905     *ppvOut = NULL;
906 
907     if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
908     {
909         if (_ILIsDrive(apidl[0]))
910             hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
911         else
912             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
913     }
914     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
915     {
916         hr = IDataObject_Constructor(hwndOwner,
917                                      pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
918     }
919     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
920     {
921         if (_ILIsDrive(apidl[0]))
922             hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
923         else
924             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
925     }
926     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
927     {
928         CComPtr<IShellFolder> psfChild;
929         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
930         if (FAILED_UNEXPECTEDLY(hr))
931             return hr;
932 
933         return psfChild->CreateViewObject(NULL, riid, ppvOut);
934     }
935     else
936         hr = E_NOINTERFACE;
937 
938     if (SUCCEEDED(hr) && !pObj)
939         hr = E_OUTOFMEMORY;
940 
941     *ppvOut = pObj;
942     TRACE("(%p)->hr=0x%08x\n", this, hr);
943     return hr;
944 }
945 
946 /**************************************************************************
947 *    CDrivesFolder::GetDisplayNameOf
948 */
949 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
950 {
951     LPWSTR pszPath;
952     HRESULT hr = S_OK;
953 
954     TRACE("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
955     pdump (pidl);
956 
957     if (!strRet)
958         return E_INVALIDARG;
959 
960     if (!_ILIsPidlSimple (pidl))
961     {
962         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
963     }
964     else if (_ILIsSpecialFolder(pidl))
965     {
966         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
967     }
968     else if (!_ILIsDrive(pidl))
969     {
970         ERR("Wrong pidl type\n");
971         return E_INVALIDARG;
972     }
973 
974     pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
975     if (!pszPath)
976         return E_OUTOFMEMORY;
977 
978     pszPath[0] = 0;
979 
980     _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);    /* append my own path */
981     /* long view "lw_name (C:)" */
982     if (!(dwFlags & SHGDN_FORPARSING))
983     {
984         WCHAR wszDrive[18] = {0};
985 
986         lstrcpynW(wszDrive, pszPath, 4);
987         pszPath[0] = L'\0';
988 
989         if (!SUCCEEDED(getLabelForDrive(wszDrive, pszPath)))
990         {
991             DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
992 
993             GetVolumeInformationW(wszDrive, pszPath,
994                                   MAX_PATH - 7,
995                                   &dwVolumeSerialNumber,
996                                   &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
997             pszPath[MAX_PATH-1] = L'\0';
998 
999             if (!wcslen(pszPath))
1000             {
1001                 UINT DriveType, ResourceId;
1002                 DriveType = GetDriveTypeW(wszDrive);
1003 
1004                 switch (DriveType)
1005                 {
1006                     case DRIVE_FIXED:
1007                         ResourceId = IDS_DRIVE_FIXED;
1008                         break;
1009                     case DRIVE_REMOTE:
1010                         ResourceId = IDS_DRIVE_NETWORK;
1011                         break;
1012                     case DRIVE_CDROM:
1013                         ResourceId = IDS_DRIVE_CDROM;
1014                         break;
1015                     default:
1016                         ResourceId = 0;
1017                 }
1018 
1019                 if (ResourceId)
1020                 {
1021                     dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
1022                     if (dwFileSystemFlags > MAX_PATH - 7)
1023                         pszPath[MAX_PATH-7] = L'\0';
1024                 }
1025             }
1026         }
1027         wcscat(pszPath, L" (");
1028         wszDrive[2] = L'\0';
1029         wcscat(pszPath, wszDrive);
1030         wcscat(pszPath, L")");
1031     }
1032 
1033     if (SUCCEEDED(hr))
1034     {
1035         strRet->uType = STRRET_WSTR;
1036         strRet->pOleStr = pszPath;
1037     }
1038     else
1039         CoTaskMemFree(pszPath);
1040 
1041     TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1042     return hr;
1043 }
1044 
1045 /**************************************************************************
1046 *  CDrivesFolder::SetNameOf
1047 *  Changes the name of a file object or subfolder, possibly changing its item
1048 *  identifier in the process.
1049 *
1050 * PARAMETERS
1051 *  hwndOwner  [in]   Owner window for output
1052 *  pidl       [in]   simple pidl of item to change
1053 *  lpszName   [in]   the items new display name
1054 *  dwFlags    [in]   SHGNO formatting flags
1055 *  ppidlOut   [out]  simple pidl returned
1056 */
1057 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
1058                                         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
1059 {
1060     WCHAR szName[30];
1061 
1062     if (_ILIsDrive(pidl))
1063     {
1064         if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
1065             SetVolumeLabelW(szName, lpName);
1066         if (pPidlOut)
1067             *pPidlOut = _ILCreateDrive(szName);
1068         return S_OK;
1069     }
1070 
1071     return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
1072 }
1073 
1074 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
1075 {
1076     FIXME("(%p)\n", this);
1077     return E_NOTIMPL;
1078 }
1079 
1080 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1081 {
1082     FIXME("(%p)\n", this);
1083     return E_NOTIMPL;
1084 }
1085 
1086 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
1087 {
1088     TRACE("(%p)\n", this);
1089 
1090     if (pSort)
1091         *pSort = 0;
1092     if (pDisplay)
1093         *pDisplay = 0;
1094     return S_OK;
1095 }
1096 
1097 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF * pcsFlags)
1098 {
1099     TRACE("(%p)\n", this);
1100 
1101     if (!pcsFlags || iColumn >= _countof(MyComputerSFHeader))
1102         return E_INVALIDARG;
1103     *pcsFlags = MyComputerSFHeader[iColumn].colstate;
1104     return S_OK;
1105 }
1106 
1107 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
1108 {
1109     FIXME("(%p)\n", this);
1110     return E_NOTIMPL;
1111 }
1112 
1113 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
1114 {
1115     HRESULT hr;
1116 
1117     TRACE("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1118 
1119     if (!psd || iColumn >= _countof(MyComputerSFHeader))
1120         return E_INVALIDARG;
1121 
1122     if (!pidl)
1123     {
1124         psd->fmt = MyComputerSFHeader[iColumn].fmt;
1125         psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
1126         return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
1127     }
1128     else if (!_ILIsDrive(pidl))
1129     {
1130         switch (MyComputerSFHeader[iColumn].colnameid)
1131         {
1132             case IDS_SHV_COLUMN_NAME:
1133             case IDS_SHV_COLUMN_TYPE:
1134                 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
1135             case IDS_SHV_COLUMN_DISK_CAPACITY:
1136             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1137                 return SHSetStrRet(&psd->str, ""); /* blank col */
1138             case IDS_SHV_COLUMN_COMMENTS:
1139                 return m_regFolder->GetDetailsOf(pidl, 2, psd); /* 2 = comments */
1140             DEFAULT_UNREACHABLE;
1141         }
1142     }
1143     else
1144     {
1145         ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
1146         CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
1147         UINT DriveType = GetDriveTypeA(pszDrive);
1148         if (DriveType > DRIVE_RAMDISK)
1149             DriveType = DRIVE_FIXED;
1150 
1151         switch (MyComputerSFHeader[iColumn].colnameid)
1152         {
1153             case IDS_SHV_COLUMN_NAME:
1154                 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1155                 break;
1156             case IDS_SHV_COLUMN_TYPE:
1157                 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
1158                     hr = SHSetStrRet(&psd->str, IDS_DRIVE_REMOVABLE);
1159                 else
1160                     hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1161                 break;
1162             case IDS_SHV_COLUMN_DISK_CAPACITY:
1163             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1164                 psd->str.cStr[0] = 0x00;
1165                 psd->str.uType = STRRET_CSTR;
1166                 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1167                 {
1168                     GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1169                     if (iColumn == 2)
1170                         StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1171                     else
1172                         StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1173                 }
1174                 hr = S_OK;
1175                 break;
1176             case IDS_SHV_COLUMN_COMMENTS:
1177                 hr = SHSetStrRet(&psd->str, ""); /* FIXME: comments */
1178                 break;
1179             DEFAULT_UNREACHABLE;
1180         }
1181     }
1182 
1183     return hr;
1184 }
1185 
1186 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1187 {
1188     FIXME("(%p)\n", this);
1189     return E_NOTIMPL;
1190 }
1191 
1192 /************************************************************************
1193  *    CDrivesFolder::GetClassID
1194  */
1195 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1196 {
1197     TRACE("(%p)\n", this);
1198 
1199     if (!lpClassId)
1200         return E_POINTER;
1201 
1202     *lpClassId = CLSID_MyComputer;
1203     return S_OK;
1204 }
1205 
1206 /************************************************************************
1207  *    CDrivesFolder::Initialize
1208  *
1209  * NOTES: it makes no sense to change the pidl
1210  */
1211 HRESULT WINAPI CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1212 {
1213     return S_OK;
1214 }
1215 
1216 /**************************************************************************
1217  *    CDrivesFolder::GetCurFolder
1218  */
1219 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1220 {
1221     TRACE("(%p)->(%p)\n", this, pidl);
1222 
1223     if (!pidl)
1224         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1225 
1226     *pidl = ILClone(pidlRoot);
1227     return S_OK;
1228 }
1229 
1230 /**************************************************************************
1231  *    CDrivesFolder::ShouldShow
1232  */
1233 HRESULT WINAPI CDrivesFolder::ShouldShow(IShellFolder *psf, PCIDLIST_ABSOLUTE pidlFolder, PCUITEMID_CHILD pidlItem)
1234 {
1235     if (const CLSID* pClsid = IsRegItem(pidlItem))
1236         return SHELL32_IsShellFolderNamespaceItemHidden(L"HideMyComputerIcons", *pClsid) ? S_FALSE : S_OK;
1237     return S_OK;
1238 }
1239 
1240 /************************************************************************/
1241 /* IContextMenuCB interface */
1242 
1243 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1244 {
1245     enum { IDC_PROPERTIES };
1246     /* no data object means no selection */
1247     if (!pdtobj)
1248     {
1249         if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
1250         {
1251             // "System" properties
1252             return SHELL_ExecuteControlPanelCPL(hwndOwner, L"sysdm.cpl") ? S_OK : E_FAIL;
1253         }
1254         else if (uMsg == DFM_MERGECONTEXTMENU) // TODO: DFM_MERGECONTEXTMENU_BOTTOM
1255         {
1256             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1257             HMENU hpopup = CreatePopupMenu();
1258             _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1259             pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1260             DestroyMenu(hpopup);
1261             return S_OK;
1262         }
1263     }
1264     return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
1265 }
1266