* Copyright 1997 Marcus Meissner
* Copyright 1998, 1999, 2002 Juergen Schmied
* Copyright 2009 Andrew Hill
+ * Copyright 2017 Katayama Hirofumi MZ
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
3. The parsing name returned for my computer is incorrect. It should be "My Computer"
*/
+static int iDriveIconIds[7] = { IDI_SHELL_DRIVE, /* DRIVE_UNKNOWN */
+ IDI_SHELL_CDROM, /* DRIVE_NO_ROOT_DIR*/
+ IDI_SHELL_3_14_FLOPPY, /* DRIVE_REMOVABLE*/
+ IDI_SHELL_DRIVE, /* DRIVE_FIXED*/
+ IDI_SHELL_NETDRIVE, /* DRIVE_REMOTE*/
+ IDI_SHELL_CDROM, /* DRIVE_CDROM*/
+ IDI_SHELL_RAMDISK /* DRIVE_RAMDISK*/
+ };
+
+static int iDriveTypeIds[7] = { IDS_DRIVE_FIXED, /* DRIVE_UNKNOWN */
+ IDS_DRIVE_FIXED, /* DRIVE_NO_ROOT_DIR*/
+ IDS_DRIVE_FLOPPY, /* DRIVE_REMOVABLE*/
+ IDS_DRIVE_FIXED, /* DRIVE_FIXED*/
+ IDS_DRIVE_NETWORK, /* DRIVE_REMOTE*/
+ IDS_DRIVE_CDROM, /* DRIVE_CDROM*/
+ IDS_DRIVE_FIXED /* DRIVE_RAMDISK*/
+ };
+
/***********************************************************************
* IShellFolder implementation
*/
-class CDrivesFolderEnum :
- public CEnumIDListBase
+#define RETRY_COUNT 3
+#define RETRY_SLEEP 250
+static BOOL TryToLockOrUnlockDrive(HANDLE hDrive, BOOL bLock)
{
- public:
- CDrivesFolderEnum();
- ~CDrivesFolderEnum();
- HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags);
- BOOL CreateMyCompEnumList(DWORD dwFlags);
+ DWORD dwError, dwBytesReturned;
+ DWORD dwCode = (bLock ? FSCTL_LOCK_VOLUME : FSCTL_UNLOCK_VOLUME);
+ for (DWORD i = 0; i < RETRY_COUNT; ++i)
+ {
+ if (DeviceIoControl(hDrive, dwCode, NULL, 0, NULL, 0, &dwBytesReturned, NULL))
+ return TRUE;
- BEGIN_COM_MAP(CDrivesFolderEnum)
- COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
- END_COM_MAP()
-};
+ dwError = GetLastError();
+ if (dwError == ERROR_INVALID_FUNCTION)
+ break; /* don't sleep if function is not implemented */
-/***********************************************************************
-* IShellFolder [MyComputer] implementation
-*/
+ Sleep(RETRY_SLEEP);
+ }
+ SetLastError(dwError);
+ return FALSE;
+}
-static const shvheader MyComputerSFHeader[] = {
- {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
- {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
- {IDS_SHV_COLUMN6, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
- {IDS_SHV_COLUMN7, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
-};
+// NOTE: See also https://support.microsoft.com/en-us/help/165721/how-to-ejecting-removable-media-in-windows-nt-windows-2000-windows-xp
+static BOOL DoEjectDrive(const WCHAR *physical, UINT nDriveType, INT *pnStringID)
+{
+ /* GENERIC_WRITE isn't needed for umount */
+ DWORD dwAccessMode = GENERIC_READ;
+ DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ HANDLE hDrive = CreateFile(physical, dwAccessMode, dwShareMode, 0, OPEN_EXISTING, 0, NULL);
+ if (hDrive == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ BOOL bResult, bNeedUnlock = FALSE;
+ DWORD dwBytesReturned, dwError = NO_ERROR;
+ PREVENT_MEDIA_REMOVAL removal;
+ do
+ {
+ bResult = TryToLockOrUnlockDrive(hDrive, TRUE);
+ if (!bResult)
+ {
+ dwError = GetLastError();
+ *pnStringID = IDS_CANTLOCKVOLUME; /* Unable to lock volume */
+ break;
+ }
+ bResult = DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
+ if (!bResult)
+ {
+ dwError = GetLastError();
+ *pnStringID = IDS_CANTDISMOUNTVOLUME; /* Unable to dismount volume */
+ bNeedUnlock = TRUE;
+ break;
+ }
+ removal.PreventMediaRemoval = FALSE;
+ bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_MEDIA_REMOVAL, &removal, sizeof(removal), NULL,
+ 0, &dwBytesReturned, NULL);
+ if (!bResult)
+ {
+ *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
+ dwError = GetLastError();
+ bNeedUnlock = TRUE;
+ break;
+ }
+ bResult = DeviceIoControl(hDrive, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
+ if (!bResult)
+ {
+ *pnStringID = IDS_CANTEJECTMEDIA; /* Unable to eject media */
+ dwError = GetLastError();
+ bNeedUnlock = TRUE;
+ break;
+ }
+ } while (0);
-#define MYCOMPUTERSHELLVIEWCOLUMNS 4
+ if (bNeedUnlock)
+ {
+ TryToLockOrUnlockDrive(hDrive, FALSE);
+ }
-CDrivesFolderEnum::CDrivesFolderEnum()
-{
-}
+ CloseHandle(hDrive);
-CDrivesFolderEnum::~CDrivesFolderEnum()
-{
+ SetLastError(dwError);
+ return bResult;
}
-HRESULT WINAPI CDrivesFolderEnum::Initialize(HWND hwndOwner, DWORD dwFlags)
+HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf,
+ HWND hwnd,
+ IDataObject *pdtobj,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
{
- if (CreateMyCompEnumList(dwFlags) == FALSE)
- return E_FAIL;
-
- return S_OK;
-}
+ if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
+ return S_OK;
-/**************************************************************************
- * CDrivesFolderEnum::CreateMyCompEnumList()
- */
+ PIDLIST_ABSOLUTE pidlFolder;
+ PUITEMID_CHILD *apidl;
+ UINT cidl;
+ UINT nDriveType;
+ DWORD dwFlags;
+ HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
-BOOL CDrivesFolderEnum::CreateMyCompEnumList(DWORD dwFlags)
-{
- BOOL bRet = TRUE;
- static const WCHAR MyComputer_NameSpaceW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\Namespace";
+ char szDrive[8] = {0};
+ if (!_ILGetDrive(apidl[0], szDrive, sizeof(szDrive)))
+ {
+ ERR("pidl is not a drive\n");
+ SHFree(pidlFolder);
+ _ILFreeaPidl(apidl, cidl);
+ return E_FAIL;
+ }
+ nDriveType = GetDriveTypeA(szDrive);
+ GetVolumeInformationA(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0);
- TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
+// custom command IDs
+#define CMDID_FORMAT 1
+#define CMDID_EJECT 2
+#define CMDID_DISCONNECT 3
- /* enumerate the folders */
- if (dwFlags & SHCONTF_FOLDERS)
+ if (uMsg == DFM_MERGECONTEXTMENU)
{
- WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
- DWORD dwDrivemap = GetLogicalDrives();
- HKEY hKey;
- UINT i;
+ QCMINFO *pqcminfo = (QCMINFO *)lParam;
- while (bRet && wszDriveName[0] <= 'Z')
+ UINT idCmdFirst = pqcminfo->idCmdFirst;
+ if (!(dwFlags & FILE_READ_ONLY_VOLUME) && nDriveType != DRIVE_REMOTE)
+ {
+ /* add separator and Format */
+ UINT idCmd = idCmdFirst + CMDID_FORMAT;
+ _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
+ }
+ if (nDriveType == DRIVE_REMOVABLE || nDriveType == DRIVE_CDROM)
{
- if(dwDrivemap & 0x00000001L)
- bRet = AddToEnumList(_ILCreateDrive(wszDriveName));
- wszDriveName[0]++;
- dwDrivemap = dwDrivemap >> 1;
+ /* add separator and Eject */
+ UINT idCmd = idCmdFirst + CMDID_EJECT;
+ _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_EJECT), MFS_ENABLED);
}
+ if (nDriveType == DRIVE_REMOTE)
+ {
+ /* add separator and Disconnect */
+ UINT idCmd = idCmdFirst + CMDID_DISCONNECT;
+ _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, idCmd, MFT_STRING, MAKEINTRESOURCEW(IDS_DISCONNECT), MFS_ENABLED);
+ }
+
+ pqcminfo->idCmdFirst += 3;
+ }
+ else if (uMsg == DFM_INVOKECOMMAND)
+ {
+ WCHAR wszBuf[4] = L"A:\\";
+ wszBuf[0] = (WCHAR)szDrive[0];
+
+ INT nStringID = 0;
+ DWORD dwError = NO_ERROR;
- TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n", this);
- for (i = 0; i < 2; i++)
+ if (wParam == DFM_CMD_PROPERTIES)
{
- if (bRet && ERROR_SUCCESS == RegOpenKeyExW(i == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
- MyComputer_NameSpaceW, 0, KEY_READ, &hKey))
+ if (!SH_ShowDriveProperties(wszBuf, pidlFolder, apidl))
+ {
+ hr = E_FAIL;
+ dwError = ERROR_CAN_NOT_COMPLETE;
+ nStringID = IDS_CANTSHOWPROPERTIES;
+ }
+ }
+ else
+ {
+ if (wParam == CMDID_FORMAT)
+ {
+ /* do format */
+ DWORD dwRet = SHFormatDrive(hwnd, szDrive[0] - 'A', SHFMT_ID_DEFAULT, 0);
+ switch (dwRet)
+ {
+ case SHFMT_ERROR: case SHFMT_CANCEL: case SHFMT_NOFORMAT:
+ hr = E_FAIL;
+ break;
+ }
+ }
+ else if (wParam == CMDID_EJECT)
{
- WCHAR wszBuf[50];
- DWORD dwSize, j = 0;
- LONG ErrorCode;
- LPITEMIDLIST pidl;
+ /* do eject */
+ WCHAR physical[10];
+ wsprintfW(physical, _T("\\\\.\\%c:"), szDrive[0]);
- while (bRet)
+ if (DoEjectDrive(physical, nDriveType, &nStringID))
{
- dwSize = sizeof(wszBuf) / sizeof(wszBuf[0]);
- ErrorCode = RegEnumKeyExW(hKey, j, wszBuf, &dwSize, 0, NULL, NULL, NULL);
- if (ERROR_SUCCESS == ErrorCode)
- {
- if (wszBuf[0] != L'{')
- {
- dwSize = sizeof(wszBuf);
- RegGetValueW(hKey, wszBuf, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &dwSize);
- }
-
- /* FIXME: shell extensions - the type should be PT_SHELLEXT (tested) */
- pidl = _ILCreateGuidFromStrW(wszBuf);
- if (pidl != NULL)
- bRet = AddToEnumList(pidl);
- else
- ERR("Invalid MyComputer namespace extesion: %s\n", wszBuf);
- j++;
- }
- else if (ERROR_NO_MORE_ITEMS == ErrorCode)
- break;
- else
- bRet = FALSE;
+ SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
+ }
+ else
+ {
+ dwError = GetLastError();
+ }
+ }
+ else if (wParam == CMDID_DISCONNECT)
+ {
+ /* do disconnect */
+ wszBuf[2] = UNICODE_NULL;
+ dwError = WNetCancelConnection2W(wszBuf, 0, FALSE);
+ if (dwError == NO_ERROR)
+ {
+ SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wszBuf, NULL);
+ }
+ else
+ {
+ nStringID = IDS_CANTDISCONNECT;
}
- RegCloseKey(hKey);
}
}
+
+ if (nStringID != 0)
+ {
+ /* show error message */
+ WCHAR szFormat[128], szMessage[128];
+ LoadStringW(shell32_hInstance, nStringID, szFormat, _countof(szFormat));
+ wsprintfW(szMessage, szFormat, dwError);
+ MessageBoxW(hwnd, szMessage, NULL, MB_ICONERROR);
+ }
+ }
+
+ SHFree(pidlFolder);
+ _ILFreeaPidl(apidl, cidl);
+
+ return hr;
+}
+
+HRESULT CDrivesContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
+ HWND hwnd,
+ UINT cidl,
+ PCUITEMID_CHILD_ARRAY apidl,
+ IShellFolder *psf,
+ IContextMenu **ppcm)
+{
+ HKEY hKeys[2];
+ UINT cKeys = 0;
+ AddClassKeyToArray(L"Drive", hKeys, &cKeys);
+ AddClassKeyToArray(L"Folder", hKeys, &cKeys);
+
+ return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, DrivesContextMenuCallback, cKeys, hKeys, ppcm);
+}
+
+HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
+{
+ CComPtr<IDefaultExtractIconInit> initIcon;
+ HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
+ UINT DriveType = GetDriveTypeA(pszDrive);
+ if (DriveType > DRIVE_RAMDISK)
+ DriveType = DRIVE_FIXED;
+
+ WCHAR wTemp[MAX_PATH];
+ int icon_idx;
+ if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
+ (HCR_GetIconW(L"Drive", wTemp, NULL, MAX_PATH, &icon_idx)))
+ {
+ initIcon->SetNormalIcon(wTemp, icon_idx);
+ }
+ else
+ {
+ icon_idx = iDriveIconIds[DriveType];
+ initIcon->SetNormalIcon(swShell32Name, -icon_idx);
}
- return bRet;
+
+ return initIcon->QueryInterface(riid, ppvOut);
}
+class CDrivesFolderEnum :
+ public CEnumIDListBase
+{
+ public:
+ HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags, IEnumIDList* pRegEnumerator)
+ {
+ /* enumerate the folders */
+ if (dwFlags & SHCONTF_FOLDERS)
+ {
+ WCHAR wszDriveName[] = {'A', ':', '\\', '\0'};
+ DWORD dwDrivemap = GetLogicalDrives();
+
+ while (wszDriveName[0] <= 'Z')
+ {
+ if(dwDrivemap & 0x00000001L)
+ AddToEnumList(_ILCreateDrive(wszDriveName));
+ wszDriveName[0]++;
+ dwDrivemap = dwDrivemap >> 1;
+ }
+ }
+
+ /* Enumerate the items of the reg folder */
+ AppendItemsFromEnumerator(pRegEnumerator);
+
+ return S_OK;
+ }
+
+ BEGIN_COM_MAP(CDrivesFolderEnum)
+ COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
+ END_COM_MAP()
+};
+
+/***********************************************************************
+* IShellFolder [MyComputer] implementation
+*/
+
+static const shvheader MyComputerSFHeader[] = {
+ {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
+ {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
+ {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
+ {IDS_SHV_COLUMN_DISK_CAPACITY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
+ {IDS_SHV_COLUMN_DISK_AVAILABLE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
+};
+
+#define MYCOMPUTERSHELLVIEWCOLUMNS 5
+
+static const DWORD dwComputerAttributes =
+ SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
+ SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
+static const DWORD dwControlPanelAttributes =
+ SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
+static const DWORD dwDriveAttributes =
+ SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
+ SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
+
CDrivesFolder::CDrivesFolder()
{
pidlRoot = NULL;
- sName = NULL;
}
CDrivesFolder::~CDrivesFolder()
HRESULT WINAPI CDrivesFolder::FinalConstruct()
{
- DWORD dwSize;
- WCHAR szName[MAX_PATH];
- WCHAR wszMyCompKey[256];
- INT i;
-
pidlRoot = _ILCreateMyComputer(); /* my qualified pidl */
if (pidlRoot == NULL)
return E_OUTOFMEMORY;
- i = swprintf(wszMyCompKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\");
- StringFromGUID2(CLSID_MyComputer, wszMyCompKey + i, sizeof(wszMyCompKey) / sizeof(wszMyCompKey[0]) - i);
- dwSize = sizeof(szName);
- if (RegGetValueW(HKEY_CURRENT_USER, wszMyCompKey,
- NULL, RRF_RT_REG_SZ, NULL, szName, &dwSize) == ERROR_SUCCESS)
- {
- sName = (LPWSTR)SHAlloc((wcslen(szName) + 1) * sizeof(WCHAR));
- if (sName)
- wcscpy(sName, szName);
- TRACE("sName %s\n", debugstr_w(sName));
- }
- return S_OK;
+ HRESULT hr = CRegFolder_CreateInstance(&CLSID_MyComputer,
+ pidlRoot,
+ L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
+ L"MyComputer",
+ IID_PPV_ARG(IShellFolder2, &m_regFolder));
+
+ return hr;
}
/**************************************************************************
{
HRESULT hr = E_INVALIDARG;
LPCWSTR szNext = NULL;
- WCHAR szElement[MAX_PATH];
LPITEMIDLIST pidlTemp = NULL;
- CLSID clsid;
TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this,
hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
/* handle CLSID paths */
if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
+ return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
+
+ if (PathGetDriveNumberW(lpszDisplayName) < 0)
+ return E_INVALIDARG;
+
+ pidlTemp = _ILCreateDrive(lpszDisplayName);
+ if (!pidlTemp)
+ return E_OUTOFMEMORY;
+
+ if (lpszDisplayName[2] == L'\\')
{
- szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
- TRACE ("-- element: %s\n", debugstr_w (szElement));
- CLSIDFromString (szElement + 2, &clsid);
- pidlTemp = _ILCreateGuid (PT_GUID, clsid);
- }
- /* do we have an absolute path name ? */
- else if (PathGetDriveNumberW (lpszDisplayName) >= 0 &&
- lpszDisplayName[2] == (WCHAR) '\\')
- {
- szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
- /* make drive letter uppercase to enable PIDL comparison */
- szElement[0] = toupper(szElement[0]);
- pidlTemp = _ILCreateDrive (szElement);
+ szNext = &lpszDisplayName[3];
}
if (szNext && *szNext)
}
else
{
- if (pdwAttributes && *pdwAttributes)
- SHELL32_GetItemAttributes (this,
- pidlTemp, pdwAttributes);
hr = S_OK;
+ if (pdwAttributes && *pdwAttributes)
+ {
+ if (_ILIsDrive(pidlTemp))
+ *pdwAttributes &= dwDriveAttributes;
+ else if (_ILIsSpecialFolder(pidlTemp))
+ m_regFolder->GetAttributesOf(1, &pidlTemp, pdwAttributes);
+ else
+ ERR("Got an unkown pidl here!\n");
+ }
}
*ppidl = pidlTemp;
*/
HRESULT WINAPI CDrivesFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
{
- return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, IID_IEnumIDList, ppEnumIDList);
+ CComPtr<IEnumIDList> pRegEnumerator;
+ m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
+
+ return ShellObjectCreatorInit<CDrivesFolderEnum>(hwndOwner, dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
}
/**************************************************************************
TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this,
pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
- return SHELL32_BindToChild(pidlRoot, NULL, pidl, riid, ppvOut);
+ if (_ILIsSpecialFolder(pidl))
+ return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
+
+ CHAR* pchDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
+
+ PERSIST_FOLDER_TARGET_INFO pfti = {0};
+ pfti.dwAttributes = -1;
+ pfti.csidl = -1;
+ pfti.szTargetParsingName[0] = *pchDrive;
+ pfti.szTargetParsingName[1] = L':';
+ pfti.szTargetParsingName[2] = L'\\';
+
+ HRESULT hr = SHELL32_BindToSF(pidlRoot,
+ &pfti,
+ pidl,
+ &CLSID_ShellFSFolder,
+ riid,
+ ppvOut);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ return S_OK;
}
/**************************************************************************
HRESULT WINAPI CDrivesFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
{
- int nReturn;
+ HRESULT hres;
+
+ if (!pidl1 || !pidl2)
+ {
+ ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
+ return E_INVALIDARG;
+ }
+
+ if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
+ return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
+
+ if (!_ILIsDrive(pidl1) || !_ILIsDrive(pidl2) || LOWORD(lParam) >= MYCOMPUTERSHELLVIEWCOLUMNS)
+ return E_INVALIDARG;
- TRACE("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", this, lParam, pidl1, pidl2);
- nReturn = SHELL32_CompareIDs (this, lParam, pidl1, pidl2);
- TRACE("-- %i\n", nReturn);
- return nReturn;
+ CHAR* pszDrive1 = _ILGetDataPointer(pidl1)->u.drive.szDriveName;
+ CHAR* pszDrive2 = _ILGetDataPointer(pidl2)->u.drive.szDriveName;
+
+ int result;
+ switch(LOWORD(lParam))
+ {
+ case 0: /* name */
+ {
+ result = stricmp(pszDrive1, pszDrive2);
+ hres = MAKE_COMPARE_HRESULT(result);
+ break;
+ }
+ case 1: /* comments */
+ hres = MAKE_COMPARE_HRESULT(0);
+ break;
+ case 2: /* Type */
+ {
+ /* We want to return immediately because SHELL32_CompareDetails also compares children. */
+ return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
+ }
+ case 3: /* Size */
+ case 4: /* Size Available */
+ {
+ ULARGE_INTEGER Drive1Available, Drive1Total, Drive2Available, Drive2Total;
+
+ if (GetVolumeInformationA(pszDrive1, NULL, 0, NULL, NULL, NULL, NULL, 0))
+ GetDiskFreeSpaceExA(pszDrive1, &Drive1Available, &Drive1Total, NULL);
+ else
+ Drive1Available.QuadPart = Drive1Total.QuadPart = 0;
+
+ if (GetVolumeInformationA(pszDrive2, NULL, 0, NULL, NULL, NULL, NULL, 0))
+ GetDiskFreeSpaceExA(pszDrive2, &Drive2Available, &Drive2Total, NULL);
+ else
+ Drive2Available.QuadPart = Drive2Total.QuadPart = 0;
+
+ LARGE_INTEGER Diff;
+ if (lParam == 3) /* Size */
+ Diff.QuadPart = Drive1Total.QuadPart - Drive2Total.QuadPart;
+ else /* Size available */
+ Diff.QuadPart = Drive1Available.QuadPart - Drive2Available.QuadPart;
+
+ hres = MAKE_COMPARE_HRESULT(Diff.QuadPart);
+ break;
+ }
+ default:
+ return E_INVALIDARG;
+ }
+
+ if (HRESULT_CODE(hres) == 0)
+ return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
+
+ return hres;
}
/**************************************************************************
}
else if (IsEqualIID(riid, IID_IShellView))
{
- hr = IShellView_Constructor ((IShellFolder *)this, &pShellView);
- if (pShellView)
- {
- hr = pShellView->QueryInterface(riid, ppvOut);
- }
+ SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
+ hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
}
TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
return hr;
static BOOL _ILIsControlPanel(LPCITEMIDLIST pidl)
{
- IID *iid = _ILGetGUIDPointer(pidl);
+ GUID *guid = _ILGetGUIDPointer(pidl);
TRACE("(%p)\n", pidl);
- if (iid)
- return IsEqualIID(iid, CLSID_ControlPanel);
+ if (guid)
+ return IsEqualIID(*guid, CLSID_ControlPanel);
return FALSE;
}
*/
HRESULT WINAPI CDrivesFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
{
- static const DWORD dwComputerAttributes =
- SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
- SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
- static const DWORD dwControlPanelAttributes =
- SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
- static const DWORD dwDriveAttributes =
- SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
- SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
-
TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
*rgfInOut &= dwDriveAttributes;
else if (_ILIsControlPanel(apidl[i]))
*rgfInOut &= dwControlPanelAttributes;
+ else if (_ILIsSpecialFolder(*apidl))
+ m_regFolder->GetAttributesOf(1, &apidl[i], rgfInOut);
else
- {
- pdump(apidl[i]);
- SHELL32_GetItemAttributes(this, apidl[i], rgfInOut);
- }
+ ERR("Got unknown pidl type!\n");
}
}
UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
{
- LPITEMIDLIST pidl;
- IUnknown *pObj = NULL;
+ LPVOID pObj = NULL;
HRESULT hr = E_INVALIDARG;
TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this,
if (IsEqualIID (riid, IID_IContextMenu) && (cidl >= 1))
{
- hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, (IShellFolder*)this, NULL, 0, NULL, (IContextMenu**)&pObj);
+ if (_ILIsDrive(apidl[0]))
+ hr = CDrivesContextMenu_CreateInstance(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), (IContextMenu**)&pObj);
+ else
+ hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
}
else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
{
hr = IDataObject_Constructor (hwndOwner,
pidlRoot, apidl, cidl, (IDataObject **)&pObj);
}
- else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
- {
- pidl = ILCombine (pidlRoot, apidl[0]);
- pObj = IExtractIconA_Constructor (pidl);
- SHFree (pidl);
- hr = S_OK;
- }
- else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
- {
- pidl = ILCombine (pidlRoot, apidl[0]);
- pObj = IExtractIconW_Constructor (pidl);
- SHFree (pidl);
- hr = S_OK;
- }
- else if (IsEqualIID (riid, IID_IDropTarget) && (cidl >= 1))
+ else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
{
- IDropTarget * pDt = NULL;
- hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
- pObj = pDt;
+ if (_ILIsDrive(apidl[0]))
+ hr = CDrivesExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
+ else
+ hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
}
- else if ((IsEqualIID(riid, IID_IShellLinkW) ||
- IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
+ else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
{
- pidl = ILCombine (pidlRoot, apidl[0]);
- hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*) &pObj);
- SHFree (pidl);
+ CComPtr<IShellFolder> psfChild;
+ hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ return psfChild->CreateViewObject(NULL, riid, ppvOut);
}
else
hr = E_NOINTERFACE;
if (!strRet)
return E_INVALIDARG;
+ if (!_ILIsPidlSimple (pidl))
+ {
+ return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
+ }
+ else if (_ILIsSpecialFolder(pidl))
+ {
+ return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
+ }
+ else if (!_ILIsDrive(pidl))
+ {
+ ERR("Wrong pidl type\n");
+ return E_INVALIDARG;
+ }
+
pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
if (!pszPath)
return E_OUTOFMEMORY;
pszPath[0] = 0;
- if (!pidl->mkid.cb)
- {
- /* parsing name like ::{...} */
- pszPath[0] = ':';
- pszPath[1] = ':';
- SHELL32_GUIDToStringW(CLSID_MyComputer, &pszPath[2]);
- }
- else if (_ILIsPidlSimple(pidl))
+ _ILSimpleGetTextW(pidl, pszPath, MAX_PATH); /* append my own path */
+ /* long view "lw_name (C:)" */
+ if (!(dwFlags & SHGDN_FORPARSING))
{
- /* take names of special folders only if its only this folder */
- if (_ILIsSpecialFolder(pidl))
+ WCHAR wszDrive[18] = {0};
+ DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
+ static const WCHAR wszOpenBracket[] = {' ', '(', 0};
+ static const WCHAR wszCloseBracket[] = {')', 0};
+
+ lstrcpynW(wszDrive, pszPath, 4);
+ pszPath[0] = L'\0';
+ GetVolumeInformationW(wszDrive, pszPath,
+ MAX_PATH - 7,
+ &dwVolumeSerialNumber,
+ &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
+ pszPath[MAX_PATH-1] = L'\0';
+ if (!wcslen(pszPath))
{
- GUID const *clsid;
-
- clsid = _ILGetGUIDPointer (pidl);
- if (clsid)
- {
- if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)
- {
- static const WCHAR clsidW[] = L"CLSID\\";
- static const WCHAR shellfolderW[] = L"\\shellfolder";
- static const WCHAR wantsForParsingW[] = L"WantsForParsing";
- BOOL bWantsForParsing = FALSE;
- WCHAR szRegPath[100];
- LONG r;
-
- /*
- * We can only get a filesystem path from a shellfolder
- * if the value WantsFORPARSING exists in
- * CLSID\\{...}\\shellfolder
- * exception: the MyComputer folder has this keys not
- * but like any filesystem backed
- * folder it needs these behaviour
- *
- * Get the "WantsFORPARSING" flag from the registry
- */
-
- wcscpy(szRegPath, clsidW);
- SHELL32_GUIDToStringW(*clsid, &szRegPath[6]);
- wcscat(szRegPath, shellfolderW);
- r = SHGetValueW(HKEY_CLASSES_ROOT, szRegPath,
- wantsForParsingW, NULL, NULL, NULL);
- if (r == ERROR_SUCCESS)
- bWantsForParsing = TRUE;
-
- if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
- bWantsForParsing)
- {
- /*
- * We need the filesystem path to the destination folder
- * Only the folder itself can know it
- */
- hr = SHELL32_GetDisplayNameOfChild (this, pidl,
- dwFlags, pszPath, MAX_PATH);
- }
- else
- {
- LPWSTR p = pszPath;
-
- /* parsing name like ::{...} */
- p[0] = ':';
- p[1] = ':';
- p += 2;
- p += SHELL32_GUIDToStringW(CLSID_MyComputer, p);
-
- /* \:: */
- p[0] = '\\';
- p[1] = ':';
- p[2] = ':';
- p += 3;
- SHELL32_GUIDToStringW(*clsid, p);
- }
- }
- else
- {
- /* user friendly name */
-
- if (_ILIsMyComputer(pidl) && sName)
- wcscpy(pszPath, sName);
- else
- HCR_GetClassNameW (*clsid, pszPath, MAX_PATH);
-
- TRACE("pszPath %s\n", debugstr_w(pszPath));
- }
- }
- else
+ UINT DriveType, ResourceId;
+ DriveType = GetDriveTypeW(wszDrive);
+ switch(DriveType)
{
- /* append my own path */
- _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);
+ case DRIVE_FIXED:
+ ResourceId = IDS_DRIVE_FIXED;
+ break;
+ case DRIVE_REMOTE:
+ ResourceId = IDS_DRIVE_NETWORK;
+ break;
+ case DRIVE_CDROM:
+ ResourceId = IDS_DRIVE_CDROM;
+ break;
+ default:
+ ResourceId = 0;
}
- }
- else if (_ILIsDrive(pidl))
- {
-
- _ILSimpleGetTextW(pidl, pszPath, MAX_PATH); /* append my own path */
- /* long view "lw_name (C:)" */
- if (!(dwFlags & SHGDN_FORPARSING))
+ if (ResourceId)
{
- WCHAR wszDrive[18] = {0};
- DWORD dwVolumeSerialNumber, dwMaximumComponentLength, dwFileSystemFlags;
- static const WCHAR wszOpenBracket[] = {' ', '(', 0};
- static const WCHAR wszCloseBracket[] = {')', 0};
-
- lstrcpynW(wszDrive, pszPath, 4);
- pszPath[0] = L'\0';
- GetVolumeInformationW(wszDrive, pszPath,
- MAX_PATH - 7,
- &dwVolumeSerialNumber,
- &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0);
- pszPath[MAX_PATH-1] = L'\0';
- if (!wcslen(pszPath))
- {
- UINT DriveType, ResourceId;
- DriveType = GetDriveTypeW(wszDrive);
- switch(DriveType)
- {
- case DRIVE_FIXED:
- ResourceId = IDS_DRIVE_FIXED;
- break;
- case DRIVE_REMOTE:
- ResourceId = IDS_DRIVE_NETWORK;
- break;
- case DRIVE_CDROM:
- ResourceId = IDS_DRIVE_CDROM;
- break;
- default:
- ResourceId = 0;
- }
- if (ResourceId)
- {
- dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
- if (dwFileSystemFlags > MAX_PATH - 7)
- pszPath[MAX_PATH-7] = L'\0';
- }
- }
- wcscat (pszPath, wszOpenBracket);
- wszDrive[2] = L'\0';
- wcscat (pszPath, wszDrive);
- wcscat (pszPath, wszCloseBracket);
+ dwFileSystemFlags = LoadStringW(shell32_hInstance, ResourceId, pszPath, MAX_PATH);
+ if (dwFileSystemFlags > MAX_PATH - 7)
+ pszPath[MAX_PATH-7] = L'\0';
}
}
- else
- {
- /* Neither a shell namespace extension nor a drive letter. */
- ERR("Wrong pidl type\n");
- CoTaskMemFree(pszPath);
- return E_INVALIDARG;
- }
- }
- else
- {
- /* Complex pidl. Let the child folder do the work */
- hr = SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, pszPath, MAX_PATH);
+ wcscat (pszPath, wszOpenBracket);
+ wszDrive[2] = L'\0';
+ wcscat (pszPath, wszDrive);
+ wcscat (pszPath, wszCloseBracket);
}
if (SUCCEEDED(hr))
HRESULT WINAPI CDrivesFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,
LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
{
- LPWSTR sName;
- HKEY hKey;
- UINT length;
WCHAR szName[30];
- TRACE("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this,
- hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut);
-
if (_ILIsDrive(pidl))
{
if (_ILSimpleGetTextW(pidl, szName, _countof(szName)))
return S_OK;
}
-
- if (pPidlOut != NULL)
- *pPidlOut = _ILCreateMyComputer();
-
- length = (wcslen(lpName) + 1) * sizeof(WCHAR);
- sName = (LPWSTR)SHAlloc(length);
-
- if (!sName)
- return E_OUTOFMEMORY;
-
- if (RegOpenKeyExW(HKEY_CURRENT_USER,
- L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
- 0,
- KEY_WRITE,
- &hKey) != ERROR_SUCCESS)
- {
- WARN("Error: failed to open registry key\n");
- }
- else
- {
- RegSetValueExW(hKey, NULL, 0, REG_SZ, (const LPBYTE)lpName, length);
- RegCloseKey(hKey);
- }
-
- wcscpy(sName, lpName);
- SHFree(this->sName);
- this->sName = sName;
- TRACE("result %s\n", debugstr_w(sName));
- return S_OK;
+ return m_regFolder->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
}
HRESULT WINAPI CDrivesFolder::GetDefaultSearchGUID(GUID * pguid)
return E_NOTIMPL;
}
-/* FIXME: drive size >4GB is rolling over */
HRESULT WINAPI CDrivesFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
{
HRESULT hr;
{
psd->fmt = MyComputerSFHeader[iColumn].fmt;
psd->cxChar = MyComputerSFHeader[iColumn].cxChar;
- psd->str.uType = STRRET_CSTR;
- LoadStringA(shell32_hInstance, MyComputerSFHeader[iColumn].colnameid,
- psd->str.cStr, MAX_PATH);
- return S_OK;
+ return SHSetStrRet(&psd->str, MyComputerSFHeader[iColumn].colnameid);
+ }
+ else if (!_ILIsDrive(pidl))
+ {
+ return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
}
else
{
- char szPath[MAX_PATH];
- ULARGE_INTEGER ulBytes;
+ ULARGE_INTEGER ulTotalBytes, ulFreeBytes;
+ CHAR* pszDrive = _ILGetDataPointer(pidl)->u.drive.szDriveName;
+ UINT DriveType = GetDriveTypeA(pszDrive);
+ if (DriveType > DRIVE_RAMDISK)
+ DriveType = DRIVE_FIXED;
- psd->str.cStr[0] = 0x00;
- psd->str.uType = STRRET_CSTR;
switch (iColumn)
{
case 0: /* name */
- hr = GetDisplayNameOf(pidl,
- SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
+ hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
break;
- case 1: /* type */
- _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
+ case 1: /* FIXME: comments */
+ hr = SHSetStrRet(&psd->str, "");
break;
- case 2: /* total size */
- if (_ILIsDrive(pidl))
- {
- _ILSimpleGetText (pidl, szPath, MAX_PATH);
- GetDiskFreeSpaceExA (szPath, NULL, &ulBytes, NULL);
- StrFormatByteSizeA (ulBytes.LowPart, psd->str.cStr, MAX_PATH);
- }
+ case 2: /* type */
+ hr = SHSetStrRet(&psd->str, iDriveTypeIds[DriveType]);
break;
- case 3: /* free size */
- if (_ILIsDrive(pidl))
+ case 3: /* total size */
+ case 4: /* free size */
+ psd->str.cStr[0] = 0x00;
+ psd->str.uType = STRRET_CSTR;
+ if (GetVolumeInformationA(pszDrive, NULL, 0, NULL, NULL, NULL, NULL, 0))
{
- _ILSimpleGetText (pidl, szPath, MAX_PATH);
- GetDiskFreeSpaceExA (szPath, &ulBytes, NULL, NULL);
- StrFormatByteSizeA (ulBytes.LowPart, psd->str.cStr, MAX_PATH);
+ GetDiskFreeSpaceExA(pszDrive, &ulFreeBytes, &ulTotalBytes, NULL);
+ if (iColumn == 2)
+ StrFormatByteSize64A(ulTotalBytes.QuadPart, psd->str.cStr, MAX_PATH);
+ else
+ StrFormatByteSize64A(ulFreeBytes.QuadPart, psd->str.cStr, MAX_PATH);
}
+ hr = S_OK;
break;
}
- hr = S_OK;
}
return hr;
*/
HRESULT WINAPI CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
{
- TRACE ("(%p)->(%p)\n", this, pidl);
-
- if (pidlRoot)
- SHFree((LPVOID)pidlRoot);
-
- pidlRoot = ILClone(pidl);
return S_OK;
}
TRACE("(%p)->(%p)\n", this, pidl);
if (!pidl)
- return E_POINTER;
+ return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
*pidl = ILClone(pidlRoot);
return S_OK;