-
/*
- * file system folder
- *
- * Copyright 1997 Marcus Meissner
- * Copyright 1998, 1999, 2002 Juergen Schmied
- * Copyright 2019 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
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ * PROJECT: shell32
+ * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE: file system folder
+ * COPYRIGHT: Copyright 1997 Marcus Meissner
+ * Copyright 1998, 1999, 2002 Juergen Schmied
+ * Copyright 2019 Katayama Hirofumi MZ
+ * Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
*/
#include <precomp.h>
WINE_DEFAULT_DEBUG_CHANNEL (shell);
-HKEY OpenKeyFromFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName)
-{
- HKEY hkey;
+static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder);
- if (!_ILIsValue(pidl))
- {
- ERR("Invalid pidl!\n");
- return NULL;
- }
- FileStructW* pDataW = _ILGetFileStructW(pidl);
- if (!pDataW)
- {
- ERR("Invalid pidl!\n");
- return NULL;
- }
-
- LPWSTR pExtension = PathFindExtensionW(pDataW->wszName);
- if (!pExtension || *pExtension == NULL)
- {
- WARN("No extension for %S!\n", pDataW->wszName);
- return NULL;
- }
+HKEY OpenKeyFromFileType(LPWSTR pExtension, LPCWSTR KeyName)
+{
+ HKEY hkey;
WCHAR FullName[MAX_PATH];
DWORD dwSize = sizeof(FullName);
res = RegGetValueW(HKEY_CLASSES_ROOT, pExtension, NULL, RRF_RT_REG_SZ, NULL, FullName, &dwSize);
if (res)
{
- WARN("Failed to get progid for file %S, extension %S (%x), address %x, pidl: %x, error %d\n", pDataW->wszName, pExtension, pExtension, &dwSize, pidl, res);
+ WARN("Failed to get progid for extension %S (%x), error %d\n", pExtension, pExtension, res);
return NULL;
}
return hkey;
}
-HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid)
+LPWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl)
{
- HKEY hkeyProgId = OpenKeyFromFileType(pidl, KeyName);
+ if (!_ILIsValue(pidl))
+ {
+ ERR("Invalid pidl!\n");
+ return NULL;
+ }
+
+ FileStructW* pDataW = _ILGetFileStructW(pidl);
+ if (!pDataW)
+ {
+ ERR("Invalid pidl!\n");
+ return NULL;
+ }
+
+ LPWSTR pExtension = PathFindExtensionW(pDataW->wszName);
+ if (!pExtension || *pExtension == UNICODE_NULL)
+ {
+ WARN("No extension for %S!\n", pDataW->wszName);
+ return NULL;
+ }
+ return pExtension;
+}
+
+HRESULT GetCLSIDForFileTypeFromExtension(LPWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid)
+{
+ HKEY hkeyProgId = OpenKeyFromFileType(pExtension, KeyName);
if (!hkeyProgId)
{
WARN("OpenKeyFromFileType failed for key %S\n", KeyName);
return S_OK;
}
+HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid)
+{
+ LPWSTR pExtension = ExtensionFromPidl(pidl);
+ if (!pExtension)
+ return S_FALSE;
+
+ return GetCLSIDForFileTypeFromExtension(pExtension, KeyName, pclsid);
+}
+
static HRESULT
getDefaultIconLocation(LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT uFlags)
{
}
else
{
- HKEY hkey = OpenKeyFromFileType(pidl, L"DefaultIcon");
+ LPWSTR pExtension = ExtensionFromPidl(pidl);
+ HKEY hkey = pExtension ? OpenKeyFromFileType(pExtension, L"DefaultIcon") : NULL;
if (!hkey)
WARN("Could not open DefaultIcon key!\n");
be inserted in a removable drive.
*/
-/***********************************************************************
-* IShellFolder implementation
-*/
class CFileSysEnum :
public CEnumIDListBase
{
- private:
- public:
- CFileSysEnum();
- ~CFileSysEnum();
- HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags);
+private:
+ HRESULT _AddFindResult(LPWSTR sParentDir, const WIN32_FIND_DATAW& FindData, DWORD dwFlags)
+ {
+ // Does it need special handling because it is hidden?
+ if (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+ {
+ // Is it hidden, but are we not asked to include hidden?
+ if (!(dwFlags & SHCONTF_INCLUDEHIDDEN))
+ return S_OK;
+ }
- BEGIN_COM_MAP(CFileSysEnum)
- COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
- END_COM_MAP()
-};
+ BOOL bDirectory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-CFileSysEnum::CFileSysEnum()
-{
-}
+ HRESULT hr;
+ if (bDirectory)
+ {
+ // Skip the current and parent directory nodes
+ if (!strcmpW(FindData.cFileName, L".") || !strcmpW(FindData.cFileName, L".."))
+ return S_OK;
-CFileSysEnum::~CFileSysEnum()
-{
-}
+ // Does this directory need special handling?
+ if ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
+ {
+ WCHAR Tmp[MAX_PATH];
+ CLSID clsidFolder;
-HRESULT WINAPI CFileSysEnum::Initialize(LPWSTR lpszPath, DWORD dwFlags)
-{
- WIN32_FIND_DATAW stffile;
- HANDLE hFile;
- WCHAR szPath[MAX_PATH];
- BOOL succeeded = TRUE;
- static const WCHAR stars[] = { '*','.','*',0 };
- static const WCHAR dot[] = { '.',0 };
- static const WCHAR dotdot[] = { '.','.',0 };
+ PathCombineW(Tmp, sParentDir, FindData.cFileName);
- TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(lpszPath), dwFlags);
+ hr = SHELL32_GetCLSIDForDirectory(Tmp, L"CLSID", &clsidFolder);
+ if (SUCCEEDED(hr))
+ {
+ ERR("SHOULD DO SOMETHING WITH CLSID?\n");
+ }
+ }
+ }
+ else
+ {
+ CLSID clsidFile;
+ LPWSTR pExtension = PathFindExtensionW(FindData.cFileName);
+ if (pExtension)
+ {
+ // FIXME: Cache this?
+ hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
+ if (hr == S_OK)
+ {
+ HKEY hkey;
+ hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
+ if (SUCCEEDED(hr))
+ {
+ ::RegCloseKey(hkey);
+
+ // This should be presented as directory!
+ bDirectory = TRUE;
+ TRACE("Treating '%S' as directory!\n", FindData.cFileName);
+ }
+ }
+ }
+ }
- if(!lpszPath || !lpszPath[0]) return FALSE;
+ LPITEMIDLIST pidl = NULL;
+ if (bDirectory)
+ {
+ if (dwFlags & SHCONTF_FOLDERS)
+ {
+ TRACE("(%p)-> (folder=%s)\n", this, debugstr_w(FindData.cFileName));
+ pidl = _ILCreateFromFindDataW(&FindData);
+ }
+ }
+ else
+ {
+ if (dwFlags & SHCONTF_NONFOLDERS)
+ {
+ TRACE("(%p)-> (file =%s)\n", this, debugstr_w(FindData.cFileName));
+ pidl = _ILCreateFromFindDataW(&FindData);
+ }
+ }
- wcscpy(szPath, lpszPath);
- PathAddBackslashW(szPath);
- wcscat(szPath,stars);
+ if (pidl && !AddToEnumList(pidl))
+ {
+ FAILED_UNEXPECTEDLY(E_FAIL);
+ return E_FAIL;
+ }
- hFile = FindFirstFileW(szPath,&stffile);
- if ( hFile != INVALID_HANDLE_VALUE )
+ return S_OK;
+ }
+
+public:
+ CFileSysEnum()
+ {
+
+ }
+
+ ~CFileSysEnum()
{
- BOOL findFinished = FALSE;
+ }
+
+ HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags)
+ {
+ TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(sPathTarget), dwFlags);
+
+ if (!sPathTarget || !sPathTarget[0])
+ {
+ WARN("No path for CFileSysEnum, empty result!\n");
+ return S_FALSE;
+ }
+
+ WCHAR szFindPattern[MAX_PATH];
+ HRESULT hr = StringCchCopyW(szFindPattern, _countof(szFindPattern), sPathTarget);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ /* FIXME: UNSAFE CRAP */
+ PathAddBackslashW(szFindPattern);
+
+ hr = StringCchCatW(szFindPattern, _countof(szFindPattern), L"*.*");
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+
+ WIN32_FIND_DATAW FindData;
+ HANDLE hFind = FindFirstFileW(szFindPattern, &FindData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return HRESULT_FROM_WIN32(GetLastError());
do
{
- if ( !(stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
- || (dwFlags & SHCONTF_INCLUDEHIDDEN) )
- {
- LPITEMIDLIST pidl = NULL;
+ hr = _AddFindResult(sPathTarget, FindData, dwFlags);
- if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- dwFlags & SHCONTF_FOLDERS &&
- strcmpW(stffile.cFileName, dot) && strcmpW(stffile.cFileName, dotdot))
- {
- pidl = _ILCreateFromFindDataW(&stffile);
- succeeded = succeeded && AddToEnumList(pidl);
- }
- else if (!(stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- && dwFlags & SHCONTF_NONFOLDERS)
- {
- pidl = _ILCreateFromFindDataW(&stffile);
- succeeded = succeeded && AddToEnumList(pidl);
- }
- }
- if (succeeded)
+ if (FAILED_UNEXPECTEDLY(hr))
+ break;
+
+ } while(FindNextFileW(hFind, &FindData));
+
+ if (SUCCEEDED(hr))
+ {
+ DWORD dwError = GetLastError();
+ if (dwError != ERROR_NO_MORE_FILES)
{
- if (!FindNextFileW(hFile, &stffile))
- {
- if (GetLastError() == ERROR_NO_MORE_FILES)
- findFinished = TRUE;
- else
- succeeded = FALSE;
- }
+ hr = HRESULT_FROM_WIN32(dwError);
+ FAILED_UNEXPECTEDLY(hr);
}
- } while (succeeded && !findFinished);
- FindClose(hFile);
+ }
+ TRACE("(%p)->(hr=0x%08x)\n", this, hr);
+ FindClose(hFind);
+ return hr;
}
- return succeeded;
-}
+ BEGIN_COM_MAP(CFileSysEnum)
+ COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
+ END_COM_MAP()
+};
+
+
+/***********************************************************************
+ * IShellFolder implementation
+ */
CFSFolder::CFSFolder()
{
return pidl;
}
-void SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, CLSID* pclsidFolder)
+static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder)
{
WCHAR wszCLSIDValue[CHARS_IN_GUID];
WCHAR wszDesktopIni[MAX_PATH];
StringCchCopyW(wszDesktopIni, MAX_PATH, pwszDir);
StringCchCatW(wszDesktopIni, MAX_PATH, L"\\desktop.ini");
- if (GetPrivateProfileStringW(L".ShellClassInfo",
- L"CLSID",
+ if (GetPrivateProfileStringW(L".ShellClassInfo",
+ KeyName,
L"",
- wszCLSIDValue,
- CHARS_IN_GUID,
+ wszCLSIDValue,
+ CHARS_IN_GUID,
wszDesktopIni))
{
- CLSIDFromString (wszCLSIDValue, pclsidFolder);
+ return CLSIDFromString(wszCLSIDValue, pclsidFolder);
}
+ return E_FAIL;
}
HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
dwShellAttributes = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK | SFGAO_CANRENAME | SFGAO_CANDELETE |
SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSTEM;
- if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- dwShellAttributes |= (SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_STORAGE);
+ BOOL bDirectory = (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+
+ if (!bDirectory)
+ {
+ // https://git.reactos.org/?p=reactos.git;a=blob;f=dll/shellext/zipfldr/res/zipfldr.rgs;hb=032b5aacd233cd7b83ab6282aad638c161fdc400#l9
+ WCHAR szFileName[MAX_PATH];
+ LPWSTR pExtension;
+
+ if (_ILSimpleGetTextW(pidl, szFileName, _countof(szFileName)) && (pExtension = PathFindExtensionW(szFileName)))
+ {
+ CLSID clsidFile;
+ // FIXME: Cache this?
+ HRESULT hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
+ if (hr == S_OK)
+ {
+ HKEY hkey;
+ hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
+ if (SUCCEEDED(hr))
+ {
+ DWORD dwAttributes = 0;
+ DWORD dwSize = sizeof(dwAttributes);
+ LSTATUS Status;
+
+ Status = SHRegGetValueW(hkey, NULL, L"Attributes", RRF_RT_REG_DWORD, NULL, &dwAttributes, &dwSize);
+ if (Status == STATUS_SUCCESS)
+ {
+ TRACE("Augmenting '%S' with dwAttributes=0x%x\n", szFileName, dwAttributes);
+ dwShellAttributes |= dwAttributes;
+ }
+ ::RegCloseKey(hkey);
+
+ // This should be presented as directory!
+ bDirectory = TRUE;
+ TRACE("Treating '%S' as directory!\n", szFileName);
+ }
+ }
+ }
+ }
+
+ // This is a directory
+ if (bDirectory)
+ {
+ dwShellAttributes |= (SFGAO_FOLDER | /*SFGAO_HASSUBFOLDER |*/ SFGAO_STORAGE);
+
+ // Is this a real directory?
+ if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ dwShellAttributes |= (SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR);
+ }
+ }
else
+ {
dwShellAttributes |= SFGAO_STREAM;
+ }
if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
dwShellAttributes |= SFGAO_HIDDEN;
clsidFolder = CLSID_ShellFSFolder;
if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
- SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, &clsidFolder);
+ SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, L"CLSID", &clsidFolder);
}
else
{
{
*ppvOut = NULL;
- if (IsEqualIID (riid, IID_IDropTarget))
- hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
- else if (IsEqualIID (riid, IID_IContextMenu))
+ BOOL bIsDropTarget = IsEqualIID (riid, IID_IDropTarget);
+ BOOL bIsShellView = !bIsDropTarget && IsEqualIID (riid, IID_IShellView);
+
+ if (bIsDropTarget || bIsShellView)
{
- HKEY hKeys[16];
- UINT cKeys = 0;
- AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
+ DWORD dwDirAttributes = _ILGetFileAttributes(ILFindLastID(pidlRoot), NULL, 0);
- DEFCONTEXTMENU dcm;
- dcm.hwnd = hwndOwner;
- dcm.pcmcb = this;
- dcm.pidlFolder = pidlRoot;
- dcm.psf = this;
- dcm.cidl = 0;
- dcm.apidl = NULL;
- dcm.cKeys = cKeys;
- dcm.aKeys = hKeys;
- dcm.punkAssociationInfo = NULL;
- hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
+ if ((dwDirAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
+ {
+ CLSID clsidFolder;
+ hr = SHELL32_GetCLSIDForDirectory(sPathTarget, L"UICLSID", &clsidFolder);
+ if (SUCCEEDED(hr))
+ {
+ CComPtr<IPersistFolder> spFolder;
+ hr = SHCoCreateInstance(NULL, &clsidFolder, NULL, IID_PPV_ARG(IPersistFolder, &spFolder));
+ if (!FAILED_UNEXPECTEDLY(hr))
+ {
+ hr = spFolder->Initialize(pidlRoot);
+
+ if (!FAILED_UNEXPECTEDLY(hr))
+ {
+ hr = spFolder->QueryInterface(riid, ppvOut);
+ }
+ }
+ }
+ else
+ {
+ // No desktop.ini, or no UICLSID present, continue as if nothing happened
+ hr = E_INVALIDARG;
+ }
+ }
}
- else if (IsEqualIID (riid, IID_IShellView))
+
+ if (!SUCCEEDED(hr))
{
- SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this};
- hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
+ // No UICLSID handler found, continue to the default handlers
+ if (bIsDropTarget)
+ {
+ hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
+ }
+ else if (IsEqualIID (riid, IID_IContextMenu))
+ {
+ HKEY hKeys[16];
+ UINT cKeys = 0;
+ AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
+
+ DEFCONTEXTMENU dcm;
+ dcm.hwnd = hwndOwner;
+ dcm.pcmcb = this;
+ dcm.pidlFolder = pidlRoot;
+ dcm.psf = this;
+ dcm.cidl = 0;
+ dcm.apidl = NULL;
+ dcm.cKeys = cKeys;
+ dcm.aKeys = hKeys;
+ dcm.punkAssociationInfo = NULL;
+ hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
+ }
+ else if (bIsShellView)
+ {
+ SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this};
+ hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
+ }
+ else
+ {
+ hr = E_INVALIDARG;
+ }
}
}
TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);