[SHELL] IPersistFolder2::GetCurFolder takes a PIDLIST_ABSOLUTE*. CORE-16385
[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 HKEY hKeys[16];
673 UINT cKeys = 0;
674 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
675
676 DEFCONTEXTMENU dcm;
677 dcm.hwnd = hwndOwner;
678 dcm.pcmcb = this;
679 dcm.pidlFolder = pidlRoot;
680 dcm.psf = this;
681 dcm.cidl = 0;
682 dcm.apidl = NULL;
683 dcm.cKeys = cKeys;
684 dcm.aKeys = hKeys;
685 dcm.punkAssociationInfo = NULL;
686 hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
687 }
688 else if (IsEqualIID(riid, IID_IShellView))
689 {
690 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
691 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
692 }
693 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
694 return hr;
695 }
696
697 static BOOL _ILIsControlPanel(LPCITEMIDLIST pidl)
698 {
699 GUID *guid = _ILGetGUIDPointer(pidl);
700
701 TRACE("(%p)\n", pidl);
702
703 if (guid)
704 return IsEqualIID(*guid, CLSID_ControlPanel);
705 return FALSE;
706 }
707
708 /**************************************************************************
709 * CDrivesFolder::GetAttributesOf
710 */
711 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
712 {
713 TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
714 this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
715
716 if (cidl && !apidl)
717 return E_INVALIDARG;
718
719 if (*rgfInOut == 0)
720 *rgfInOut = ~0;
721
722 /* FIXME: always add SFGAO_CANLINK */
723 if(cidl == 0)
724 *rgfInOut &= dwComputerAttributes;
725 else
726 {
727 for (UINT i = 0; i < cidl; ++i)
728 {
729 if (_ILIsDrive(apidl[i]))
730 *rgfInOut &= dwDriveAttributes;
731 else if (_ILIsControlPanel(apidl[i]))
732 *rgfInOut &= dwControlPanelAttributes;
733 else if (_ILIsSpecialFolder(*apidl))
734 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
735 else
736 ERR("Got unknown pidl type!\n");
737 }
738 }
739
740 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
741 *rgfInOut &= ~SFGAO_VALIDATE;
742
743 TRACE ("-- result=0x%08x\n", *rgfInOut);
744 return S_OK;
745 }
746
747 /**************************************************************************
748 * CDrivesFolder::GetUIObjectOf
749 *
750 * PARAMETERS
751 * hwndOwner [in] Parent window for any output
752 * cidl [in] array size
753 * apidl [in] simple pidl array
754 * riid [in] Requested Interface
755 * prgfInOut [ ] reserved
756 * ppvObject [out] Resulting Interface
757 *
758 */
759 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner,
760 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
761 REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
762 {
763 LPVOID pObj = NULL;
764 HRESULT hr = E_INVALIDARG;
765
766 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
767 hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
768
769 if (!ppvOut)
770 return hr;
771
772 *ppvOut = NULL;
773
774 if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
775 {
776 if (_ILIsDrive(apidl[0]))
777 hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
778 else
779 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
780 }
781 else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
782 {
783 hr = IDataObject_Constructor (hwndOwner,
784 pidlRoot, apidl, cidl, (IDataObject **)&pObj);
785 }
786 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
787 {
788 if (_ILIsDrive(apidl[0]))
789 hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
790 else
791 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
792 }
793 else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
794 {
795 CComPtr<IShellFolder> psfChild;
796 hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
797 if (FAILED_UNEXPECTEDLY(hr))
798 return hr;
799
800 return psfChild->CreateViewObject(NULL, riid, ppvOut);
801 }
802 else
803 hr = E_NOINTERFACE;
804
805 if (SUCCEEDED(hr) && !pObj)
806 hr = E_OUTOFMEMORY;
807
808 *ppvOut = pObj;
809 TRACE ("(%p)->hr=0x%08x\n", this, hr);
810 return hr;
811 }
812
813 /**************************************************************************
814 * CDrivesFolder::GetDisplayNameOf
815 */
816 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
817 {
818 LPWSTR pszPath;
819 HRESULT hr = S_OK;
820
821 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
822 pdump (pidl);
823
824 if (!strRet)
825 return E_INVALIDARG;
826
827 if (!_ILIsPidlSimple (pidl))
828 {
829 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
830 }
831 else if (_ILIsSpecialFolder(pidl))
832 {
833 return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
834 }
835 else if (!_ILIsDrive(pidl))
836 {
837 ERR("Wrong pidl type\n");
838 return E_INVALIDARG;
839 }
840
841 pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
842 if (!pszPath)
843 return E_OUTOFMEMORY;
844
845 pszPath[0] = 0;
846
847 _ILSimpleGetTextW(pidl, pszPath, MAX_PATH); /* append my own path */
848 /* long view "lw_name (C:)" */
849 if (!(dwFlags & SHGDN_FORPARSING))
850 {
851 WCHAR wszDrive[18] = {0};
852 DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
853 static const WCHAR wszOpenBracket[] = {' ', '(', 0};
854 static const WCHAR wszCloseBracket[] = {')', 0};
855
856 lstrcpynW(wszDrive, pszPath, 4);
857 pszPath[0] = L'\0';
858 GetVolumeInformationW(wszDrive, pszPath,
859 MAX_PATH - 7,
860 &dwVolumeSerialNumber,
861 &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
862 pszPath[MAX_PATH-1] = L'\0';
863 if (!wcslen(pszPath))
864 {
865 UINT DriveType, ResourceId;
866 DriveType = GetDriveTypeW(wszDrive);
867 switch(DriveType)
868 {
869 case DRIVE_FIXED:
870 ResourceId = IDS_DRIVE_FIXED;
871 break;
872 case DRIVE_REMOTE:
873 ResourceId = IDS_DRIVE_NETWORK;
874 break;
875 case DRIVE_CDROM:
876 ResourceId = IDS_DRIVE_CDROM;
877 break;
878 default:
879 ResourceId = 0;
880 }
881 if (ResourceId)
882 {
883 dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
884 if (dwFileSystemFlags > MAX_PATH - 7)
885 pszPath[MAX_PATH-7] = L'\0';
886 }
887 }
888 wcscat (pszPath, wszOpenBracket);
889 wszDrive[2] = L'\0';
890 wcscat (pszPath, wszDrive);
891 wcscat (pszPath, wszCloseBracket);
892 }
893
894 if (SUCCEEDED(hr))
895 {
896 strRet->uType = STRRET_WSTR;
897 strRet->pOleStr = pszPath;
898 }
899 else
900 CoTaskMemFree(pszPath);
901
902 TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
903 return hr;
904 }
905
906 /**************************************************************************
907 * CDrivesFolder::SetNameOf
908 * Changes the name of a file object or subfolder, possibly changing its item
909 * identifier in the process.
910 *
911 * PARAMETERS
912 * hwndOwner [in] Owner window for output
913 * pidl [in] simple pidl of item to change
914 * lpszName [in] the items new display name
915 * dwFlags [in] SHGNO formatting flags
916 * ppidlOut [out] simple pidl returned
917 */
918 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
919 LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
920 {
921 WCHAR szName[30];
922
923 if (_ILIsDrive(pidl))
924 {
925 if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
926 SetVolumeLabelW(szName, lpName);
927 if (pPidlOut)
928 *pPidlOut = _ILCreateDrive(szName);
929 return S_OK;
930 }
931
932 return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
933 }
934
935 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
936 {
937 FIXME ("(%p)\n", this);
938 return E_NOTIMPL;
939 }
940
941 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum)
942 {
943 FIXME ("(%p)\n", this);
944 return E_NOTIMPL;
945 }
946
947 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
948 {
949 TRACE ("(%p)\n", this);
950
951 if (pSort)
952 *pSort = 0;
953 if (pDisplay)
954 *pDisplay = 0;
955 return S_OK;
956 }
957
958 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags)
959 {
960 TRACE ("(%p)\n", this);
961
962 if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
963 return E_INVALIDARG;
964 *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags;
965 return S_OK;
966 }
967
968 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv)
969 {
970 FIXME ("(%p)\n", this);
971 return E_NOTIMPL;
972 }
973
974 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
975 {
976 HRESULT hr;
977
978 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
979
980 if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS)
981 return E_INVALIDARG;
982
983 if (!pidl)
984 {
985 psd->fmt = MyComputerSFHeader[iColumn].fmt;
986 psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
987 return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
988 }
989 else if (!_ILIsDrive(pidl))
990 {
991 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
992 }
993 else
994 {
995 ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
996 CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
997 UINT DriveType = GetDriveTypeA(pszDrive);
998 if (DriveType > DRIVE_RAMDISK)
999 DriveType = DRIVE_FIXED;
1000
1001 switch (iColumn)
1002 {
1003 case 0: /* name */
1004 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1005 break;
1006 case 1: /* FIXME: comments */
1007 hr = SHSetStrRet(&psd->str, "");
1008 break;
1009 case 2: /* type */
1010 hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
1011 break;
1012 case 3: /* total size */
1013 case 4: /* free size */
1014 psd->str.cStr[0] = 0x00;
1015 psd->str.uType = STRRET_CSTR;
1016 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
1017 {
1018 GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
1019 if (iColumn == 3)
1020 StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
1021 else
1022 StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
1023 }
1024 hr = S_OK;
1025 break;
1026 }
1027 }
1028
1029 return hr;
1030 }
1031
1032 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid)
1033 {
1034 FIXME("(%p)\n", this);
1035 return E_NOTIMPL;
1036 }
1037
1038 /************************************************************************
1039 * CDrivesFolder::GetClassID
1040 */
1041 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId)
1042 {
1043 TRACE ("(%p)\n", this);
1044
1045 if (!lpClassId)
1046 return E_POINTER;
1047
1048 *lpClassId = CLSID_MyComputer;
1049 return S_OK;
1050 }
1051
1052 /************************************************************************
1053 * CDrivesFolder::Initialize
1054 *
1055 * NOTES: it makes no sense to change the pidl
1056 */
1057 HRESULT WINAPI CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
1058 {
1059 return S_OK;
1060 }
1061
1062 /**************************************************************************
1063 * CDrivesFolder::GetCurFolder
1064 */
1065 HRESULT WINAPI CDrivesFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
1066 {
1067 TRACE("(%p)->(%p)\n", this, pidl);
1068
1069 if (!pidl)
1070 return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1071
1072 *pidl = ILClone(pidlRoot);
1073 return S_OK;
1074 }
1075
1076 /************************************************************************/
1077 /* IContextMenuCB interface */
1078
1079 HRESULT WINAPI CDrivesFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1080 {
1081 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1082 return S_OK;
1083
1084 /* no data object means no selection */
1085 if (!pdtobj)
1086 {
1087 if (uMsg == DFM_INVOKECOMMAND && wParam == 1) // #1
1088 {
1089 // "System" properties
1090 ShellExecuteW(hwndOwner,
1091 NULL,
1092 L"rundll32.exe",
1093 L"shell32.dll,Control_RunDLL sysdm.cpl",
1094 NULL,
1095 SW_SHOWNORMAL);
1096 }
1097 else if (uMsg == DFM_MERGECONTEXTMENU)
1098 {
1099 QCMINFO *pqcminfo = (QCMINFO *)lParam;
1100 HMENU hpopup = CreatePopupMenu();
1101 _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_SEPARATOR, NULL, MFS_ENABLED); // #0
1102 _InsertMenuItemW(hpopup, 1, TRUE, 1, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); // #1
1103 Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1104 DestroyMenu(hpopup);
1105 }
1106
1107 return S_OK;
1108 }
1109
1110 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1111 return S_OK;
1112
1113 return Shell_DefaultContextMenuCallBack(this, pdtobj);
1114 }