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