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                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 
26 WINE_DEFAULT_DEBUG_CHANNEL (shell);
27 
28 /*
29 CDrivesFolder should create a CRegFolder to represent the virtual items that exist only in
30 the registry. The CRegFolder is aggregated by the CDrivesFolder.
31 The CDrivesFolderEnum class should enumerate only drives on the system. Since the CRegFolder
32 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
33 CDrivesFolderEnum is only responsible for returning the physical items.
34 
35 2. At least on my XP system, the drive pidls returned are of type PT_DRIVE1, not PT_DRIVE
36 3. The parsing name returned for my computer is incorrect. It should be "My Computer"
37 */
38 
39 static int iDriveIconIds[7] = { IDI_SHELL_DRIVE,       /* DRIVE_UNKNOWN */
40                                 IDI_SHELL_CDROM,       /* DRIVE_NO_ROOT_DIR*/
41                                 IDI_SHELL_3_14_FLOPPY, /* DRIVE_REMOVABLE*/
42                                 IDI_SHELL_DRIVE,       /* DRIVE_FIXED*/
43                                 IDI_SHELL_NETDRIVE,    /* DRIVE_REMOTE*/
44                                 IDI_SHELL_CDROM,       /* DRIVE_CDROM*/
45                                 IDI_SHELL_RAMDISK      /* DRIVE_RAMDISK*/
46                                 };
47 
48 static int iDriveTypeIds[7] = { IDS_DRIVE_FIXED,       /* DRIVE_UNKNOWN */
49                                 IDS_DRIVE_FIXED,       /* DRIVE_NO_ROOT_DIR*/
50                                 IDS_DRIVE_FLOPPY,      /* DRIVE_REMOVABLE*/
51                                 IDS_DRIVE_FIXED,       /* DRIVE_FIXED*/
52                                 IDS_DRIVE_NETWORK,     /* DRIVE_REMOTE*/
53                                 IDS_DRIVE_CDROM,       /* DRIVE_CDROM*/
54                                 IDS_DRIVE_FIXED        /* DRIVE_RAMDISK*/
55                                 };
56 
57 /***********************************************************************
58 *   IShellFolder implementation
59 */
60 
61 #define RETRY_COUNT 3
62 #define RETRY_SLEEP 250
63 static BOOL TryToLockOrUnlockDrive(HANDLE hDrive, BOOL bLock)
64 {
65     DWORD dwError, dwBytesReturned;
66     DWORD dwCode = (bLock ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME);
67     for (DWORD i = 0; i < RETRY_COUNT; ++i)
68     {
69         if (DeviceIoControl(hDrive, dwCode, NULL, 0, NULL, 0, &dwBytesReturned, NULL))
70             return TRUE;
71 
72         dwError = GetLastError();
73         if (dwError == ERROR_INVALID_FUNCTION)
74             break; /* don't sleep if function is not implemented */
75 
76         Sleep(RETRY_SLEEP);
77     }
78     SetLastError(dwError);
79     return FALSE;
80 }
81 
82 // NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp
83 static BOOL DoEjectDrive(const WCHAR *physical, UINT nDriveType, INT *pnStringID)
84 {
85     /* GENERIC_WRITE isn't needed for umount */
86     DWORD dwAccessMode = GENERIC_READ;
87     DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
88 
89     HANDLE hDrive = CreateFile(physical, dwAccessMode, dwShareMode, 0, OPEN_EXISTING, 0, NULL);
90     if (hDrive == INVALID_HANDLE_VALUE)
91         return FALSE;
92 
93     BOOL bResult, bNeedUnlock = FALSE;
94     DWORD dwBytesReturned, dwError = NO_ERROR;
95     PREVENT_MEDIA_REMOVAL removal;
96     do
97     {
98         bResult = TryToLockOrUnlockDrive(hDrive, TRUE);
99         if (!bResult)
100         {
101             dwError = GetLastError();
102             *pnStringID = IDS_CANTLOCKVOLUME; /* Unable to lock volume */
103             break;
104         }
105         bResult = DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
106         if (!bResult)
107         {
108             dwError = GetLastError();
109             *pnStringID = IDS_CANTDISMOUNTVOLUME; /* Unable to dismount volume */
110             bNeedUnlock = TRUE;
111             break;
112         }
113         removal.PreventMediaRemoval = FALSE;
114         bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_MEDIA_REMOVAL, &removal, sizeof(removal), NULL,
115                                   0, &dwBytesReturned, NULL);
116         if (!bResult)
117         {
118             *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
119             dwError = GetLastError();
120             bNeedUnlock = TRUE;
121             break;
122         }
123         bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
124         if (!bResult)
125         {
126             *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
127             dwError = GetLastError();
128             bNeedUnlock = TRUE;
129             break;
130         }
131     } while (0);
132 
133     if (bNeedUnlock)
134     {
135         TryToLockOrUnlockDrive(hDrive, FALSE);
136     }
137 
138     CloseHandle(hDrive);
139 
140     SetLastError(dwError);
141     return bResult;
142 }
143 
144 HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf,
145                                            HWND         hwnd,
146                                            IDataObject  *pdtobj,
147                                            UINT         uMsg,
148                                            WPARAM       wParam,
149                                            LPARAM       lParam)
150 {
151     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
152         return S_OK;
153 
154     PIDLIST_ABSOLUTE pidlFolder;
155     PUITEMID_CHILD *apidl;
156     UINT cidl;
157     UINT nDriveType;
158     DWORD dwFlags;
159     HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
160     if (FAILED_UNEXPECTEDLY(hr))
161         return hr;
162 
163     char szDrive[8] = {0};
164     if (!_ILGetDrive(apidl[0], szDrive, sizeof(szDrive)))
165     {
166         ERR("pidl is not a drive\n");
167         SHFree(pidlFolder);
168         _ILFreeaPidl(apidl, cidl);
169         return E_FAIL;
170     }
171     nDriveType = GetDriveTypeA(szDrive);
172     GetVolumeInformationA(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0);
173 
174 // custom command IDs
175 #define CMDID_FORMAT        1
176 #define CMDID_EJECT         2
177 #define CMDID_DISCONNECT    3
178 
179     if (uMsg == DFM_MERGECONTEXTMENU)
180     {
181         QCMINFO *pqcminfo = (QCMINFO *)lParam;
182 
183         UINT idCmdFirst = pqcminfo->idCmdFirst;
184         if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE)
185         {
186             /* add separator and Format */
187             UINT idCmd = idCmdFirst + CMDID_FORMAT;
188             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
189             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
190         }
191         if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM)
192         {
193             /* add separator and Eject */
194             UINT idCmd = idCmdFirst + CMDID_EJECT;
195             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
196             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED);
197         }
198         if (nDriveType == DRIVE_REMOTE)
199         {
200             /* add separator and Disconnect */
201             UINT idCmd = idCmdFirst + CMDID_DISCONNECT;
202             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
203             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED);
204         }
205 
206         pqcminfo->idCmdFirst += 3;
207     }
208     else if (uMsg == DFM_INVOKECOMMAND)
209     {
210         WCHAR wszBuf[4] = L"A:\\";
211         wszBuf[0] = (WCHAR)szDrive[0];
212 
213         INT nStringID = 0;
214         DWORD dwError = NO_ERROR;
215 
216         if (wParam == DFM_CMD_PROPERTIES)
217         {
218             hr = SH_ShowDriveProperties(wszBuf, pidlFolder, apidl);
219             if (FAILED(hr))
220             {
221                 dwError = ERROR_CAN_NOT_COMPLETE;
222                 nStringID = IDS_CANTSHOWPROPERTIES;
223             }
224         }
225         else
226         {
227             if (wParam == CMDID_FORMAT)
228             {
229                 /* do format */
230                 DWORD dwRet = SHFormatDrive(hwnd, szDrive[0] - 'A', SHFMT_ID_DEFAULT, 0);
231                 switch (dwRet)
232                 {
233                 case SHFMT_ERROR: case SHFMT_CANCEL: case SHFMT_NOFORMAT:
234                     hr = E_FAIL;
235                     break;
236                 }
237             }
238             else if (wParam == CMDID_EJECT)
239             {
240                 /* do eject */
241                 WCHAR physical[10];
242                 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
243 
244                 if (DoEjectDrive(physical, nDriveType, &nStringID))
245                 {
246                     SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
247                 }
248                 else
249                 {
250                     dwError = GetLastError();
251                 }
252             }
253             else if (wParam == CMDID_DISCONNECT)
254             {
255                 /* do disconnect */
256                 wszBuf[2] = UNICODE_NULL;
257                 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
258                 if (dwError == NO_ERROR)
259                 {
260                     SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
261                 }
262                 else
263                 {
264                     nStringID = IDS_CANTDISCONNECT;
265                 }
266             }
267         }
268 
269         if (nStringID != 0)
270         {
271             /* show error message */
272             WCHAR szFormat[128], szMessage[128];
273             LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
274             wsprintfW(szMessage, szFormat, dwError);
275             MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
276         }
277     }
278 
279     SHFree(pidlFolder);
280     _ILFreeaPidl(apidl, cidl);
281 
282     return hr;
283 }
284 
285 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
286                                           HWND hwnd,
287                                           UINT cidl,
288                                           PCUITEMID_CHILD_ARRAY apidl,
289                                           IShellFolder *psf,
290                                           IContextMenu **ppcm)
291 {
292     HKEY hKeys[2];
293     UINT cKeys = 0;
294     AddClassKeyToArray(L"Drive", hKeys, &cKeys);
295     AddClassKeyToArray(L"Folder", hKeys, &cKeys);
296 
297     return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
298 }
299 
300 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
301 {
302     CComPtr<IDefaultExtractIconInit> initIcon;
303     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
304     if (FAILED_UNEXPECTEDLY(hr))
305         return hr;
306 
307     CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
308     UINT DriveType = GetDriveTypeA(pszDrive);
309     if (DriveType > DRIVE_RAMDISK)
310         DriveType = DRIVE_FIXED;
311 
312     WCHAR wTemp[MAX_PATH];
313     int icon_idx;
314     if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
315         (HCR_GetIconW(L"Drive", wTemp, NULL, MAX_PATH, &icon_idx)))
316     {
317         initIcon->SetNormalIcon(wTemp, icon_idx);
318     }
319     else
320     {
321         icon_idx = iDriveIconIds[DriveType];
322         initIcon->SetNormalIcon(swShell32Name, -icon_idx);
323     }
324 
325     return initIcon->QueryInterface(riid, ppvOut);
326 }
327 
328 class CDrivesFolderEnum :
329     public CEnumIDListBase
330 {
331     public:
332         HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
333         {
334             /* enumerate the folders */
335             if (dwFlags & SHCONTF_FOLDERS)
336             {
337                 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
338                 DWORD dwDrivemap = GetLogicalDrives();
339 
340                 while (wszDriveName[0] <= 'Z')
341                 {
342                     if(dwDrivemap & 0x00000001L)
343                         AddToEnumList(_ILCreateDrive(wszDriveName));
344                     wszDriveName[0]++;
345                     dwDrivemap = dwDrivemap >> 1;
346                 }
347             }
348 
349             /* Enumerate the items of the reg folder */
350             AppendItemsFromEnumerator(pRegEnumerator);
351 
352             return S_OK;
353         }
354 
355         BEGIN_COM_MAP(CDrivesFolderEnum)
356         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
357         END_COM_MAP()
358 };
359 
360 /***********************************************************************
361 *   IShellFolder [MyComputer] implementation
362 */
363 
364 static const shvheader MyComputerSFHeader[] = {
365     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
366     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
367     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
368     {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
369     {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
370 };
371 
372 #define MYCOMPUTERSHELLVIEWCOLUMNS 5
373 
374 static const DWORD dwComputerAttributes =
375     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
376     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
377 static const DWORD dwControlPanelAttributes =
378     SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
379 static const DWORD dwDriveAttributes =
380     SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
381     SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
382 
383 CDrivesFolder::CDrivesFolder()
384 {
385     pidlRoot = NULL;
386 }
387 
388 CDrivesFolder::~CDrivesFolder()
389 {
390     TRACE ("-- destroying IShellFolder(%p)\n", this);
391     SHFree(pidlRoot);
392 }
393 
394 HRESULT WINAPI CDrivesFolder::FinalConstruct()
395 {
396     pidlRoot = _ILCreateMyComputer();    /* my qualified pidl */
397     if (pidlRoot == NULL)
398         return E_OUTOFMEMORY;
399 
400     HRESULT hr = CRegFolder_CreateInstance(&CLSID_MyComputer,
401                                            pidlRoot,
402                                            L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
403                                            L"MyComputer",
404                                            IID_PPV_ARG(IShellFolder2, &m_regFolder));
405 
406     return hr;
407 }
408 
409 /**************************************************************************
410 *    CDrivesFolder::ParseDisplayName
411 */
412 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
413         DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes)
414 {
415     HRESULT hr = E_INVALIDARG;
416     LPCWSTR szNext = NULL;
417     LPITEMIDLIST pidlTemp = NULL;
418 
419     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
420           hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
421           pchEaten, ppidl, pdwAttributes);
422 
423     *ppidl = 0;
424     if (pchEaten)
425         *pchEaten = 0;        /* strange but like the original */
426 
427     /* handle CLSID paths */
428     if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
429         return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
430 
431     if (PathGetDriveNumberW(lpszDisplayName) < 0)
432         return E_INVALIDARG;
433 
434     pidlTemp = _ILCreateDrive(lpszDisplayName);
435     if (!pidlTemp)
436         return E_OUTOFMEMORY;
437 
438     if (lpszDisplayName[2] == L'\\')
439     {
440         szNext = &lpszDisplayName[3];
441     }
442 
443     if (szNext && *szNext)
444     {
445         hr = SHELL32_ParseNextElement (this, hwndOwner, pbc, &pidlTemp,
446                                        (LPOLESTR) szNext, pchEaten, pdwAttributes);
447     }
448     else
449     {
450         hr = S_OK;
451         if (pdwAttributes && *pdwAttributes)
452         {
453             if (_ILIsDrive(pidlTemp))
454                 *pdwAttributes &= dwDriveAttributes;
455             else if (_ILIsSpecialFolder(pidlTemp))
456                 m_regFolder->GetAttributesOf(1, &pidlTemp, pdwAttributes);
457             else
458                 ERR("Got an unkown pidl here!\n");
459         }
460     }
461 
462     *ppidl = pidlTemp;
463 
464     TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
465 
466     return hr;
467 }
468 
469 /**************************************************************************
470 *        CDrivesFolder::EnumObjects
471 */
472 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
473 {
474     CComPtr<IEnumIDList> pRegEnumerator;
475     m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
476 
477     return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
478 }
479 
480 /**************************************************************************
481 *        CDrivesFolder::BindToObject
482 */
483 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
484 {
485     TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
486           pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
487 
488     if (_ILIsSpecialFolder(pidl))
489         return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
490 
491     CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
492 
493     PERSIST_FOLDER_TARGET_INFO pfti = {0};
494     pfti.dwAttributes = -1;
495     pfti.csidl = -1;
496     pfti.szTargetParsingName[0] = *pchDrive;
497     pfti.szTargetParsingName[1] = L':';
498     pfti.szTargetParsingName[2] = L'\\';
499 
500     HRESULT hr = SHELL32_BindToSF(pidlRoot,
501                                   &pfti,
502                                   pidl,
503                                   &CLSID_ShellFSFolder,
504                                   riid,
505                                   ppvOut);
506     if (FAILED_UNEXPECTEDLY(hr))
507         return hr;
508 
509     return S_OK;
510 }
511 
512 /**************************************************************************
513 *    CDrivesFolder::BindToStorage
514 */
515 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
516 {
517     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
518           pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
519 
520     *ppvOut = NULL;
521     return E_NOTIMPL;
522 }
523 
524 /**************************************************************************
525 *     CDrivesFolder::CompareIDs
526 */
527 
528 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
529 {
530     HRESULT hres;
531 
532     if (!pidl1 || !pidl2)
533     {
534         ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
535         return E_INVALIDARG;
536     }
537 
538     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
539         return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
540 
541     if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || LOWORD(lParam) >= MYCOMPUTERSHELLVIEWCOLUMNS)
542         return E_INVALIDARG;
543 
544     CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
545     CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
546 
547     int result;
548     switch(LOWORD(lParam))
549     {
550         case 0:        /* name */
551         {
552             result = stricmp(pszDrive1, pszDrive2);
553             hres = MAKE_COMPARE_HRESULT(result);
554             break;
555         }
556         case 1:        /* comments */
557             hres = MAKE_COMPARE_HRESULT(0);
558             break;
559         case 2:        /* Type */
560         {
561             /* We want to return immediately because SHELL32_CompareDetails also compares children. */
562             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
563         }
564         case 3:       /* Size */
565         case 4:       /* Size Available */
566         {
567             ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
568 
569             if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
570                 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
571             else
572                 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
573 
574             if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
575                 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
576             else
577                 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
578 
579             LARGE_INTEGER Diff;
580             if (lParam == 3) /* Size */
581                 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
582             else /* Size available */
583                 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
584 
585             hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
586             break;
587         }
588         default:
589             return E_INVALIDARG;
590     }
591 
592     if (HRESULT_CODE(hres) == 0)
593         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
594 
595     return hres;
596 }
597 
598 /**************************************************************************
599 *    CDrivesFolder::CreateViewObject
600 */
601 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
602 {
603     CComPtr<IShellView> pShellView;
604     HRESULT hr = E_INVALIDARG;
605 
606     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
607           hwndOwner, shdebugstr_guid (&riid), ppvOut);
608 
609     if (!ppvOut)
610         return hr;
611 
612     *ppvOut = NULL;
613 
614     if (IsEqualIID(riid, IID_IDropTarget))
615     {
616         WARN("IDropTarget not implemented\n");
617         hr = E_NOTIMPL;
618     }
619     else if (IsEqualIID(riid, IID_IContextMenu))
620     {
621         WARN("IContextMenu not implemented\n");
622         hr = E_NOTIMPL;
623     }
624     else if (IsEqualIID(riid, IID_IShellView))
625     {
626             SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
627             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
628     }
629     TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
630     return hr;
631 }
632 
633 static BOOL _ILIsControlPanel(LPCITEMIDLIST pidl)
634 {
635     GUID *guid = _ILGetGUIDPointer(pidl);
636 
637     TRACE("(%p)\n", pidl);
638 
639     if (guid)
640         return IsEqualIID(*guid, CLSID_ControlPanel);
641     return FALSE;
642 }
643 
644 /**************************************************************************
645 *  CDrivesFolder::GetAttributesOf
646 */
647 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
648 {
649     TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
650            this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
651 
652     if (cidl && !apidl)
653         return E_INVALIDARG;
654 
655     if (*rgfInOut == 0)
656         *rgfInOut = ~0;
657 
658     /* FIXME: always add SFGAO_CANLINK */
659     if(cidl == 0)
660         *rgfInOut &= dwComputerAttributes;
661     else
662     {
663         for (UINT i = 0; i < cidl; ++i)
664         {
665             if (_ILIsDrive(apidl[i]))
666                 *rgfInOut &= dwDriveAttributes;
667             else if (_ILIsControlPanel(apidl[i]))
668                 *rgfInOut &= dwControlPanelAttributes;
669             else if (_ILIsSpecialFolder(*apidl))
670                 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
671             else
672                 ERR("Got unknown pidl type!\n");
673         }
674     }
675 
676     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
677     *rgfInOut &= ~SFGAO_VALIDATE;
678 
679     TRACE ("-- result=0x%08x\n", *rgfInOut);
680     return S_OK;
681 }
682 
683 /**************************************************************************
684 *    CDrivesFolder::GetUIObjectOf
685 *
686 * PARAMETERS
687 *  hwndOwner [in]  Parent window for any output
688 *  cidl      [in]  array size
689 *  apidl     [in]  simple pidl array
690 *  riid      [in]  Requested Interface
691 *  prgfInOut [   ] reserved
692 *  ppvObject [out] Resulting Interface
693 *
694 */
695 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
696     UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
697     REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
698 {
699     LPVOID pObj = NULL;
700     HRESULT hr = E_INVALIDARG;
701 
702     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
703           hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
704 
705     if (!ppvOut)
706         return hr;
707 
708     *ppvOut = NULL;
709 
710     if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
711     {
712         if (_ILIsDrive(apidl[0]))
713             hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
714         else
715             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
716     }
717     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
718     {
719         hr = IDataObject_Constructor (hwndOwner,
720                                       pidlRoot, apidl, cidl, (IDataObject **)&pObj);
721     }
722     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
723     {
724         if (_ILIsDrive(apidl[0]))
725             hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
726         else
727             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
728     }
729     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
730     {
731         CComPtr<IShellFolder> psfChild;
732         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
733         if (FAILED_UNEXPECTEDLY(hr))
734             return hr;
735 
736         return psfChild->CreateViewObject(NULL, riid, ppvOut);
737     }
738     else
739         hr = E_NOINTERFACE;
740 
741     if (SUCCEEDED(hr) && !pObj)
742         hr = E_OUTOFMEMORY;
743 
744     *ppvOut = pObj;
745     TRACE ("(%p)->hr=0x%08x\n", this, hr);
746     return hr;
747 }
748 
749 /**************************************************************************
750 *    CDrivesFolder::GetDisplayNameOf
751 */
752 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
753 {
754     LPWSTR pszPath;
755     HRESULT hr = S_OK;
756 
757     TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
758     pdump (pidl);
759 
760     if (!strRet)
761         return E_INVALIDARG;
762 
763     if (!_ILIsPidlSimple (pidl))
764     {
765         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
766     }
767     else if (_ILIsSpecialFolder(pidl))
768     {
769         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
770     }
771     else if (!_ILIsDrive(pidl))
772     {
773         ERR("Wrong pidl type\n");
774         return E_INVALIDARG;
775     }
776 
777     pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
778     if (!pszPath)
779         return E_OUTOFMEMORY;
780 
781     pszPath[0] = 0;
782 
783     _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);    /* append my own path */
784     /* long view "lw_name (C:)" */
785     if (!(dwFlags & SHGDN_FORPARSING))
786     {
787         WCHAR wszDrive[18] = {0};
788         DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
789         static const WCHAR wszOpenBracket[] = {' ', '(', 0};
790         static const WCHAR wszCloseBracket[] = {')', 0};
791 
792         lstrcpynW(wszDrive, pszPath, 4);
793         pszPath[0] = L'\0';
794         GetVolumeInformationW(wszDrive, pszPath,
795                                 MAX_PATH - 7,
796                                 &dwVolumeSerialNumber,
797                                 &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
798         pszPath[MAX_PATH-1] = L'\0';
799         if (!wcslen(pszPath))
800         {
801             UINT DriveType, ResourceId;
802             DriveType = GetDriveTypeW(wszDrive);
803             switch(DriveType)
804             {
805                 case DRIVE_FIXED:
806                     ResourceId = IDS_DRIVE_FIXED;
807                     break;
808                 case DRIVE_REMOTE:
809                     ResourceId = IDS_DRIVE_NETWORK;
810                     break;
811                 case DRIVE_CDROM:
812                     ResourceId = IDS_DRIVE_CDROM;
813                     break;
814                 default:
815                     ResourceId = 0;
816             }
817             if (ResourceId)
818             {
819                 dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
820                 if (dwFileSystemFlags > MAX_PATH - 7)
821                     pszPath[MAX_PATH-7] = L'\0';
822             }
823         }
824         wcscat (pszPath, wszOpenBracket);
825         wszDrive[2] = L'\0';
826         wcscat (pszPath, wszDrive);
827         wcscat (pszPath, wszCloseBracket);
828     }
829 
830     if (SUCCEEDED(hr))
831     {
832         strRet->uType = STRRET_WSTR;
833         strRet->pOleStr = pszPath;
834     }
835     else
836         CoTaskMemFree(pszPath);
837 
838     TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
839     return hr;
840 }
841 
842 /**************************************************************************
843 *  CDrivesFolder::SetNameOf
844 *  Changes the name of a file object or subfolder, possibly changing its item
845 *  identifier in the process.
846 *
847 * PARAMETERS
848 *  hwndOwner  [in]   Owner window for output
849 *  pidl       [in]   simple pidl of item to change
850 *  lpszName   [in]   the items new display name
851 *  dwFlags    [in]   SHGNO formatting flags
852 *  ppidlOut   [out]  simple pidl returned
853 */
854 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
855                                         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
856 {
857     WCHAR szName[30];
858 
859     if (_ILIsDrive(pidl))
860     {
861         if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
862             SetVolumeLabelW(szName, lpName);
863         if (pPidlOut)
864             *pPidlOut = _ILCreateDrive(szName);
865         return S_OK;
866     }
867 
868     return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
869 }
870 
871 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
872 {
873     FIXME ("(%p)\n", this);
874     return E_NOTIMPL;
875 }
876 
877 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
878 {
879     FIXME ("(%p)\n", this);
880     return E_NOTIMPL;
881 }
882 
883 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
884 {
885     TRACE ("(%p)\n", this);
886 
887     if (pSort)
888         *pSort = 0;
889     if (pDisplay)
890         *pDisplay = 0;
891     return S_OK;
892 }
893 
894 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags)
895 {
896     TRACE ("(%p)\n", this);
897 
898     if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
899         return E_INVALIDARG;
900     *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags;
901     return S_OK;
902 }
903 
904 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
905 {
906     FIXME ("(%p)\n", this);
907     return E_NOTIMPL;
908 }
909 
910 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
911 {
912     HRESULT hr;
913 
914     TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
915 
916     if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
917         return E_INVALIDARG;
918 
919     if (!pidl)
920     {
921         psd->fmt = MyComputerSFHeader[iColumn].fmt;
922         psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
923         return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
924     }
925     else if (!_ILIsDrive(pidl))
926     {
927         return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
928     }
929     else
930     {
931         ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
932         CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
933         UINT DriveType = GetDriveTypeA(pszDrive);
934         if (DriveType > DRIVE_RAMDISK)
935             DriveType = DRIVE_FIXED;
936 
937         switch (iColumn)
938         {
939             case 0:        /* name */
940                 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
941                 break;
942             case 1:                /* FIXME: comments */
943                 hr = SHSetStrRet(&psd->str, "");
944                 break;
945             case 2:        /* type */
946                 hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
947                 break;
948             case 3:        /* total size */
949             case 4:        /* free size */
950                 psd->str.cStr[0] = 0x00;
951                 psd->str.uType = STRRET_CSTR;
952                 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
953                 {
954                     GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
955                     if (iColumn == 3)
956                         StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
957                     else
958                         StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
959                 }
960                 hr = S_OK;
961                 break;
962         }
963     }
964 
965     return hr;
966 }
967 
968 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
969 {
970     FIXME("(%p)\n", this);
971     return E_NOTIMPL;
972 }
973 
974 /************************************************************************
975  *    CDrivesFolder::GetClassID
976  */
977 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
978 {
979     TRACE ("(%p)\n", this);
980 
981     if (!lpClassId)
982         return E_POINTER;
983 
984     *lpClassId = CLSID_MyComputer;
985     return S_OK;
986 }
987 
988 /************************************************************************
989  *    CDrivesFolder::Initialize
990  *
991  * NOTES: it makes no sense to change the pidl
992  */
993 HRESULT WINAPI CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
994 {
995     return S_OK;
996 }
997 
998 /**************************************************************************
999  *    CDrivesFolder::GetCurFolder
1000  */
1001 HRESULT WINAPI CDrivesFolder::GetCurFolder(LPITEMIDLIST *pidl)
1002 {
1003     TRACE("(%p)->(%p)\n", this, pidl);
1004 
1005     if (!pidl)
1006         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1007 
1008     *pidl = ILClone(pidlRoot);
1009     return S_OK;
1010 }
1011