[SHELL32] CDrivesFolder: Implement the eject and disconnect menu items. CORE-13841
[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 if (!SH_ShowDriveProperties(wszBuf, pidlFolder, apidl))
219 {
220 hr = E_FAIL;
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 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
257 if (dwError == NO_ERROR)
258 {
259 SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
260 }
261 else
262 {
263 nStringID = IDS_CANTDISCONNECT;
264 }
265 }
266 }
267
268 if (nStringID != 0)
269 {
270 /* show error message */
271 WCHAR szFormat[128], szMessage[128];
272 LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
273 wsprintfW(szMessage, szFormat, dwError);
274 MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
275 }
276 }
277
278 SHFree(pidlFolder);
279 _ILFreeaPidl(apidl, cidl);
280
281 return hr;
282 }
283
284 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
285 HWND hwnd,
286 UINT cidl,
287 PCUITEMID_CHILD_ARRAY apidl,
288 IShellFolder *psf,
289 IContextMenu **ppcm)
290 {
291 HKEY hKeys[2];
292 UINT cKeys = 0;
293 AddClassKeyToArray(L"Drive", hKeys, &cKeys);
294 AddClassKeyToArray(L"Folder", hKeys, &cKeys);
295
296 return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
297 }
298
299 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
300 {
301 CComPtr<IDefaultExtractIconInit> initIcon;
302 HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
303 if (FAILED_UNEXPECTEDLY(hr))
304 return hr;
305
306 CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
307 UINT DriveType = GetDriveTypeA(pszDrive);
308 if (DriveType > DRIVE_RAMDISK)
309 DriveType = DRIVE_FIXED;
310
311 WCHAR wTemp[MAX_PATH];
312 int icon_idx;
313 if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
314 (HCR_GetIconW(L"Drive", wTemp, NULL, MAX_PATH, &icon_idx)))
315 {
316 initIcon->SetNormalIcon(wTemp, icon_idx);
317 }
318 else
319 {
320 icon_idx = iDriveIconIds[DriveType];
321 initIcon->SetNormalIcon(swShell32Name, -icon_idx);
322 }
323
324 return initIcon->QueryInterface(riid, ppvOut);
325 }
326
327 class CDrivesFolderEnum :
328 public CEnumIDListBase
329 {
330 public:
331 HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
332 {
333 /* enumerate the folders */
334 if (dwFlags & SHCONTF_FOLDERS)
335 {
336 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
337 DWORD dwDrivemap = GetLogicalDrives();
338
339 while (wszDriveName[0] <= 'Z')
340 {
341 if(dwDrivemap & 0x00000001L)
342 AddToEnumList(_ILCreateDrive(wszDriveName));
343 wszDriveName[0]++;
344 dwDrivemap = dwDrivemap >> 1;
345 }
346 }
347
348 /* Enumerate the items of the reg folder */
349 AppendItemsFromEnumerator(pRegEnumerator);
350
351 return S_OK;
352 }
353
354 BEGIN_COM_MAP(CDrivesFolderEnum)
355 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
356 END_COM_MAP()
357 };
358
359 /***********************************************************************
360 * IShellFolder [MyComputer] implementation
361 */
362
363 static const shvheader MyComputerSFHeader[] = {
364 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
365 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
366 {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
367 {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
368 {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
369 };
370
371 #define MYCOMPUTERSHELLVIEWCOLUMNS 5
372
373 static const DWORD dwComputerAttributes =
374 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
375 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
376 static const DWORD dwControlPanelAttributes =
377 SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
378 static const DWORD dwDriveAttributes =
379 SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
380 SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
381
382 CDrivesFolder::CDrivesFolder()
383 {
384 pidlRoot = NULL;
385 }
386
387 CDrivesFolder::~CDrivesFolder()
388 {
389 TRACE ("-- destroying IShellFolder(%p)\n", this);
390 SHFree(pidlRoot);
391 }
392
393 HRESULT WINAPI CDrivesFolder::FinalConstruct()
394 {
395 pidlRoot = _ILCreateMyComputer(); /* my qualified pidl */
396 if (pidlRoot == NULL)
397 return E_OUTOFMEMORY;
398
399 HRESULT hr = CRegFolder_CreateInstance(&CLSID_MyComputer,
400 pidlRoot,
401 L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
402 L"MyComputer",
403 IID_PPV_ARG(IShellFolder2, &m_regFolder));
404
405 return hr;
406 }
407
408 /**************************************************************************
409 * CDrivesFolder::ParseDisplayName
410 */
411 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
412 DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes)
413 {
414 HRESULT hr = E_INVALIDARG;
415 LPCWSTR szNext = NULL;
416 LPITEMIDLIST pidlTemp = NULL;
417
418 TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
419 hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
420 pchEaten, ppidl, pdwAttributes);
421
422 *ppidl = 0;
423 if (pchEaten)
424 *pchEaten = 0; /* strange but like the original */
425
426 /* handle CLSID paths */
427 if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
428 return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
429
430 if (PathGetDriveNumberW(lpszDisplayName) < 0)
431 return E_INVALIDARG;
432
433 pidlTemp = _ILCreateDrive(lpszDisplayName);
434 if (!pidlTemp)
435 return E_OUTOFMEMORY;
436
437 if (lpszDisplayName[2] == L'\\')
438 {
439 szNext = &lpszDisplayName[3];
440 }
441
442 if (szNext && *szNext)
443 {
444 hr = SHELL32_ParseNextElement (this, hwndOwner, pbc, &pidlTemp,
445 (LPOLESTR) szNext, pchEaten, pdwAttributes);
446 }
447 else
448 {
449 hr = S_OK;
450 if (pdwAttributes && *pdwAttributes)
451 {
452 if (_ILIsDrive(pidlTemp))
453 *pdwAttributes &= dwDriveAttributes;
454 else if (_ILIsSpecialFolder(pidlTemp))
455 m_regFolder->GetAttributesOf(1, &pidlTemp, pdwAttributes);
456 else
457 ERR("Got an unkown pidl here!\n");
458 }
459 }
460
461 *ppidl = pidlTemp;
462
463 TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
464
465 return hr;
466 }
467
468 /**************************************************************************
469 * CDrivesFolder::EnumObjects
470 */
471 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
472 {
473 CComPtr<IEnumIDList> pRegEnumerator;
474 m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
475
476 return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
477 }
478
479 /**************************************************************************
480 * CDrivesFolder::BindToObject
481 */
482 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
483 {
484 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
485 pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
486
487 if (_ILIsSpecialFolder(pidl))
488 return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
489
490 CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
491
492 PERSIST_FOLDER_TARGET_INFO pfti = {0};
493 pfti.dwAttributes = -1;
494 pfti.csidl = -1;
495 pfti.szTargetParsingName[0] = *pchDrive;
496 pfti.szTargetParsingName[1] = L':';
497 pfti.szTargetParsingName[2] = L'\\';
498
499 HRESULT hr = SHELL32_BindToSF(pidlRoot,
500 &pfti,
501 pidl,
502 &CLSID_ShellFSFolder,
503 riid,
504 ppvOut);
505 if (FAILED_UNEXPECTEDLY(hr))
506 return hr;
507
508 return S_OK;
509 }
510
511 /**************************************************************************
512 * CDrivesFolder::BindToStorage
513 */
514 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
515 {
516 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this,
517 pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
518
519 *ppvOut = NULL;
520 return E_NOTIMPL;
521 }
522
523 /**************************************************************************
524 * CDrivesFolder::CompareIDs
525 */
526
527 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
528 {
529 HRESULT hres;
530
531 if (!pidl1 || !pidl2)
532 {
533 ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
534 return E_INVALIDARG;
535 }
536
537 if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
538 return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
539
540 if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || LOWORD(lParam) >= MYCOMPUTERSHELLVIEWCOLUMNS)
541 return E_INVALIDARG;
542
543 CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
544 CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
545
546 int result;
547 switch(LOWORD(lParam))
548 {
549 case 0: /* name */
550 {
551 result = stricmp(pszDrive1, pszDrive2);
552 hres = MAKE_COMPARE_HRESULT(result);
553 break;
554 }
555 case 1: /* comments */
556 hres = MAKE_COMPARE_HRESULT(0);
557 break;
558 case 2: /* Type */
559 {
560 /* We want to return immediately because SHELL32_CompareDetails also compares children. */
561 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
562 }
563 case 3: /* Size */
564 case 4: /* Size Available */
565 {
566 ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
567
568 if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
569 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
570 else
571 Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
572
573 if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
574 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
575 else
576 Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
577
578 LARGE_INTEGER Diff;
579 if (lParam == 3) /* Size */
580 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
581 else /* Size available */
582 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
583
584 hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
585 break;
586 }
587 default:
588 return E_INVALIDARG;
589 }
590
591 if (HRESULT_CODE(hres) == 0)
592 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
593
594 return hres;
595 }
596
597 /**************************************************************************
598 * CDrivesFolder::CreateViewObject
599 */
600 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
601 {
602 CComPtr<IShellView> pShellView;
603 HRESULT hr = E_INVALIDARG;
604
605 TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
606 hwndOwner, shdebugstr_guid (&riid), ppvOut);
607
608 if (!ppvOut)
609 return hr;
610
611 *ppvOut = NULL;
612
613 if (IsEqualIID(riid, IID_IDropTarget))
614 {
615 WARN("IDropTarget not implemented\n");
616 hr = E_NOTIMPL;
617 }
618 else if (IsEqualIID(riid, IID_IContextMenu))
619 {
620 WARN("IContextMenu not implemented\n");
621 hr = E_NOTIMPL;
622 }
623 else if (IsEqualIID(riid, IID_IShellView))
624 {
625 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
626 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
627 }
628 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
629 return hr;
630 }
631
632 static BOOL _ILIsControlPanel(LPCITEMIDLIST pidl)
633 {
634 GUID *guid = _ILGetGUIDPointer(pidl);
635
636 TRACE("(%p)\n", pidl);
637
638 if (guid)
639 return IsEqualIID(*guid, CLSID_ControlPanel);
640 return FALSE;
641 }
642
643 /**************************************************************************
644 * CDrivesFolder::GetAttributesOf
645 */
646 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
647 {
648 TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
649 this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
650
651 if (cidl && !apidl)
652 return E_INVALIDARG;
653
654 if (*rgfInOut == 0)
655 *rgfInOut = ~0;
656
657 /* FIXME: always add SFGAO_CANLINK */
658 if(cidl == 0)
659 *rgfInOut &= dwComputerAttributes;
660 else
661 {
662 for (UINT i = 0; i < cidl; ++i)
663 {
664 if (_ILIsDrive(apidl[i]))
665 *rgfInOut &= dwDriveAttributes;
666 else if (_ILIsControlPanel(apidl[i]))
667 *rgfInOut &= dwControlPanelAttributes;
668 else if (_ILIsSpecialFolder(*apidl))
669 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
670 else
671 ERR("Got unknown pidl type!\n");
672 }
673 }
674
675 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
676 *rgfInOut &= ~SFGAO_VALIDATE;
677
678 TRACE ("-- result=0x%08x\n", *rgfInOut);
679 return S_OK;
680 }
681
682 /**************************************************************************
683 * CDrivesFolder::GetUIObjectOf
684 *
685 * PARAMETERS
686 * hwndOwner [in] Parent window for any output
687 * cidl [in] array size
688 * apidl [in] simple pidl array
689 * riid [in] Requested Interface
690 * prgfInOut [ ] reserved
691 * ppvObject [out] Resulting Interface
692 *
693 */
694 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
695 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
696 REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
697 {
698 LPVOID pObj = NULL;
699 HRESULT hr = E_INVALIDARG;
700
701 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
702 hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
703
704 if (!ppvOut)
705 return hr;
706
707 *ppvOut = NULL;
708
709 if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
710 {
711 if (_ILIsDrive(apidl[0]))
712 hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
713 else
714 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
715 }
716 else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
717 {
718 hr = IDataObject_Constructor (hwndOwner,
719 pidlRoot, apidl, cidl, (IDataObject **)&pObj);
720 }
721 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
722 {
723 if (_ILIsDrive(apidl[0]))
724 hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
725 else
726 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
727 }
728 else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
729 {
730 CComPtr<IShellFolder> psfChild;
731 hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
732 if (FAILED_UNEXPECTEDLY(hr))
733 return hr;
734
735 return psfChild->CreateViewObject(NULL, riid, ppvOut);
736 }
737 else
738 hr = E_NOINTERFACE;
739
740 if (SUCCEEDED(hr) && !pObj)
741 hr = E_OUTOFMEMORY;
742
743 *ppvOut = pObj;
744 TRACE ("(%p)->hr=0x%08x\n", this, hr);
745 return hr;
746 }
747
748 /**************************************************************************
749 * CDrivesFolder::GetDisplayNameOf
750 */
751 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
752 {
753 LPWSTR pszPath;
754 HRESULT hr = S_OK;
755
756 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
757 pdump (pidl);
758
759 if (!strRet)
760 return E_INVALIDARG;
761
762 if (!_ILIsPidlSimple (pidl))
763 {
764 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
765 }
766 else if (_ILIsSpecialFolder(pidl))
767 {
768 return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
769 }
770 else if (!_ILIsDrive(pidl))
771 {
772 ERR("Wrong pidl type\n");
773 return E_INVALIDARG;
774 }
775
776 pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
777 if (!pszPath)
778 return E_OUTOFMEMORY;
779
780 pszPath[0] = 0;
781
782 _ILSimpleGetTextW(pidl, pszPath, MAX_PATH); /* append my own path */
783 /* long view "lw_name (C:)" */
784 if (!(dwFlags & SHGDN_FORPARSING))
785 {
786 WCHAR wszDrive[18] = {0};
787 DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
788 static const WCHAR wszOpenBracket[] = {' ', '(', 0};
789 static const WCHAR wszCloseBracket[] = {')', 0};
790
791 lstrcpynW(wszDrive, pszPath, 4);
792 pszPath[0] = L'\0';
793 GetVolumeInformationW(wszDrive, pszPath,
794 MAX_PATH - 7,
795 &dwVolumeSerialNumber,
796 &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
797 pszPath[MAX_PATH-1] = L'\0';
798 if (!wcslen(pszPath))
799 {
800 UINT DriveType, ResourceId;
801 DriveType = GetDriveTypeW(wszDrive);
802 switch(DriveType)
803 {
804 case DRIVE_FIXED:
805 ResourceId = IDS_DRIVE_FIXED;
806 break;
807 case DRIVE_REMOTE:
808 ResourceId = IDS_DRIVE_NETWORK;
809 break;
810 case DRIVE_CDROM:
811 ResourceId = IDS_DRIVE_CDROM;
812 break;
813 default:
814 ResourceId = 0;
815 }
816 if (ResourceId)
817 {
818 dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
819 if (dwFileSystemFlags > MAX_PATH - 7)
820 pszPath[MAX_PATH-7] = L'\0';
821 }
822 }
823 wcscat (pszPath, wszOpenBracket);
824 wszDrive[2] = L'\0';
825 wcscat (pszPath, wszDrive);
826 wcscat (pszPath, wszCloseBracket);
827 }
828
829 if (SUCCEEDED(hr))
830 {
831 strRet->uType = STRRET_WSTR;
832 strRet->pOleStr = pszPath;
833 }
834 else
835 CoTaskMemFree(pszPath);
836
837 TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
838 return hr;
839 }
840
841 /**************************************************************************
842 * CDrivesFolder::SetNameOf
843 * Changes the name of a file object or subfolder, possibly changing its item
844 * identifier in the process.
845 *
846 * PARAMETERS
847 * hwndOwner [in] Owner window for output
848 * pidl [in] simple pidl of item to change
849 * lpszName [in] the items new display name
850 * dwFlags [in] SHGNO formatting flags
851 * ppidlOut [out] simple pidl returned
852 */
853 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
854 LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
855 {
856 WCHAR szName[30];
857
858 if (_ILIsDrive(pidl))
859 {
860 if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
861 SetVolumeLabelW(szName, lpName);
862 if (pPidlOut)
863 *pPidlOut = _ILCreateDrive(szName);
864 return S_OK;
865 }
866
867 return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
868 }
869
870 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
871 {
872 FIXME ("(%p)\n", this);
873 return E_NOTIMPL;
874 }
875
876 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
877 {
878 FIXME ("(%p)\n", this);
879 return E_NOTIMPL;
880 }
881
882 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
883 {
884 TRACE ("(%p)\n", this);
885
886 if (pSort)
887 *pSort = 0;
888 if (pDisplay)
889 *pDisplay = 0;
890 return S_OK;
891 }
892
893 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags)
894 {
895 TRACE ("(%p)\n", this);
896
897 if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
898 return E_INVALIDARG;
899 *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags;
900 return S_OK;
901 }
902
903 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
904 {
905 FIXME ("(%p)\n", this);
906 return E_NOTIMPL;
907 }
908
909 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
910 {
911 HRESULT hr;
912
913 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
914
915 if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
916 return E_INVALIDARG;
917
918 if (!pidl)
919 {
920 psd->fmt = MyComputerSFHeader[iColumn].fmt;
921 psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
922 return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
923 }
924 else if (!_ILIsDrive(pidl))
925 {
926 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
927 }
928 else
929 {
930 ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
931 CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
932 UINT DriveType = GetDriveTypeA(pszDrive);
933 if (DriveType > DRIVE_RAMDISK)
934 DriveType = DRIVE_FIXED;
935
936 switch (iColumn)
937 {
938 case 0: /* name */
939 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
940 break;
941 case 1: /* FIXME: comments */
942 hr = SHSetStrRet(&psd->str, "");
943 break;
944 case 2: /* type */
945 hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
946 break;
947 case 3: /* total size */
948 case 4: /* free size */
949 psd->str.cStr[0] = 0x00;
950 psd->str.uType = STRRET_CSTR;
951 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
952 {
953 GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
954 if (iColumn == 2)
955 StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
956 else
957 StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
958 }
959 hr = S_OK;
960 break;
961 }
962 }
963
964 return hr;
965 }
966
967 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
968 {
969 FIXME("(%p)\n", this);
970 return E_NOTIMPL;
971 }
972
973 /************************************************************************
974 * CDrivesFolder::GetClassID
975 */
976 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
977 {
978 TRACE ("(%p)\n", this);
979
980 if (!lpClassId)
981 return E_POINTER;
982
983 *lpClassId = CLSID_MyComputer;
984 return S_OK;
985 }
986
987 /************************************************************************
988 * CDrivesFolder::Initialize
989 *
990 * NOTES: it makes no sense to change the pidl
991 */
992 HRESULT WINAPI CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
993 {
994 return S_OK;
995 }
996
997 /**************************************************************************
998 * CDrivesFolder::GetCurFolder
999 */
1000 HRESULT WINAPI CDrivesFolder::GetCurFolder(LPITEMIDLIST *pidl)
1001 {
1002 TRACE("(%p)->(%p)\n", this, pidl);
1003
1004 if (!pidl)
1005 return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1006
1007 *pidl = ILClone(pidlRoot);
1008 return S_OK;
1009 }