https://git.reactos.org/?p=reactos.git;a=commitdiff;h=63bb46a2fd416f539ae11…
commit 63bb46a2fd416f539ae118b7a656f162252dadd4
Author: Whindmar Saksit <whindsaks(a)proton.me>
AuthorDate: Thu Dec 5 15:14:55 2024 +0100
Commit: GitHub <noreply(a)github.com>
CommitDate: Thu Dec 5 15:14:55 2024 +0100
[CABVIEW] Add CabView shell extension (#7494)
CORE-14616
---
dll/shellext/CMakeLists.txt | 1 +
dll/shellext/cabview/CMakeLists.txt | 21 +
dll/shellext/cabview/cabview.cpp | 66 +++
dll/shellext/cabview/cabview.h | 240 +++++++++
dll/shellext/cabview/cabview.rc | 75 +++
dll/shellext/cabview/cabview.spec | 4 +
dll/shellext/cabview/extract.cpp | 248 ++++++++++
dll/shellext/cabview/folder.cpp | 857 +++++++++++++++++++++++++++++++++
dll/shellext/cabview/lang/de-DE.rc | 21 +
dll/shellext/cabview/lang/en-US.rc | 21 +
dll/shellext/cabview/lang/et-EE.rc | 21 +
dll/shellext/cabview/lang/fr-FR.rc | 21 +
dll/shellext/cabview/lang/hi-IN.rc | 21 +
dll/shellext/cabview/lang/it-IT.rc | 21 +
dll/shellext/cabview/lang/ja-JP.rc | 21 +
dll/shellext/cabview/lang/pl-PL.rc | 22 +
dll/shellext/cabview/lang/pt-PT.rc | 21 +
dll/shellext/cabview/lang/ro-RO.rc | 22 +
dll/shellext/cabview/lang/ru-RU.rc | 21 +
dll/shellext/cabview/lang/sv-SE.rc | 21 +
dll/shellext/cabview/lang/tr-TR.rc | 21 +
dll/shellext/cabview/lang/zh-CN.rc | 22 +
dll/shellext/cabview/lang/zh-HK.rc | 22 +
dll/shellext/cabview/lang/zh-TW.rc | 23 +
dll/shellext/cabview/precomp.h | 34 ++
dll/shellext/cabview/res/cabview.rgs | 47 ++
dll/shellext/cabview/res/folder.ico | Bin 0 -> 26070 bytes
dll/shellext/cabview/resource.h | 25 +
dll/shellext/cabview/util.h | 156 ++++++
dll/win32/browseui/CProgressDialog.cpp | 1 +
media/inf/syssetup.inf | 1 +
31 files changed, 2118 insertions(+)
diff --git a/dll/shellext/CMakeLists.txt b/dll/shellext/CMakeLists.txt
index 8d005319c4c..a9c8fe256ef 100644
--- a/dll/shellext/CMakeLists.txt
+++ b/dll/shellext/CMakeLists.txt
@@ -1,5 +1,6 @@
add_subdirectory(acppage)
+add_subdirectory(cabview)
add_subdirectory(cryptext)
add_subdirectory(deskadp)
add_subdirectory(deskmon)
diff --git a/dll/shellext/cabview/CMakeLists.txt b/dll/shellext/cabview/CMakeLists.txt
new file mode 100644
index 00000000000..3a574a8933f
--- /dev/null
+++ b/dll/shellext/cabview/CMakeLists.txt
@@ -0,0 +1,21 @@
+
+spec2def(cabview.dll cabview.spec)
+
+list(APPEND SOURCE
+ cabview.cpp
+ extract.cpp
+ folder.cpp
+ resource.h)
+
+add_library(cabview MODULE
+ ${SOURCE}
+ cabview.spec
+ cabview.rc
+ ${CMAKE_CURRENT_BINARY_DIR}/cabview.def)
+
+set_module_type(cabview win32dll UNICODE)
+target_link_libraries(cabview uuid cpprt atl_classes)
+set_target_cpp_properties(cabview WITH_EXCEPTIONS)
+add_importlibs(cabview cabinet oleaut32 ole32 shlwapi comctl32 shell32 user32 advapi32
msvcrt kernel32 ntdll)
+add_pch(cabview precomp.h SOURCE)
+add_cd_file(TARGET cabview DESTINATION reactos/system32 FOR all)
diff --git a/dll/shellext/cabview/cabview.cpp b/dll/shellext/cabview/cabview.cpp
new file mode 100644
index 00000000000..fc6d2d7302e
--- /dev/null
+++ b/dll/shellext/cabview/cabview.cpp
@@ -0,0 +1,66 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: DLL entry point
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#include "cabview.h"
+
+#include <initguid.h>
+DEFINE_GUID(CLSID_CabFolder,
0x0CD7A5C0,0x9F37,0x11CE,0xAE,0x65,0x08,0x00,0x2B,0x2E,0x12,0x62);
+
+CComModule g_Module;
+
+BEGIN_OBJECT_MAP(ObjectMap)
+ OBJECT_ENTRY(CLSID_CabFolder, CCabFolder)
+END_OBJECT_MAP()
+
+EXTERN_C BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hInstance);
+ g_Module.Init(ObjectMap, hInstance, NULL);
+ break;
+ }
+
+ return TRUE;
+}
+
+STDAPI DllCanUnloadNow()
+{
+ return g_Module.DllCanUnloadNow();
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ return g_Module.DllGetClassObject(rclsid, riid, ppv);
+}
+
+STDAPI DllRegisterServer()
+{
+ HRESULT hr;
+
+ hr = g_Module.DllRegisterServer(FALSE);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ hr = g_Module.UpdateRegistryFromResource(IDR_FOLDER, TRUE, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ return S_OK;
+}
+
+STDAPI DllUnregisterServer()
+{
+ HRESULT hr1 = g_Module.DllUnregisterServer(FALSE);
+ HRESULT hr2 = g_Module.UpdateRegistryFromResource(IDR_FOLDER, FALSE, NULL);
+ if (FAILED_UNEXPECTEDLY(hr1))
+ return hr1;
+ if (FAILED_UNEXPECTEDLY(hr2))
+ return hr2;
+ return S_OK;
+}
diff --git a/dll/shellext/cabview/cabview.h b/dll/shellext/cabview/cabview.h
new file mode 100644
index 00000000000..dbc20744dfd
--- /dev/null
+++ b/dll/shellext/cabview/cabview.h
@@ -0,0 +1,240 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Main header file
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#pragma once
+#include "precomp.h"
+#include "resource.h"
+
+#define FLATFOLDER TRUE
+
+EXTERN_C const GUID CLSID_CabFolder;
+
+enum EXTRACTCALLBACKMSG { ECM_BEGIN, ECM_FILE, ECM_PREPAREPATH, ECM_ERROR };
+struct EXTRACTCALLBACKDATA
+{
+ LPCWSTR Path;
+ const FDINOTIFICATION *pfdin;
+ HRESULT hr;
+};
+typedef HRESULT (CALLBACK*EXTRACTCALLBACK)(EXTRACTCALLBACKMSG msg, const
EXTRACTCALLBACKDATA &data, LPVOID cookie);
+HRESULT ExtractCabinet(LPCWSTR cab, LPCWSTR destination, EXTRACTCALLBACK callback, LPVOID
cookie);
+
+class CEnumIDList :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IEnumIDList
+{
+protected:
+ HDPA m_Items;
+ ULONG m_Pos;
+
+public:
+ static int CALLBACK DPADestroyCallback(void *pidl, void *pData)
+ {
+ SHFree(pidl);
+ return TRUE;
+ }
+
+ CEnumIDList() : m_Pos(0)
+ {
+ m_Items = DPA_Create(0);
+ }
+
+ virtual ~CEnumIDList()
+ {
+ DPA_DestroyCallback(m_Items, DPADestroyCallback, NULL);
+ }
+
+ int FindNamedItem(PCUITEMID_CHILD pidl) const;
+ HRESULT Fill(LPCWSTR path, HWND hwnd = NULL, SHCONTF contf = 0);
+ HRESULT Fill(PCIDLIST_ABSOLUTE pidl, HWND hwnd = NULL, SHCONTF contf = 0);
+
+ HRESULT Append(LPCITEMIDLIST pidl)
+ {
+ return DPA_AppendPtr(m_Items, (void*)pidl) != DPA_ERR ? S_OK : E_OUTOFMEMORY;
+ }
+
+ UINT GetCount() const { return m_Items ? DPA_GetPtrCount(m_Items) : 0; }
+
+ // IEnumIDList
+ IFACEMETHODIMP Next(ULONG celt, PITEMID_CHILD *rgelt, ULONG *pceltFetched)
+ {
+ if (!rgelt)
+ return E_INVALIDARG;
+ HRESULT hr = S_FALSE;
+ UINT count = GetCount(), fetched = 0;
+ if (m_Pos < count && fetched < celt)
+ {
+ if (SUCCEEDED(hr = SHILClone(DPA_FastGetPtr(m_Items, m_Pos),
&rgelt[fetched])))
+ fetched++;
+ }
+ if (pceltFetched)
+ *pceltFetched = fetched;
+ m_Pos += fetched;
+ return FAILED(hr) ? hr : (celt == fetched && fetched) ? S_OK : S_FALSE;
+ }
+
+ IFACEMETHODIMP Reset()
+ {
+ m_Pos = 0;
+ return S_OK;
+ }
+
+ IFACEMETHODIMP Skip(ULONG celt)
+ {
+ UINT count = GetCount(), newpos = m_Pos + celt;
+ if (celt > count || newpos >= count)
+ return E_INVALIDARG;
+ m_Pos = newpos;
+ return S_OK;
+ }
+
+ IFACEMETHODIMP Clone(IEnumIDList **ppenum)
+ {
+ UNIMPLEMENTED;
+ *ppenum = NULL;
+ return E_NOTIMPL;
+ }
+
+ static CEnumIDList* CreateInstance()
+ {
+ CComPtr<CEnumIDList> obj;
+ return SUCCEEDED(ShellObjectCreator(obj)) ? obj.Detach() : NULL;
+ }
+
+ DECLARE_NO_REGISTRY()
+ DECLARE_NOT_AGGREGATABLE(CEnumIDList)
+
+ BEGIN_COM_MAP(CEnumIDList)
+ COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
+ END_COM_MAP()
+};
+
+class CCabFolder :
+ public CComCoClass<CCabFolder, &CLSID_CabFolder>,
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IShellFolder2,
+ public IPersistFolder2,
+ public IShellFolderViewCB,
+ public IShellIcon
+{
+protected:
+ CComHeapPtr<ITEMIDLIST> m_CurDir;
+ HWND m_ShellViewWindow = NULL;
+
+public:
+ HRESULT ExtractFilesUI(HWND hWnd, IDataObject *pDO);
+ HRESULT GetItemDetails(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd, VARIANT
*pv);
+ int MapSCIDToColumn(const SHCOLUMNID &scid);
+ HRESULT CompareID(LPARAM lParam, PCUITEMID_CHILD pidl1, PCUITEMID_CHILD pidl2);
+
+ HRESULT CreateEnum(CEnumIDList **List)
+ {
+ CEnumIDList *pEIDL = CEnumIDList::CreateInstance();
+ *List = pEIDL;
+ return pEIDL ? pEIDL->Fill(m_CurDir) : E_OUTOFMEMORY;
+ }
+
+ // IShellFolder2
+ IFACEMETHODIMP GetDefaultSearchGUID(GUID *pguid) override
+ {
+ return E_NOTIMPL;
+ }
+
+ IFACEMETHODIMP EnumSearches(IEnumExtraSearch **ppenum) override
+ {
+ return E_NOTIMPL;
+ }
+
+ IFACEMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
override;
+
+ IFACEMETHODIMP GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags) override;
+
+ IFACEMETHODIMP GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT
*pv) override;
+
+ IFACEMETHODIMP GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
override;
+
+ IFACEMETHODIMP MapColumnToSCID(UINT column, SHCOLUMNID *pscid) override;
+
+ IFACEMETHODIMP ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes) override
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+
+ IFACEMETHODIMP EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
override;
+
+ IFACEMETHODIMP BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid,
LPVOID *ppvOut) override;
+
+ IFACEMETHODIMP BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid,
LPVOID *ppvOut) override
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+
+ IFACEMETHODIMP CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE
pidl2) override;
+
+ IFACEMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
override;
+
+ IFACEMETHODIMP GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, SFGAOF
*rgfInOut) override;
+
+ IFACEMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
REFIID riid, UINT *prgfInOut, LPVOID *ppvOut) override;
+
+ IFACEMETHODIMP GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET pName)
override;
+
+ IFACEMETHODIMP SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName,
DWORD dwFlags, PITEMID_CHILD *pPidlOut) override
+ {
+ return E_NOTIMPL;
+ }
+
+ // IPersistFolder2
+ IFACEMETHODIMP GetCurFolder(PIDLIST_ABSOLUTE *pidl) override
+ {
+ LPITEMIDLIST curdir = (LPITEMIDLIST)m_CurDir;
+ return curdir ? SHILClone(curdir, pidl) : E_UNEXPECTED;
+ }
+
+ IFACEMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidl) override
+ {
+ WCHAR path[MAX_PATH];
+ if (SHGetPathFromIDListW(pidl, path))
+ {
+ PIDLIST_ABSOLUTE curdir = ILClone(pidl);
+ if (curdir)
+ {
+ m_CurDir.Attach(curdir);
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+ }
+ return E_INVALIDARG;
+ }
+
+ IFACEMETHODIMP GetClassID(CLSID *lpClassId) override
+ {
+ *lpClassId = CLSID_CabFolder;
+ return S_OK;
+ }
+
+ // IShellFolderViewCB
+ IFACEMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
+
+ // IShellIcon
+ IFACEMETHODIMP GetIconOf(PCUITEMID_CHILD pidl, UINT flags, int *pIconIndex)
override;
+
+ DECLARE_NO_REGISTRY()
+ DECLARE_NOT_AGGREGATABLE(CCabFolder)
+
+ BEGIN_COM_MAP(CCabFolder)
+ COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
+ COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
+ COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
+ COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
+ COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
+ COM_INTERFACE_ENTRY_IID(IID_IShellFolderViewCB, IShellFolderViewCB)
+ COM_INTERFACE_ENTRY_IID(IID_IShellIcon, IShellIcon)
+ END_COM_MAP()
+};
diff --git a/dll/shellext/cabview/cabview.rc b/dll/shellext/cabview/cabview.rc
new file mode 100644
index 00000000000..526888b7b69
--- /dev/null
+++ b/dll/shellext/cabview/cabview.rc
@@ -0,0 +1,75 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Main resource file
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#include <windef.h>
+#include <winuser.h>
+
+#include "resource.h"
+
+IDI_FOLDER ICON "res/folder.ico"
+
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Cabinet Shell Extension"
+#define REACTOS_STR_INTERNAL_NAME "cabview"
+#define REACTOS_STR_ORIGINAL_FILENAME "cabview.dll"
+#include <reactos/version.rc>
+
+#include <reactos/manifest_dll.rc>
+
+IDR_FOLDER REGISTRY "res/cabview.rgs"
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+#ifdef LANGUAGE_DE_DE
+ #include "lang/de-DE.rc"
+#endif
+#ifdef LANGUAGE_EN_US
+ #include "lang/en-US.rc"
+#endif
+#ifdef LANGUAGE_ET_EE
+ #include "lang/et-EE.rc"
+#endif
+#ifdef LANGUAGE_FR_FR
+ #include "lang/fr-FR.rc"
+#endif
+#ifdef LANGUAGE_HI_IN
+ #include "lang/hi-IN.rc"
+#endif
+#ifdef LANGUAGE_IT_IT
+ #include "lang/it-IT.rc"
+#endif
+#ifdef LANGUAGE_JA_JP
+ #include "lang/ja-JP.rc"
+#endif
+#ifdef LANGUAGE_PL_PL
+ #include "lang/pl-PL.rc"
+#endif
+#ifdef LANGUAGE_PT_PT
+ #include "lang/pt-PT.rc"
+#endif
+#ifdef LANGUAGE_RO_RO
+ #include "lang/ro-RO.rc"
+#endif
+#ifdef LANGUAGE_RU_RU
+ #include "lang/ru-RU.rc"
+#endif
+#ifdef LANGUAGE_SV_SE
+ #include "lang/sv-SE.rc"
+#endif
+#ifdef LANGUAGE_TR_TR
+ #include "lang/tr-TR.rc"
+#endif
+#ifdef LANGUAGE_ZH_CN
+ #include "lang/zh-CN.rc"
+#endif
+#ifdef LANGUAGE_ZH_HK
+ #include "lang/zh-HK.rc"
+#endif
+#ifdef LANGUAGE_ZH_TW
+ #include "lang/zh-TW.rc"
+#endif
diff --git a/dll/shellext/cabview/cabview.spec b/dll/shellext/cabview/cabview.spec
new file mode 100644
index 00000000000..b16365d0c9f
--- /dev/null
+++ b/dll/shellext/cabview/cabview.spec
@@ -0,0 +1,4 @@
+@ stdcall -private DllCanUnloadNow()
+@ stdcall -private DllGetClassObject(ptr ptr ptr)
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
diff --git a/dll/shellext/cabview/extract.cpp b/dll/shellext/cabview/extract.cpp
new file mode 100644
index 00000000000..cdb1ff19d79
--- /dev/null
+++ b/dll/shellext/cabview/extract.cpp
@@ -0,0 +1,248 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: FDI API wrapper
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#include "precomp.h"
+#include "cabview.h"
+#include "util.h"
+#include <fcntl.h>
+
+struct EXTRACTCABINETINTERNALDATA
+{
+ LPCWSTR destination;
+ EXTRACTCALLBACK callback;
+ LPVOID cookie;
+};
+
+static LPWSTR BuildPath(LPCWSTR Dir, LPCSTR File, UINT Attr)
+{
+ UINT cp = Attr & _A_NAME_IS_UTF ? CP_UTF8 : CP_ACP;
+ UINT cchfile = MultiByteToWideChar(cp, 0, File, -1, 0, 0);
+ SIZE_T lendir = lstrlenW(Dir), cch = lendir + 1 + cchfile;
+ LPWSTR path = (LPWSTR)SHAlloc(cch * sizeof(*path));
+ if (path)
+ {
+ lstrcpyW(path, Dir);
+ if (lendir && !IsPathSep(path[lendir - 1]))
+ path[lendir++] = '\\';
+
+ LPWSTR dst = &path[lendir];
+ MultiByteToWideChar(cp, 0, File + IsPathSep(*File), -1, dst, cchfile);
+ for (SIZE_T i = 0; dst[i]; ++i)
+ {
+ if (dst[i] == L':' && lendir) // Don't allow absolute
paths
+ dst[i] = L'_';
+ if (dst[i] == L'/') // Normalize
+ dst[i] = L'\\';
+ }
+ }
+ return path;
+}
+
+static HRESULT HResultFrom(const ERF &erf)
+{
+ switch (erf.fError ? erf.erfOper : FDIERROR_NONE)
+ {
+ case FDIERROR_NONE:
+ return erf.fError ? HRESULT_FROM_WIN32(erf.erfType) : S_OK;
+ case FDIERROR_CABINET_NOT_FOUND:
+ return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ case FDIERROR_ALLOC_FAIL:
+ return E_OUTOFMEMORY;
+ case FDIERROR_USER_ABORT:
+ return S_FALSE;
+ default:
+ return erf.erfType ? HRESULT_FROM_WIN32(erf.erfType) : E_FAIL;
+ }
+}
+
+FNFREE(CabMemFree)
+{
+ SHFree(pv);
+}
+
+FNALLOC(CabMemAlloc)
+{
+ return SHAlloc(cb);
+}
+
+FNCLOSE(CabClose)
+{
+ return CloseHandle((HANDLE)hf) ? 0 : -1;
+}
+
+static INT_PTR CabOpenEx(LPCWSTR path, UINT access, UINT share, UINT disp, UINT attr)
+{
+ return (INT_PTR)CreateFileW(path, access, share, NULL, disp, attr, NULL);
+}
+
+FNOPEN(CabOpen)
+{
+ UINT disp = (oflag & _O_CREAT) ? CREATE_ALWAYS : OPEN_EXISTING;
+ UINT access = GENERIC_READ;
+ if (oflag & _O_RDWR)
+ access = GENERIC_READ | GENERIC_WRITE;
+ else if (oflag & _O_WRONLY)
+ access = GENERIC_WRITE;
+ UNREFERENCED_PARAMETER(pmode);
+ WCHAR buf[MAX_PATH * 2];
+ MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, buf, _countof(buf));
+ return CabOpenEx(buf, access, FILE_SHARE_READ, disp, FILE_ATTRIBUTE_NORMAL);
+}
+
+FNREAD(CabRead)
+{
+ DWORD dwBytesRead;
+ return ReadFile((HANDLE)hf, pv, cb, &dwBytesRead, NULL) ? dwBytesRead : -1;
+}
+
+FNWRITE(CabWrite)
+{
+ DWORD dwBytesWritten;
+ return WriteFile((HANDLE)hf, pv, cb, &dwBytesWritten, NULL) ? dwBytesWritten :
-1;
+}
+
+FNSEEK(CabSeek)
+{
+ return SetFilePointer((HANDLE)hf, dist, NULL, seektype);
+}
+
+static HRESULT Init(HFDI &hfdi, ERF &erf)
+{
+ const int cpu = cpuUNKNOWN;
+ hfdi = FDICreate(CabMemAlloc, CabMemFree, CabOpen, CabRead, CabWrite, CabClose,
CabSeek, cpu, &erf);
+ return hfdi ? S_OK : HResultFrom(erf);
+}
+
+FNFDINOTIFY(ExtractCabinetCallback)
+{
+ const UINT attrmask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
FILE_ATTRIBUTE_ARCHIVE;
+ EXTRACTCABINETINTERNALDATA &ecd = *(EXTRACTCABINETINTERNALDATA*)pfdin->pv;
+ EXTRACTCALLBACKDATA noti;
+ HRESULT hr;
+ FILETIME ft;
+
+ noti.pfdin = pfdin;
+ switch (fdint)
+ {
+ case fdintCOPY_FILE:
+ hr = ecd.callback(ECM_FILE, noti, ecd.cookie);
+ if (hr == S_OK)
+ {
+ hr = E_OUTOFMEMORY;
+ LPWSTR path = BuildPath(ecd.destination, pfdin->psz1,
pfdin->attribs);
+ if (path)
+ {
+ // Callee is using SHPPFW_IGNOREFILENAME so we don't need to
remove the name.
+ /*LPWSTR file = PathFindFileNameW(path);
+ if (file > path)
+ {
+ file[-1] = L'\0';*/
+ noti.Path = path;
+ ecd.callback(ECM_PREPAREPATH, noti, ecd.cookie);
+ /* file[-1] = L'\\';
+ }*/
+ UINT attr = pfdin->attribs & attrmask;
+ UINT access = GENERIC_READ | GENERIC_WRITE, share =
FILE_SHARE_DELETE;
+ INT_PTR handle = CabOpenEx(path, access, share, CREATE_NEW, attr |
FILE_FLAG_SEQUENTIAL_SCAN);
+ noti.hr = HResultFromWin32(GetLastError());
+ SHFree(path);
+ if (handle != (INT_PTR)-1)
+ return handle;
+ if (ecd.callback(ECM_ERROR, noti, ecd.cookie) != E_NOTIMPL)
+ hr = noti.hr;
+ }
+ }
+ return hr == S_FALSE ? 0 : -1;
+
+ case fdintCLOSE_FILE_INFO:
+ if (DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
+ SetFileTime((HANDLE)(pfdin->hf), NULL, NULL, &ft);
+ return !CabClose(pfdin->hf);
+
+ case fdintNEXT_CABINET:
+ if (pfdin->fdie && pfdin->fdie != FDIERROR_USER_ABORT)
+ {
+ if (pfdin->fdie == FDIERROR_CABINET_NOT_FOUND)
+ noti.hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ else
+ noti.hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ ecd.callback(ECM_ERROR, noti, ecd.cookie);
+ }
+ return pfdin->fdie ? -1 : 0;
+
+ case fdintPARTIAL_FILE:
+ return 0;
+
+ case fdintCABINET_INFO:
+ return 0;
+
+ case fdintENUMERATE:
+ return 0;
+ }
+ return -1;
+}
+
+HRESULT ExtractCabinet(LPCWSTR cab, LPCWSTR destination, EXTRACTCALLBACK callback, LPVOID
cookie)
+{
+ BOOL quick = !destination;
+ if (!destination)
+ destination = L"?:"; // Dummy path for callers that enumerate without
extracting
+ EXTRACTCABINETINTERNALDATA data = { destination, callback, cookie };
+ EXTRACTCALLBACKDATA noti;
+ ERF erf = { };
+ HFDI hfdi;
+ UINT total = 0, files = 0;
+ HRESULT hr = Init(hfdi, erf);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ UINT share = FILE_SHARE_READ | FILE_SHARE_DELETE;
+ INT_PTR hf = quick ? -1 : CabOpenEx(cab, GENERIC_READ, share, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL);
+ if (hf != -1)
+ {
+ FDICABINETINFO ci;
+ if (FDIIsCabinet(hfdi, hf, &ci))
+ {
+ total = ci.cbCabinet;
+ files = ci.cFiles;
+ }
+ CabClose(hf);
+ }
+
+ hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ char buf[MAX_PATH * 2], *name = 0;
+ if (!WideCharToMultiByte(CP_UTF8, 0, cab, -1, buf, _countof(buf), NULL, NULL))
+ {
+ *buf = '\0';
+ hr = E_INVALIDARG;
+ }
+ for (UINT i = 0; buf[i]; ++i)
+ {
+ if (buf[i] == '\\' || buf[i] == '/')
+ name = &buf[i + 1];
+ }
+ if (name > buf && *name)
+ {
+ // Format the name the way FDI likes it
+ name[-1] = ANSI_NULL;
+ char namebuf[MAX_PATH];
+ namebuf[0] = '\\';
+ lstrcpyA(namebuf + 1, name);
+ name = namebuf;
+
+ FDINOTIFICATION fdin;
+ fdin.cb = total;
+ fdin.hf = files;
+ noti.Path = cab;
+ noti.pfdin = &fdin;
+ callback(ECM_BEGIN, noti, cookie);
+
+ hr = FDICopy(hfdi, name, buf, 0, ExtractCabinetCallback, NULL, &data) ? S_OK
: HResultFrom(erf);
+ }
+ FDIDestroy(hfdi);
+ return hr;
+}
diff --git a/dll/shellext/cabview/folder.cpp b/dll/shellext/cabview/folder.cpp
new file mode 100644
index 00000000000..c96e941392c
--- /dev/null
+++ b/dll/shellext/cabview/folder.cpp
@@ -0,0 +1,857 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Shell folder implementation
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#include "cabview.h"
+#include "util.h"
+
+enum FOLDERCOLUMNS
+{
+ COL_NAME, // PKEY_ItemNameDisplay
+ COL_SIZE, // PKEY_Size
+ COL_TYPE, // PKEY_ItemTypeText
+ COL_MDATE, // PKEY_DateModified
+ COL_PATH, // PKEY_?: Archive-relative path
+ COL_ATT, // PKEY_FileAttributes
+ COLCOUNT
+};
+
+static const struct FOLDERCOLUMN
+{
+ BYTE TextId;
+ BYTE LvcFmt;
+ BYTE LvcChars;
+ BYTE ColFlags;
+ const GUID *pkg;
+ BYTE pki;
+} g_Columns[] =
+{
+ { IDS_COL_NAME, LVCFMT_LEFT, 20, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,
&FMTID_Storage, PID_STG_NAME },
+ { IDS_COL_SIZE, LVCFMT_RIGHT, 16, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT,
&FMTID_Storage, PID_STG_SIZE },
+ { IDS_COL_TYPE, LVCFMT_LEFT, 20, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,
&FMTID_Storage, PID_STG_STORAGETYPE },
+ { IDS_COL_MDATE, LVCFMT_LEFT, 20, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT,
&FMTID_Storage, PID_STG_WRITETIME },
+ { IDS_COL_PATH, LVCFMT_LEFT, 30, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,
&CLSID_CabFolder, 0 },
+ { IDS_COL_ATT, LVCFMT_RIGHT, 10, SHCOLSTATE_TYPE_STR, &FMTID_Storage,
PID_STG_ATTRIBUTES },
+};
+
+#include <pshpack1.h>
+struct CABITEM
+{
+ WORD cb;
+ WORD Unknown; // Not sure what Windows uses this for, we always store 0
+ UINT Size;
+ WORD Date, Time; // DOS
+ WORD Attrib;
+ WORD NameOffset;
+ WCHAR Path[ANYSIZE_ARRAY];
+
+#if FLATFOLDER
+ inline bool IsFolder() const { return false; }
+#else
+ inline BOOL IsFolder() const { return Attrib & FILE_ATTRIBUTE_DIRECTORY; }
+#endif
+ enum { FSATTS = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY };
+ WORD GetFSAttributes() const { return Attrib & FSATTS; }
+ LPCWSTR GetName() const { return Path + NameOffset; }
+
+ template<class PIDL> static CABITEM* Validate(PIDL pidl)
+ {
+ CABITEM *p = (CABITEM*)pidl;
+ return p && p->cb > FIELD_OFFSET(CABITEM, Path[1]) &&
p->Unknown == 0 ? p : NULL;
+ }
+};
+#include <poppack.h>
+
+static CABITEM* CreateItem(LPCWSTR Path, UINT Attrib, UINT Size, UINT DateTime)
+{
+ const SIZE_T len = lstrlenW(Path), cb = FIELD_OFFSET(CABITEM, Path[len + 1]);
+ if (cb > 0xffff)
+ return NULL;
+ CABITEM *p = (CABITEM*)SHAlloc(cb + sizeof(USHORT));
+ if (p)
+ {
+ p->cb = (USHORT)cb;
+ p->Unknown = 0;
+ p->Size = Size;
+ p->Attrib = Attrib;
+ p->Date = HIWORD(DateTime);
+ p->Time = LOWORD(DateTime);
+ p->NameOffset = 0;
+ for (UINT i = 0;; ++i)
+ {
+ WCHAR c = Path[i];
+ if (c == L':') // Don't allow absolute paths
+ c = L'_';
+ if (c == L'/') // Normalize
+ c = L'\\';
+ if (c == '\\')
+ p->NameOffset = i + 1;
+ p->Path[i] = c;
+ if (!c)
+ break;
+ }
+ ((SHITEMID*)((BYTE*)p + cb))->cb = 0;
+ }
+ return p;
+}
+
+static CABITEM* CreateItem(LPCSTR Path, UINT Attrib, UINT Size = 0, UINT DateTime = 0)
+{
+ WCHAR buf[MAX_PATH * 2];
+ UINT codepage = (Attrib & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP;
+ if (MultiByteToWideChar(codepage, 0, Path, -1, buf, _countof(buf)))
+ return CreateItem(buf, Attrib, Size, DateTime);
+ return NULL;
+}
+
+static HRESULT CALLBACK ItemMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject
*pdtobj,
+ UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ enum { IDC_EXTRACT, IDC_EXTRACTALL };
+ HRESULT hr = E_NOTIMPL;
+ const BOOL Background = !pdtobj;
+
+ switch (uMsg)
+ {
+ case DFM_MODIFYQCMFLAGS:
+ {
+ *((UINT*)lParam) = wParam | CMF_NOVERBS | (Background ? 0 : CMF_VERBSONLY);
+ return S_OK;
+ }
+
+ case DFM_MERGECONTEXTMENU:
+ {
+ QCMINFO &qcmi = *(QCMINFO*)lParam;
+ UINT pos = qcmi.indexMenu, id = 0;
+ if (Background || SUCCEEDED(hr = InsertMenuItem(qcmi, pos, id, IDC_EXTRACT,
IDS_EXTRACT, MFS_DEFAULT)))
+ {
+ hr = InsertMenuItem(qcmi, pos, id, IDC_EXTRACTALL, IDS_EXTRACTALL);
+ if (SUCCEEDED(hr) && !Background)
+ {
+ --pos;
+ InsertMenuItem(qcmi, pos, id, 0, -1); // Separator
+ }
+ }
+ if (SUCCEEDED(hr))
+ {
+ qcmi.idCmdFirst = id + 1;
+ hr = S_FALSE; // Don't add verbs
+ }
+ break;
+ }
+
+ case DFM_INVOKECOMMAND:
+ {
+ hr = S_FALSE;
+ CCabFolder *pCabFolder = static_cast<CCabFolder*>(psf);
+ switch (wParam)
+ {
+ case IDC_EXTRACT:
+ case IDC_EXTRACTALL:
+ hr = pCabFolder->ExtractFilesUI(hwnd, wParam == IDC_EXTRACT ?
pdtobj : NULL);
+ break;
+ }
+ break;
+ }
+ }
+ return hr;
+}
+
+static HRESULT CALLBACK FolderBackgroundMenuCallback(IShellFolder *psf, HWND hwnd,
+ IDataObject *pdtobj, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+{
+ return ItemMenuCallback(psf, hwnd, NULL, uMsg, wParam, lParam);
+}
+
+int CEnumIDList::FindNamedItem(PCUITEMID_CHILD pidl) const
+{
+ CABITEM *needle = (CABITEM*)pidl;
+ for (ULONG i = 0, c = GetCount(); i < c; ++i)
+ {
+ CABITEM *item = (CABITEM*)DPA_FastGetPtr(m_Items, i);
+ if (!lstrcmpiW(needle->Path, item->Path))
+ return i;
+ }
+ return -1;
+}
+
+struct FILLCALLBACKDATA
+{
+ CEnumIDList *pEIDL;
+ SHCONTF ContF;
+};
+
+static HRESULT CALLBACK EnumFillCallback(EXTRACTCALLBACKMSG msg, const
EXTRACTCALLBACKDATA &ecd, LPVOID cookie)
+{
+ FILLCALLBACKDATA &data = *(FILLCALLBACKDATA*)cookie;
+
+ switch ((UINT)msg)
+ {
+ case ECM_FILE:
+ {
+ const FDINOTIFICATION &fdin = *ecd.pfdin;
+ HRESULT hr = S_FALSE;
+ SFGAOF attr = MapFSToSFAttributes(fdin.attribs & CABITEM::FSATTS);
+ if (IncludeInEnumIDList(data.ContF, attr))
+ {
+ UINT datetime = MAKELONG(fdin.time, fdin.date);
+ CABITEM *item = CreateItem(fdin.psz1, fdin.attribs, fdin.cb, datetime);
+ if (!item)
+ return E_OUTOFMEMORY;
+ if (FAILED(hr = data.pEIDL->Append((LPCITEMIDLIST)item)))
+ SHFree(item);
+ }
+ return SUCCEEDED(hr) ? S_FALSE : hr; // Never extract
+ }
+ }
+ return E_NOTIMPL;
+}
+
+HRESULT CEnumIDList::Fill(LPCWSTR path, HWND hwnd, SHCONTF contf)
+{
+ FILLCALLBACKDATA data = { this, contf };
+ return ExtractCabinet(path, NULL, EnumFillCallback, &data);
+}
+
+HRESULT CEnumIDList::Fill(PCIDLIST_ABSOLUTE pidl, HWND hwnd, SHCONTF contf)
+{
+ WCHAR path[MAX_PATH];
+ if (SHGetPathFromIDListW(pidl, path))
+ return Fill(path, hwnd, contf);
+ return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+}
+
+IFACEMETHODIMP CCabFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
+{
+ if (pSort)
+ *pSort = COL_NAME;
+ if (pDisplay)
+ *pDisplay = COL_NAME;
+ return S_OK;
+}
+
+IFACEMETHODIMP CCabFolder::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
+{
+ if (!pcsFlags || iColumn >= _countof(g_Columns))
+ return E_INVALIDARG;
+ *pcsFlags = g_Columns[iColumn].ColFlags;
+ return S_OK;
+}
+
+IFACEMETHODIMP CCabFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET
pName)
+{
+ CABITEM *item = CABITEM::Validate(pidl);
+ if (!item || !pName)
+ return E_INVALIDARG;
+
+ if (dwFlags & SHGDN_FORPARSING)
+ {
+ if (dwFlags & SHGDN_INFOLDER)
+ return StrTo(FLATFOLDER ? item->Path : item->GetName(), *pName);
+
+ WCHAR parent[MAX_PATH];
+ if (!SHGetPathFromIDListW(m_CurDir, parent))
+ return E_FAIL;
+ UINT cch = lstrlenW(parent) + 1 + lstrlenW(item->Path) + 1;
+ pName->uType = STRRET_WSTR;
+ pName->pOleStr = (LPWSTR)SHAlloc(cch * sizeof(WCHAR));
+ if (!pName->pOleStr)
+ return E_OUTOFMEMORY;
+ lstrcpyW(pName->pOleStr, parent);
+ PathAppendW(pName->pOleStr, item->Path);
+ return S_OK;
+ }
+
+ SHFILEINFO fi;
+ DWORD attr = item->IsFolder() ? FILE_ATTRIBUTE_DIRECTORY : 0;
+ UINT flags = SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES;
+ if (SHGetFileInfo(item->GetName(), attr, &fi, sizeof(fi), flags))
+ return StrTo(fi.szDisplayName, *pName);
+ return StrTo(item->GetName(), *pName);
+}
+
+HRESULT CCabFolder::GetItemDetails(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd,
VARIANT *pv)
+{
+ HRESULT hr = E_FAIL;
+ STRRET *psr = &psd->str, srvar;
+ CABITEM *item = CABITEM::Validate(pidl);
+ if (!item)
+ return E_INVALIDARG;
+
+ switch (iColumn)
+ {
+ case COL_NAME:
+ {
+ hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, pv ? &srvar :
psr);
+ return SUCCEEDED(hr) && pv ? StrRetToVariantBSTR(&srvar, *pv) :
hr;
+ }
+
+ case COL_SIZE:
+ {
+ UINT data = item->Size;
+ if (pv)
+ {
+ V_VT(pv) = VT_UI4;
+ V_UI4(pv) = data;
+ }
+ else
+ {
+ psr->uType = STRRET_CSTR;
+ StrFormatByteSizeA(data, psr->cStr, _countof(psr->cStr));
+ }
+ return S_OK;
+ }
+
+ case COL_TYPE:
+ {
+ SHFILEINFO fi;
+ LPCWSTR data = fi.szTypeName;
+ DWORD attr = item->GetFSAttributes();
+ UINT flags = SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES;
+ if (SHGetFileInfo(item->GetName(), attr, &fi, sizeof(fi), flags))
+ return pv ? StrTo(data, *pv) : StrTo(data, *psr);
+ break;
+ }
+
+ case COL_MDATE:
+ {
+ if (pv)
+ {
+ if (DosDateTimeToVariantTime(item->Date, item->Time,
&V_DATE(pv)))
+ {
+ V_VT(pv) = VT_DATE;
+ return S_OK;
+ }
+ }
+ else
+ {
+ FILETIME utc, loc;
+ if (DosDateTimeToFileTime(item->Date, item->Time, &utc)
&& FileTimeToLocalFileTime(&utc, &loc))
+ {
+ psr->uType = STRRET_CSTR;
+ if (SHFormatDateTimeA(&loc, NULL, psr->cStr,
_countof(psr->cStr)))
+ {
+ return S_OK;
+ }
+ }
+ }
+ break;
+ }
+
+ case COL_PATH:
+ {
+ UINT len = item->NameOffset ? item->NameOffset - 1 : 0;
+ return pv ? StrTo(item->Path, len, *pv) : StrTo(item->Path, len,
*psr);
+ }
+
+ case COL_ATT:
+ {
+ UINT data = item->GetFSAttributes();
+ if (pv)
+ {
+ V_VT(pv) = VT_UI4;
+ V_UI4(pv) = data;
+ }
+ else
+ {
+ UINT i = 0;
+ psr->uType = STRRET_CSTR;
+ if (data & FILE_ATTRIBUTE_READONLY) psr->cStr[i++] =
'R';
+ if (data & FILE_ATTRIBUTE_HIDDEN) psr->cStr[i++] = 'H';
+ if (data & FILE_ATTRIBUTE_SYSTEM) psr->cStr[i++] = 'S';
+ if (data & FILE_ATTRIBUTE_ARCHIVE) psr->cStr[i++] = 'A';
+ psr->cStr[i++] = '\0';
+ }
+ return S_OK;
+ }
+ }
+ return hr;
+}
+
+IFACEMETHODIMP CCabFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid,
VARIANT *pv)
+{
+ if (!pscid || !pv)
+ return E_INVALIDARG;
+
+ CABITEM *item;
+ int col = MapSCIDToColumn(*pscid);
+ if (col >= 0)
+ {
+ return GetItemDetails(pidl, col, NULL, pv);
+ }
+ else if ((item = CABITEM::Validate(pidl)) == NULL)
+ {
+ return E_INVALIDARG;
+ }
+ else if (IsEqual(*pscid, FMTID_ShellDetails, PID_FINDDATA))
+ {
+ WIN32_FIND_DATA wfd;
+ ZeroMemory(&wfd, sizeof(wfd));
+ wfd.dwFileAttributes = item->GetFSAttributes();
+ wfd.nFileSizeLow = item->Size;
+ DosDateTimeToFileTime(item->Date, item->Time, &wfd.ftLastWriteTime);
+ lstrcpyn(wfd.cFileName, item->GetName(), MAX_PATH);
+ return InitVariantFromBuffer(&wfd, sizeof(wfd), pv);
+ }
+ return E_FAIL;
+}
+
+IFACEMETHODIMP CCabFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS
*psd)
+{
+ if (!psd || iColumn >= _countof(g_Columns))
+ {
+ return E_INVALIDARG;
+ }
+ else if (!pidl)
+ {
+ psd->fmt = g_Columns[iColumn].LvcFmt;
+ psd->cxChar = g_Columns[iColumn].LvcChars;
+ WCHAR buf[MAX_PATH];
+ if (LoadStringW(_AtlBaseModule.GetResourceInstance(), g_Columns[iColumn].TextId,
buf, _countof(buf)))
+ return StrTo(buf, psd->str);
+ return E_FAIL;
+ }
+ return GetItemDetails(pidl, iColumn, psd, NULL);
+}
+
+int CCabFolder::MapSCIDToColumn(const SHCOLUMNID &scid)
+{
+ for (UINT i = 0; i < _countof(g_Columns); ++i)
+ {
+ if (g_Columns[i].pkg && IsEqual(scid, *g_Columns[i].pkg,
g_Columns[i].pki))
+ return i;
+ }
+ return -1;
+}
+
+IFACEMETHODIMP CCabFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
+{
+ if (column < _countof(g_Columns) && g_Columns[column].pkg)
+ {
+ pscid->fmtid = *g_Columns[column].pkg;
+ pscid->pid = g_Columns[column].pki;
+ return S_OK;
+ }
+ return E_FAIL;
+}
+
+IFACEMETHODIMP CCabFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST
*ppEnumIDList)
+{
+ CEnumIDList *p = CEnumIDList::CreateInstance();
+ *ppEnumIDList = static_cast<LPENUMIDLIST>(p);
+ return p ? p->Fill(m_CurDir, hwndOwner, dwFlags) : E_OUTOFMEMORY;
+}
+
+IFACEMETHODIMP CCabFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID
riid, LPVOID *ppvOut)
+{
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+}
+
+HRESULT CCabFolder::CompareID(LPARAM lParam, PCUITEMID_CHILD pidl1, PCUITEMID_CHILD
pidl2)
+{
+ CABITEM *p1 = (CABITEM*)pidl1, *p2 = (CABITEM*)pidl2;
+ HRESULT hr = S_OK;
+ int ret = 0;
+
+ if (lParam & (SHCIDS_ALLFIELDS | SHCIDS_CANONICALONLY))
+ {
+ ret = lstrcmpiW(p1->Path, p2->Path);
+ if (ret && (lParam & SHCIDS_ALLFIELDS))
+ {
+ for (UINT i = 0; ret && SUCCEEDED(hr) && i < COLCOUNT;
++i)
+ {
+ hr = (i == COL_NAME) ? 0 : CompareID(i, pidl1, pidl2);
+ ret = (short)HRESULT_CODE(hr);
+ }
+ }
+ }
+ else
+ {
+ UINT col = lParam & SHCIDS_COLUMNMASK;
+ switch (col)
+ {
+ case COL_NAME:
+ ret = StrCmpLogicalW(p1->GetName(), p2->GetName());
+ break;
+
+ case COL_SIZE:
+ ret = p1->Size - p2->Size;
+ break;
+
+ case COL_MDATE:
+ ret = MAKELONG(p1->Time, p1->Date) - MAKELONG(p2->Time,
p2->Date);
+ break;
+
+ default:
+ {
+ if (col < COLCOUNT)
+ {
+ PWSTR str1, str2;
+ if (SUCCEEDED(hr = ::GetDetailsOf(*this, pidl1, col, str1)))
+ {
+ if (SUCCEEDED(hr = ::GetDetailsOf(*this, pidl2, col, str2)))
+ {
+ ret = StrCmpLogicalW(str1, str2);
+ SHFree(str2);
+ }
+ SHFree(str1);
+ }
+ }
+ else
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+ }
+ }
+ return SUCCEEDED(hr) ? MAKE_COMPARE_HRESULT(ret) : hr;
+}
+
+IFACEMETHODIMP CCabFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1,
PCUIDLIST_RELATIVE pidl2)
+{
+ C_ASSERT(FLATFOLDER);
+ if (!pidl1 || !ILIsSingle(pidl1) || !pidl2 || !ILIsSingle(pidl2))
+ return E_UNEXPECTED;
+
+ return CompareID(lParam, pidl1, pidl2);
+}
+
+IFACEMETHODIMP CCabFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppv)
+{
+ if (riid == IID_IShellView)
+ {
+ SFV_CREATE sfvc = { sizeof(SFV_CREATE), static_cast<IShellFolder*>(this)
};
+ return SHCreateShellFolderView(&sfvc, (IShellView**)ppv);
+ }
+ if (riid == IID_IContextMenu)
+ {
+ LPFNDFMCALLBACK func = FolderBackgroundMenuCallback;
+ IContextMenu **ppCM = (IContextMenu**)ppv;
+ return CDefFolderMenu_Create2(m_CurDir, hwndOwner, 0, NULL, this, func, 0, NULL,
ppCM);
+ }
+ return E_NOINTERFACE;
+}
+
+IFACEMETHODIMP CCabFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, SFGAOF
*rgfInOut)
+{
+ if (!cidl)
+ {
+ const SFGAOF ThisFolder = (SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_CANLINK);
+ *rgfInOut = *rgfInOut & ThisFolder;
+ return S_OK;
+ }
+ else if (!apidl)
+ {
+ return E_INVALIDARG;
+ }
+ HRESULT hr = S_OK;
+ const SFGAOF filemask = SFGAO_READONLY | SFGAO_HIDDEN | SFGAO_SYSTEM |
SFGAO_ISSLOW;
+ SFGAOF remain = *rgfInOut & filemask, validate = *rgfInOut &
SFGAO_VALIDATE;
+ CComPtr<CEnumIDList> list;
+ for (UINT i = 0; i < cidl && (remain || validate); ++i)
+ {
+ CABITEM *item = CABITEM::Validate(apidl[i]);
+ if (!item)
+ {
+ hr = E_INVALIDARG;
+ break;
+ }
+ else if (validate)
+ {
+ if (!list && FAILED_UNEXPECTEDLY(hr = CreateEnum(&list)))
+ return hr;
+ if (list->FindNamedItem((PCUITEMID_CHILD)item) == -1)
+ return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ }
+ SFGAOF att = MapFSToSFAttributes(item->GetFSAttributes()) | SFGAO_ISSLOW;
+ remain &= att & ~(FLATFOLDER ? SFGAO_FOLDER : 0);
+ }
+ *rgfInOut = remain;
+ return hr;
+}
+
+IFACEMETHODIMP CCabFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY
apidl, REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
+{
+ HRESULT hr = E_NOINTERFACE;
+ if (riid == IID_IExtractIconA || riid == IID_IExtractIconW)
+ {
+ if (cidl != 1)
+ return E_INVALIDARG;
+ CABITEM *item = CABITEM::Validate(apidl[0]);
+ if (!item)
+ return E_INVALIDARG;
+
+ DWORD attr = item->GetFSAttributes();
+ return SHCreateFileExtractIconW(item->GetName(), attr, riid, ppvOut);
+ }
+ else if (riid == IID_IContextMenu && cidl)
+ {
+ LPFNDFMCALLBACK func = ItemMenuCallback;
+ IContextMenu **ppCM = (IContextMenu**)ppvOut;
+ return CDefFolderMenu_Create2(NULL, hwndOwner, cidl, apidl, this, func, 0, NULL,
ppCM);
+ }
+ else if (riid == IID_IDataObject && cidl)
+ {
+ // Note: This IDataObject is only compatible with IContextMenu, it cannot handle
drag&drop of virtual items!
+ return CIDLData_CreateFromIDArray(m_CurDir, cidl, apidl,
(IDataObject**)ppvOut);
+ }
+ return hr;
+}
+
+IFACEMETHODIMP CCabFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case SFVM_WINDOWCREATED:
+ m_ShellViewWindow = (HWND)wParam;
+ return S_OK;
+ case SFVM_WINDOWCLOSING:
+ m_ShellViewWindow = NULL;
+ return S_OK;
+ }
+ return E_NOTIMPL;
+}
+
+IFACEMETHODIMP CCabFolder::GetIconOf(PCUITEMID_CHILD pidl, UINT flags, int *pIconIndex)
+{
+ if (CABITEM *item = CABITEM::Validate(pidl))
+ {
+ int index = MapPIDLToSystemImageListIndex(this, pidl, flags);
+ if (index == -1 && item->IsFolder())
+ index = (flags & GIL_OPENICON) ? SIID_FOLDEROPEN : SIID_FOLDER;
+ if (index != -1)
+ {
+ *pIconIndex = index;
+ return S_OK;
+ }
+ }
+ return S_FALSE;
+}
+
+static HRESULT GetFsPathFromIDList(PCIDLIST_ABSOLUTE pidl, PWSTR pszPath)
+{
+ BOOL ret = SHGetPathFromIDListW(pidl, pszPath);
+ if (!ret && ILIsEmpty(pidl))
+ ret = SHGetSpecialFolderPathW(NULL, pszPath, CSIDL_DESKTOPDIRECTORY, TRUE);
+ return ret ? S_OK : HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+}
+
+static int CALLBACK FolderBrowseCallback(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM
lpData)
+{
+ WCHAR buf[MAX_PATH];
+ switch (uMsg)
+ {
+ case BFFM_INITIALIZED:
+ {
+ if (LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_EXTRACT, buf,
_countof(buf)))
+ {
+ // Remove leading and trailing dots
+ WCHAR *s = buf, *e = s + lstrlenW(s);
+ while (*s == '.') ++s;
+ while (e > s && e[-1] == '.') *--e = UNICODE_NULL;
+ SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)s);
+ SendMessageW(GetDlgItem(hwnd, IDOK), WM_SETTEXT, 0, (LPARAM)s);
+ }
+ if (lpData)
+ {
+ SendMessageW(hwnd, BFFM_SETEXPANDED, FALSE, lpData);
+ SendMessageW(hwnd, BFFM_SETSELECTION, FALSE, lpData);
+ }
+ break;
+ }
+
+ case BFFM_SELCHANGED:
+ {
+ SFGAOF wanted = SFGAO_FILESYSTEM | SFGAO_FOLDER, query = wanted |
SFGAO_STREAM;
+ PCIDLIST_ABSOLUTE pidl = (PCIDLIST_ABSOLUTE)lParam;
+ BOOL enable = ILIsEmpty(pidl); // Allow the desktop
+ PCUITEMID_CHILD child;
+ IShellFolder *pSF;
+ if (SUCCEEDED(SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &pSF),
&child)))
+ {
+ SFGAOF attrib = query;
+ if (SUCCEEDED(pSF->GetAttributesOf(1, &child, &attrib)))
+ enable = (attrib & query) == wanted;
+ pSF->Release();
+ }
+ if (enable)
+ {
+ // We don't trust .zip folders, check the file-system to make sure
+ UINT attrib = SUCCEEDED(GetFsPathFromIDList(pidl, buf)) ?
GetFileAttributesW(buf) : 0;
+ enable = (attrib & FILE_ATTRIBUTE_DIRECTORY) && attrib !=
INVALID_FILE_ATTRIBUTES;
+ }
+ PostMessageW(hwnd, BFFM_ENABLEOK, 0, enable);
+ break;
+ }
+ }
+ return 0;
+}
+
+struct EXTRACTFILESDATA
+{
+ CCabFolder *pLifetimeCF;
+ HWND hWndOwner;
+ CIDA *pCIDA;
+ STGMEDIUM cidamedium;
+ IDataObject *pDO;
+ IStream *pMarshalDO;
+ IProgressDialog *pPD;
+ UINT cabfiles, completed;
+ WCHAR path[MAX_PATH], cab[MAX_PATH];
+};
+
+static HWND GetUiOwner(const EXTRACTFILESDATA &data)
+{
+ HWND hWnd;
+ if (SUCCEEDED(IUnknown_GetWindow(data.pPD, &hWnd)) &&
IsWindowVisible(hWnd))
+ return hWnd;
+ return IsWindowVisible(data.hWndOwner) ? data.hWndOwner : NULL;
+}
+
+static HRESULT CALLBACK ExtractFilesCallback(EXTRACTCALLBACKMSG msg, const
EXTRACTCALLBACKDATA &ecd, LPVOID cookie)
+{
+ EXTRACTFILESDATA &data = *(EXTRACTFILESDATA*)cookie;
+ switch ((UINT)msg)
+ {
+ case ECM_BEGIN:
+ {
+ data.cabfiles = (UINT)(SIZE_T)ecd.pfdin->hf;
+ return S_OK;
+ }
+
+ case ECM_FILE:
+ {
+ if (data.pPD && data.pPD->HasUserCancelled())
+ return HRESULT_FROM_WIN32(ERROR_CANCELLED);
+ HRESULT hr = data.pCIDA ? S_FALSE : S_OK; // Filtering or all items?
+ if (hr != S_OK)
+ {
+ CABITEM *needle = CreateItem(ecd.pfdin->psz1,
ecd.pfdin->attribs);
+ if (!needle)
+ return E_OUTOFMEMORY;
+ for (UINT i = 0; i < data.pCIDA->cidl && hr == S_FALSE;
++i)
+ {
+ C_ASSERT(FLATFOLDER);
+ LPCITEMIDLIST pidlChild = ILFindLastID(HIDA_GetPIDLItem(data.pCIDA,
i));
+ CABITEM *haystack = CABITEM::Validate(pidlChild);
+ if (!haystack && FAILED_UNEXPECTEDLY(hr = E_FAIL))
+ break;
+ if (!lstrcmpiW(needle->Path, haystack->Path))
+ {
+ if (data.pPD)
+ data.pPD->SetLine(1, needle->Path, TRUE, NULL);
+ hr = S_OK; // Found it in the list of files to extract
+ }
+ }
+ SHFree(needle);
+ }
+ if (data.pPD)
+ data.pPD->SetProgress(data.completed++, data.cabfiles);
+ return hr;
+ }
+
+ case ECM_PREPAREPATH:
+ {
+ UINT flags = SHPPFW_DIRCREATE | SHPPFW_IGNOREFILENAME;
+ return SHPathPrepareForWriteW(GetUiOwner(data), NULL, ecd.Path, flags);
+ }
+
+ case ECM_ERROR:
+ {
+ return ErrorBox(GetUiOwner(data), ecd.hr);
+ }
+ }
+ return E_NOTIMPL;
+}
+
+static void Free(EXTRACTFILESDATA &data)
+{
+ if (data.pPD)
+ {
+ data.pPD->StopProgressDialog();
+ data.pPD->Release();
+ }
+ CDataObjectHIDA::DestroyCIDA(data.pCIDA, data.cidamedium);
+ IUnknown_Set((IUnknown**)&data.pDO, NULL);
+ IUnknown_Set((IUnknown**)&data.pMarshalDO, NULL);
+ IUnknown_Set((IUnknown**)&data.pLifetimeCF, NULL);
+ SHFree(&data);
+}
+
+static DWORD CALLBACK ExtractFilesThread(LPVOID pParam)
+{
+ EXTRACTFILESDATA &data = *(EXTRACTFILESDATA*)pParam;
+ HRESULT hr = S_OK;
+ if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ProgressDialog, NULL,
IID_PPV_ARG(IProgressDialog, &data.pPD))))
+ {
+ // TODO: IActionProgress SPACTION_COPYING
+ if (SUCCEEDED(data.pPD->StartProgressDialog(data.hWndOwner, NULL,
PROGDLG_NOTIME, NULL)))
+ {
+ data.pPD->SetTitle(data.cab);
+ data.pPD->SetLine(2, data.path, TRUE, NULL);
+ data.pPD->SetAnimation(GetModuleHandleW(L"SHELL32"), 161);
+ data.pPD->SetProgress(0, 0);
+ }
+ }
+ if (data.pMarshalDO)
+ {
+ hr = CoGetInterfaceAndReleaseStream(data.pMarshalDO, IID_PPV_ARG(IDataObject,
&data.pDO));
+ data.pMarshalDO = NULL;
+ if (SUCCEEDED(hr))
+ hr = CDataObjectHIDA::CreateCIDA(data.pDO, &data.pCIDA,
data.cidamedium);
+ }
+ if (SUCCEEDED(hr))
+ {
+ ExtractCabinet(data.cab, data.path, ExtractFilesCallback, &data);
+ }
+ Free(data);
+ return 0;
+}
+
+HRESULT CCabFolder::ExtractFilesUI(HWND hWnd, IDataObject *pDO)
+{
+ if (!IsWindowVisible(hWnd) && IsWindowVisible(m_ShellViewWindow))
+ hWnd = m_ShellViewWindow;
+
+ EXTRACTFILESDATA *pData = (EXTRACTFILESDATA*)SHAlloc(sizeof(*pData));
+ if (!pData)
+ return E_OUTOFMEMORY;
+ ZeroMemory(pData, sizeof(*pData));
+ pData->hWndOwner = hWnd;
+ pData->pLifetimeCF = this;
+ pData->pLifetimeCF->AddRef();
+
+ HRESULT hr = GetFsPathFromIDList(m_CurDir, pData->cab);
+ if (SUCCEEDED(hr) && pDO)
+ {
+ hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDO,
&pData->pMarshalDO);
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
+ LPITEMIDLIST pidlInitial = ILClone(m_CurDir);
+ ILRemoveLastID(pidlInitial); // Remove the "name.cab" part (we
can't extract into ourselves)
+ UINT bif = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
+ BROWSEINFO bi = { hWnd, NULL, NULL, pData->cab, bif, FolderBrowseCallback,
(LPARAM)pidlInitial };
+ if (PIDLIST_ABSOLUTE folder = SHBrowseForFolderW(&bi))
+ {
+ hr = GetFsPathFromIDList(folder, pData->path);
+ ILFree(folder);
+ if (SUCCEEDED(hr))
+ {
+ UINT ctf = CTF_COINIT | CTF_PROCESS_REF | CTF_THREAD_REF |
CTF_FREELIBANDEXIT;
+ hr = SHCreateThread(ExtractFilesThread, pData, ctf, NULL) ? S_OK :
E_OUTOFMEMORY;
+ }
+ }
+ ILFree(pidlInitial);
+ }
+ if (hr != S_OK)
+ Free(*pData);
+ return hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) ? S_OK : hr;
+}
diff --git a/dll/shellext/cabview/lang/de-DE.rc b/dll/shellext/cabview/lang/de-DE.rc
new file mode 100644
index 00000000000..3951491271b
--- /dev/null
+++ b/dll/shellext/cabview/lang/de-DE.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: German (Germany) resource file
+ * TRANSLATOR: Copyright 2018 Robert Naumann <gonzomdx(a)gmail.com>
+ */
+
+LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Name"
+ IDS_COL_TYPE "Dateityp"
+ IDS_COL_SIZE "Größe"
+ IDS_COL_MDATE "Änderungsdatum"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/en-US.rc b/dll/shellext/cabview/lang/en-US.rc
new file mode 100644
index 00000000000..4205c4912dc
--- /dev/null
+++ b/dll/shellext/cabview/lang/en-US.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: English (United States) resource file
+ * TRANSLATOR: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Name"
+ IDS_COL_SIZE "Size"
+ IDS_COL_TYPE "Type"
+ IDS_COL_MDATE "Date modified"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/et-EE.rc b/dll/shellext/cabview/lang/et-EE.rc
new file mode 100644
index 00000000000..bd1cd3029d9
--- /dev/null
+++ b/dll/shellext/cabview/lang/et-EE.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Estonian resource file
+ * TRANSLATOR: Copyright 2018 Joann Mõndresku <joann.mondresku(a)gmail.com>
+ */
+
+LANGUAGE LANG_ESTONIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Nimi"
+ IDS_COL_TYPE "Tüüp"
+ IDS_COL_SIZE "Suurus"
+ IDS_COL_MDATE "Kuupäeval muudetud"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/fr-FR.rc b/dll/shellext/cabview/lang/fr-FR.rc
new file mode 100644
index 00000000000..5c848d20827
--- /dev/null
+++ b/dll/shellext/cabview/lang/fr-FR.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: French (France) resource file
+ * TRANSLATOR: Copyright 2018 Pierre Schweitzer <pierre(a)reactos.org>
+ */
+
+LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Nom"
+ IDS_COL_TYPE "Type"
+ IDS_COL_SIZE "Taille"
+ IDS_COL_MDATE "Date de modification"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/hi-IN.rc b/dll/shellext/cabview/lang/hi-IN.rc
new file mode 100644
index 00000000000..943076df123
--- /dev/null
+++ b/dll/shellext/cabview/lang/hi-IN.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Hindi (India) resource file
+ * TRANSLATOR: Copyright 2019 Arnav Bhatt <arnavbhatt2004(a)gmail.com>
+*/
+
+LANGUAGE LANG_HINDI, SUBLANG_HINDI_INDIA
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "नाम"
+ IDS_COL_TYPE "प्रकार"
+ IDS_COL_SIZE "साइज़"
+ IDS_COL_MDATE "तिथि संशोधित"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/it-IT.rc b/dll/shellext/cabview/lang/it-IT.rc
new file mode 100644
index 00000000000..912949f98df
--- /dev/null
+++ b/dll/shellext/cabview/lang/it-IT.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Italian resource file
+ * TRANSLATOR: Copyright 2018 George Bișoc <george.bisoc(a)reactos.org>
+ */
+
+LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Nome"
+ IDS_COL_TYPE "Tipo"
+ IDS_COL_SIZE "Dimensione"
+ IDS_COL_MDATE "Data modificata"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/ja-JP.rc b/dll/shellext/cabview/lang/ja-JP.rc
new file mode 100644
index 00000000000..fcc6dc8d90e
--- /dev/null
+++ b/dll/shellext/cabview/lang/ja-JP.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Japanese resource file
+ * TRANSLATOR: Copyright 2018 Katayama Hirofumi MZ
<katayama.hirofumi.mz(a)gmail.com>
+ */
+
+LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "名前"
+ IDS_COL_TYPE "種類"
+ IDS_COL_SIZE "サイズ"
+ IDS_COL_MDATE "変更日"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/pl-PL.rc b/dll/shellext/cabview/lang/pl-PL.rc
new file mode 100644
index 00000000000..0d6fb1ba41a
--- /dev/null
+++ b/dll/shellext/cabview/lang/pl-PL.rc
@@ -0,0 +1,22 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Polish resource file
+ * TRANSLATORS: Copyright 2018-2019 Adam Słaboń <asaillen456esx(a)gmail.com>
+ * Copyright 2020 Piotr Hetnarowicz <piotrhwz(a)gmail.com>
+ */
+
+LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Nazwa"
+ IDS_COL_TYPE "Typ"
+ IDS_COL_SIZE "Rozmiar oryginalny"
+ IDS_COL_MDATE "Data modyfikacji"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/pt-PT.rc b/dll/shellext/cabview/lang/pt-PT.rc
new file mode 100644
index 00000000000..9671f14a3bf
--- /dev/null
+++ b/dll/shellext/cabview/lang/pt-PT.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Portuguese (Portugal) resource file
+ * TRANSLATOR: Copyright 2020 Jose Carlos Jesus <zecarlos1957(a)hotmail.com>
+ */
+
+LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Nome"
+ IDS_COL_TYPE "Tipo"
+ IDS_COL_SIZE "Tamanho"
+ IDS_COL_MDATE "Data da modificação"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/ro-RO.rc b/dll/shellext/cabview/lang/ro-RO.rc
new file mode 100644
index 00000000000..9e9c0684fd3
--- /dev/null
+++ b/dll/shellext/cabview/lang/ro-RO.rc
@@ -0,0 +1,22 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Romanian resource file
+ * TRANSLATORS: Copyright 2018 George Bișoc <george.bisoc(a)reactos.org>
+ * Copyright 2022-2024 Andrei Miloiu <miloiuandrei(a)gmail.com>
+ */
+
+LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Nume"
+ IDS_COL_TYPE "Tip"
+ IDS_COL_SIZE "Dimensiune"
+ IDS_COL_MDATE "Data modificată"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/ru-RU.rc b/dll/shellext/cabview/lang/ru-RU.rc
new file mode 100644
index 00000000000..e377821fcb3
--- /dev/null
+++ b/dll/shellext/cabview/lang/ru-RU.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Russian resource translation
+ * TRANSLATOR: Copyright 2018-2020 Stanislav Motylkov <x86corez(a)gmail.com>
+ */
+
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Название"
+ IDS_COL_TYPE "Тип"
+ IDS_COL_SIZE "Размер"
+ IDS_COL_MDATE "Дата изменения"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/sv-SE.rc b/dll/shellext/cabview/lang/sv-SE.rc
new file mode 100644
index 00000000000..f65e8352d64
--- /dev/null
+++ b/dll/shellext/cabview/lang/sv-SE.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Swedish resource file
+ * TRANSLATOR: Copyright 2018 Andreas Bjerkeholt
<andreas.bjerkeholt(a)reactos.org>
+ */
+
+LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Namn"
+ IDS_COL_TYPE "Typ"
+ IDS_COL_SIZE "Storlek"
+ IDS_COL_MDATE "Ändrad den"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extrahera..."
+ IDS_EXTRACTALL "Extrahera &alla..."
+END
diff --git a/dll/shellext/cabview/lang/tr-TR.rc b/dll/shellext/cabview/lang/tr-TR.rc
new file mode 100644
index 00000000000..e0d5a7a81bf
--- /dev/null
+++ b/dll/shellext/cabview/lang/tr-TR.rc
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Turkish resource file
+ * TRANSLATOR: Copyright 2021 Süleyman Poyraz <zaryob.dev(a)gmail.com>
+ */
+
+LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "İsim"
+ IDS_COL_TYPE "Tür"
+ IDS_COL_SIZE "Boyut"
+ IDS_COL_MDATE "Değiştirilme Tarihi"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/zh-CN.rc b/dll/shellext/cabview/lang/zh-CN.rc
new file mode 100644
index 00000000000..3437d81d1b3
--- /dev/null
+++ b/dll/shellext/cabview/lang/zh-CN.rc
@@ -0,0 +1,22 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Chinese (Simplified) resource file
+ * TRANSLATORS: Copyright 2018 Li Keqing <auroracloud4096(a)gmail.com>
+ * Copyright 2021 Wu Haotian <rigoligo03(a)gmail.com>
+ */
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "名称"
+ IDS_COL_TYPE "类型"
+ IDS_COL_SIZE "大小"
+ IDS_COL_MDATE "修改日期"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/zh-HK.rc b/dll/shellext/cabview/lang/zh-HK.rc
new file mode 100644
index 00000000000..a5969223b8d
--- /dev/null
+++ b/dll/shellext/cabview/lang/zh-HK.rc
@@ -0,0 +1,22 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Chinese (Hong Kong) resources file
+ * TRANSLATOR: Copyright 2021 Chan Chilung <eason066(a)gmail.com>
+ * REFERENCES: Chinese (Simplified) resource file
+ */
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_HONGKONG
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "名稱"
+ IDS_COL_TYPE "類型"
+ IDS_COL_SIZE "大小"
+ IDS_COL_MDATE "修改日期"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/lang/zh-TW.rc b/dll/shellext/cabview/lang/zh-TW.rc
new file mode 100644
index 00000000000..bbe5accd92f
--- /dev/null
+++ b/dll/shellext/cabview/lang/zh-TW.rc
@@ -0,0 +1,23 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Chinese (Traditional) resource file
+ * TRANSLATOR: Copyright 2020-2021 Chan Chilung <eason066(a)gmail.com>
+ * REFERENCES: Chinese (Simplified) resource translation
+ * Copyright 2018 Li Keqing <auroracloud4096(a)gmail.com>
+ */
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "名稱"
+ IDS_COL_TYPE "類型"
+ IDS_COL_SIZE "大小"
+ IDS_COL_MDATE "修改日期"
+ IDS_COL_PATH "Path"
+ IDS_COL_ATT "Attributes"
+
+ IDS_EXTRACT "Extract..."
+ IDS_EXTRACTALL "Extract &All..."
+END
diff --git a/dll/shellext/cabview/precomp.h b/dll/shellext/cabview/precomp.h
new file mode 100644
index 00000000000..d69f972a2e6
--- /dev/null
+++ b/dll/shellext/cabview/precomp.h
@@ -0,0 +1,34 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Precompiled header file
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#pragma once
+#define NTOS_MODE_USER
+#include <windows.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <strsafe.h>
+#include <shlobj.h>
+#include <shobjidl.h>
+#include <shlwapi.h>
+#include <shellapi.h>
+#include <shlguid_undoc.h>
+#define NTSTATUS LONG // for debug.h
+#include <reactos/debug.h>
+#include <shellutils.h>
+#include <ntquery.h>
+#include <fdi.h>
+
+#ifndef SFGAO_SYSTEM
+#define SFGAO_SYSTEM 0x00001000
+#endif
+
+#ifndef SHGSI_ICONLOCATION
+#define SIID_FOLDER 3
+#define SIID_FOLDEROPEN 4
+#endif
+
+EXTERN_C INT WINAPI SHFormatDateTimeA(const FILETIME UNALIGNED *fileTime, DWORD *flags,
LPSTR buf, UINT size);
diff --git a/dll/shellext/cabview/res/cabview.rgs b/dll/shellext/cabview/res/cabview.rgs
new file mode 100644
index 00000000000..f9316200962
--- /dev/null
+++ b/dll/shellext/cabview/res/cabview.rgs
@@ -0,0 +1,47 @@
+HKCR
+{
+ NoRemove CLSID
+ {
+ '{0CD7A5C0-9F37-11CE-AE65-08002B2E1262}' = s 'Cabinet Shell
Folder'
+ {
+ InprocServer32 = s '%MODULE%' { val ThreadingModel = s
'Apartment' }
+ ShellFolder
+ {
+ val Attributes = d '0x680001a0'
+ }
+ 'Implemented Categories'
+ {
+ '{00021490-0000-0000-C000-000000000046}'
+ {
+ }
+ }
+ }
+ }
+
+ NoRemove CABFolder
+ {
+ CLSID = s '{0CD7A5C0-9F37-11CE-AE65-08002B2E1262}'
+ DefaultIcon = s '%MODULE%'
+
+ NoRemove Shell
+ {
+ NoRemove Open
+ {
+ val BrowserFlags = d '0x10'
+ val ExplorerFlags = d '0x20'
+ command = s '"explorer.exe" "%%L"'
+ }
+ }
+ }
+ NoRemove '.cab' = s 'CABFolder'
+ {
+ }
+
+ NoRemove SystemFileAssociations
+ {
+ '.cab'
+ {
+ CLSID = s '{0CD7A5C0-9F37-11CE-AE65-08002B2E1262}'
+ }
+ }
+}
diff --git a/dll/shellext/cabview/res/folder.ico b/dll/shellext/cabview/res/folder.ico
new file mode 100644
index 00000000000..04f98dcb2fd
Binary files /dev/null and b/dll/shellext/cabview/res/folder.ico differ
diff --git a/dll/shellext/cabview/resource.h b/dll/shellext/cabview/resource.h
new file mode 100644
index 00000000000..b964a8c2c10
--- /dev/null
+++ b/dll/shellext/cabview/resource.h
@@ -0,0 +1,25 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Resource header file
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#pragma once
+
+/* Icons */
+#define IDI_FOLDER 1
+
+/* Registry */
+#define IDR_FOLDER 8000
+
+/* Strings */
+#define IDS_COL_NAME 1
+#define IDS_COL_SIZE 2
+#define IDS_COL_TYPE 3
+#define IDS_COL_MDATE 4
+#define IDS_COL_PATH 5
+#define IDS_COL_ATT 6
+
+#define IDS_EXTRACT 72
+#define IDS_EXTRACTALL 82
diff --git a/dll/shellext/cabview/util.h b/dll/shellext/cabview/util.h
new file mode 100644
index 00000000000..5f3d5e38196
--- /dev/null
+++ b/dll/shellext/cabview/util.h
@@ -0,0 +1,156 @@
+/*
+ * PROJECT: ReactOS CabView Shell Extension
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Utility header file
+ * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#pragma once
+
+template<class H> static int ErrorBox(H hWnd, int Error)
+{
+ SHELL_ErrorBox(hWnd, Error);
+ return Error;
+}
+
+template<class T> static inline bool IsPathSep(T c)
+{
+ return c == '\\' || c == '/';
+}
+
+inline bool IsEqual(const SHCOLUMNID &scid, REFGUID guid, UINT pid)
+{
+ return scid.pid == pid && IsEqualGUID(scid.fmtid, guid);
+}
+
+inline HRESULT InitVariantFromBuffer(const void *buffer, UINT cb, VARIANT *pv)
+{
+ SAFEARRAY *pa = SafeArrayCreateVector(VT_UI1, 0, cb);
+ if (pa)
+ {
+ CopyMemory(pa->pvData, buffer, cb);
+ V_VT(pv) = VT_UI1 | VT_ARRAY;
+ V_UNION(pv, parray) = pa;
+ return S_OK;
+ }
+ V_VT(pv) = VT_EMPTY;
+ return E_OUTOFMEMORY;
+}
+
+inline void FreeStrRet(STRRET &str)
+{
+ if (str.uType == STRRET_WSTR)
+ {
+ SHFree(str.pOleStr);
+ str.uType = STRRET_CSTR;
+ }
+}
+
+inline HRESULT StrTo(LPCWSTR str, UINT len, STRRET &sr)
+{
+ LPWSTR data = (LPWSTR)SHAlloc(++len * sizeof(WCHAR));
+ if (!data)
+ return E_OUTOFMEMORY;
+ lstrcpynW(data, str, len);
+ sr.uType = STRRET_WSTR;
+ sr.pOleStr = data;
+ return S_OK;
+}
+
+inline HRESULT StrTo(LPCWSTR str, STRRET &sr)
+{
+ return StrTo(str, lstrlenW(str), sr);
+}
+
+inline HRESULT StrTo(LPCWSTR str, UINT len, VARIANT &v)
+{
+ BSTR data = SysAllocStringLen(str, len);
+ if (!data)
+ return E_OUTOFMEMORY;
+ V_VT(&v) = VT_BSTR;
+ V_BSTR(&v) = data;
+ return S_OK;
+}
+
+inline HRESULT StrTo(LPCWSTR str, VARIANT &v)
+{
+ return StrTo(str, lstrlenW(str), v);
+}
+
+inline HRESULT StrRetToVariantBSTR(STRRET *psr, VARIANT &v)
+{
+ HRESULT hr = StrRetToBSTR(psr, NULL, &V_BSTR(&v));
+ if (SUCCEEDED(hr))
+ V_VT(&v) = VT_BSTR;
+ return hr;
+}
+
+inline HRESULT GetDetailsOf(IShellFolder2 &Folder, PCUITEMID_CHILD pidl, UINT Column,
PWSTR &String)
+{
+ SHELLDETAILS details;
+ HRESULT hr = Folder.GetDetailsOf(pidl, Column, &details);
+ if (SUCCEEDED(hr))
+ hr = StrRetToStrW(&details.str, pidl, &String);
+ return hr;
+}
+
+inline HRESULT InsertMenuItem(QCMINFO &qcmi, UINT &Pos, UINT &TrackId, UINT
Id, UINT ResId, int State = 0)
+{
+ UINT flags = 0;
+ WCHAR string[MAX_PATH];
+ string[0] = UNICODE_NULL;
+ if ((Id += qcmi.idCmdFirst) > qcmi.idCmdLast)
+ return E_FAIL;
+ else if (ResId == (UINT)-1)
+ flags |= MF_SEPARATOR;
+ else if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), ResId, string,
_countof(string)))
+ return E_FAIL;
+
+ MENUITEMINFOW mii;
+ mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); // USER32 version agnostic
+ mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE;
+ mii.fType = flags;
+ mii.wID = Id;
+ mii.dwTypeData = string;
+ mii.cch = 0;
+ mii.fState = State;
+ if (!InsertMenuItemW(qcmi.hmenu, Pos, TRUE, &mii))
+ return E_FAIL;
+ Pos++;
+ TrackId = max(TrackId, Id);
+ return S_OK;
+}
+
+inline SFGAOF MapFSToSFAttributes(UINT att)
+{
+ return ((att & FILE_ATTRIBUTE_READONLY) ? SFGAO_READONLY : 0) |
+ ((att & FILE_ATTRIBUTE_HIDDEN) ? SFGAO_HIDDEN : 0) |
+ ((att & FILE_ATTRIBUTE_SYSTEM) ? SFGAO_SYSTEM : 0);
+}
+
+inline bool IncludeInEnumIDList(SHCONTF contf, SFGAOF att)
+{
+ const SHCONTF both = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
+ const SFGAOF superbits = SFGAO_HIDDEN | SFGAO_READONLY | SFGAO_SYSTEM;
+ const bool isfile = (att & (SFGAO_STREAM | SFGAO_FOLDER)) != SFGAO_FOLDER;
+ if ((contf & both) != both && !(contf & SHCONTF_STORAGE))
+ {
+ if (isfile && (contf & SHCONTF_FOLDERS))
+ return false;
+ if ((att & SFGAO_FOLDER) && (contf & SHCONTF_NONFOLDERS))
+ return false;
+ }
+ if ((att & SFGAO_HIDDEN) && !(contf & (SHCONTF_INCLUDEHIDDEN |
SHCONTF_STORAGE)))
+ return false;
+ if ((att & superbits) > SFGAO_HIDDEN && !(contf &
(SHCONTF_INCLUDESUPERHIDDEN | SHCONTF_STORAGE)))
+ return false;
+ return true;
+}
+
+inline int MapPIDLToSystemImageListIndex(IShellFolder *pSF, PCUITEMID_CHILD pidl, UINT
GilFlags = 0)
+{
+ int normal, open;
+ BOOL qopen = GilFlags & GIL_OPENICON;
+ normal = SHMapPIDLToSystemImageListIndex(pSF, pidl, qopen ? &open : NULL);
+ return qopen && open != -1 ? open : normal;
+}
diff --git a/dll/win32/browseui/CProgressDialog.cpp
b/dll/win32/browseui/CProgressDialog.cpp
index 6d32d01ecb2..4f748b12057 100644
--- a/dll/win32/browseui/CProgressDialog.cpp
+++ b/dll/win32/browseui/CProgressDialog.cpp
@@ -44,6 +44,7 @@
CProgressDialog::CProgressDialog()
{
+ this->hwnd = NULL;
this->lines[0] = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
this->lines[1] = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
this->lines[2] = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
diff --git a/media/inf/syssetup.inf b/media/inf/syssetup.inf
index 14700f060e1..1ef4a04bc3d 100644
--- a/media/inf/syssetup.inf
+++ b/media/inf/syssetup.inf
@@ -52,6 +52,7 @@ AddReg=Classes
11,,amstream.dll,1
11,,avifil32.dll,1
11,,browseui.dll,1
+11,,cabview.dll,1
11,,comcat.dll,1
11,,cryptdlg.dll,1
11,,cryptnet.dll,1