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