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