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(LPITEMIDLIST *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, NULL, L"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL, NULL, SW_SHOWNORMAL); 1091 } 1092 else if (uMsg == DFM_MERGECONTEXTMENU) 1093 { 1094 QCMINFO *pqcminfo = (QCMINFO *)lParam; 1095 HMENU hpopup = CreatePopupMenu(); 1096 _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_SEPARATOR, NULL, MFS_ENABLED); // #0 1097 _InsertMenuItemW(hpopup, 1, TRUE, 1, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); // #1 1098 Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR); 1099 DestroyMenu(hpopup); 1100 } 1101 1102 return S_OK; 1103 } 1104 1105 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES) 1106 return S_OK; 1107 1108 return Shell_DefaultContextMenuCallBack(this, pdtobj); 1109 } 1110