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         HKEY hKeys[16];
919         UINT cKeys = 0;
920         AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
921 
922         DEFCONTEXTMENU dcm;
923         dcm.hwnd = hwndOwner;
924         dcm.pcmcb = this;
925         dcm.pidlFolder = pidlRoot;
926         dcm.psf = this;
927         dcm.cidl = 0;
928         dcm.apidl = NULL;
929         dcm.cKeys = cKeys;
930         dcm.aKeys = hKeys;
931         dcm.punkAssociationInfo = NULL;
932         hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
933     }
934     else if (IsEqualIID(riid, IID_IShellView))
935     {
936             SFV_CREATE sfvparams = { sizeof(SFV_CREATE), this, NULL, static_cast<IShellFolderViewCB*>(this) };
937             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
938     }
939     TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
940     return hr;
941 }
942 
943 /**************************************************************************
944 *  CDrivesFolder::GetAttributesOf
945 */
946 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
947 {
948     TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
949           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
950 
951     if (cidl && !apidl)
952         return E_INVALIDARG;
953 
954     if (*rgfInOut == 0)
955         *rgfInOut = ~0;
956 
957     /* FIXME: always add SFGAO_CANLINK */
958     if(cidl == 0)
959         *rgfInOut &= dwComputerAttributes;
960     else
961     {
962         for (UINT i = 0; i < cidl; ++i)
963         {
964             if (_ILIsDrive(apidl[i]))
965             {
966                 *rgfInOut &= dwDriveAttributes;
967 
968                 if (_ILGetDriveType(apidl[i]) == DRIVE_CDROM)
969                     *rgfInOut &= ~SFGAO_CANRENAME; // CD-ROM drive cannot rename
970             }
971             else if (_ILIsControlPanel(apidl[i]))
972             {
973                 *rgfInOut &= dwControlPanelAttributes;
974             }
975             else if (_ILIsSpecialFolder(*apidl))
976             {
977                 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
978             }
979             else
980             {
981                 ERR("Got unknown pidl type!\n");
982             }
983         }
984     }
985 
986     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
987     *rgfInOut &= ~SFGAO_VALIDATE;
988 
989     TRACE("-- result=0x%08x\n", *rgfInOut);
990     return S_OK;
991 }
992 
993 /**************************************************************************
994 *    CDrivesFolder::GetUIObjectOf
995 *
996 * PARAMETERS
997 *  hwndOwner [in]  Parent window for any output
998 *  cidl      [in]  array size
999 *  apidl     [in]  simple pidl array
1000 *  riid      [in]  Requested Interface
1001 *  prgfInOut [   ] reserved
1002 *  ppvObject [out] Resulting Interface
1003 *
1004 */
1005 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
1006     UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
1007     REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
1008 {
1009     LPVOID pObj = NULL;
1010     HRESULT hr = E_INVALIDARG;
1011 
1012     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
1013           hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
1014 
1015     if (!ppvOut)
1016         return hr;
1017 
1018     *ppvOut = NULL;
1019 
1020     if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
1021     {
1022         if (_ILIsDrive(apidl[0]))
1023             hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
1024         else
1025             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
1026     }
1027     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
1028     {
1029         hr = IDataObject_Constructor(hwndOwner,
1030                                      pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
1031     }
1032     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
1033     {
1034         if (_ILIsDrive(apidl[0]))
1035             hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
1036         else
1037             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
1038     }
1039     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
1040     {
1041         CComPtr<IShellFolder> psfChild;
1042         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1043         if (FAILED_UNEXPECTEDLY(hr))
1044             return hr;
1045 
1046         return psfChild->CreateViewObject(NULL, riid, ppvOut);
1047     }
1048     else
1049         hr = E_NOINTERFACE;
1050 
1051     if (SUCCEEDED(hr) && !pObj)
1052         hr = E_OUTOFMEMORY;
1053 
1054     *ppvOut = pObj;
1055     TRACE("(%p)->hr=0x%08x\n", this, hr);
1056     return hr;
1057 }
1058 
1059 /**************************************************************************
1060 *    CDrivesFolder::GetDisplayNameOf
1061 */
1062 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
1063 {
1064     LPWSTR pszPath;
1065     HRESULT hr = S_OK;
1066 
1067     TRACE("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
1068     pdump (pidl);
1069 
1070     if (!strRet)
1071         return E_INVALIDARG;
1072 
1073     if (!_ILIsPidlSimple (pidl))
1074     {
1075         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
1076     }
1077     else if (_ILIsSpecialFolder(pidl))
1078     {
1079         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
1080     }
1081     else if (!_ILIsDrive(pidl))
1082     {
1083         ERR("Wrong pidl type\n");
1084         return E_INVALIDARG;
1085     }
1086 
1087     pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
1088     if (!pszPath)
1089         return E_OUTOFMEMORY;
1090 
1091     pszPath[0] = 0;
1092 
1093     _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);    /* append my own path */
1094     /* long view "lw_name (C:)" */
1095     if (!(dwFlags & SHGDN_FORPARSING))
1096     {
1097         WCHAR wszDrive[18] = {0};
1098 
1099         lstrcpynW(wszDrive, pszPath, 4);
1100         pszPath[0] = L'\0';
1101 
1102         if (!SUCCEEDED(getLabelForDrive(wszDrive, pszPath)))
1103         {
1104             DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
1105 
1106             GetVolumeInformationW(wszDrive, pszPath,
1107                                   MAX_PATH - 7,
1108                                   &dwVolumeSerialNumber,
1109                                   &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
1110             pszPath[MAX_PATH-1] = L'\0';
1111 
1112             if (!wcslen(pszPath))
1113             {
1114                 UINT DriveType, ResourceId;
1115                 DriveType = GetDriveTypeW(wszDrive);
1116 
1117                 switch (DriveType)
1118                 {
1119                     case DRIVE_FIXED:
1120                         ResourceId = IDS_DRIVE_FIXED;
1121                         break;
1122                     case DRIVE_REMOTE:
1123                         ResourceId = IDS_DRIVE_NETWORK;
1124                         break;
1125                     case DRIVE_CDROM:
1126                         ResourceId = IDS_DRIVE_CDROM;
1127                         break;
1128                     default:
1129                         ResourceId = 0;
1130                 }
1131 
1132                 if (ResourceId)
1133                 {
1134                     dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
1135                     if (dwFileSystemFlags > MAX_PATH - 7)
1136                         pszPath[MAX_PATH-7] = L'\0';
1137                 }
1138             }
1139         }
1140         wcscat(pszPath, L" (");
1141         wszDrive[2] = L'\0';
1142         wcscat(pszPath, wszDrive);
1143         wcscat(pszPath, L")");
1144     }
1145 
1146     if (SUCCEEDED(hr))
1147     {
1148         strRet->uType = STRRET_WSTR;
1149         strRet->pOleStr = pszPath;
1150     }
1151     else
1152         CoTaskMemFree(pszPath);
1153 
1154     TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1155     return hr;
1156 }
1157 
1158 /**************************************************************************
1159 *  CDrivesFolder::SetNameOf
1160 *  Changes the name of a file object or subfolder, possibly changing its item
1161 *  identifier in the process.
1162 *
1163 * PARAMETERS
1164 *  hwndOwner  [in]   Owner window for output
1165 *  pidl       [in]   simple pidl of item to change
1166 *  lpszName   [in]   the items new display name
1167 *  dwFlags    [in]   SHGNO formatting flags
1168 *  ppidlOut   [out]  simple pidl returned
1169 */
1170 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
1171                                         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
1172 {
1173     WCHAR szName[30];
1174 
1175     if (_ILIsDrive(pidl))
1176     {
1177         if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
1178             SetVolumeLabelW(szName, lpName);
1179         if (pPidlOut)
1180             *pPidlOut = _ILCreateDrive(szName);
1181         return S_OK;
1182     }
1183 
1184     return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
1185 }
1186 
1187 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
1188 {
1189     FIXME("(%p)\n", this);
1190     return E_NOTIMPL;
1191 }
1192 
1193 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1194 {
1195     FIXME("(%p)\n", this);
1196     return E_NOTIMPL;
1197 }
1198 
1199 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
1200 {
1201     TRACE("(%p)\n", this);
1202 
1203     if (pSort)
1204         *pSort = 0;
1205     if (pDisplay)
1206         *pDisplay = 0;
1207     return S_OK;
1208 }
1209 
1210 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF * pcsFlags)
1211 {
1212     TRACE("(%p)\n", this);
1213 
1214     if (!pcsFlags || iColumn >= _countof(MyComputerSFHeader))
1215         return E_INVALIDARG;
1216     *pcsFlags = MyComputerSFHeader[iColumn].colstate;
1217     return S_OK;
1218 }
1219 
1220 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
1221 {
1222     FIXME("(%p)\n", this);
1223     return E_NOTIMPL;
1224 }
1225 
1226 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
1227 {
1228     HRESULT hr;
1229 
1230     TRACE("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1231 
1232     if (!psd || iColumn >= _countof(MyComputerSFHeader))
1233         return E_INVALIDARG;
1234 
1235     if (!pidl)
1236     {
1237         psd->fmt = MyComputerSFHeader[iColumn].fmt;
1238         psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
1239         return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
1240     }
1241     else if (!_ILIsDrive(pidl))
1242     {
1243         switch (MyComputerSFHeader[iColumn].colnameid)
1244         {
1245             case IDS_SHV_COLUMN_NAME:
1246             case IDS_SHV_COLUMN_TYPE:
1247                 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
1248             case IDS_SHV_COLUMN_DISK_CAPACITY:
1249             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1250                 return SHSetStrRet(&psd->str, ""); /* blank col */
1251             case IDS_SHV_COLUMN_COMMENTS:
1252                 return m_regFolder->GetDetailsOf(pidl, 2, psd); /* 2 = comments */
1253             DEFAULT_UNREACHABLE;
1254         }
1255     }
1256     else
1257     {
1258         ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
1259         CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
1260         UINT DriveType = GetDriveTypeA(pszDrive);
1261         if (DriveType > DRIVE_RAMDISK)
1262             DriveType = DRIVE_FIXED;
1263 
1264         switch (MyComputerSFHeader[iColumn].colnameid)
1265         {
1266             case IDS_SHV_COLUMN_NAME:
1267                 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1268                 break;
1269             case IDS_SHV_COLUMN_TYPE:
1270                 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
1271                     hr = SHSetStrRet(&psd->str, IDS_DRIVE_REMOVABLE);
1272                 else
1273                     hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1274                 break;
1275             case IDS_SHV_COLUMN_DISK_CAPACITY:
1276             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1277                 psd->str.cStr[0] = 0x00;
1278                 psd->str.uType = STRRET_CSTR;
1279                 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1280                 {
1281                     GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1282                     if (iColumn == 2)
1283                         StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1284                     else
1285                         StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1286                 }
1287                 hr = S_OK;
1288                 break;
1289             case IDS_SHV_COLUMN_COMMENTS:
1290                 hr = SHSetStrRet(&psd->str, ""); /* FIXME: comments */
1291                 break;
1292             DEFAULT_UNREACHABLE;
1293         }
1294     }
1295 
1296     return hr;
1297 }
1298 
1299 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1300 {
1301     FIXME("(%p)\n", this);
1302     return E_NOTIMPL;
1303 }
1304 
1305 /************************************************************************
1306  *    CDrivesFolder::GetClassID
1307  */
1308 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1309 {
1310     TRACE("(%p)\n", this);
1311 
1312     if (!lpClassId)
1313         return E_POINTER;
1314 
1315     *lpClassId = CLSID_MyComputer;
1316     return S_OK;
1317 }
1318 
1319 /************************************************************************
1320  *    CDrivesFolder::Initialize
1321  *
1322  * NOTES: it makes no sense to change the pidl
1323  */
1324 HRESULT WINAPI CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1325 {
1326     return S_OK;
1327 }
1328 
1329 /**************************************************************************
1330  *    CDrivesFolder::GetCurFolder
1331  */
1332 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1333 {
1334     TRACE("(%p)->(%p)\n", this, pidl);
1335 
1336     if (!pidl)
1337         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1338 
1339     *pidl = ILClone(pidlRoot);
1340     return S_OK;
1341 }
1342 
1343 /**************************************************************************
1344  *    CDrivesFolder::ShouldShow
1345  */
1346 HRESULT WINAPI CDrivesFolder::ShouldShow(IShellFolder *psf, PCIDLIST_ABSOLUTE pidlFolder, PCUITEMID_CHILD pidlItem)
1347 {
1348     if (const CLSID* pClsid = IsRegItem(pidlItem))
1349         return SHELL32_IsShellFolderNamespaceItemHidden(L"HideMyComputerIcons", *pClsid) ? S_FALSE : S_OK;
1350     return S_OK;
1351 }
1352 
1353 /************************************************************************/
1354 /* IContextMenuCB interface */
1355 
1356 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1357 {
1358     enum { IDC_PROPERTIES };
1359     /* no data object means no selection */
1360     if (!pdtobj)
1361     {
1362         if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
1363         {
1364             // "System" properties
1365             return SHELL_ExecuteControlPanelCPL(hwndOwner, L"sysdm.cpl") ? S_OK : E_FAIL;
1366         }
1367         else if (uMsg == DFM_MERGECONTEXTMENU)
1368         {
1369             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1370             HMENU hpopup = CreatePopupMenu();
1371             _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1372             pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1373             DestroyMenu(hpopup);
1374             return S_OK;
1375         }
1376     }
1377     return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
1378 }
1379