[SHELL32] CDrivesFolder: Fix showing the drive capacity. CORE-14201
[reactos.git] / dll / win32 / shell32 / folders / CDrivesFolder.cpp
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 }