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