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