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 // A callback function for finding the stub windows.
190 static BOOL CALLBACK
191 EnumStubProc(HWND hwnd, LPARAM lParam)
192 {
193     CSimpleArray<HWND> *pStubs = reinterpret_cast<CSimpleArray<HWND> *>(lParam);
194 
195     WCHAR szClass[64];
196     GetClassNameW(hwnd, szClass, _countof(szClass));
197 
198     if (lstrcmpiW(szClass, L"StubWindow32") == 0)
199     {
200         pStubs->Add(hwnd);
201     }
202 
203     return TRUE;
204 }
205 
206 // Another callback function to find the owned window of the stub window.
207 static BOOL CALLBACK
208 EnumStubProc2(HWND hwnd, LPARAM lParam)
209 {
210     HWND *phwnd = reinterpret_cast<HWND *>(lParam);
211 
212     if (phwnd[0] == GetWindow(hwnd, GW_OWNER))
213     {
214         phwnd[1] = hwnd;
215         return FALSE;
216     }
217 
218     return TRUE;
219 }
220 
221 // Parameters for format_drive_thread function below.
222 struct THREAD_PARAMS
223 {
224     UINT nDriveNumber;
225 };
226 
227 static unsigned __stdcall format_drive_thread(void *args)
228 {
229     THREAD_PARAMS *params = (THREAD_PARAMS *)args;
230     UINT nDriveNumber = params->nDriveNumber;
231     LONG_PTR nProp = nDriveNumber | 0x7F00;
232 
233     // Search the stub windows that already exist.
234     CSimpleArray<HWND> old_stubs;
235     EnumWindows(EnumStubProc, (LPARAM)&old_stubs);
236 
237     for (INT n = 0; n < old_stubs.GetSize(); ++n)
238     {
239         HWND hwndStub = old_stubs[n];
240 
241         // The target stub window has the prop.
242         if (GetPropW(hwndStub, L"DriveNumber") == (HANDLE)nProp)
243         {
244             // Found.
245             HWND ahwnd[2];
246             ahwnd[0] = hwndStub;
247             ahwnd[1] = NULL;
248             EnumWindows(EnumStubProc2, (LPARAM)ahwnd);
249 
250             // Activate.
251             BringWindowToTop(ahwnd[1]);
252 
253             delete params;
254             return 0;
255         }
256     }
257 
258     // Create a stub window.
259     DWORD style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION;
260     DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW;
261     CStubWindow32 stub;
262     if (!stub.Create(NULL, NULL, NULL, style, exstyle))
263     {
264         ERR("StubWindow32 creation failed\n");
265         delete params;
266         return 0;
267     }
268 
269     // Add prop to the target stub window.
270     SetPropW(stub, L"DriveNumber", (HANDLE)nProp);
271 
272     // Do format.
273     SHFormatDrive(stub, nDriveNumber, SHFMT_ID_DEFAULT, 0);
274 
275     // Clean up.
276     RemovePropW(stub, L"DriveNumber");
277     stub.DestroyWindow();
278     delete params;
279 
280     return 0;
281 }
282 
283 static HRESULT DoFormatDrive(HWND hwnd, UINT nDriveNumber)
284 {
285     THREAD_PARAMS *params = new THREAD_PARAMS;
286     params->nDriveNumber = nDriveNumber;
287 
288     // Create thread to avoid locked.
289     unsigned tid;
290     HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, format_drive_thread, params, 0, &tid);
291     if (hThread == NULL)
292     {
293         delete params;
294         return E_FAIL;
295     }
296 
297     CloseHandle(hThread);
298 
299     return S_OK;
300 }
301 
302 HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf,
303                                            HWND         hwnd,
304                                            IDataObject  *pdtobj,
305                                            UINT         uMsg,
306                                            WPARAM       wParam,
307                                            LPARAM       lParam)
308 {
309     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
310         return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
311 
312     PIDLIST_ABSOLUTE pidlFolder;
313     PUITEMID_CHILD *apidl;
314     UINT cidl;
315     UINT nDriveType;
316     DWORD dwFlags;
317     HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
318     if (FAILED_UNEXPECTEDLY(hr))
319         return hr;
320 
321     WCHAR szDrive[8] = {0};
322     if (!_ILGetDrive(apidl[0], szDrive, _countof(szDrive)))
323     {
324         ERR("pidl is not a drive\n");
325         SHFree(pidlFolder);
326         _ILFreeaPidl(apidl, cidl);
327         return E_FAIL;
328     }
329     nDriveType = GetDriveTypeW(szDrive);
330     GetVolumeInformationW(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0);
331 
332 // custom command IDs
333 #if 0 // Disabled until our menu building system is fixed
334 #define CMDID_FORMAT        0
335 #define CMDID_EJECT         1
336 #define CMDID_DISCONNECT    2
337 #else
338 /* FIXME: These IDs should start from 0, however there is difference
339  * between ours and Windows' menu building systems, which should be fixed. */
340 #define CMDID_FORMAT        1
341 #define CMDID_EJECT         2
342 #define CMDID_DISCONNECT    3
343 #endif
344 
345     if (uMsg == DFM_MERGECONTEXTMENU)
346     {
347         QCMINFO *pqcminfo = (QCMINFO *)lParam;
348 
349         UINT idCmdFirst = pqcminfo->idCmdFirst;
350         UINT idCmd = 0;
351         if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE)
352         {
353             /* add separator and Format */
354             idCmd = idCmdFirst + CMDID_FORMAT;
355             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
356             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
357         }
358         if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM)
359         {
360             /* add separator and Eject */
361             idCmd = idCmdFirst + CMDID_EJECT;
362             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
363             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED);
364         }
365         if (nDriveType == DRIVE_REMOTE)
366         {
367             /* add separator and Disconnect */
368             idCmd = idCmdFirst + CMDID_DISCONNECT;
369             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
370             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED);
371         }
372 
373         if (idCmd)
374 #if 0 // see FIXME above
375             pqcminfo->idCmdFirst = ++idCmd;
376 #else
377             pqcminfo->idCmdFirst = (idCmd + 2);
378 #endif
379         hr = S_OK;
380     }
381     else if (uMsg == DFM_INVOKECOMMAND)
382     {
383         WCHAR wszBuf[4] = L"A:\\";
384         wszBuf[0] = (WCHAR)szDrive[0];
385 
386         INT nStringID = 0;
387         DWORD dwError = NO_ERROR;
388 
389         if (wParam == DFM_CMD_PROPERTIES)
390         {
391             // pdtobj should be valid at this point!
392             ATLASSERT(pdtobj);
393             hr = SH_ShowDriveProperties(wszBuf, pdtobj) ? S_OK : E_UNEXPECTED;
394             if (FAILED(hr))
395             {
396                 dwError = ERROR_CAN_NOT_COMPLETE;
397                 nStringID = IDS_CANTSHOWPROPERTIES;
398             }
399         }
400         else
401         {
402             if (wParam == CMDID_FORMAT)
403             {
404                 hr = DoFormatDrive(hwnd, szDrive[0] - 'A');
405             }
406             else if (wParam == CMDID_EJECT)
407             {
408                 /* do eject */
409                 WCHAR physical[10];
410                 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
411 
412                 if (DoEjectDrive(physical, nDriveType, &nStringID))
413                 {
414                     SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
415                 }
416                 else
417                 {
418                     dwError = GetLastError();
419                 }
420             }
421             else if (wParam == CMDID_DISCONNECT)
422             {
423                 /* do disconnect */
424                 wszBuf[2] = UNICODE_NULL;
425                 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
426                 if (dwError == NO_ERROR)
427                 {
428                     SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
429                 }
430                 else
431                 {
432                     nStringID = IDS_CANTDISCONNECT;
433                 }
434             }
435         }
436 
437         if (nStringID != 0)
438         {
439             /* show error message */
440             WCHAR szFormat[128], szMessage[128];
441             LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
442             wsprintfW(szMessage, szFormat, dwError);
443             MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
444         }
445     }
446 
447     SHFree(pidlFolder);
448     _ILFreeaPidl(apidl, cidl);
449 
450     return hr;
451 }
452 
453 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
454                                           HWND hwnd,
455                                           UINT cidl,
456                                           PCUITEMID_CHILD_ARRAY apidl,
457                                           IShellFolder *psf,
458                                           IContextMenu **ppcm)
459 {
460     HKEY hKeys[2];
461     UINT cKeys = 0;
462     AddClassKeyToArray(L"Drive", hKeys, &cKeys);
463     AddClassKeyToArray(L"Folder", hKeys, &cKeys);
464 
465     return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
466 }
467 
468 static HRESULT
469 getIconLocationForDrive(IShellFolder *psf, PCITEMID_CHILD pidl, UINT uFlags,
470                         LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
471 {
472     WCHAR wszPath[MAX_PATH];
473     WCHAR wszAutoRunInfPath[MAX_PATH];
474     WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
475 
476     // get path
477     if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
478         return E_FAIL;
479     if (!PathIsDirectoryW(wszPath))
480         return E_FAIL;
481 
482     // build the full path of autorun.inf
483     StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
484     PathAppendW(wszAutoRunInfPath, L"autorun.inf");
485 
486     // autorun.inf --> wszValue
487     if (GetPrivateProfileStringW(L"autorun", L"icon", NULL, wszValue, _countof(wszValue),
488                                  wszAutoRunInfPath) && wszValue[0] != 0)
489     {
490         // wszValue --> wszTemp
491         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
492 
493         // parse the icon location
494         *piIndex = PathParseIconLocationW(wszTemp);
495 
496         // wszPath + wszTemp --> wszPath
497         if (PathIsRelativeW(wszTemp))
498             PathAppendW(wszPath, wszTemp);
499         else
500             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
501 
502         // wszPath --> szIconFile
503         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
504 
505         return S_OK;
506     }
507 
508     return E_FAIL;
509 }
510 
511 static HRESULT
512 getLabelForDrive(LPWSTR wszPath, LPWSTR wszLabel)
513 {
514     WCHAR wszAutoRunInfPath[MAX_PATH];
515     WCHAR wszTemp[MAX_PATH];
516 
517     if (!PathIsDirectoryW(wszPath))
518         return E_FAIL;
519 
520     StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
521     PathAppendW(wszAutoRunInfPath, L"autorun.inf");
522 
523     if (GetPrivateProfileStringW(L"autorun", L"label", NULL, wszTemp, _countof(wszTemp),
524                                  wszAutoRunInfPath) && wszTemp[0] != 0)
525     {
526         StringCchCopyW(wszLabel, _countof(wszTemp), wszTemp);
527         return S_OK;
528     }
529 
530     return E_FAIL;
531 }
532 
533 BOOL IsDriveFloppyA(LPCSTR pszDriveRoot);
534 
535 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
536 {
537     CComPtr<IDefaultExtractIconInit> initIcon;
538     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
539     if (FAILED_UNEXPECTEDLY(hr))
540         return hr;
541 
542     CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
543     UINT DriveType = GetDriveTypeA(pszDrive);
544     if (DriveType > DRIVE_RAMDISK)
545         DriveType = DRIVE_FIXED;
546 
547     WCHAR wTemp[MAX_PATH];
548     int icon_idx, reg_idx;
549     UINT flags = 0;
550 
551     switch (DriveType)
552     {
553         case DRIVE_FIXED:
554         case DRIVE_UNKNOWN:
555             reg_idx = IDI_SHELL_DRIVE;
556             break;
557         case DRIVE_CDROM:
558             reg_idx = IDI_SHELL_CDROM;
559             break;
560         case DRIVE_REMOTE:
561             reg_idx = IDI_SHELL_NETDRIVE;
562             break;
563         case DRIVE_REMOVABLE:
564             if (!IsDriveFloppyA(pszDrive))
565                 reg_idx = IDI_SHELL_REMOVEABLE;
566             else
567                 reg_idx = IDI_SHELL_3_14_FLOPPY;
568             break;
569         case DRIVE_RAMDISK:
570             reg_idx = IDI_SHELL_RAMDISK;
571             break;
572         case DRIVE_NO_ROOT_DIR:
573         default:
574             reg_idx = IDI_SHELL_DOCUMENT;
575             break;
576     }
577 
578     hr = getIconLocationForDrive(psf, pidl, 0, wTemp, _countof(wTemp),
579                                  &icon_idx, &flags);
580     if (SUCCEEDED(hr))
581     {
582         initIcon->SetNormalIcon(wTemp, icon_idx);
583     }
584     else if (HLM_GetIconW(reg_idx - 1, wTemp, _countof(wTemp), &icon_idx))
585     {
586         initIcon->SetNormalIcon(wTemp, icon_idx);
587     }
588     else if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
589              (HCR_GetIconW(L"Drive", wTemp, NULL, _countof(wTemp), &icon_idx)))
590     {
591         initIcon->SetNormalIcon(wTemp, icon_idx);
592     }
593     else
594     {
595         if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
596         {
597             icon_idx = IDI_SHELL_REMOVEABLE;
598         }
599         else
600         {
601             icon_idx = iDriveIconIds[DriveType];
602         }
603         initIcon->SetNormalIcon(swShell32Name, -icon_idx);
604     }
605 
606     return initIcon->QueryInterface(riid, ppvOut);
607 }
608 
609 class CDrivesFolderEnum :
610     public CEnumIDListBase
611 {
612     public:
613         HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
614         {
615             /* enumerate the folders */
616             if (dwFlags & SHCONTF_FOLDERS)
617             {
618                 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
619                 DWORD dwDrivemap = GetLogicalDrives();
620 
621                 while (wszDriveName[0] <= 'Z')
622                 {
623                     if(dwDrivemap & 0x00000001L)
624                         AddToEnumList(_ILCreateDrive(wszDriveName));
625                     wszDriveName[0]++;
626                     dwDrivemap = dwDrivemap >> 1;
627                 }
628             }
629 
630             /* Enumerate the items of the reg folder */
631             AppendItemsFromEnumerator(pRegEnumerator);
632 
633             return S_OK;
634         }
635 
636         BEGIN_COM_MAP(CDrivesFolderEnum)
637         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
638         END_COM_MAP()
639 };
640 
641 /***********************************************************************
642 *   IShellFolder [MyComputer] implementation
643 */
644 
645 static const shvheader MyComputerSFHeader[] = {
646     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
647     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
648     {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
649     {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
650     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
651 };
652 
653 static const DWORD dwComputerAttributes =
654     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
655     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
656 static const DWORD dwControlPanelAttributes =
657     SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
658 static const DWORD dwDriveAttributes =
659     SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
660     SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
661 
662 CDrivesFolder::CDrivesFolder()
663 {
664     pidlRoot = NULL;
665 }
666 
667 CDrivesFolder::~CDrivesFolder()
668 {
669     TRACE("-- destroying IShellFolder(%p)\n", this);
670     SHFree(pidlRoot);
671 }
672 
673 HRESULT WINAPI CDrivesFolder::FinalConstruct()
674 {
675     pidlRoot = _ILCreateMyComputer();    /* my qualified pidl */
676     if (pidlRoot == NULL)
677         return E_OUTOFMEMORY;
678 
679     REGFOLDERINITDATA RegInit = { static_cast<IShellFolder*>(this), &g_RegFolderInfo };
680     HRESULT hr = CRegFolder_CreateInstance(&RegInit,
681                                            pidlRoot,
682                                            IID_PPV_ARG(IShellFolder2, &m_regFolder));
683 
684     return hr;
685 }
686 
687 /**************************************************************************
688 *    CDrivesFolder::ParseDisplayName
689 */
690 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
691         DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes)
692 {
693     HRESULT hr = E_INVALIDARG;
694 
695     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
696           hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
697           pchEaten, ppidl, pdwAttributes);
698 
699     if (!ppidl)
700         return hr;
701 
702     *ppidl = NULL;
703 
704     if (!lpszDisplayName)
705         return hr;
706 
707     /* handle CLSID paths */
708     if (lpszDisplayName[0] == L':' && lpszDisplayName[1] == L':')
709     {
710         return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl,
711                                              pdwAttributes);
712     }
713 
714     if (lpszDisplayName[0] &&
715         ((L'A' <= lpszDisplayName[0] && lpszDisplayName[0] <= L'Z') ||
716          (L'a' <= lpszDisplayName[0] && lpszDisplayName[0] <= L'z')) &&
717         lpszDisplayName[1] == L':' && lpszDisplayName[2] == L'\\')
718     {
719         // "C:\..."
720         WCHAR szRoot[8];
721         PathBuildRootW(szRoot, ((*lpszDisplayName - 1) & 0x1F));
722 
723         if (SHIsFileSysBindCtx(pbc, NULL) != S_OK && !(BindCtx_GetMode(pbc, 0) & STGM_CREATE))
724         {
725             if (::GetDriveType(szRoot) == DRIVE_NO_ROOT_DIR)
726                 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
727         }
728 
729         CComHeapPtr<ITEMIDLIST> pidlTemp(_ILCreateDrive(szRoot));
730         if (!pidlTemp)
731             return E_OUTOFMEMORY;
732 
733         if (lpszDisplayName[3])
734         {
735             CComPtr<IShellFolder> pChildFolder;
736             hr = BindToObject(pidlTemp, pbc, IID_PPV_ARG(IShellFolder, &pChildFolder));
737             if (FAILED_UNEXPECTEDLY(hr))
738                 return hr;
739 
740             ULONG chEaten;
741             CComHeapPtr<ITEMIDLIST> pidlChild;
742             hr = pChildFolder->ParseDisplayName(hwndOwner, pbc, &lpszDisplayName[3], &chEaten,
743                                                 &pidlChild, pdwAttributes);
744             if (FAILED_UNEXPECTEDLY(hr))
745                 return hr;
746 
747             hr = SHILCombine(pidlTemp, pidlChild, ppidl);
748         }
749         else
750         {
751             *ppidl = pidlTemp.Detach();
752             if (pdwAttributes && *pdwAttributes)
753                 GetAttributesOf(1, (PCUITEMID_CHILD_ARRAY)ppidl, pdwAttributes);
754             hr = S_OK;
755         }
756     }
757 
758     TRACE("(%p)->(-- ret=0x%08x)\n", this, hr);
759 
760     return hr;
761 }
762 
763 /**************************************************************************
764 *        CDrivesFolder::EnumObjects
765 */
766 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
767 {
768     CComPtr<IEnumIDList> pRegEnumerator;
769     m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
770 
771     return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
772 }
773 
774 /**************************************************************************
775 *        CDrivesFolder::BindToObject
776 */
777 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
778 {
779     TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
780           pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
781 
782     if (!pidl)
783         return E_INVALIDARG;
784 
785     if (_ILIsSpecialFolder(pidl))
786         return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
787 
788     CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
789 
790     PERSIST_FOLDER_TARGET_INFO pfti = {0};
791     pfti.dwAttributes = -1;
792     pfti.csidl = -1;
793     pfti.szTargetParsingName[0] = *pchDrive;
794     pfti.szTargetParsingName[1] = L':';
795     pfti.szTargetParsingName[2] = L'\\';
796 
797     HRESULT hr = SHELL32_BindToSF(pidlRoot,
798                                   &pfti,
799                                   pidl,
800                                   &CLSID_ShellFSFolder,
801                                   riid,
802                                   ppvOut);
803     if (FAILED_UNEXPECTEDLY(hr))
804         return hr;
805 
806     return S_OK;
807 }
808 
809 /**************************************************************************
810 *    CDrivesFolder::BindToStorage
811 */
812 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
813 {
814     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
815           pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
816 
817     *ppvOut = NULL;
818     return E_NOTIMPL;
819 }
820 
821 /**************************************************************************
822 *     CDrivesFolder::CompareIDs
823 */
824 
825 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
826 {
827     HRESULT hres;
828 
829     if (!pidl1 || !pidl2)
830     {
831         ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
832         return E_INVALIDARG;
833     }
834 
835     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
836         return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
837 
838     UINT iColumn = LOWORD(lParam);
839     if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || iColumn >= _countof(MyComputerSFHeader))
840         return E_INVALIDARG;
841 
842     CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
843     CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
844 
845     int result;
846     switch (MyComputerSFHeader[iColumn].colnameid)
847     {
848         case IDS_SHV_COLUMN_NAME:
849         {
850             result = _stricmp(pszDrive1, pszDrive2);
851             hres = MAKE_COMPARE_HRESULT(result);
852             break;
853         }
854         case IDS_SHV_COLUMN_TYPE:
855         {
856             /* We want to return immediately because SHELL32_CompareDetails also compares children. */
857             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
858         }
859         case IDS_SHV_COLUMN_DISK_CAPACITY:
860         case IDS_SHV_COLUMN_DISK_AVAILABLE:
861         {
862             ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
863 
864             if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
865                 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
866             else
867                 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
868 
869             if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
870                 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
871             else
872                 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
873 
874             LARGE_INTEGER Diff;
875             if (lParam == 3) /* Size */
876                 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
877             else /* Size available */
878                 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
879 
880             hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
881             break;
882         }
883         case IDS_SHV_COLUMN_COMMENTS:
884             hres = MAKE_COMPARE_HRESULT(0);
885             break;
886         DEFAULT_UNREACHABLE;
887     }
888 
889     if (HRESULT_CODE(hres) == 0)
890         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
891 
892     return hres;
893 }
894 
895 /**************************************************************************
896 *    CDrivesFolder::CreateViewObject
897 */
898 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
899 {
900     CComPtr<IShellView> pShellView;
901     HRESULT hr = E_INVALIDARG;
902 
903     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
904           hwndOwner, shdebugstr_guid (&riid), ppvOut);
905 
906     if (!ppvOut)
907         return hr;
908 
909     *ppvOut = NULL;
910 
911     if (IsEqualIID(riid, IID_IDropTarget))
912     {
913         WARN("IDropTarget not implemented\n");
914         hr = E_NOTIMPL;
915     }
916     else if (IsEqualIID(riid, IID_IContextMenu))
917     {
918         DEFCONTEXTMENU dcm = { hwndOwner, this, pidlRoot, this };
919         hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
920     }
921     else if (IsEqualIID(riid, IID_IShellView))
922     {
923             SFV_CREATE sfvparams = { sizeof(SFV_CREATE), this, NULL, static_cast<IShellFolderViewCB*>(this) };
924             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
925     }
926     TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
927     return hr;
928 }
929 
930 /**************************************************************************
931 *  CDrivesFolder::GetAttributesOf
932 */
933 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
934 {
935     TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
936           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
937 
938     if (cidl && !apidl)
939         return E_INVALIDARG;
940 
941     if (*rgfInOut == 0)
942         *rgfInOut = ~0;
943 
944     /* FIXME: always add SFGAO_CANLINK */
945     if(cidl == 0)
946         *rgfInOut &= dwComputerAttributes;
947     else
948     {
949         for (UINT i = 0; i < cidl; ++i)
950         {
951             if (_ILIsDrive(apidl[i]))
952             {
953                 *rgfInOut &= dwDriveAttributes;
954 
955                 if (_ILGetDriveType(apidl[i]) == DRIVE_CDROM)
956                     *rgfInOut &= ~SFGAO_CANRENAME; // CD-ROM drive cannot rename
957             }
958             else if (_ILIsControlPanel(apidl[i]))
959             {
960                 *rgfInOut &= dwControlPanelAttributes;
961             }
962             else if (_ILIsSpecialFolder(*apidl))
963             {
964                 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
965             }
966             else
967             {
968                 ERR("Got unknown pidl type!\n");
969             }
970         }
971     }
972 
973     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
974     *rgfInOut &= ~SFGAO_VALIDATE;
975 
976     TRACE("-- result=0x%08x\n", *rgfInOut);
977     return S_OK;
978 }
979 
980 /**************************************************************************
981 *    CDrivesFolder::GetUIObjectOf
982 *
983 * PARAMETERS
984 *  hwndOwner [in]  Parent window for any output
985 *  cidl      [in]  array size
986 *  apidl     [in]  simple pidl array
987 *  riid      [in]  Requested Interface
988 *  prgfInOut [   ] reserved
989 *  ppvObject [out] Resulting Interface
990 *
991 */
992 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
993     UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
994     REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
995 {
996     LPVOID pObj = NULL;
997     HRESULT hr = E_INVALIDARG;
998 
999     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
1000           hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
1001 
1002     if (!ppvOut)
1003         return hr;
1004 
1005     *ppvOut = NULL;
1006 
1007     if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
1008     {
1009         if (_ILIsDrive(apidl[0]))
1010             hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
1011         else
1012             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
1013     }
1014     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
1015     {
1016         hr = IDataObject_Constructor(hwndOwner,
1017                                      pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
1018     }
1019     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
1020     {
1021         if (_ILIsDrive(apidl[0]))
1022             hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
1023         else
1024             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
1025     }
1026     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
1027     {
1028         CComPtr<IShellFolder> psfChild;
1029         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1030         if (FAILED_UNEXPECTEDLY(hr))
1031             return hr;
1032 
1033         return psfChild->CreateViewObject(NULL, riid, ppvOut);
1034     }
1035     else
1036         hr = E_NOINTERFACE;
1037 
1038     if (SUCCEEDED(hr) && !pObj)
1039         hr = E_OUTOFMEMORY;
1040 
1041     *ppvOut = pObj;
1042     TRACE("(%p)->hr=0x%08x\n", this, hr);
1043     return hr;
1044 }
1045 
1046 /**************************************************************************
1047 *    CDrivesFolder::GetDisplayNameOf
1048 */
1049 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
1050 {
1051     LPWSTR pszPath;
1052     HRESULT hr = S_OK;
1053 
1054     TRACE("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
1055     pdump (pidl);
1056 
1057     if (!strRet)
1058         return E_INVALIDARG;
1059 
1060     if (!_ILIsPidlSimple (pidl))
1061     {
1062         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
1063     }
1064     else if (_ILIsSpecialFolder(pidl))
1065     {
1066         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
1067     }
1068     else if (!_ILIsDrive(pidl))
1069     {
1070         ERR("Wrong pidl type\n");
1071         return E_INVALIDARG;
1072     }
1073 
1074     pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
1075     if (!pszPath)
1076         return E_OUTOFMEMORY;
1077 
1078     pszPath[0] = 0;
1079 
1080     _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);    /* append my own path */
1081     /* long view "lw_name (C:)" */
1082     if (!(dwFlags & SHGDN_FORPARSING))
1083     {
1084         WCHAR wszDrive[18] = {0};
1085 
1086         lstrcpynW(wszDrive, pszPath, 4);
1087         pszPath[0] = L'\0';
1088 
1089         if (!SUCCEEDED(getLabelForDrive(wszDrive, pszPath)))
1090         {
1091             DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
1092 
1093             GetVolumeInformationW(wszDrive, pszPath,
1094                                   MAX_PATH - 7,
1095                                   &dwVolumeSerialNumber,
1096                                   &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
1097             pszPath[MAX_PATH-1] = L'\0';
1098 
1099             if (!wcslen(pszPath))
1100             {
1101                 UINT DriveType, ResourceId;
1102                 DriveType = GetDriveTypeW(wszDrive);
1103 
1104                 switch (DriveType)
1105                 {
1106                     case DRIVE_FIXED:
1107                         ResourceId = IDS_DRIVE_FIXED;
1108                         break;
1109                     case DRIVE_REMOTE:
1110                         ResourceId = IDS_DRIVE_NETWORK;
1111                         break;
1112                     case DRIVE_CDROM:
1113                         ResourceId = IDS_DRIVE_CDROM;
1114                         break;
1115                     default:
1116                         ResourceId = 0;
1117                 }
1118 
1119                 if (ResourceId)
1120                 {
1121                     dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
1122                     if (dwFileSystemFlags > MAX_PATH - 7)
1123                         pszPath[MAX_PATH-7] = L'\0';
1124                 }
1125             }
1126         }
1127         wcscat(pszPath, L" (");
1128         wszDrive[2] = L'\0';
1129         wcscat(pszPath, wszDrive);
1130         wcscat(pszPath, L")");
1131     }
1132 
1133     if (SUCCEEDED(hr))
1134     {
1135         strRet->uType = STRRET_WSTR;
1136         strRet->pOleStr = pszPath;
1137     }
1138     else
1139         CoTaskMemFree(pszPath);
1140 
1141     TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1142     return hr;
1143 }
1144 
1145 /**************************************************************************
1146 *  CDrivesFolder::SetNameOf
1147 *  Changes the name of a file object or subfolder, possibly changing its item
1148 *  identifier in the process.
1149 *
1150 * PARAMETERS
1151 *  hwndOwner  [in]   Owner window for output
1152 *  pidl       [in]   simple pidl of item to change
1153 *  lpszName   [in]   the items new display name
1154 *  dwFlags    [in]   SHGNO formatting flags
1155 *  ppidlOut   [out]  simple pidl returned
1156 */
1157 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
1158                                         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
1159 {
1160     WCHAR szName[30];
1161 
1162     if (_ILIsDrive(pidl))
1163     {
1164         if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
1165             SetVolumeLabelW(szName, lpName);
1166         if (pPidlOut)
1167             *pPidlOut = _ILCreateDrive(szName);
1168         return S_OK;
1169     }
1170 
1171     return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
1172 }
1173 
1174 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
1175 {
1176     FIXME("(%p)\n", this);
1177     return E_NOTIMPL;
1178 }
1179 
1180 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1181 {
1182     FIXME("(%p)\n", this);
1183     return E_NOTIMPL;
1184 }
1185 
1186 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
1187 {
1188     TRACE("(%p)\n", this);
1189 
1190     if (pSort)
1191         *pSort = 0;
1192     if (pDisplay)
1193         *pDisplay = 0;
1194     return S_OK;
1195 }
1196 
1197 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF * pcsFlags)
1198 {
1199     TRACE("(%p)\n", this);
1200 
1201     if (!pcsFlags || iColumn >= _countof(MyComputerSFHeader))
1202         return E_INVALIDARG;
1203     *pcsFlags = MyComputerSFHeader[iColumn].colstate;
1204     return S_OK;
1205 }
1206 
1207 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
1208 {
1209     FIXME("(%p)\n", this);
1210     return E_NOTIMPL;
1211 }
1212 
1213 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
1214 {
1215     HRESULT hr;
1216 
1217     TRACE("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1218 
1219     if (!psd || iColumn >= _countof(MyComputerSFHeader))
1220         return E_INVALIDARG;
1221 
1222     if (!pidl)
1223     {
1224         psd->fmt = MyComputerSFHeader[iColumn].fmt;
1225         psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
1226         return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
1227     }
1228     else if (!_ILIsDrive(pidl))
1229     {
1230         switch (MyComputerSFHeader[iColumn].colnameid)
1231         {
1232             case IDS_SHV_COLUMN_NAME:
1233             case IDS_SHV_COLUMN_TYPE:
1234                 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
1235             case IDS_SHV_COLUMN_DISK_CAPACITY:
1236             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1237                 return SHSetStrRet(&psd->str, ""); /* blank col */
1238             case IDS_SHV_COLUMN_COMMENTS:
1239                 return m_regFolder->GetDetailsOf(pidl, 2, psd); /* 2 = comments */
1240             DEFAULT_UNREACHABLE;
1241         }
1242     }
1243     else
1244     {
1245         ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
1246         CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
1247         UINT DriveType = GetDriveTypeA(pszDrive);
1248         if (DriveType > DRIVE_RAMDISK)
1249             DriveType = DRIVE_FIXED;
1250 
1251         switch (MyComputerSFHeader[iColumn].colnameid)
1252         {
1253             case IDS_SHV_COLUMN_NAME:
1254                 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1255                 break;
1256             case IDS_SHV_COLUMN_TYPE:
1257                 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
1258                     hr = SHSetStrRet(&psd->str, IDS_DRIVE_REMOVABLE);
1259                 else
1260                     hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1261                 break;
1262             case IDS_SHV_COLUMN_DISK_CAPACITY:
1263             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1264                 psd->str.cStr[0] = 0x00;
1265                 psd->str.uType = STRRET_CSTR;
1266                 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1267                 {
1268                     GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1269                     if (iColumn == 2)
1270                         StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1271                     else
1272                         StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1273                 }
1274                 hr = S_OK;
1275                 break;
1276             case IDS_SHV_COLUMN_COMMENTS:
1277                 hr = SHSetStrRet(&psd->str, ""); /* FIXME: comments */
1278                 break;
1279             DEFAULT_UNREACHABLE;
1280         }
1281     }
1282 
1283     return hr;
1284 }
1285 
1286 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1287 {
1288     FIXME("(%p)\n", this);
1289     return E_NOTIMPL;
1290 }
1291 
1292 /************************************************************************
1293  *    CDrivesFolder::GetClassID
1294  */
1295 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1296 {
1297     TRACE("(%p)\n", this);
1298 
1299     if (!lpClassId)
1300         return E_POINTER;
1301 
1302     *lpClassId = CLSID_MyComputer;
1303     return S_OK;
1304 }
1305 
1306 /************************************************************************
1307  *    CDrivesFolder::Initialize
1308  *
1309  * NOTES: it makes no sense to change the pidl
1310  */
1311 HRESULT WINAPI CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1312 {
1313     return S_OK;
1314 }
1315 
1316 /**************************************************************************
1317  *    CDrivesFolder::GetCurFolder
1318  */
1319 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1320 {
1321     TRACE("(%p)->(%p)\n", this, pidl);
1322 
1323     if (!pidl)
1324         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1325 
1326     *pidl = ILClone(pidlRoot);
1327     return S_OK;
1328 }
1329 
1330 /**************************************************************************
1331  *    CDrivesFolder::ShouldShow
1332  */
1333 HRESULT WINAPI CDrivesFolder::ShouldShow(IShellFolder *psf, PCIDLIST_ABSOLUTE pidlFolder, PCUITEMID_CHILD pidlItem)
1334 {
1335     if (const CLSID* pClsid = IsRegItem(pidlItem))
1336         return SHELL32_IsShellFolderNamespaceItemHidden(L"HideMyComputerIcons", *pClsid) ? S_FALSE : S_OK;
1337     return S_OK;
1338 }
1339 
1340 /************************************************************************/
1341 /* IContextMenuCB interface */
1342 
1343 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1344 {
1345     enum { IDC_PROPERTIES };
1346     /* no data object means no selection */
1347     if (!pdtobj)
1348     {
1349         if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
1350         {
1351             // "System" properties
1352             return SHELL_ExecuteControlPanelCPL(hwndOwner, L"sysdm.cpl") ? S_OK : E_FAIL;
1353         }
1354         else if (uMsg == DFM_MERGECONTEXTMENU) // TODO: DFM_MERGECONTEXTMENU_BOTTOM
1355         {
1356             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1357             HMENU hpopup = CreatePopupMenu();
1358             _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1359             pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1360             DestroyMenu(hpopup);
1361             return S_OK;
1362         }
1363     }
1364     return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
1365 }
1366