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 #define CMDID_FORMAT        1
290 #define CMDID_EJECT         2
291 #define CMDID_DISCONNECT    3
292 
293     if (uMsg == DFM_MERGECONTEXTMENU)
294     {
295         QCMINFO *pqcminfo = (QCMINFO *)lParam;
296 
297         UINT idCmdFirst = pqcminfo->idCmdFirst;
298         if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE)
299         {
300             /* add separator and Format */
301             UINT idCmd = idCmdFirst + CMDID_FORMAT;
302             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
303             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
304         }
305         if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM)
306         {
307             /* add separator and Eject */
308             UINT idCmd = idCmdFirst + CMDID_EJECT;
309             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
310             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED);
311         }
312         if (nDriveType == DRIVE_REMOTE)
313         {
314             /* add separator and Disconnect */
315             UINT idCmd = idCmdFirst + CMDID_DISCONNECT;
316             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
317             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED);
318         }
319 
320         pqcminfo->idCmdFirst += 3;
321     }
322     else if (uMsg == DFM_INVOKECOMMAND)
323     {
324         WCHAR wszBuf[4] = L"A:\\";
325         wszBuf[0] = (WCHAR)szDrive[0];
326 
327         INT nStringID = 0;
328         DWORD dwError = NO_ERROR;
329 
330         if (wParam == DFM_CMD_PROPERTIES)
331         {
332             hr = SH_ShowDriveProperties(wszBuf, pidlFolder, apidl);
333             if (FAILED(hr))
334             {
335                 dwError = ERROR_CAN_NOT_COMPLETE;
336                 nStringID = IDS_CANTSHOWPROPERTIES;
337             }
338         }
339         else
340         {
341             if (wParam == CMDID_FORMAT)
342             {
343                 hr = DoFormatDrive(hwnd, szDrive[0] - 'A');
344             }
345             else if (wParam == CMDID_EJECT)
346             {
347                 /* do eject */
348                 WCHAR physical[10];
349                 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
350 
351                 if (DoEjectDrive(physical, nDriveType, &nStringID))
352                 {
353                     SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
354                 }
355                 else
356                 {
357                     dwError = GetLastError();
358                 }
359             }
360             else if (wParam == CMDID_DISCONNECT)
361             {
362                 /* do disconnect */
363                 wszBuf[2] = UNICODE_NULL;
364                 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
365                 if (dwError == NO_ERROR)
366                 {
367                     SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
368                 }
369                 else
370                 {
371                     nStringID = IDS_CANTDISCONNECT;
372                 }
373             }
374         }
375 
376         if (nStringID != 0)
377         {
378             /* show error message */
379             WCHAR szFormat[128], szMessage[128];
380             LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
381             wsprintfW(szMessage, szFormat, dwError);
382             MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
383         }
384     }
385 
386     SHFree(pidlFolder);
387     _ILFreeaPidl(apidl, cidl);
388 
389     return hr;
390 }
391 
392 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
393                                           HWND hwnd,
394                                           UINT cidl,
395                                           PCUITEMID_CHILD_ARRAY apidl,
396                                           IShellFolder *psf,
397                                           IContextMenu **ppcm)
398 {
399     HKEY hKeys[2];
400     UINT cKeys = 0;
401     AddClassKeyToArray(L"Drive", hKeys, &cKeys);
402     AddClassKeyToArray(L"Folder", hKeys, &cKeys);
403 
404     return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
405 }
406 
407 static HRESULT
408 getIconLocationForDrive(IShellFolder *psf, PCITEMID_CHILD pidl, UINT uFlags,
409                         LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
410 {
411     WCHAR wszPath[MAX_PATH];
412     WCHAR wszAutoRunInfPath[MAX_PATH];
413     WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
414     static const WCHAR wszAutoRunInf[] = { 'a','u','t','o','r','u','n','.','i','n','f',0 };
415     static const WCHAR wszAutoRun[] = { 'a','u','t','o','r','u','n',0 };
416 
417     // get path
418     if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
419         return E_FAIL;
420     if (!PathIsDirectoryW(wszPath))
421         return E_FAIL;
422 
423     // build the full path of autorun.inf
424     StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
425     PathAppendW(wszAutoRunInfPath, wszAutoRunInf);
426 
427     // autorun.inf --> wszValue
428     if (GetPrivateProfileStringW(wszAutoRun, L"icon", NULL, wszValue, _countof(wszValue),
429                                  wszAutoRunInfPath) && wszValue[0] != 0)
430     {
431         // wszValue --> wszTemp
432         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
433 
434         // parse the icon location
435         *piIndex = PathParseIconLocationW(wszTemp);
436 
437         // wszPath + wszTemp --> wszPath
438         if (PathIsRelativeW(wszTemp))
439             PathAppendW(wszPath, wszTemp);
440         else
441             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
442 
443         // wszPath --> szIconFile
444         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
445 
446         return S_OK;
447     }
448 
449     return E_FAIL;
450 }
451 
452 BOOL IsDriveFloppyA(LPCSTR pszDriveRoot);
453 
454 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
455 {
456     CComPtr<IDefaultExtractIconInit> initIcon;
457     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
458     if (FAILED_UNEXPECTEDLY(hr))
459         return hr;
460 
461     CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
462     UINT DriveType = GetDriveTypeA(pszDrive);
463     if (DriveType > DRIVE_RAMDISK)
464         DriveType = DRIVE_FIXED;
465 
466     WCHAR wTemp[MAX_PATH];
467     int icon_idx;
468     UINT flags = 0;
469     if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
470         (HCR_GetIconW(L"Drive", wTemp, NULL, MAX_PATH, &icon_idx)))
471     {
472         initIcon->SetNormalIcon(wTemp, icon_idx);
473     }
474     else if (SUCCEEDED(getIconLocationForDrive(psf, pidl, 0, wTemp, _countof(wTemp),
475                                                &icon_idx, &flags)))
476     {
477         initIcon->SetNormalIcon(wTemp, icon_idx);
478     }
479     else
480     {
481         if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
482         {
483             icon_idx = IDI_SHELL_REMOVEABLE;
484         }
485         else
486         {
487             icon_idx = iDriveIconIds[DriveType];
488         }
489         initIcon->SetNormalIcon(swShell32Name, -icon_idx);
490     }
491 
492     return initIcon->QueryInterface(riid, ppvOut);
493 }
494 
495 class CDrivesFolderEnum :
496     public CEnumIDListBase
497 {
498     public:
499         HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
500         {
501             /* enumerate the folders */
502             if (dwFlags & SHCONTF_FOLDERS)
503             {
504                 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
505                 DWORD dwDrivemap = GetLogicalDrives();
506 
507                 while (wszDriveName[0] <= 'Z')
508                 {
509                     if(dwDrivemap & 0x00000001L)
510                         AddToEnumList(_ILCreateDrive(wszDriveName));
511                     wszDriveName[0]++;
512                     dwDrivemap = dwDrivemap >> 1;
513                 }
514             }
515 
516             /* Enumerate the items of the reg folder */
517             AppendItemsFromEnumerator(pRegEnumerator);
518 
519             return S_OK;
520         }
521 
522         BEGIN_COM_MAP(CDrivesFolderEnum)
523         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
524         END_COM_MAP()
525 };
526 
527 /***********************************************************************
528 *   IShellFolder [MyComputer] implementation
529 */
530 
531 static const shvheader MyComputerSFHeader[] = {
532     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
533     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
534     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
535     {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
536     {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
537 };
538 
539 #define MYCOMPUTERSHELLVIEWCOLUMNS 5
540 
541 static const DWORD dwComputerAttributes =
542     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
543     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
544 static const DWORD dwControlPanelAttributes =
545     SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
546 static const DWORD dwDriveAttributes =
547     SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
548     SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
549 
550 CDrivesFolder::CDrivesFolder()
551 {
552     pidlRoot = NULL;
553 }
554 
555 CDrivesFolder::~CDrivesFolder()
556 {
557     TRACE ("-- destroying IShellFolder(%p)\n", this);
558     SHFree(pidlRoot);
559 }
560 
561 HRESULT WINAPI CDrivesFolder::FinalConstruct()
562 {
563     pidlRoot = _ILCreateMyComputer();    /* my qualified pidl */
564     if (pidlRoot == NULL)
565         return E_OUTOFMEMORY;
566 
567     HRESULT hr = CRegFolder_CreateInstance(&CLSID_MyComputer,
568                                            pidlRoot,
569                                            L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
570                                            L"MyComputer",
571                                            IID_PPV_ARG(IShellFolder2, &m_regFolder));
572 
573     return hr;
574 }
575 
576 /**************************************************************************
577 *    CDrivesFolder::ParseDisplayName
578 */
579 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
580         DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes)
581 {
582     HRESULT hr = E_INVALIDARG;
583     LPCWSTR szNext = NULL;
584     LPITEMIDLIST pidlTemp = NULL;
585     INT nDriveNumber;
586 
587     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
588           hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
589           pchEaten, ppidl, pdwAttributes);
590 
591     *ppidl = 0;
592     if (pchEaten)
593         *pchEaten = 0;        /* strange but like the original */
594 
595     /* handle CLSID paths */
596     if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
597         return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
598 
599     nDriveNumber = PathGetDriveNumberW(lpszDisplayName);
600     if (nDriveNumber < 0)
601         return E_INVALIDARG;
602 
603     /* check if this drive actually exists */
604     if ((::GetLogicalDrives() & (1 << nDriveNumber)) == 0)
605     {
606         return HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE);
607     }
608 
609     pidlTemp = _ILCreateDrive(lpszDisplayName);
610     if (!pidlTemp)
611         return E_OUTOFMEMORY;
612 
613     if (lpszDisplayName[2] == L'\\')
614     {
615         szNext = &lpszDisplayName[3];
616     }
617 
618     if (szNext && *szNext)
619     {
620         hr = SHELL32_ParseNextElement (this, hwndOwner, pbc, &pidlTemp,
621                                        (LPOLESTR) szNext, pchEaten, pdwAttributes);
622     }
623     else
624     {
625         hr = S_OK;
626         if (pdwAttributes && *pdwAttributes)
627         {
628             if (_ILIsDrive(pidlTemp))
629                 *pdwAttributes &= dwDriveAttributes;
630             else if (_ILIsSpecialFolder(pidlTemp))
631                 m_regFolder->GetAttributesOf(1, &pidlTemp, pdwAttributes);
632             else
633                 ERR("Got an unkown pidl here!\n");
634         }
635     }
636 
637     *ppidl = pidlTemp;
638 
639     TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
640 
641     return hr;
642 }
643 
644 /**************************************************************************
645 *        CDrivesFolder::EnumObjects
646 */
647 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
648 {
649     CComPtr<IEnumIDList> pRegEnumerator;
650     m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
651 
652     return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
653 }
654 
655 /**************************************************************************
656 *        CDrivesFolder::BindToObject
657 */
658 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
659 {
660     TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
661           pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
662 
663     if (!pidl)
664         return E_INVALIDARG;
665 
666     if (_ILIsSpecialFolder(pidl))
667         return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
668 
669     CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
670 
671     PERSIST_FOLDER_TARGET_INFO pfti = {0};
672     pfti.dwAttributes = -1;
673     pfti.csidl = -1;
674     pfti.szTargetParsingName[0] = *pchDrive;
675     pfti.szTargetParsingName[1] = L':';
676     pfti.szTargetParsingName[2] = L'\\';
677 
678     HRESULT hr = SHELL32_BindToSF(pidlRoot,
679                                   &pfti,
680                                   pidl,
681                                   &CLSID_ShellFSFolder,
682                                   riid,
683                                   ppvOut);
684     if (FAILED_UNEXPECTEDLY(hr))
685         return hr;
686 
687     return S_OK;
688 }
689 
690 /**************************************************************************
691 *    CDrivesFolder::BindToStorage
692 */
693 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
694 {
695     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
696           pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
697 
698     *ppvOut = NULL;
699     return E_NOTIMPL;
700 }
701 
702 /**************************************************************************
703 *     CDrivesFolder::CompareIDs
704 */
705 
706 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
707 {
708     HRESULT hres;
709 
710     if (!pidl1 || !pidl2)
711     {
712         ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
713         return E_INVALIDARG;
714     }
715 
716     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
717         return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
718 
719     if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || LOWORD(lParam) >= MYCOMPUTERSHELLVIEWCOLUMNS)
720         return E_INVALIDARG;
721 
722     CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
723     CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
724 
725     int result;
726     switch(LOWORD(lParam))
727     {
728         case 0:        /* name */
729         {
730             result = stricmp(pszDrive1, pszDrive2);
731             hres = MAKE_COMPARE_HRESULT(result);
732             break;
733         }
734         case 1:        /* comments */
735             hres = MAKE_COMPARE_HRESULT(0);
736             break;
737         case 2:        /* Type */
738         {
739             /* We want to return immediately because SHELL32_CompareDetails also compares children. */
740             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
741         }
742         case 3:       /* Size */
743         case 4:       /* Size Available */
744         {
745             ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
746 
747             if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
748                 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
749             else
750                 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
751 
752             if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
753                 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
754             else
755                 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
756 
757             LARGE_INTEGER Diff;
758             if (lParam == 3) /* Size */
759                 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
760             else /* Size available */
761                 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
762 
763             hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
764             break;
765         }
766         default:
767             return E_INVALIDARG;
768     }
769 
770     if (HRESULT_CODE(hres) == 0)
771         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
772 
773     return hres;
774 }
775 
776 /**************************************************************************
777 *    CDrivesFolder::CreateViewObject
778 */
779 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
780 {
781     CComPtr<IShellView> pShellView;
782     HRESULT hr = E_INVALIDARG;
783 
784     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
785           hwndOwner, shdebugstr_guid (&riid), ppvOut);
786 
787     if (!ppvOut)
788         return hr;
789 
790     *ppvOut = NULL;
791 
792     if (IsEqualIID(riid, IID_IDropTarget))
793     {
794         WARN("IDropTarget not implemented\n");
795         hr = E_NOTIMPL;
796     }
797     else if (IsEqualIID(riid, IID_IContextMenu))
798     {
799         HKEY hKeys[16];
800         UINT cKeys = 0;
801         AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
802 
803         DEFCONTEXTMENU dcm;
804         dcm.hwnd = hwndOwner;
805         dcm.pcmcb = this;
806         dcm.pidlFolder = pidlRoot;
807         dcm.psf = this;
808         dcm.cidl = 0;
809         dcm.apidl = NULL;
810         dcm.cKeys = cKeys;
811         dcm.aKeys = hKeys;
812         dcm.punkAssociationInfo = NULL;
813         hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
814     }
815     else if (IsEqualIID(riid, IID_IShellView))
816     {
817             SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
818             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
819     }
820     TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
821     return hr;
822 }
823 
824 static BOOL _ILIsControlPanel(LPCITEMIDLIST pidl)
825 {
826     GUID *guid = _ILGetGUIDPointer(pidl);
827 
828     TRACE("(%p)\n", pidl);
829 
830     if (guid)
831         return IsEqualIID(*guid, CLSID_ControlPanel);
832     return FALSE;
833 }
834 
835 /**************************************************************************
836 *  CDrivesFolder::GetAttributesOf
837 */
838 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
839 {
840     TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
841            this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
842 
843     if (cidl && !apidl)
844         return E_INVALIDARG;
845 
846     if (*rgfInOut == 0)
847         *rgfInOut = ~0;
848 
849     /* FIXME: always add SFGAO_CANLINK */
850     if(cidl == 0)
851         *rgfInOut &= dwComputerAttributes;
852     else
853     {
854         for (UINT i = 0; i < cidl; ++i)
855         {
856             if (_ILIsDrive(apidl[i]))
857                 *rgfInOut &= dwDriveAttributes;
858             else if (_ILIsControlPanel(apidl[i]))
859                 *rgfInOut &= dwControlPanelAttributes;
860             else if (_ILIsSpecialFolder(*apidl))
861                 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
862             else
863                 ERR("Got unknown pidl type!\n");
864         }
865     }
866 
867     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
868     *rgfInOut &= ~SFGAO_VALIDATE;
869 
870     TRACE ("-- result=0x%08x\n", *rgfInOut);
871     return S_OK;
872 }
873 
874 /**************************************************************************
875 *    CDrivesFolder::GetUIObjectOf
876 *
877 * PARAMETERS
878 *  hwndOwner [in]  Parent window for any output
879 *  cidl      [in]  array size
880 *  apidl     [in]  simple pidl array
881 *  riid      [in]  Requested Interface
882 *  prgfInOut [   ] reserved
883 *  ppvObject [out] Resulting Interface
884 *
885 */
886 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
887     UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
888     REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
889 {
890     LPVOID pObj = NULL;
891     HRESULT hr = E_INVALIDARG;
892 
893     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
894           hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
895 
896     if (!ppvOut)
897         return hr;
898 
899     *ppvOut = NULL;
900 
901     if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
902     {
903         if (_ILIsDrive(apidl[0]))
904             hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
905         else
906             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
907     }
908     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
909     {
910         hr = IDataObject_Constructor (hwndOwner,
911                                       pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
912     }
913     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
914     {
915         if (_ILIsDrive(apidl[0]))
916             hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
917         else
918             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
919     }
920     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
921     {
922         CComPtr<IShellFolder> psfChild;
923         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
924         if (FAILED_UNEXPECTEDLY(hr))
925             return hr;
926 
927         return psfChild->CreateViewObject(NULL, riid, ppvOut);
928     }
929     else
930         hr = E_NOINTERFACE;
931 
932     if (SUCCEEDED(hr) && !pObj)
933         hr = E_OUTOFMEMORY;
934 
935     *ppvOut = pObj;
936     TRACE ("(%p)->hr=0x%08x\n", this, hr);
937     return hr;
938 }
939 
940 /**************************************************************************
941 *    CDrivesFolder::GetDisplayNameOf
942 */
943 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
944 {
945     LPWSTR pszPath;
946     HRESULT hr = S_OK;
947 
948     TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
949     pdump (pidl);
950 
951     if (!strRet)
952         return E_INVALIDARG;
953 
954     if (!_ILIsPidlSimple (pidl))
955     {
956         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
957     }
958     else if (_ILIsSpecialFolder(pidl))
959     {
960         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
961     }
962     else if (!_ILIsDrive(pidl))
963     {
964         ERR("Wrong pidl type\n");
965         return E_INVALIDARG;
966     }
967 
968     pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
969     if (!pszPath)
970         return E_OUTOFMEMORY;
971 
972     pszPath[0] = 0;
973 
974     _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);    /* append my own path */
975     /* long view "lw_name (C:)" */
976     if (!(dwFlags & SHGDN_FORPARSING))
977     {
978         WCHAR wszDrive[18] = {0};
979         DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
980         static const WCHAR wszOpenBracket[] = {' ', '(', 0};
981         static const WCHAR wszCloseBracket[] = {')', 0};
982 
983         lstrcpynW(wszDrive, pszPath, 4);
984         pszPath[0] = L'\0';
985         GetVolumeInformationW(wszDrive, pszPath,
986                                 MAX_PATH - 7,
987                                 &dwVolumeSerialNumber,
988                                 &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
989         pszPath[MAX_PATH-1] = L'\0';
990         if (!wcslen(pszPath))
991         {
992             UINT DriveType, ResourceId;
993             DriveType = GetDriveTypeW(wszDrive);
994             switch(DriveType)
995             {
996                 case DRIVE_FIXED:
997                     ResourceId = IDS_DRIVE_FIXED;
998                     break;
999                 case DRIVE_REMOTE:
1000                     ResourceId = IDS_DRIVE_NETWORK;
1001                     break;
1002                 case DRIVE_CDROM:
1003                     ResourceId = IDS_DRIVE_CDROM;
1004                     break;
1005                 default:
1006                     ResourceId = 0;
1007             }
1008             if (ResourceId)
1009             {
1010                 dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
1011                 if (dwFileSystemFlags > MAX_PATH - 7)
1012                     pszPath[MAX_PATH-7] = L'\0';
1013             }
1014         }
1015         wcscat (pszPath, wszOpenBracket);
1016         wszDrive[2] = L'\0';
1017         wcscat (pszPath, wszDrive);
1018         wcscat (pszPath, wszCloseBracket);
1019     }
1020 
1021     if (SUCCEEDED(hr))
1022     {
1023         strRet->uType = STRRET_WSTR;
1024         strRet->pOleStr = pszPath;
1025     }
1026     else
1027         CoTaskMemFree(pszPath);
1028 
1029     TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1030     return hr;
1031 }
1032 
1033 /**************************************************************************
1034 *  CDrivesFolder::SetNameOf
1035 *  Changes the name of a file object or subfolder, possibly changing its item
1036 *  identifier in the process.
1037 *
1038 * PARAMETERS
1039 *  hwndOwner  [in]   Owner window for output
1040 *  pidl       [in]   simple pidl of item to change
1041 *  lpszName   [in]   the items new display name
1042 *  dwFlags    [in]   SHGNO formatting flags
1043 *  ppidlOut   [out]  simple pidl returned
1044 */
1045 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
1046                                         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
1047 {
1048     WCHAR szName[30];
1049 
1050     if (_ILIsDrive(pidl))
1051     {
1052         if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
1053             SetVolumeLabelW(szName, lpName);
1054         if (pPidlOut)
1055             *pPidlOut = _ILCreateDrive(szName);
1056         return S_OK;
1057     }
1058 
1059     return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
1060 }
1061 
1062 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
1063 {
1064     FIXME ("(%p)\n", this);
1065     return E_NOTIMPL;
1066 }
1067 
1068 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1069 {
1070     FIXME ("(%p)\n", this);
1071     return E_NOTIMPL;
1072 }
1073 
1074 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
1075 {
1076     TRACE ("(%p)\n", this);
1077 
1078     if (pSort)
1079         *pSort = 0;
1080     if (pDisplay)
1081         *pDisplay = 0;
1082     return S_OK;
1083 }
1084 
1085 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags)
1086 {
1087     TRACE ("(%p)\n", this);
1088 
1089     if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
1090         return E_INVALIDARG;
1091     *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags;
1092     return S_OK;
1093 }
1094 
1095 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
1096 {
1097     FIXME ("(%p)\n", this);
1098     return E_NOTIMPL;
1099 }
1100 
1101 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
1102 {
1103     HRESULT hr;
1104 
1105     TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1106 
1107     if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
1108         return E_INVALIDARG;
1109 
1110     if (!pidl)
1111     {
1112         psd->fmt = MyComputerSFHeader[iColumn].fmt;
1113         psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
1114         return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
1115     }
1116     else if (!_ILIsDrive(pidl))
1117     {
1118         return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
1119     }
1120     else
1121     {
1122         ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
1123         CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
1124         UINT DriveType = GetDriveTypeA(pszDrive);
1125         if (DriveType > DRIVE_RAMDISK)
1126             DriveType = DRIVE_FIXED;
1127 
1128         switch (iColumn)
1129         {
1130             case 0:        /* name */
1131                 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1132                 break;
1133             case 1:                /* FIXME: comments */
1134                 hr = SHSetStrRet(&psd->str, "");
1135                 break;
1136             case 2:        /* type */
1137                 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
1138                     hr = SHSetStrRet(&psd->str, IDS_DRIVE_REMOVABLE);
1139                 else
1140                     hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1141                 break;
1142             case 3:        /* total size */
1143             case 4:        /* free size */
1144                 psd->str.cStr[0] = 0x00;
1145                 psd->str.uType = STRRET_CSTR;
1146                 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1147                 {
1148                     GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1149                     if (iColumn == 3)
1150                         StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1151                     else
1152                         StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1153                 }
1154                 hr = S_OK;
1155                 break;
1156         }
1157     }
1158 
1159     return hr;
1160 }
1161 
1162 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1163 {
1164     FIXME("(%p)\n", this);
1165     return E_NOTIMPL;
1166 }
1167 
1168 /************************************************************************
1169  *    CDrivesFolder::GetClassID
1170  */
1171 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1172 {
1173     TRACE ("(%p)\n", this);
1174 
1175     if (!lpClassId)
1176         return E_POINTER;
1177 
1178     *lpClassId = CLSID_MyComputer;
1179     return S_OK;
1180 }
1181 
1182 /************************************************************************
1183  *    CDrivesFolder::Initialize
1184  *
1185  * NOTES: it makes no sense to change the pidl
1186  */
1187 HRESULT WINAPI CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1188 {
1189     return S_OK;
1190 }
1191 
1192 /**************************************************************************
1193  *    CDrivesFolder::GetCurFolder
1194  */
1195 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1196 {
1197     TRACE("(%p)->(%p)\n", this, pidl);
1198 
1199     if (!pidl)
1200         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1201 
1202     *pidl = ILClone(pidlRoot);
1203     return S_OK;
1204 }
1205 
1206 /************************************************************************/
1207 /* IContextMenuCB interface */
1208 
1209 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1210 {
1211     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1212         return S_OK;
1213 
1214     /* no data object means no selection */
1215     if (!pdtobj)
1216     {
1217         if (uMsg == DFM_INVOKECOMMAND && wParam == 1)   // #1
1218         {
1219             // "System" properties
1220             ShellExecuteW(hwndOwner,
1221                           NULL,
1222                           L"rundll32.exe",
1223                           L"shell32.dll,Control_RunDLL sysdm.cpl",
1224                           NULL,
1225                           SW_SHOWNORMAL);
1226         }
1227         else if (uMsg == DFM_MERGECONTEXTMENU)
1228         {
1229             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1230             HMENU hpopup = CreatePopupMenu();
1231             _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_SEPARATOR, NULL, MFS_ENABLED); // #0
1232             _InsertMenuItemW(hpopup, 1, TRUE, 1, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); // #1
1233             Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1234             DestroyMenu(hpopup);
1235         }
1236 
1237         return S_OK;
1238     }
1239 
1240     if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1241         return S_OK;
1242 
1243     return Shell_DefaultContextMenuCallBack(this, pdtobj);
1244 }
1245