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