1 /* 2 * Virtual Workplace folder 3 * 4 * Copyright 1997 Marcus Meissner 5 * Copyright 1998, 1999, 2002 Juergen Schmied 6 * Copyright 2009 Andrew Hill 7 * Copyright 2017 Katayama Hirofumi MZ 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 */ 23 24 #include <precomp.h> 25 26 WINE_DEFAULT_DEBUG_CHANNEL (shell); 27 28 /* 29 CDrivesFolder should create a CRegFolder to represent the virtual items that exist only in 30 the registry. The CRegFolder is aggregated by the CDrivesFolder. 31 The CDrivesFolderEnum class should enumerate only drives on the system. Since the CRegFolder 32 implementation of IShellFolder::EnumObjects enumerates the virtual items, the 33 CDrivesFolderEnum is only responsible for returning the physical items. 34 35 2. At least on my XP system, the drive pidls returned are of type PT_DRIVE1, not PT_DRIVE 36 3. The parsing name returned for my computer is incorrect. It should be "My Computer" 37 */ 38 39 static int iDriveIconIds[7] = { IDI_SHELL_DRIVE, /* DRIVE_UNKNOWN */ 40 IDI_SHELL_CDROM, /* DRIVE_NO_ROOT_DIR*/ 41 IDI_SHELL_3_14_FLOPPY, /* DRIVE_REMOVABLE*/ 42 IDI_SHELL_DRIVE, /* DRIVE_FIXED*/ 43 IDI_SHELL_NETDRIVE, /* DRIVE_REMOTE*/ 44 IDI_SHELL_CDROM, /* DRIVE_CDROM*/ 45 IDI_SHELL_RAMDISK /* DRIVE_RAMDISK*/ 46 }; 47 48 static int iDriveTypeIds[7] = { IDS_DRIVE_FIXED, /* DRIVE_UNKNOWN */ 49 IDS_DRIVE_FIXED, /* DRIVE_NO_ROOT_DIR*/ 50 IDS_DRIVE_FLOPPY, /* DRIVE_REMOVABLE*/ 51 IDS_DRIVE_FIXED, /* DRIVE_FIXED*/ 52 IDS_DRIVE_NETWORK, /* DRIVE_REMOTE*/ 53 IDS_DRIVE_CDROM, /* DRIVE_CDROM*/ 54 IDS_DRIVE_FIXED /* DRIVE_RAMDISK*/ 55 }; 56 57 /*********************************************************************** 58 * IShellFolder implementation 59 */ 60 61 #define RETRY_COUNT 3 62 #define RETRY_SLEEP 250 63 static BOOL TryToLockOrUnlockDrive(HANDLE hDrive, BOOL bLock) 64 { 65 DWORD dwError, dwBytesReturned; 66 DWORD dwCode = (bLock ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME); 67 for (DWORD i = 0; i < RETRY_COUNT; ++i) 68 { 69 if (DeviceIoControl(hDrive, dwCode, NULL, 0, NULL, 0, &dwBytesReturned, NULL)) 70 return TRUE; 71 72 dwError = GetLastError(); 73 if (dwError == ERROR_INVALID_FUNCTION) 74 break; /* don't sleep if function is not implemented */ 75 76 Sleep(RETRY_SLEEP); 77 } 78 SetLastError(dwError); 79 return FALSE; 80 } 81 82 // NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp 83 static BOOL DoEjectDrive(const WCHAR *physical, UINT nDriveType, INT *pnStringID) 84 { 85 /* GENERIC_WRITE isn't needed for umount */ 86 DWORD dwAccessMode = GENERIC_READ; 87 DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; 88 89 HANDLE hDrive = CreateFile(physical, dwAccessMode, dwShareMode, 0, OPEN_EXISTING, 0, NULL); 90 if (hDrive == INVALID_HANDLE_VALUE) 91 return FALSE; 92 93 BOOL bResult, bNeedUnlock = FALSE; 94 DWORD dwBytesReturned, dwError = NO_ERROR; 95 PREVENT_MEDIA_REMOVAL removal; 96 do 97 { 98 bResult = TryToLockOrUnlockDrive(hDrive, TRUE); 99 if (!bResult) 100 { 101 dwError = GetLastError(); 102 *pnStringID = IDS_CANTLOCKVOLUME; /* Unable to lock volume */ 103 break; 104 } 105 bResult = DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL); 106 if (!bResult) 107 { 108 dwError = GetLastError(); 109 *pnStringID = IDS_CANTDISMOUNTVOLUME; /* Unable to dismount volume */ 110 bNeedUnlock = TRUE; 111 break; 112 } 113 removal.PreventMediaRemoval = FALSE; 114 bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_MEDIA_REMOVAL, &removal, sizeof(removal), NULL, 115 0, &dwBytesReturned, NULL); 116 if (!bResult) 117 { 118 *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */ 119 dwError = GetLastError(); 120 bNeedUnlock = TRUE; 121 break; 122 } 123 bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwBytesReturned, NULL); 124 if (!bResult) 125 { 126 *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */ 127 dwError = GetLastError(); 128 bNeedUnlock = TRUE; 129 break; 130 } 131 } while (0); 132 133 if (bNeedUnlock) 134 { 135 TryToLockOrUnlockDrive(hDrive, FALSE); 136 } 137 138 CloseHandle(hDrive); 139 140 SetLastError(dwError); 141 return bResult; 142 } 143 144 HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf, 145 HWND hwnd, 146 IDataObject *pdtobj, 147 UINT uMsg, 148 WPARAM wParam, 149 LPARAM lParam) 150 { 151 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND) 152 return S_OK; 153 154 PIDLIST_ABSOLUTE pidlFolder; 155 PUITEMID_CHILD *apidl; 156 UINT cidl; 157 UINT nDriveType; 158 DWORD dwFlags; 159 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl); 160 if (FAILED_UNEXPECTEDLY(hr)) 161 return hr; 162 163 char szDrive[8] = {0}; 164 if (!_ILGetDrive(apidl[0], szDrive, sizeof(szDrive))) 165 { 166 ERR("pidl is not a drive\n"); 167 SHFree(pidlFolder); 168 _ILFreeaPidl(apidl, cidl); 169 return E_FAIL; 170 } 171 nDriveType = GetDriveTypeA(szDrive); 172 GetVolumeInformationA(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0); 173 174 // custom command IDs 175 #define CMDID_FORMAT 1 176 #define CMDID_EJECT 2 177 #define CMDID_DISCONNECT 3 178 179 if (uMsg == DFM_MERGECONTEXTMENU) 180 { 181 QCMINFO *pqcminfo = (QCMINFO *)lParam; 182 183 UINT idCmdFirst = pqcminfo->idCmdFirst; 184 if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE) 185 { 186 /* add separator and Format */ 187 UINT idCmd = idCmdFirst + CMDID_FORMAT; 188 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0); 189 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED); 190 } 191 if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM) 192 { 193 /* add separator and Eject */ 194 UINT idCmd = idCmdFirst + CMDID_EJECT; 195 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0); 196 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED); 197 } 198 if (nDriveType == DRIVE_REMOTE) 199 { 200 /* add separator and Disconnect */ 201 UINT idCmd = idCmdFirst + CMDID_DISCONNECT; 202 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0); 203 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED); 204 } 205 206 pqcminfo->idCmdFirst += 3; 207 } 208 else if (uMsg == DFM_INVOKECOMMAND) 209 { 210 WCHAR wszBuf[4] = L"A:\\"; 211 wszBuf[0] = (WCHAR)szDrive[0]; 212 213 INT nStringID = 0; 214 DWORD dwError = NO_ERROR; 215 216 if (wParam == DFM_CMD_PROPERTIES) 217 { 218 hr = SH_ShowDriveProperties(wszBuf, pidlFolder, apidl); 219 if (FAILED(hr)) 220 { 221 dwError = ERROR_CAN_NOT_COMPLETE; 222 nStringID = IDS_CANTSHOWPROPERTIES; 223 } 224 } 225 else 226 { 227 if (wParam == CMDID_FORMAT) 228 { 229 /* do format */ 230 DWORD dwRet = SHFormatDrive(hwnd, szDrive[0] - 'A', SHFMT_ID_DEFAULT, 0); 231 switch (dwRet) 232 { 233 case SHFMT_ERROR: case SHFMT_CANCEL: case SHFMT_NOFORMAT: 234 hr = E_FAIL; 235 break; 236 } 237 } 238 else if (wParam == CMDID_EJECT) 239 { 240 /* do eject */ 241 WCHAR physical[10]; 242 wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]); 243 244 if (DoEjectDrive(physical, nDriveType, &nStringID)) 245 { 246 SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL); 247 } 248 else 249 { 250 dwError = GetLastError(); 251 } 252 } 253 else if (wParam == CMDID_DISCONNECT) 254 { 255 /* do disconnect */ 256 wszBuf[2] = UNICODE_NULL; 257 dwError = WNetCancelConnection2W(wszBuf, 0, FALSE); 258 if (dwError == NO_ERROR) 259 { 260 SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL); 261 } 262 else 263 { 264 nStringID = IDS_CANTDISCONNECT; 265 } 266 } 267 } 268 269 if (nStringID != 0) 270 { 271 /* show error message */ 272 WCHAR szFormat[128], szMessage[128]; 273 LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat)); 274 wsprintfW(szMessage, szFormat, dwError); 275 MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR); 276 } 277 } 278 279 SHFree(pidlFolder); 280 _ILFreeaPidl(apidl, cidl); 281 282 return hr; 283 } 284 285 HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder, 286 HWND hwnd, 287 UINT cidl, 288 PCUITEMID_CHILD_ARRAY apidl, 289 IShellFolder *psf, 290 IContextMenu **ppcm) 291 { 292 HKEY hKeys[2]; 293 UINT cKeys = 0; 294 AddClassKeyToArray(L"Drive", hKeys, &cKeys); 295 AddClassKeyToArray(L"Folder", hKeys, &cKeys); 296 297 return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm); 298 } 299 300 HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut) 301 { 302 CComPtr<IDefaultExtractIconInit> initIcon; 303 HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon)); 304 if (FAILED_UNEXPECTEDLY(hr)) 305 return hr; 306 307 CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName; 308 UINT DriveType = GetDriveTypeA(pszDrive); 309 if (DriveType > DRIVE_RAMDISK) 310 DriveType = DRIVE_FIXED; 311 312 WCHAR wTemp[MAX_PATH]; 313 int icon_idx; 314 if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) && 315 (HCR_GetIconW(L"Drive", wTemp, NULL, MAX_PATH, &icon_idx))) 316 { 317 initIcon->SetNormalIcon(wTemp, icon_idx); 318 } 319 else 320 { 321 icon_idx = iDriveIconIds[DriveType]; 322 initIcon->SetNormalIcon(swShell32Name, -icon_idx); 323 } 324 325 return initIcon->QueryInterface(riid, ppvOut); 326 } 327 328 class CDrivesFolderEnum : 329 public CEnumIDListBase 330 { 331 public: 332 HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator) 333 { 334 /* enumerate the folders */ 335 if (dwFlags & SHCONTF_FOLDERS) 336 { 337 WCHAR wszDriveName[] = {'A', ':', '\\', '\0'}; 338 DWORD dwDrivemap = GetLogicalDrives(); 339 340 while (wszDriveName[0] <= 'Z') 341 { 342 if(dwDrivemap & 0x00000001L) 343 AddToEnumList(_ILCreateDrive(wszDriveName)); 344 wszDriveName[0]++; 345 dwDrivemap = dwDrivemap >> 1; 346 } 347 } 348 349 /* Enumerate the items of the reg folder */ 350 AppendItemsFromEnumerator(pRegEnumerator); 351 352 return S_OK; 353 } 354 355 BEGIN_COM_MAP(CDrivesFolderEnum) 356 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) 357 END_COM_MAP() 358 }; 359 360 /*********************************************************************** 361 * IShellFolder [MyComputer] implementation 362 */ 363 364 static const shvheader MyComputerSFHeader[] = { 365 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15}, 366 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10}, 367 {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10}, 368 {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}, 369 {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}, 370 }; 371 372 #define MYCOMPUTERSHELLVIEWCOLUMNS 5 373 374 static const DWORD dwComputerAttributes = 375 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | 376 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK; 377 static const DWORD dwControlPanelAttributes = 378 SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK; 379 static const DWORD dwDriveAttributes = 380 SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | 381 SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK; 382 383 CDrivesFolder::CDrivesFolder() 384 { 385 pidlRoot = NULL; 386 } 387 388 CDrivesFolder::~CDrivesFolder() 389 { 390 TRACE ("-- destroying IShellFolder(%p)\n", this); 391 SHFree(pidlRoot); 392 } 393 394 HRESULT WINAPI CDrivesFolder::FinalConstruct() 395 { 396 pidlRoot = _ILCreateMyComputer(); /* my qualified pidl */ 397 if (pidlRoot == NULL) 398 return E_OUTOFMEMORY; 399 400 HRESULT hr = CRegFolder_CreateInstance(&CLSID_MyComputer, 401 pidlRoot, 402 L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", 403 L"MyComputer", 404 IID_PPV_ARG(IShellFolder2, &m_regFolder)); 405 406 return hr; 407 } 408 409 /************************************************************************** 410 * CDrivesFolder::ParseDisplayName 411 */ 412 HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, 413 DWORD * pchEaten, PIDLIST_RELATIVE * ppidl, DWORD * pdwAttributes) 414 { 415 HRESULT hr = E_INVALIDARG; 416 LPCWSTR szNext = NULL; 417 LPITEMIDLIST pidlTemp = NULL; 418 419 TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this, 420 hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), 421 pchEaten, ppidl, pdwAttributes); 422 423 *ppidl = 0; 424 if (pchEaten) 425 *pchEaten = 0; /* strange but like the original */ 426 427 /* handle CLSID paths */ 428 if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':') 429 return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes); 430 431 if (PathGetDriveNumberW(lpszDisplayName) < 0) 432 return E_INVALIDARG; 433 434 pidlTemp = _ILCreateDrive(lpszDisplayName); 435 if (!pidlTemp) 436 return E_OUTOFMEMORY; 437 438 if (lpszDisplayName[2] == L'\\') 439 { 440 szNext = &lpszDisplayName[3]; 441 } 442 443 if (szNext && *szNext) 444 { 445 hr = SHELL32_ParseNextElement (this, hwndOwner, pbc, &pidlTemp, 446 (LPOLESTR) szNext, pchEaten, pdwAttributes); 447 } 448 else 449 { 450 hr = S_OK; 451 if (pdwAttributes && *pdwAttributes) 452 { 453 if (_ILIsDrive(pidlTemp)) 454 *pdwAttributes &= dwDriveAttributes; 455 else if (_ILIsSpecialFolder(pidlTemp)) 456 m_regFolder->GetAttributesOf(1, &pidlTemp, pdwAttributes); 457 else 458 ERR("Got an unkown pidl here!\n"); 459 } 460 } 461 462 *ppidl = pidlTemp; 463 464 TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr); 465 466 return hr; 467 } 468 469 /************************************************************************** 470 * CDrivesFolder::EnumObjects 471 */ 472 HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) 473 { 474 CComPtr<IEnumIDList> pRegEnumerator; 475 m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator); 476 477 return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList)); 478 } 479 480 /************************************************************************** 481 * CDrivesFolder::BindToObject 482 */ 483 HRESULT WINAPI CDrivesFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 484 { 485 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, 486 pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut); 487 488 if (_ILIsSpecialFolder(pidl)) 489 return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut); 490 491 CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName; 492 493 PERSIST_FOLDER_TARGET_INFO pfti = {0}; 494 pfti.dwAttributes = -1; 495 pfti.csidl = -1; 496 pfti.szTargetParsingName[0] = *pchDrive; 497 pfti.szTargetParsingName[1] = L':'; 498 pfti.szTargetParsingName[2] = L'\\'; 499 500 HRESULT hr = SHELL32_BindToSF(pidlRoot, 501 &pfti, 502 pidl, 503 &CLSID_ShellFSFolder, 504 riid, 505 ppvOut); 506 if (FAILED_UNEXPECTEDLY(hr)) 507 return hr; 508 509 return S_OK; 510 } 511 512 /************************************************************************** 513 * CDrivesFolder::BindToStorage 514 */ 515 HRESULT WINAPI CDrivesFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 516 { 517 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, 518 pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut); 519 520 *ppvOut = NULL; 521 return E_NOTIMPL; 522 } 523 524 /************************************************************************** 525 * CDrivesFolder::CompareIDs 526 */ 527 528 HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) 529 { 530 HRESULT hres; 531 532 if (!pidl1 || !pidl2) 533 { 534 ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2); 535 return E_INVALIDARG; 536 } 537 538 if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2)) 539 return m_regFolder->CompareIDs(lParam, pidl1, pidl2); 540 541 if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || LOWORD(lParam) >= MYCOMPUTERSHELLVIEWCOLUMNS) 542 return E_INVALIDARG; 543 544 CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName; 545 CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName; 546 547 int result; 548 switch(LOWORD(lParam)) 549 { 550 case 0: /* name */ 551 { 552 result = stricmp(pszDrive1, pszDrive2); 553 hres = MAKE_COMPARE_HRESULT(result); 554 break; 555 } 556 case 1: /* comments */ 557 hres = MAKE_COMPARE_HRESULT(0); 558 break; 559 case 2: /* Type */ 560 { 561 /* We want to return immediately because SHELL32_CompareDetails also compares children. */ 562 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2); 563 } 564 case 3: /* Size */ 565 case 4: /* Size Available */ 566 { 567 ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total; 568 569 if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0)) 570 GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL); 571 else 572 Drive1Available.QuadPart = Drive1Total.QuadPart = 0; 573 574 if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0)) 575 GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL); 576 else 577 Drive2Available.QuadPart = Drive2Total.QuadPart = 0; 578 579 LARGE_INTEGER Diff; 580 if (lParam == 3) /* Size */ 581 Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart; 582 else /* Size available */ 583 Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart; 584 585 hres = MAKE_COMPARE_HRESULT(Diff.QuadPart); 586 break; 587 } 588 default: 589 return E_INVALIDARG; 590 } 591 592 if (HRESULT_CODE(hres) == 0) 593 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2); 594 595 return hres; 596 } 597 598 /************************************************************************** 599 * CDrivesFolder::CreateViewObject 600 */ 601 HRESULT WINAPI CDrivesFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut) 602 { 603 CComPtr<IShellView> pShellView; 604 HRESULT hr = E_INVALIDARG; 605 606 TRACE("(%p)->(hwnd=%p,%s,%p)\n", this, 607 hwndOwner, shdebugstr_guid (&riid), ppvOut); 608 609 if (!ppvOut) 610 return hr; 611 612 *ppvOut = NULL; 613 614 if (IsEqualIID(riid, IID_IDropTarget)) 615 { 616 WARN("IDropTarget not implemented\n"); 617 hr = E_NOTIMPL; 618 } 619 else if (IsEqualIID(riid, IID_IContextMenu)) 620 { 621 WARN("IContextMenu not implemented\n"); 622 hr = E_NOTIMPL; 623 } 624 else if (IsEqualIID(riid, IID_IShellView)) 625 { 626 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this}; 627 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut); 628 } 629 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut); 630 return hr; 631 } 632 633 static BOOL _ILIsControlPanel(LPCITEMIDLIST pidl) 634 { 635 GUID *guid = _ILGetGUIDPointer(pidl); 636 637 TRACE("(%p)\n", pidl); 638 639 if (guid) 640 return IsEqualIID(*guid, CLSID_ControlPanel); 641 return FALSE; 642 } 643 644 /************************************************************************** 645 * CDrivesFolder::GetAttributesOf 646 */ 647 HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut) 648 { 649 TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n", 650 this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0); 651 652 if (cidl && !apidl) 653 return E_INVALIDARG; 654 655 if (*rgfInOut == 0) 656 *rgfInOut = ~0; 657 658 /* FIXME: always add SFGAO_CANLINK */ 659 if(cidl == 0) 660 *rgfInOut &= dwComputerAttributes; 661 else 662 { 663 for (UINT i = 0; i < cidl; ++i) 664 { 665 if (_ILIsDrive(apidl[i])) 666 *rgfInOut &= dwDriveAttributes; 667 else if (_ILIsControlPanel(apidl[i])) 668 *rgfInOut &= dwControlPanelAttributes; 669 else if (_ILIsSpecialFolder(*apidl)) 670 m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut); 671 else 672 ERR("Got unknown pidl type!\n"); 673 } 674 } 675 676 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */ 677 *rgfInOut &= ~SFGAO_VALIDATE; 678 679 TRACE ("-- result=0x%08x\n", *rgfInOut); 680 return S_OK; 681 } 682 683 /************************************************************************** 684 * CDrivesFolder::GetUIObjectOf 685 * 686 * PARAMETERS 687 * hwndOwner [in] Parent window for any output 688 * cidl [in] array size 689 * apidl [in] simple pidl array 690 * riid [in] Requested Interface 691 * prgfInOut [ ] reserved 692 * ppvObject [out] Resulting Interface 693 * 694 */ 695 HRESULT WINAPI CDrivesFolder::GetUIObjectOf(HWND hwndOwner, 696 UINT cidl, PCUITEMID_CHILD_ARRAY apidl, 697 REFIID riid, UINT *prgfInOut, LPVOID *ppvOut) 698 { 699 LPVOID pObj = NULL; 700 HRESULT hr = E_INVALIDARG; 701 702 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this, 703 hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut); 704 705 if (!ppvOut) 706 return hr; 707 708 *ppvOut = NULL; 709 710 if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1)) 711 { 712 if (_ILIsDrive(apidl[0])) 713 hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj); 714 else 715 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj); 716 } 717 else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1)) 718 { 719 hr = IDataObject_Constructor (hwndOwner, 720 pidlRoot, apidl, cidl, (IDataObject **)&pObj); 721 } 722 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1)) 723 { 724 if (_ILIsDrive(apidl[0])) 725 hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj); 726 else 727 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj); 728 } 729 else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1)) 730 { 731 CComPtr<IShellFolder> psfChild; 732 hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild)); 733 if (FAILED_UNEXPECTEDLY(hr)) 734 return hr; 735 736 return psfChild->CreateViewObject(NULL, riid, ppvOut); 737 } 738 else 739 hr = E_NOINTERFACE; 740 741 if (SUCCEEDED(hr) && !pObj) 742 hr = E_OUTOFMEMORY; 743 744 *ppvOut = pObj; 745 TRACE ("(%p)->hr=0x%08x\n", this, hr); 746 return hr; 747 } 748 749 /************************************************************************** 750 * CDrivesFolder::GetDisplayNameOf 751 */ 752 HRESULT WINAPI CDrivesFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet) 753 { 754 LPWSTR pszPath; 755 HRESULT hr = S_OK; 756 757 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet); 758 pdump (pidl); 759 760 if (!strRet) 761 return E_INVALIDARG; 762 763 if (!_ILIsPidlSimple (pidl)) 764 { 765 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet); 766 } 767 else if (_ILIsSpecialFolder(pidl)) 768 { 769 return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet); 770 } 771 else if (!_ILIsDrive(pidl)) 772 { 773 ERR("Wrong pidl type\n"); 774 return E_INVALIDARG; 775 } 776 777 pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR)); 778 if (!pszPath) 779 return E_OUTOFMEMORY; 780 781 pszPath[0] = 0; 782 783 _ILSimpleGetTextW(pidl, pszPath, MAX_PATH); /* append my own path */ 784 /* long view "lw_name (C:)" */ 785 if (!(dwFlags & SHGDN_FORPARSING)) 786 { 787 WCHAR wszDrive[18] = {0}; 788 DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags; 789 static const WCHAR wszOpenBracket[] = {' ', '(', 0}; 790 static const WCHAR wszCloseBracket[] = {')', 0}; 791 792 lstrcpynW(wszDrive, pszPath, 4); 793 pszPath[0] = L'\0'; 794 GetVolumeInformationW(wszDrive, pszPath, 795 MAX_PATH - 7, 796 &dwVolumeSerialNumber, 797 &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0); 798 pszPath[MAX_PATH-1] = L'\0'; 799 if (!wcslen(pszPath)) 800 { 801 UINT DriveType, ResourceId; 802 DriveType = GetDriveTypeW(wszDrive); 803 switch(DriveType) 804 { 805 case DRIVE_FIXED: 806 ResourceId = IDS_DRIVE_FIXED; 807 break; 808 case DRIVE_REMOTE: 809 ResourceId = IDS_DRIVE_NETWORK; 810 break; 811 case DRIVE_CDROM: 812 ResourceId = IDS_DRIVE_CDROM; 813 break; 814 default: 815 ResourceId = 0; 816 } 817 if (ResourceId) 818 { 819 dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH); 820 if (dwFileSystemFlags > MAX_PATH - 7) 821 pszPath[MAX_PATH-7] = L'\0'; 822 } 823 } 824 wcscat (pszPath, wszOpenBracket); 825 wszDrive[2] = L'\0'; 826 wcscat (pszPath, wszDrive); 827 wcscat (pszPath, wszCloseBracket); 828 } 829 830 if (SUCCEEDED(hr)) 831 { 832 strRet->uType = STRRET_WSTR; 833 strRet->pOleStr = pszPath; 834 } 835 else 836 CoTaskMemFree(pszPath); 837 838 TRACE("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr)); 839 return hr; 840 } 841 842 /************************************************************************** 843 * CDrivesFolder::SetNameOf 844 * Changes the name of a file object or subfolder, possibly changing its item 845 * identifier in the process. 846 * 847 * PARAMETERS 848 * hwndOwner [in] Owner window for output 849 * pidl [in] simple pidl of item to change 850 * lpszName [in] the items new display name 851 * dwFlags [in] SHGNO formatting flags 852 * ppidlOut [out] simple pidl returned 853 */ 854 HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, 855 LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut) 856 { 857 WCHAR szName[30]; 858 859 if (_ILIsDrive(pidl)) 860 { 861 if (_ILSimpleGetTextW(pidl, szName, _countof(szName))) 862 SetVolumeLabelW(szName, lpName); 863 if (pPidlOut) 864 *pPidlOut = _ILCreateDrive(szName); 865 return S_OK; 866 } 867 868 return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut); 869 } 870 871 HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid) 872 { 873 FIXME ("(%p)\n", this); 874 return E_NOTIMPL; 875 } 876 877 HRESULT WINAPI CDrivesFolder::EnumSearches(IEnumExtraSearch ** ppenum) 878 { 879 FIXME ("(%p)\n", this); 880 return E_NOTIMPL; 881 } 882 883 HRESULT WINAPI CDrivesFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay) 884 { 885 TRACE ("(%p)\n", this); 886 887 if (pSort) 888 *pSort = 0; 889 if (pDisplay) 890 *pDisplay = 0; 891 return S_OK; 892 } 893 894 HRESULT WINAPI CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD * pcsFlags) 895 { 896 TRACE ("(%p)\n", this); 897 898 if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS) 899 return E_INVALIDARG; 900 *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags; 901 return S_OK; 902 } 903 904 HRESULT WINAPI CDrivesFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID * pscid, VARIANT * pv) 905 { 906 FIXME ("(%p)\n", this); 907 return E_NOTIMPL; 908 } 909 910 HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd) 911 { 912 HRESULT hr; 913 914 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd); 915 916 if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS) 917 return E_INVALIDARG; 918 919 if (!pidl) 920 { 921 psd->fmt = MyComputerSFHeader[iColumn].fmt; 922 psd->cxChar = MyComputerSFHeader[iColumn].cxChar; 923 return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid); 924 } 925 else if (!_ILIsDrive(pidl)) 926 { 927 return m_regFolder->GetDetailsOf(pidl, iColumn, psd); 928 } 929 else 930 { 931 ULARGE_INTEGER ulTotalBytes, ulFreeBytes; 932 CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName; 933 UINT DriveType = GetDriveTypeA(pszDrive); 934 if (DriveType > DRIVE_RAMDISK) 935 DriveType = DRIVE_FIXED; 936 937 switch (iColumn) 938 { 939 case 0: /* name */ 940 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); 941 break; 942 case 1: /* FIXME: comments */ 943 hr = SHSetStrRet(&psd->str, ""); 944 break; 945 case 2: /* type */ 946 hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]); 947 break; 948 case 3: /* total size */ 949 case 4: /* free size */ 950 psd->str.cStr[0] = 0x00; 951 psd->str.uType = STRRET_CSTR; 952 if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0)) 953 { 954 GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL); 955 if (iColumn == 3) 956 StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH); 957 else 958 StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH); 959 } 960 hr = S_OK; 961 break; 962 } 963 } 964 965 return hr; 966 } 967 968 HRESULT WINAPI CDrivesFolder::MapColumnToSCID(UINT column, SHCOLUMNID * pscid) 969 { 970 FIXME("(%p)\n", this); 971 return E_NOTIMPL; 972 } 973 974 /************************************************************************ 975 * CDrivesFolder::GetClassID 976 */ 977 HRESULT WINAPI CDrivesFolder::GetClassID(CLSID *lpClassId) 978 { 979 TRACE ("(%p)\n", this); 980 981 if (!lpClassId) 982 return E_POINTER; 983 984 *lpClassId = CLSID_MyComputer; 985 return S_OK; 986 } 987 988 /************************************************************************ 989 * CDrivesFolder::Initialize 990 * 991 * NOTES: it makes no sense to change the pidl 992 */ 993 HRESULT WINAPI CDrivesFolder::Initialize(LPCITEMIDLIST pidl) 994 { 995 return S_OK; 996 } 997 998 /************************************************************************** 999 * CDrivesFolder::GetCurFolder 1000 */ 1001 HRESULT WINAPI CDrivesFolder::GetCurFolder(LPITEMIDLIST *pidl) 1002 { 1003 TRACE("(%p)->(%p)\n", this, pidl); 1004 1005 if (!pidl) 1006 return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */ 1007 1008 *pidl = ILClone(pidlRoot); 1009 return S_OK; 1010 } 1011