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             // pdtobj should be valid at this point!
333             ATLASSERT(pdtobj);
334             hr = SH_ShowDriveProperties(wszBuf, pdtobj);
335             if (FAILED(hr))
336             {
337                 dwError = ERROR_CAN_NOT_COMPLETE;
338                 nStringID = IDS_CANTSHOWPROPERTIES;
339             }
340         }
341         else
342         {
343             if (wParam == CMDID_FORMAT)
344             {
345                 hr = DoFormatDrive(hwnd, szDrive[0] - 'A');
346             }
347             else if (wParam == CMDID_EJECT)
348             {
349                 /* do eject */
350                 WCHAR physical[10];
351                 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
352 
353                 if (DoEjectDrive(physical, nDriveType, &nStringID))
354                 {
355                     SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
356                 }
357                 else
358                 {
359                     dwError = GetLastError();
360                 }
361             }
362             else if (wParam == CMDID_DISCONNECT)
363             {
364                 /* do disconnect */
365                 wszBuf[2] = UNICODE_NULL;
366                 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
367                 if (dwError == NO_ERROR)
368                 {
369                     SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
370                 }
371                 else
372                 {
373                     nStringID = IDS_CANTDISCONNECT;
374                 }
375             }
376         }
377 
378         if (nStringID != 0)
379         {
380             /* show error message */
381             WCHAR szFormat[128], szMessage[128];
382             LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
383             wsprintfW(szMessage, szFormat, dwError);
384             MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
385         }
386     }
387 
388     SHFree(pidlFolder);
389     _ILFreeaPidl(apidl, cidl);
390 
391     return hr;
392 }
393 
394 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
395                                           HWND hwnd,
396                                           UINT cidl,
397                                           PCUITEMID_CHILD_ARRAY apidl,
398                                           IShellFolder *psf,
399                                           IContextMenu **ppcm)
400 {
401     HKEY hKeys[2];
402     UINT cKeys = 0;
403     AddClassKeyToArray(L"Drive", hKeys, &cKeys);
404     AddClassKeyToArray(L"Folder", hKeys, &cKeys);
405 
406     return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
407 }
408 
409 static HRESULT
410 getIconLocationForDrive(IShellFolder *psf, PCITEMID_CHILD pidl, UINT uFlags,
411                         LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
412 {
413     WCHAR wszPath[MAX_PATH];
414     WCHAR wszAutoRunInfPath[MAX_PATH];
415     WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
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, L"autorun.inf");
426 
427     // autorun.inf --> wszValue
428     if (GetPrivateProfileStringW(L"autorun", 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_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
534     {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
535     {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
536     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 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:        /* Type */
735         {
736             /* We want to return immediately because SHELL32_CompareDetails also compares children. */
737             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
738         }
739         case 2:       /* Size */
740         case 3:       /* Size Available */
741         {
742             ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
743 
744             if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
745                 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
746             else
747                 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
748 
749             if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
750                 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
751             else
752                 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
753 
754             LARGE_INTEGER Diff;
755             if (lParam == 3) /* Size */
756                 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
757             else /* Size available */
758                 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
759 
760             hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
761             break;
762         }
763         case 4:        /* comments */
764             hres = MAKE_COMPARE_HRESULT(0);
765             break;
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 /**************************************************************************
825 *  CDrivesFolder::GetAttributesOf
826 */
827 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
828 {
829     TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
830            this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
831 
832     if (cidl && !apidl)
833         return E_INVALIDARG;
834 
835     if (*rgfInOut == 0)
836         *rgfInOut = ~0;
837 
838     /* FIXME: always add SFGAO_CANLINK */
839     if(cidl == 0)
840         *rgfInOut &= dwComputerAttributes;
841     else
842     {
843         for (UINT i = 0; i < cidl; ++i)
844         {
845             if (_ILIsDrive(apidl[i]))
846                 *rgfInOut &= dwDriveAttributes;
847             else if (_ILIsControlPanel(apidl[i]))
848                 *rgfInOut &= dwControlPanelAttributes;
849             else if (_ILIsSpecialFolder(*apidl))
850                 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
851             else
852                 ERR("Got unknown pidl type!\n");
853         }
854     }
855 
856     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
857     *rgfInOut &= ~SFGAO_VALIDATE;
858 
859     TRACE ("-- result=0x%08x\n", *rgfInOut);
860     return S_OK;
861 }
862 
863 /**************************************************************************
864 *    CDrivesFolder::GetUIObjectOf
865 *
866 * PARAMETERS
867 *  hwndOwner [in]  Parent window for any output
868 *  cidl      [in]  array size
869 *  apidl     [in]  simple pidl array
870 *  riid      [in]  Requested Interface
871 *  prgfInOut [   ] reserved
872 *  ppvObject [out] Resulting Interface
873 *
874 */
875 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
876     UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
877     REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
878 {
879     LPVOID pObj = NULL;
880     HRESULT hr = E_INVALIDARG;
881 
882     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
883           hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
884 
885     if (!ppvOut)
886         return hr;
887 
888     *ppvOut = NULL;
889 
890     if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
891     {
892         if (_ILIsDrive(apidl[0]))
893             hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
894         else
895             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
896     }
897     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
898     {
899         hr = IDataObject_Constructor (hwndOwner,
900                                       pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
901     }
902     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
903     {
904         if (_ILIsDrive(apidl[0]))
905             hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
906         else
907             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
908     }
909     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
910     {
911         CComPtr<IShellFolder> psfChild;
912         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
913         if (FAILED_UNEXPECTEDLY(hr))
914             return hr;
915 
916         return psfChild->CreateViewObject(NULL, riid, ppvOut);
917     }
918     else
919         hr = E_NOINTERFACE;
920 
921     if (SUCCEEDED(hr) && !pObj)
922         hr = E_OUTOFMEMORY;
923 
924     *ppvOut = pObj;
925     TRACE ("(%p)->hr=0x%08x\n", this, hr);
926     return hr;
927 }
928 
929 /**************************************************************************
930 *    CDrivesFolder::GetDisplayNameOf
931 */
932 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
933 {
934     LPWSTR pszPath;
935     HRESULT hr = S_OK;
936 
937     TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
938     pdump (pidl);
939 
940     if (!strRet)
941         return E_INVALIDARG;
942 
943     if (!_ILIsPidlSimple (pidl))
944     {
945         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
946     }
947     else if (_ILIsSpecialFolder(pidl))
948     {
949         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
950     }
951     else if (!_ILIsDrive(pidl))
952     {
953         ERR("Wrong pidl type\n");
954         return E_INVALIDARG;
955     }
956 
957     pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
958     if (!pszPath)
959         return E_OUTOFMEMORY;
960 
961     pszPath[0] = 0;
962 
963     _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);    /* append my own path */
964     /* long view "lw_name (C:)" */
965     if (!(dwFlags & SHGDN_FORPARSING))
966     {
967         WCHAR wszDrive[18] = {0};
968         DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
969 
970         lstrcpynW(wszDrive, pszPath, 4);
971         pszPath[0] = L'\0';
972         GetVolumeInformationW(wszDrive, pszPath,
973                                 MAX_PATH - 7,
974                                 &dwVolumeSerialNumber,
975                                 &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
976         pszPath[MAX_PATH-1] = L'\0';
977         if (!wcslen(pszPath))
978         {
979             UINT DriveType, ResourceId;
980             DriveType = GetDriveTypeW(wszDrive);
981             switch(DriveType)
982             {
983                 case DRIVE_FIXED:
984                     ResourceId = IDS_DRIVE_FIXED;
985                     break;
986                 case DRIVE_REMOTE:
987                     ResourceId = IDS_DRIVE_NETWORK;
988                     break;
989                 case DRIVE_CDROM:
990                     ResourceId = IDS_DRIVE_CDROM;
991                     break;
992                 default:
993                     ResourceId = 0;
994             }
995             if (ResourceId)
996             {
997                 dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
998                 if (dwFileSystemFlags > MAX_PATH - 7)
999                     pszPath[MAX_PATH-7] = L'\0';
1000             }
1001         }
1002         wcscat (pszPath, L" (");
1003         wszDrive[2] = L'\0';
1004         wcscat (pszPath, wszDrive);
1005         wcscat (pszPath, L")");
1006     }
1007 
1008     if (SUCCEEDED(hr))
1009     {
1010         strRet->uType = STRRET_WSTR;
1011         strRet->pOleStr = pszPath;
1012     }
1013     else
1014         CoTaskMemFree(pszPath);
1015 
1016     TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1017     return hr;
1018 }
1019 
1020 /**************************************************************************
1021 *  CDrivesFolder::SetNameOf
1022 *  Changes the name of a file object or subfolder, possibly changing its item
1023 *  identifier in the process.
1024 *
1025 * PARAMETERS
1026 *  hwndOwner  [in]   Owner window for output
1027 *  pidl       [in]   simple pidl of item to change
1028 *  lpszName   [in]   the items new display name
1029 *  dwFlags    [in]   SHGNO formatting flags
1030 *  ppidlOut   [out]  simple pidl returned
1031 */
1032 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
1033                                         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
1034 {
1035     WCHAR szName[30];
1036 
1037     if (_ILIsDrive(pidl))
1038     {
1039         if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
1040             SetVolumeLabelW(szName, lpName);
1041         if (pPidlOut)
1042             *pPidlOut = _ILCreateDrive(szName);
1043         return S_OK;
1044     }
1045 
1046     return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
1047 }
1048 
1049 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
1050 {
1051     FIXME ("(%p)\n", this);
1052     return E_NOTIMPL;
1053 }
1054 
1055 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1056 {
1057     FIXME ("(%p)\n", this);
1058     return E_NOTIMPL;
1059 }
1060 
1061 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
1062 {
1063     TRACE ("(%p)\n", this);
1064 
1065     if (pSort)
1066         *pSort = 0;
1067     if (pDisplay)
1068         *pDisplay = 0;
1069     return S_OK;
1070 }
1071 
1072 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags)
1073 {
1074     TRACE ("(%p)\n", this);
1075 
1076     if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
1077         return E_INVALIDARG;
1078     *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags;
1079     return S_OK;
1080 }
1081 
1082 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
1083 {
1084     FIXME ("(%p)\n", this);
1085     return E_NOTIMPL;
1086 }
1087 
1088 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
1089 {
1090     HRESULT hr;
1091 
1092     TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1093 
1094     if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
1095         return E_INVALIDARG;
1096 
1097     if (!pidl)
1098     {
1099         psd->fmt = MyComputerSFHeader[iColumn].fmt;
1100         psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
1101         return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
1102     }
1103     else if (!_ILIsDrive(pidl))
1104     {
1105         return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
1106     }
1107     else
1108     {
1109         ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
1110         CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
1111         UINT DriveType = GetDriveTypeA(pszDrive);
1112         if (DriveType > DRIVE_RAMDISK)
1113             DriveType = DRIVE_FIXED;
1114 
1115         switch (iColumn)
1116         {
1117             case 0:        /* name */
1118                 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1119                 break;
1120             case 1:        /* type */
1121                 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
1122                     hr = SHSetStrRet(&psd->str, IDS_DRIVE_REMOVABLE);
1123                 else
1124                     hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1125                 break;
1126             case 2:        /* total size */
1127             case 3:        /* free size */
1128                 psd->str.cStr[0] = 0x00;
1129                 psd->str.uType = STRRET_CSTR;
1130                 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1131                 {
1132                     GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1133                     if (iColumn == 2)
1134                         StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1135                     else
1136                         StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1137                 }
1138                 hr = S_OK;
1139                 break;
1140             case 4:                /* FIXME: comments */
1141                 hr = SHSetStrRet(&psd->str, "");
1142                 break;
1143         }
1144     }
1145 
1146     return hr;
1147 }
1148 
1149 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1150 {
1151     FIXME("(%p)\n", this);
1152     return E_NOTIMPL;
1153 }
1154 
1155 /************************************************************************
1156  *    CDrivesFolder::GetClassID
1157  */
1158 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1159 {
1160     TRACE ("(%p)\n", this);
1161 
1162     if (!lpClassId)
1163         return E_POINTER;
1164 
1165     *lpClassId = CLSID_MyComputer;
1166     return S_OK;
1167 }
1168 
1169 /************************************************************************
1170  *    CDrivesFolder::Initialize
1171  *
1172  * NOTES: it makes no sense to change the pidl
1173  */
1174 HRESULT WINAPI CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1175 {
1176     return S_OK;
1177 }
1178 
1179 /**************************************************************************
1180  *    CDrivesFolder::GetCurFolder
1181  */
1182 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1183 {
1184     TRACE("(%p)->(%p)\n", this, pidl);
1185 
1186     if (!pidl)
1187         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1188 
1189     *pidl = ILClone(pidlRoot);
1190     return S_OK;
1191 }
1192 
1193 /************************************************************************/
1194 /* IContextMenuCB interface */
1195 
1196 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1197 {
1198     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1199         return S_OK;
1200 
1201     /* no data object means no selection */
1202     if (!pdtobj)
1203     {
1204         if (uMsg == DFM_INVOKECOMMAND && wParam == 1)   // #1
1205         {
1206             // "System" properties
1207             ShellExecuteW(hwndOwner,
1208                           NULL,
1209                           L"rundll32.exe",
1210                           L"shell32.dll,Control_RunDLL sysdm.cpl",
1211                           NULL,
1212                           SW_SHOWNORMAL);
1213         }
1214         else if (uMsg == DFM_MERGECONTEXTMENU)
1215         {
1216             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1217             HMENU hpopup = CreatePopupMenu();
1218             _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_SEPARATOR, NULL, MFS_ENABLED); // #0
1219             _InsertMenuItemW(hpopup, 1, TRUE, 1, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); // #1
1220             Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1221             DestroyMenu(hpopup);
1222         }
1223 
1224         return S_OK;
1225     }
1226 
1227     if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1228         return S_OK;
1229 
1230     return Shell_DefaultContextMenuCallBack(this, pdtobj);
1231 }
1232