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-2024           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 static const REQUIREDREGITEM g_RequiredItems[] =
59 {
60     { CLSID_ControlPanel, 0, 0x50 },
61 };
62 static const REGFOLDERINFO g_RegFolderInfo =
63 {
64     PT_COMPUTER_REGITEM,
65     _countof(g_RequiredItems), g_RequiredItems,
66     CLSID_MyComputer,
67     L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
68     L"MyComputer",
69 };
70 
IsRegItem(PCUITEMID_CHILD pidl)71 static const CLSID* IsRegItem(PCUITEMID_CHILD pidl)
72 {
73     if (pidl && pidl->mkid.cb == 2 + 2 + sizeof(CLSID))
74     {
75         if (pidl->mkid.abID[0] == PT_SHELLEXT || pidl->mkid.abID[0] == PT_GUID) // FIXME: Remove PT_GUID when CRegFolder is fixed
76             return (const CLSID*)(&pidl->mkid.abID[2]);
77     }
78     return NULL;
79 }
80 
_ILGetDriveType(LPCITEMIDLIST pidl)81 BOOL _ILGetDriveType(LPCITEMIDLIST pidl)
82 {
83     WCHAR szDrive[8];
84     if (!_ILGetDrive(pidl, szDrive, _countof(szDrive)))
85     {
86         ERR("pidl %p is not a drive\n", pidl);
87         return DRIVE_UNKNOWN;
88     }
89     return ::GetDriveTypeW(szDrive);
90 }
91 
SHELL32_IsShellFolderNamespaceItemHidden(LPCWSTR SubKey,REFCLSID Clsid)92 BOOL SHELL32_IsShellFolderNamespaceItemHidden(LPCWSTR SubKey, REFCLSID Clsid)
93 {
94     // If this function returns true, the item should be hidden in DefView but not in the Explorer folder tree.
95     WCHAR path[MAX_PATH], name[CHARS_IN_GUID];
96     wsprintfW(path, L"%s\\%s", REGSTR_PATH_EXPLORER, SubKey);
97     SHELL32_GUIDToStringW(Clsid, name);
98     DWORD data = 0, size = sizeof(data);
99     return !RegGetValueW(HKEY_CURRENT_USER, path, name, RRF_RT_DWORD, NULL, &data, &size) && data;
100 }
101 
102 /***********************************************************************
103 *   IShellFolder implementation
104 */
105 
106 #define RETRY_COUNT 3
107 #define RETRY_SLEEP 250
TryToLockOrUnlockDrive(HANDLE hDrive,BOOL bLock)108 static BOOL TryToLockOrUnlockDrive(HANDLE hDrive, BOOL bLock)
109 {
110     DWORD dwError, dwBytesReturned;
111     DWORD dwCode = (bLock ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME);
112     for (DWORD i = 0; i < RETRY_COUNT; ++i)
113     {
114         if (DeviceIoControl(hDrive, dwCode, NULL, 0, NULL, 0, &dwBytesReturned, NULL))
115             return TRUE;
116 
117         dwError = GetLastError();
118         if (dwError == ERROR_INVALID_FUNCTION)
119             break; /* don't sleep if function is not implemented */
120 
121         Sleep(RETRY_SLEEP);
122     }
123     SetLastError(dwError);
124     return FALSE;
125 }
126 
127 // NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp
DoEjectDrive(const WCHAR * physical,UINT nDriveType,INT * pnStringID)128 static BOOL DoEjectDrive(const WCHAR *physical, UINT nDriveType, INT *pnStringID)
129 {
130     /* GENERIC_WRITE isn't needed for umount */
131     DWORD dwAccessMode = GENERIC_READ;
132     DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
133 
134     HANDLE hDrive = CreateFile(physical, dwAccessMode, dwShareMode, 0, OPEN_EXISTING, 0, NULL);
135     if (hDrive == INVALID_HANDLE_VALUE)
136         return FALSE;
137 
138     BOOL bResult, bNeedUnlock = FALSE;
139     DWORD dwBytesReturned, dwError = NO_ERROR;
140     PREVENT_MEDIA_REMOVAL removal;
141     do
142     {
143         bResult = TryToLockOrUnlockDrive(hDrive, TRUE);
144         if (!bResult)
145         {
146             dwError = GetLastError();
147             *pnStringID = IDS_CANTLOCKVOLUME; /* Unable to lock volume */
148             break;
149         }
150         bResult = DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
151         if (!bResult)
152         {
153             dwError = GetLastError();
154             *pnStringID = IDS_CANTDISMOUNTVOLUME; /* Unable to dismount volume */
155             bNeedUnlock = TRUE;
156             break;
157         }
158         removal.PreventMediaRemoval = FALSE;
159         bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_MEDIA_REMOVAL, &removal, sizeof(removal), NULL,
160                                   0, &dwBytesReturned, NULL);
161         if (!bResult)
162         {
163             *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
164             dwError = GetLastError();
165             bNeedUnlock = TRUE;
166             break;
167         }
168         bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
169         if (!bResult)
170         {
171             *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
172             dwError = GetLastError();
173             bNeedUnlock = TRUE;
174             break;
175         }
176     } while (0);
177 
178     if (bNeedUnlock)
179     {
180         TryToLockOrUnlockDrive(hDrive, FALSE);
181     }
182 
183     CloseHandle(hDrive);
184 
185     SetLastError(dwError);
186     return bResult;
187 }
188 
DoFormatDriveThread(LPVOID args)189 static DWORD CALLBACK DoFormatDriveThread(LPVOID args)
190 {
191     UINT nDrive = PtrToUlong(args);
192     WCHAR szPath[] = { LOWORD(L'A' + nDrive), L'\0' }; // Arbitrary, just needs to include nDrive
193     CStubWindow32 stub;
194     HRESULT hr = stub.CreateStub(CStubWindow32::TYPE_FORMATDRIVE, szPath, NULL);
195     if (FAILED(hr))
196         return hr;
197     SHFormatDrive(stub, nDrive, SHFMT_ID_DEFAULT, 0);
198     return stub.DestroyWindow();
199 }
200 
DoFormatDriveAsync(HWND hwnd,UINT nDrive)201 static HRESULT DoFormatDriveAsync(HWND hwnd, UINT nDrive)
202 {
203     BOOL succ = SHCreateThread(DoFormatDriveThread, UlongToPtr(nDrive), CTF_PROCESS_REF, NULL);
204     return succ ? S_OK : E_FAIL;
205 }
206 
DrivesContextMenuCallback(IShellFolder * psf,HWND hwnd,IDataObject * pdtobj,UINT uMsg,WPARAM wParam,LPARAM lParam)207 HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf,
208                                            HWND         hwnd,
209                                            IDataObject  *pdtobj,
210                                            UINT         uMsg,
211                                            WPARAM       wParam,
212                                            LPARAM       lParam)
213 {
214     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
215         return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
216 
217     PIDLIST_ABSOLUTE pidlFolder;
218     PUITEMID_CHILD *apidl;
219     UINT cidl;
220     UINT nDriveType;
221     DWORD dwFlags;
222     HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
223     if (FAILED_UNEXPECTEDLY(hr))
224         return hr;
225 
226     WCHAR szDrive[8] = {0};
227     if (!_ILGetDrive(apidl[0], szDrive, _countof(szDrive)))
228     {
229         ERR("pidl is not a drive\n");
230         SHFree(pidlFolder);
231         _ILFreeaPidl(apidl, cidl);
232         return E_FAIL;
233     }
234     nDriveType = GetDriveTypeW(szDrive);
235     GetVolumeInformationW(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0);
236 
237 // custom command IDs
238 #if 0 // Disabled until our menu building system is fixed
239 #define CMDID_FORMAT        0
240 #define CMDID_EJECT         1
241 #define CMDID_DISCONNECT    2
242 #else
243 /* FIXME: These IDs should start from 0, however there is difference
244  * between ours and Windows' menu building systems, which should be fixed. */
245 #define CMDID_FORMAT        1
246 #define CMDID_EJECT         2
247 #define CMDID_DISCONNECT    3
248 #endif
249 
250     if (uMsg == DFM_MERGECONTEXTMENU)
251     {
252         QCMINFO *pqcminfo = (QCMINFO *)lParam;
253 
254         UINT idCmdFirst = pqcminfo->idCmdFirst;
255         UINT idCmd = 0;
256         if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE)
257         {
258             /* add separator and Format */
259             idCmd = idCmdFirst + CMDID_FORMAT;
260             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
261             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
262         }
263         if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM)
264         {
265             /* add separator and Eject */
266             idCmd = idCmdFirst + CMDID_EJECT;
267             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
268             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED);
269         }
270         if (nDriveType == DRIVE_REMOTE)
271         {
272             /* add separator and Disconnect */
273             idCmd = idCmdFirst + CMDID_DISCONNECT;
274             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
275             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED);
276         }
277 
278         if (idCmd)
279 #if 0 // see FIXME above
280             pqcminfo->idCmdFirst = ++idCmd;
281 #else
282             pqcminfo->idCmdFirst = (idCmd + 2);
283 #endif
284         hr = S_OK;
285     }
286     else if (uMsg == DFM_INVOKECOMMAND)
287     {
288         WCHAR wszBuf[4] = L"A:\\";
289         wszBuf[0] = (WCHAR)szDrive[0];
290 
291         INT nStringID = 0;
292         DWORD dwError = NO_ERROR;
293 
294         if (wParam == DFM_CMD_PROPERTIES)
295         {
296             ATLASSERT(pdtobj);
297             hr = SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj);
298             // Not setting nStringID because SHOpenPropSheet already displayed an error box
299         }
300         else
301         {
302             if (wParam == CMDID_FORMAT)
303             {
304                 hr = DoFormatDriveAsync(hwnd, szDrive[0] - 'A');
305             }
306             else if (wParam == CMDID_EJECT)
307             {
308                 /* do eject */
309                 WCHAR physical[10];
310                 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
311 
312                 if (DoEjectDrive(physical, nDriveType, &nStringID))
313                 {
314                     SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
315                 }
316                 else
317                 {
318                     dwError = GetLastError();
319                 }
320             }
321             else if (wParam == CMDID_DISCONNECT)
322             {
323                 /* do disconnect */
324                 wszBuf[2] = UNICODE_NULL;
325                 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
326                 if (dwError == NO_ERROR)
327                 {
328                     SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
329                 }
330                 else
331                 {
332                     nStringID = IDS_CANTDISCONNECT;
333                 }
334             }
335         }
336 
337         if (nStringID != 0)
338         {
339             /* show error message */
340             WCHAR szFormat[128], szMessage[128];
341             LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
342             wsprintfW(szMessage, szFormat, dwError);
343             MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
344         }
345     }
346 
347     SHFree(pidlFolder);
348     _ILFreeaPidl(apidl, cidl);
349 
350     return hr;
351 }
352 
CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,HWND hwnd,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,IShellFolder * psf,IContextMenu ** ppcm)353 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
354                                           HWND hwnd,
355                                           UINT cidl,
356                                           PCUITEMID_CHILD_ARRAY apidl,
357                                           IShellFolder *psf,
358                                           IContextMenu **ppcm)
359 {
360     HKEY hKeys[2];
361     UINT cKeys = 0;
362     AddClassKeyToArray(L"Drive", hKeys, &cKeys);
363     AddClassKeyToArray(L"Folder", hKeys, &cKeys);
364 
365     return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
366 }
367 
368 static HRESULT
getIconLocationForDrive(IShellFolder * psf,PCITEMID_CHILD pidl,UINT uFlags,LPWSTR szIconFile,UINT cchMax,int * piIndex,UINT * pwFlags)369 getIconLocationForDrive(IShellFolder *psf, PCITEMID_CHILD pidl, UINT uFlags,
370                         LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
371 {
372     WCHAR wszPath[MAX_PATH];
373     WCHAR wszAutoRunInfPath[MAX_PATH];
374     WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
375 
376     // get path
377     if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
378         return E_FAIL;
379     if (!PathIsDirectoryW(wszPath))
380         return E_FAIL;
381 
382     // build the full path of autorun.inf
383     StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
384     PathAppendW(wszAutoRunInfPath, L"autorun.inf");
385 
386     // autorun.inf --> wszValue
387     if (GetPrivateProfileStringW(L"autorun", L"icon", NULL, wszValue, _countof(wszValue),
388                                  wszAutoRunInfPath) && wszValue[0] != 0)
389     {
390         // wszValue --> wszTemp
391         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
392 
393         // parse the icon location
394         *piIndex = PathParseIconLocationW(wszTemp);
395 
396         // wszPath + wszTemp --> wszPath
397         if (PathIsRelativeW(wszTemp))
398             PathAppendW(wszPath, wszTemp);
399         else
400             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
401 
402         // wszPath --> szIconFile
403         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
404 
405         return S_OK;
406     }
407 
408     return E_FAIL;
409 }
410 
411 static HRESULT
getLabelForDrive(LPWSTR wszPath,LPWSTR wszLabel)412 getLabelForDrive(LPWSTR wszPath, LPWSTR wszLabel)
413 {
414     WCHAR wszAutoRunInfPath[MAX_PATH];
415     WCHAR wszTemp[MAX_PATH];
416 
417     if (!PathIsDirectoryW(wszPath))
418         return E_FAIL;
419 
420     StringCchCopyW(wszAutoRunInfPath, _countof(wszAutoRunInfPath), wszPath);
421     PathAppendW(wszAutoRunInfPath, L"autorun.inf");
422 
423     if (GetPrivateProfileStringW(L"autorun", L"label", NULL, wszTemp, _countof(wszTemp),
424                                  wszAutoRunInfPath) && wszTemp[0] != 0)
425     {
426         StringCchCopyW(wszLabel, _countof(wszTemp), wszTemp);
427         return S_OK;
428     }
429 
430     return E_FAIL;
431 }
432 
433 BOOL IsDriveFloppyA(LPCSTR pszDriveRoot);
434 
CDrivesExtractIcon_CreateInstance(IShellFolder * psf,LPCITEMIDLIST pidl,REFIID riid,LPVOID * ppvOut)435 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
436 {
437     CComPtr<IDefaultExtractIconInit> initIcon;
438     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
439     if (FAILED_UNEXPECTEDLY(hr))
440         return hr;
441 
442     CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
443     UINT DriveType = GetDriveTypeA(pszDrive);
444     if (DriveType > DRIVE_RAMDISK)
445         DriveType = DRIVE_FIXED;
446 
447     WCHAR wTemp[MAX_PATH];
448     int icon_idx, reg_idx;
449     UINT flags = 0;
450 
451     switch (DriveType)
452     {
453         case DRIVE_FIXED:
454         case DRIVE_UNKNOWN:
455             reg_idx = IDI_SHELL_DRIVE;
456             break;
457         case DRIVE_CDROM:
458             reg_idx = IDI_SHELL_CDROM;
459             break;
460         case DRIVE_REMOTE:
461             reg_idx = IDI_SHELL_NETDRIVE;
462             break;
463         case DRIVE_REMOVABLE:
464             if (!IsDriveFloppyA(pszDrive))
465                 reg_idx = IDI_SHELL_REMOVEABLE;
466             else
467                 reg_idx = IDI_SHELL_3_14_FLOPPY;
468             break;
469         case DRIVE_RAMDISK:
470             reg_idx = IDI_SHELL_RAMDISK;
471             break;
472         case DRIVE_NO_ROOT_DIR:
473         default:
474             reg_idx = IDI_SHELL_DOCUMENT;
475             break;
476     }
477 
478     hr = getIconLocationForDrive(psf, pidl, 0, wTemp, _countof(wTemp),
479                                  &icon_idx, &flags);
480     if (SUCCEEDED(hr))
481     {
482         initIcon->SetNormalIcon(wTemp, icon_idx);
483     }
484     else if (HLM_GetIconW(reg_idx - 1, wTemp, _countof(wTemp), &icon_idx))
485     {
486         initIcon->SetNormalIcon(wTemp, icon_idx);
487     }
488     else if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
489              (HCR_GetIconW(L"Drive", wTemp, NULL, _countof(wTemp), &icon_idx)))
490     {
491         initIcon->SetNormalIcon(wTemp, icon_idx);
492     }
493     else
494     {
495         if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
496         {
497             icon_idx = IDI_SHELL_REMOVEABLE;
498         }
499         else
500         {
501             icon_idx = iDriveIconIds[DriveType];
502         }
503         initIcon->SetNormalIcon(swShell32Name, -icon_idx);
504     }
505 
506     return initIcon->QueryInterface(riid, ppvOut);
507 }
508 
509 class CDrivesFolderEnum :
510     public CEnumIDListBase
511 {
512     public:
Initialize(HWND hwndOwner,DWORD dwFlags,IEnumIDList * pRegEnumerator)513         HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
514         {
515             /* enumerate the folders */
516             if (dwFlags & SHCONTF_FOLDERS)
517             {
518                 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
519                 DWORD dwDrivemap = GetLogicalDrives();
520 
521                 while (wszDriveName[0] <= 'Z')
522                 {
523                     if(dwDrivemap & 0x00000001L)
524                         AddToEnumList(_ILCreateDrive(wszDriveName));
525                     wszDriveName[0]++;
526                     dwDrivemap = dwDrivemap >> 1;
527                 }
528             }
529 
530             /* Enumerate the items of the reg folder */
531             AppendItemsFromEnumerator(pRegEnumerator);
532 
533             return S_OK;
534         }
535 
536         BEGIN_COM_MAP(CDrivesFolderEnum)
537         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
538         END_COM_MAP()
539 };
540 
541 /***********************************************************************
542 *   IShellFolder [MyComputer] implementation
543 */
544 
545 static const shvheader MyComputerSFHeader[] = {
546     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
547     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
548     {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
549     {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
550     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
551 };
552 
553 static const DWORD dwComputerAttributes =
554     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
555     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
556 static const DWORD dwControlPanelAttributes =
557     SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
558 static const DWORD dwDriveAttributes =
559     SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
560     SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK | SFGAO_CANCOPY;
561 
CDrivesFolder()562 CDrivesFolder::CDrivesFolder()
563 {
564     pidlRoot = NULL;
565 }
566 
~CDrivesFolder()567 CDrivesFolder::~CDrivesFolder()
568 {
569     TRACE("-- destroying IShellFolder(%p)\n", this);
570     SHFree(pidlRoot);
571 }
572 
FinalConstruct()573 HRESULT WINAPI CDrivesFolder::FinalConstruct()
574 {
575     pidlRoot = _ILCreateMyComputer();    /* my qualified pidl */
576     if (pidlRoot == NULL)
577         return E_OUTOFMEMORY;
578 
579     REGFOLDERINITDATA RegInit = { static_cast<IShellFolder*>(this), &g_RegFolderInfo };
580     HRESULT hr = CRegFolder_CreateInstance(&RegInit,
581                                            pidlRoot,
582                                            IID_PPV_ARG(IShellFolder2, &m_regFolder));
583 
584     return hr;
585 }
586 
587 /**************************************************************************
588 *    CDrivesFolder::ParseDisplayName
589 */
ParseDisplayName(HWND hwndOwner,LPBC pbc,LPOLESTR lpszDisplayName,DWORD * pchEaten,PIDLIST_RELATIVE * ppidl,DWORD * pdwAttributes)590 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
591         DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes)
592 {
593     HRESULT hr = E_INVALIDARG;
594 
595     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
596           hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
597           pchEaten, ppidl, pdwAttributes);
598 
599     if (!ppidl)
600         return hr;
601 
602     *ppidl = NULL;
603 
604     if (!lpszDisplayName)
605         return hr;
606 
607     /* handle CLSID paths */
608     if (lpszDisplayName[0] == L':' && lpszDisplayName[1] == L':')
609     {
610         return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl,
611                                              pdwAttributes);
612     }
613 
614     if (((L'A' <= lpszDisplayName[0] && lpszDisplayName[0] <= L'Z') ||
615          (L'a' <= lpszDisplayName[0] && lpszDisplayName[0] <= L'z')) &&
616         lpszDisplayName[1] == L':' && (lpszDisplayName[2] == L'\\' || !lpszDisplayName[2]))
617     {
618         // "C:\..."
619         WCHAR szRoot[8];
620         PathBuildRootW(szRoot, ((*lpszDisplayName - 1) & 0x1F));
621 
622         if (SHIsFileSysBindCtx(pbc, NULL) != S_OK && !(BindCtx_GetMode(pbc, 0) & STGM_CREATE))
623         {
624             if (::GetDriveType(szRoot) == DRIVE_NO_ROOT_DIR)
625                 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
626         }
627 
628         CComHeapPtr<ITEMIDLIST> pidlTemp(_ILCreateDrive(szRoot));
629         if (!pidlTemp)
630             return E_OUTOFMEMORY;
631 
632         if (lpszDisplayName[2] && lpszDisplayName[3])
633         {
634             CComPtr<IShellFolder> pChildFolder;
635             hr = BindToObject(pidlTemp, pbc, IID_PPV_ARG(IShellFolder, &pChildFolder));
636             if (FAILED_UNEXPECTEDLY(hr))
637                 return hr;
638 
639             ULONG chEaten;
640             CComHeapPtr<ITEMIDLIST> pidlChild;
641             hr = pChildFolder->ParseDisplayName(hwndOwner, pbc, &lpszDisplayName[3], &chEaten,
642                                                 &pidlChild, pdwAttributes);
643             if (FAILED_UNEXPECTEDLY(hr))
644                 return hr;
645 
646             hr = SHILCombine(pidlTemp, pidlChild, ppidl);
647         }
648         else
649         {
650             *ppidl = pidlTemp.Detach();
651             if (pdwAttributes && *pdwAttributes)
652                 GetAttributesOf(1, (PCUITEMID_CHILD_ARRAY)ppidl, pdwAttributes);
653             hr = S_OK;
654         }
655     }
656 
657     TRACE("(%p)->(-- ret=0x%08x)\n", this, hr);
658 
659     return hr;
660 }
661 
662 /**************************************************************************
663 *        CDrivesFolder::EnumObjects
664 */
EnumObjects(HWND hwndOwner,DWORD dwFlags,LPENUMIDLIST * ppEnumIDList)665 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
666 {
667     CComPtr<IEnumIDList> pRegEnumerator;
668     m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
669 
670     return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
671 }
672 
673 /**************************************************************************
674 *        CDrivesFolder::BindToObject
675 */
BindToObject(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)676 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
677 {
678     TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
679           pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
680 
681     if (!pidl)
682         return E_INVALIDARG;
683 
684     if (_ILIsSpecialFolder(pidl))
685         return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
686 
687     CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
688 
689     PERSIST_FOLDER_TARGET_INFO pfti = {0};
690     pfti.dwAttributes = -1;
691     pfti.csidl = -1;
692     pfti.szTargetParsingName[0] = *pchDrive;
693     pfti.szTargetParsingName[1] = L':';
694     pfti.szTargetParsingName[2] = L'\\';
695 
696     HRESULT hr = SHELL32_BindToSF(pidlRoot,
697                                   &pfti,
698                                   pidl,
699                                   &CLSID_ShellFSFolder,
700                                   riid,
701                                   ppvOut);
702     if (FAILED_UNEXPECTEDLY(hr))
703         return hr;
704 
705     return S_OK;
706 }
707 
708 /**************************************************************************
709 *    CDrivesFolder::BindToStorage
710 */
BindToStorage(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)711 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
712 {
713     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
714           pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
715 
716     *ppvOut = NULL;
717     return E_NOTIMPL;
718 }
719 
720 /**************************************************************************
721 *     CDrivesFolder::CompareIDs
722 */
723 
CompareIDs(LPARAM lParam,PCUIDLIST_RELATIVE pidl1,PCUIDLIST_RELATIVE pidl2)724 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
725 {
726     HRESULT hres;
727 
728     if (!pidl1 || !pidl2)
729     {
730         ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
731         return E_INVALIDARG;
732     }
733 
734     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
735         return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
736 
737     UINT iColumn = LOWORD(lParam);
738     if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || iColumn >= _countof(MyComputerSFHeader))
739         return E_INVALIDARG;
740 
741     CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
742     CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
743 
744     int result;
745     switch (MyComputerSFHeader[iColumn].colnameid)
746     {
747         case IDS_SHV_COLUMN_NAME:
748         {
749             result = _stricmp(pszDrive1, pszDrive2);
750             hres = MAKE_COMPARE_HRESULT(result);
751             break;
752         }
753         case IDS_SHV_COLUMN_TYPE:
754         {
755             /* We want to return immediately because SHELL32_CompareDetails also compares children. */
756             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
757         }
758         case IDS_SHV_COLUMN_DISK_CAPACITY:
759         case IDS_SHV_COLUMN_DISK_AVAILABLE:
760         {
761             ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
762 
763             if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
764                 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
765             else
766                 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
767 
768             if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
769                 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
770             else
771                 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
772 
773             LARGE_INTEGER Diff;
774             if (lParam == 3) /* Size */
775                 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
776             else /* Size available */
777                 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
778 
779             hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
780             break;
781         }
782         case IDS_SHV_COLUMN_COMMENTS:
783             hres = MAKE_COMPARE_HRESULT(0);
784             break;
785         DEFAULT_UNREACHABLE;
786     }
787 
788     if (HRESULT_CODE(hres) == 0)
789         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
790 
791     return hres;
792 }
793 
794 /**************************************************************************
795 *    CDrivesFolder::CreateViewObject
796 */
CreateViewObject(HWND hwndOwner,REFIID riid,LPVOID * ppvOut)797 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
798 {
799     CComPtr<IShellView> pShellView;
800     HRESULT hr = E_INVALIDARG;
801 
802     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
803           hwndOwner, shdebugstr_guid (&riid), ppvOut);
804 
805     if (!ppvOut)
806         return hr;
807 
808     *ppvOut = NULL;
809 
810     if (IsEqualIID(riid, IID_IDropTarget))
811     {
812         WARN("IDropTarget not implemented\n");
813         hr = E_NOTIMPL;
814     }
815     else if (IsEqualIID(riid, IID_IContextMenu))
816     {
817         DEFCONTEXTMENU dcm = { hwndOwner, this, pidlRoot, this };
818         hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
819     }
820     else if (IsEqualIID(riid, IID_IShellView))
821     {
822             SFV_CREATE sfvparams = { sizeof(SFV_CREATE), this, NULL, static_cast<IShellFolderViewCB*>(this) };
823             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
824     }
825     TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
826     return hr;
827 }
828 
829 /**************************************************************************
830 *  CDrivesFolder::GetAttributesOf
831 */
GetAttributesOf(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,DWORD * rgfInOut)832 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
833 {
834     TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
835           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
836 
837     if (cidl && !apidl)
838         return E_INVALIDARG;
839 
840     if (*rgfInOut == 0)
841         *rgfInOut = ~0;
842 
843     /* FIXME: always add SFGAO_CANLINK */
844     if(cidl == 0)
845         *rgfInOut &= dwComputerAttributes;
846     else
847     {
848         for (UINT i = 0; i < cidl; ++i)
849         {
850             if (_ILIsDrive(apidl[i]))
851             {
852                 *rgfInOut &= dwDriveAttributes;
853 
854                 if (_ILGetDriveType(apidl[i]) == DRIVE_CDROM)
855                     *rgfInOut &= ~SFGAO_CANRENAME; // CD-ROM drive cannot rename
856             }
857             else if (_ILIsControlPanel(apidl[i]))
858             {
859                 *rgfInOut &= dwControlPanelAttributes;
860             }
861             else if (_ILIsSpecialFolder(*apidl))
862             {
863                 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
864             }
865             else
866             {
867                 ERR("Got unknown pidl type!\n");
868             }
869         }
870     }
871 
872     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
873     *rgfInOut &= ~SFGAO_VALIDATE;
874 
875     TRACE("-- result=0x%08x\n", *rgfInOut);
876     return S_OK;
877 }
878 
879 /**************************************************************************
880 *    CDrivesFolder::GetUIObjectOf
881 *
882 * PARAMETERS
883 *  hwndOwner [in]  Parent window for any output
884 *  cidl      [in]  array size
885 *  apidl     [in]  simple pidl array
886 *  riid      [in]  Requested Interface
887 *  prgfInOut [   ] reserved
888 *  ppvObject [out] Resulting Interface
889 *
890 */
GetUIObjectOf(HWND hwndOwner,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,REFIID riid,UINT * prgfInOut,LPVOID * ppvOut)891 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
892     UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
893     REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
894 {
895     LPVOID pObj = NULL;
896     HRESULT hr = E_INVALIDARG;
897 
898     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
899           hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
900 
901     if (!ppvOut)
902         return hr;
903 
904     *ppvOut = NULL;
905 
906     if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
907     {
908         if (_ILIsDrive(apidl[0]))
909             hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
910         else
911             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
912     }
913     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
914     {
915         hr = IDataObject_Constructor(hwndOwner,
916                                      pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
917     }
918     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
919     {
920         if (_ILIsDrive(apidl[0]))
921             hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
922         else
923             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
924     }
925     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
926     {
927         CComPtr<IShellFolder> psfChild;
928         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
929         if (FAILED_UNEXPECTEDLY(hr))
930             return hr;
931 
932         return psfChild->CreateViewObject(NULL, riid, ppvOut);
933     }
934     else
935         hr = E_NOINTERFACE;
936 
937     if (SUCCEEDED(hr) && !pObj)
938         hr = E_OUTOFMEMORY;
939 
940     *ppvOut = pObj;
941     TRACE("(%p)->hr=0x%08x\n", this, hr);
942     return hr;
943 }
944 
945 /**************************************************************************
946 *    CDrivesFolder::GetDisplayNameOf
947 */
GetDisplayNameOf(PCUITEMID_CHILD pidl,DWORD dwFlags,LPSTRRET strRet)948 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
949 {
950     LPWSTR pszPath;
951     HRESULT hr = S_OK;
952 
953     TRACE("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
954     pdump (pidl);
955 
956     if (!strRet)
957         return E_INVALIDARG;
958 
959     if (!_ILIsPidlSimple (pidl))
960     {
961         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
962     }
963     else if (_ILIsSpecialFolder(pidl))
964     {
965         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
966     }
967     else if (!_ILIsDrive(pidl))
968     {
969         ERR("Wrong pidl type\n");
970         return E_INVALIDARG;
971     }
972 
973     pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
974     if (!pszPath)
975         return E_OUTOFMEMORY;
976 
977     pszPath[0] = 0;
978 
979     _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);    /* append my own path */
980     /* long view "lw_name (C:)" */
981     if (!(dwFlags & SHGDN_FORPARSING))
982     {
983         WCHAR wszDrive[18] = {0};
984 
985         lstrcpynW(wszDrive, pszPath, 4);
986         pszPath[0] = L'\0';
987 
988         if (!SUCCEEDED(getLabelForDrive(wszDrive, pszPath)))
989         {
990             DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
991 
992             GetVolumeInformationW(wszDrive, pszPath,
993                                   MAX_PATH - 7,
994                                   &dwVolumeSerialNumber,
995                                   &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
996             pszPath[MAX_PATH-1] = L'\0';
997 
998             if (!wcslen(pszPath))
999             {
1000                 UINT DriveType, ResourceId;
1001                 DriveType = GetDriveTypeW(wszDrive);
1002 
1003                 switch (DriveType)
1004                 {
1005                     case DRIVE_FIXED:
1006                         ResourceId = IDS_DRIVE_FIXED;
1007                         break;
1008                     case DRIVE_REMOTE:
1009                         ResourceId = IDS_DRIVE_NETWORK;
1010                         break;
1011                     case DRIVE_CDROM:
1012                         ResourceId = IDS_DRIVE_CDROM;
1013                         break;
1014                     default:
1015                         ResourceId = 0;
1016                 }
1017 
1018                 if (ResourceId)
1019                 {
1020                     dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
1021                     if (dwFileSystemFlags > MAX_PATH - 7)
1022                         pszPath[MAX_PATH-7] = L'\0';
1023                 }
1024             }
1025         }
1026         wcscat(pszPath, L" (");
1027         wszDrive[2] = L'\0';
1028         wcscat(pszPath, wszDrive);
1029         wcscat(pszPath, L")");
1030     }
1031 
1032     if (SUCCEEDED(hr))
1033     {
1034         strRet->uType = STRRET_WSTR;
1035         strRet->pOleStr = pszPath;
1036     }
1037     else
1038         CoTaskMemFree(pszPath);
1039 
1040     TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1041     return hr;
1042 }
1043 
1044 /**************************************************************************
1045 *  CDrivesFolder::SetNameOf
1046 *  Changes the name of a file object or subfolder, possibly changing its item
1047 *  identifier in the process.
1048 *
1049 * PARAMETERS
1050 *  hwndOwner  [in]   Owner window for output
1051 *  pidl       [in]   simple pidl of item to change
1052 *  lpszName   [in]   the items new display name
1053 *  dwFlags    [in]   SHGNO formatting flags
1054 *  ppidlOut   [out]  simple pidl returned
1055 */
SetNameOf(HWND hwndOwner,PCUITEMID_CHILD pidl,LPCOLESTR lpName,DWORD dwFlags,PITEMID_CHILD * pPidlOut)1056 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
1057                                         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
1058 {
1059     WCHAR szName[30];
1060 
1061     if (_ILIsDrive(pidl))
1062     {
1063         if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
1064             SetVolumeLabelW(szName, lpName);
1065         if (pPidlOut)
1066             *pPidlOut = _ILCreateDrive(szName);
1067         return S_OK;
1068     }
1069 
1070     return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
1071 }
1072 
GetDefaultSearchGUID(GUID * pguid)1073 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
1074 {
1075     FIXME("(%p)\n", this);
1076     return E_NOTIMPL;
1077 }
1078 
EnumSearches(IEnumExtraSearch ** ppenum)1079 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1080 {
1081     FIXME("(%p)\n", this);
1082     return E_NOTIMPL;
1083 }
1084 
GetDefaultColumn(DWORD dwRes,ULONG * pSort,ULONG * pDisplay)1085 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
1086 {
1087     TRACE("(%p)\n", this);
1088 
1089     if (pSort)
1090         *pSort = 0;
1091     if (pDisplay)
1092         *pDisplay = 0;
1093     return S_OK;
1094 }
1095 
GetDefaultColumnState(UINT iColumn,SHCOLSTATEF * pcsFlags)1096 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF * pcsFlags)
1097 {
1098     TRACE("(%p)\n", this);
1099 
1100     if (!pcsFlags || iColumn >= _countof(MyComputerSFHeader))
1101         return E_INVALIDARG;
1102     *pcsFlags = MyComputerSFHeader[iColumn].colstate;
1103     return S_OK;
1104 }
1105 
GetDetailsEx(PCUITEMID_CHILD pidl,const SHCOLUMNID * pscid,VARIANT * pv)1106 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
1107 {
1108     FIXME("(%p)\n", this);
1109     return E_NOTIMPL;
1110 }
1111 
GetDetailsOf(PCUITEMID_CHILD pidl,UINT iColumn,SHELLDETAILS * psd)1112 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
1113 {
1114     HRESULT hr;
1115 
1116     TRACE("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1117 
1118     if (!psd || iColumn >= _countof(MyComputerSFHeader))
1119         return E_INVALIDARG;
1120 
1121     if (!pidl)
1122     {
1123         psd->fmt = MyComputerSFHeader[iColumn].fmt;
1124         psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
1125         return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
1126     }
1127     else if (!_ILIsDrive(pidl))
1128     {
1129         switch (MyComputerSFHeader[iColumn].colnameid)
1130         {
1131             case IDS_SHV_COLUMN_NAME:
1132             case IDS_SHV_COLUMN_TYPE:
1133                 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
1134             case IDS_SHV_COLUMN_DISK_CAPACITY:
1135             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1136                 return SHSetStrRet(&psd->str, ""); /* blank col */
1137             case IDS_SHV_COLUMN_COMMENTS:
1138                 return m_regFolder->GetDetailsOf(pidl, 2, psd); /* 2 = comments */
1139             DEFAULT_UNREACHABLE;
1140         }
1141     }
1142     else
1143     {
1144         ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
1145         CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
1146         UINT DriveType = GetDriveTypeA(pszDrive);
1147         if (DriveType > DRIVE_RAMDISK)
1148             DriveType = DRIVE_FIXED;
1149 
1150         switch (MyComputerSFHeader[iColumn].colnameid)
1151         {
1152             case IDS_SHV_COLUMN_NAME:
1153                 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1154                 break;
1155             case IDS_SHV_COLUMN_TYPE:
1156                 if (DriveType == DRIVE_REMOVABLE && !IsDriveFloppyA(pszDrive))
1157                     hr = SHSetStrRet(&psd->str, IDS_DRIVE_REMOVABLE);
1158                 else
1159                     hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1160                 break;
1161             case IDS_SHV_COLUMN_DISK_CAPACITY:
1162             case IDS_SHV_COLUMN_DISK_AVAILABLE:
1163                 psd->str.cStr[0] = 0x00;
1164                 psd->str.uType = STRRET_CSTR;
1165                 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1166                 {
1167                     GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1168                     if (iColumn == 2)
1169                         StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1170                     else
1171                         StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1172                 }
1173                 hr = S_OK;
1174                 break;
1175             case IDS_SHV_COLUMN_COMMENTS:
1176                 hr = SHSetStrRet(&psd->str, ""); /* FIXME: comments */
1177                 break;
1178             DEFAULT_UNREACHABLE;
1179         }
1180     }
1181 
1182     return hr;
1183 }
1184 
MapColumnToSCID(UINT column,SHCOLUMNID * pscid)1185 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1186 {
1187     FIXME("(%p)\n", this);
1188     return E_NOTIMPL;
1189 }
1190 
1191 /************************************************************************
1192  *    CDrivesFolder::GetClassID
1193  */
GetClassID(CLSID * lpClassId)1194 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1195 {
1196     TRACE("(%p)\n", this);
1197 
1198     if (!lpClassId)
1199         return E_POINTER;
1200 
1201     *lpClassId = CLSID_MyComputer;
1202     return S_OK;
1203 }
1204 
1205 /************************************************************************
1206  *    CDrivesFolder::Initialize
1207  *
1208  * NOTES: it makes no sense to change the pidl
1209  */
Initialize(PCIDLIST_ABSOLUTE pidl)1210 HRESULT WINAPI CDrivesFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1211 {
1212     return S_OK;
1213 }
1214 
1215 /**************************************************************************
1216  *    CDrivesFolder::GetCurFolder
1217  */
GetCurFolder(PIDLIST_ABSOLUTE * pidl)1218 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1219 {
1220     TRACE("(%p)->(%p)\n", this, pidl);
1221 
1222     if (!pidl)
1223         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1224 
1225     *pidl = ILClone(pidlRoot);
1226     return S_OK;
1227 }
1228 
1229 /**************************************************************************
1230  *    CDrivesFolder::ShouldShow
1231  */
ShouldShow(IShellFolder * psf,PCIDLIST_ABSOLUTE pidlFolder,PCUITEMID_CHILD pidlItem)1232 HRESULT WINAPI CDrivesFolder::ShouldShow(IShellFolder *psf, PCIDLIST_ABSOLUTE pidlFolder, PCUITEMID_CHILD pidlItem)
1233 {
1234     if (const CLSID* pClsid = IsRegItem(pidlItem))
1235         return SHELL32_IsShellFolderNamespaceItemHidden(L"HideMyComputerIcons", *pClsid) ? S_FALSE : S_OK;
1236     return S_OK;
1237 }
1238 
1239 /************************************************************************/
1240 /* IContextMenuCB interface */
1241 
CallBack(IShellFolder * psf,HWND hwndOwner,IDataObject * pdtobj,UINT uMsg,WPARAM wParam,LPARAM lParam)1242 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1243 {
1244     enum { IDC_PROPERTIES };
1245     /* no data object means no selection */
1246     if (!pdtobj)
1247     {
1248         if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
1249         {
1250             // "System" properties
1251             return SHELL_ExecuteControlPanelCPL(hwndOwner, L"sysdm.cpl") ? S_OK : E_FAIL;
1252         }
1253         else if (uMsg == DFM_MERGECONTEXTMENU) // TODO: DFM_MERGECONTEXTMENU_BOTTOM
1254         {
1255             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1256             HMENU hpopup = CreatePopupMenu();
1257             _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1258             pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1259             DestroyMenu(hpopup);
1260             return S_OK;
1261         }
1262     }
1263     return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
1264 }
1265