https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c7e6a9d04b08131fd1bc6…
commit c7e6a9d04b08131fd1bc6b5ed70d1b9e4bf8140a
Author: Mark Jansen <mark.jansen(a)reactos.org>
AuthorDate: Fri Dec 29 23:45:02 2017 +0100
Commit: Mark Jansen <mark.jansen(a)reactos.org>
CommitDate: Sat Apr 7 15:29:59 2018 +0200
[ZIPFLDR] Initial implementation.
Icon by Jared Smudde
CORE-7684
---
dll/shellext/CMakeLists.txt | 1 +
dll/shellext/zipfldr/CEnumZipContents.cpp | 95 +++++
dll/shellext/zipfldr/CExplorerCommand.cpp | 189 +++++++++
dll/shellext/zipfldr/CFolderViewCB.cpp | 54 +++
dll/shellext/zipfldr/CMakeLists.txt | 50 +++
dll/shellext/zipfldr/CZipEnumerator.hpp | 94 +++++
dll/shellext/zipfldr/CZipExtract.cpp | 481 +++++++++++++++++++++++
dll/shellext/zipfldr/CZipFolder.hpp | 625 ++++++++++++++++++++++++++++++
dll/shellext/zipfldr/Debug.cpp | 44 +++
dll/shellext/zipfldr/IZip.hpp | 12 +
dll/shellext/zipfldr/precomp.h | 62 +++
dll/shellext/zipfldr/res/zipfldr.ico | Bin 0 -> 26070 bytes
dll/shellext/zipfldr/res/zipfldr.rgs | 54 +++
dll/shellext/zipfldr/resource.h | 58 +++
dll/shellext/zipfldr/zipfldr.cpp | 117 ++++++
dll/shellext/zipfldr/zipfldr.rc | 88 +++++
dll/shellext/zipfldr/zipfldr.spec | 5 +
dll/shellext/zipfldr/zippidl.cpp | 42 ++
dll/shellext/zipfldr/zippidl.hpp | 34 ++
19 files changed, 2105 insertions(+)
diff --git a/dll/shellext/CMakeLists.txt b/dll/shellext/CMakeLists.txt
index 56fabe1299..e11df36001 100644
--- a/dll/shellext/CMakeLists.txt
+++ b/dll/shellext/CMakeLists.txt
@@ -8,3 +8,4 @@ add_subdirectory(netshell)
add_subdirectory(ntobjshex)
add_subdirectory(shellbtrfs)
add_subdirectory(stobject)
+add_subdirectory(zipfldr)
diff --git a/dll/shellext/zipfldr/CEnumZipContents.cpp
b/dll/shellext/zipfldr/CEnumZipContents.cpp
new file mode 100644
index 0000000000..f135fe756f
--- /dev/null
+++ b/dll/shellext/zipfldr/CEnumZipContents.cpp
@@ -0,0 +1,95 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: CEnumZipContents
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+class CEnumZipContents :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IEnumIDList
+{
+private:
+ CZipEnumerator mEnumerator;
+ DWORD dwFlags;
+ CStringA m_Prefix;
+public:
+ CEnumZipContents()
+ :dwFlags(0)
+ {
+ }
+
+ STDMETHODIMP Initialize(IZip* zip, DWORD flags, const char* prefix)
+ {
+ dwFlags = flags;
+ m_Prefix = prefix;
+ if (mEnumerator.initialize(zip))
+ return S_OK;
+ return E_FAIL;
+ }
+
+ // *** IEnumIDList methods ***
+ STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
+ {
+ if (!pceltFetched || !rgelt)
+ return E_POINTER;
+
+ *pceltFetched = 0;
+
+ if (celt != 1)
+ return E_FAIL;
+
+ CStringA name;
+ bool dir;
+ unz_file_info64 info;
+ if (mEnumerator.next_unique(m_Prefix, name, dir, info))
+ {
+ *pceltFetched = 1;
+ *rgelt = _ILCreate(dir ? ZIP_PIDL_DIRECTORY : ZIP_PIDL_FILE, name, info);
+ return S_OK;
+ }
+
+ return S_FALSE;
+ }
+ STDMETHODIMP Skip(ULONG celt)
+ {
+ CStringA name;
+ bool dir;
+ unz_file_info64 info;
+ while (celt--)
+ {
+ if (!mEnumerator.next_unique(m_Prefix, name, dir, info))
+ return E_FAIL;
+ ;
+ }
+ return S_OK;
+ }
+ STDMETHODIMP Reset()
+ {
+ if (mEnumerator.reset())
+ return S_OK;
+ return E_FAIL;
+ }
+ STDMETHODIMP Clone(IEnumIDList **ppenum)
+ {
+ return E_NOTIMPL;
+ }
+
+
+public:
+ DECLARE_NOT_AGGREGATABLE(CEnumZipContents)
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(CEnumZipContents)
+ COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
+ END_COM_MAP()
+};
+
+
+HRESULT _CEnumZipContents_CreateInstance(IZip* zip, DWORD flags, const char* prefix,
REFIID riid, LPVOID * ppvOut)
+{
+ return ShellObjectCreatorInit<CEnumZipContents>(zip, flags, prefix, riid,
ppvOut);
+}
+
diff --git a/dll/shellext/zipfldr/CExplorerCommand.cpp
b/dll/shellext/zipfldr/CExplorerCommand.cpp
new file mode 100644
index 0000000000..1e634f81c8
--- /dev/null
+++ b/dll/shellext/zipfldr/CExplorerCommand.cpp
@@ -0,0 +1,189 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: IExplorerCommand implementation
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+class CExplorerCommand :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IExplorerCommand
+{
+private:
+ CComPtr<IContextMenu> m_pZipObject;
+
+public:
+
+ STDMETHODIMP Initialize(IContextMenu* zipObject)
+ {
+ m_pZipObject = zipObject;
+ return S_OK;
+ }
+
+ // *** IExplorerCommand methods ***
+ STDMETHODIMP GetTitle(IShellItemArray *psiItemArray, LPWSTR *ppszName)
+ {
+ CStringW Title(MAKEINTRESOURCEW(IDS_MENUITEM));
+ return SHStrDup(Title, ppszName);
+ }
+ STDMETHODIMP GetIcon(IShellItemArray *psiItemArray, LPWSTR *ppszIcon)
+ {
+ CStringW IconName = L"zipfldr.dll,-1";
+ return SHStrDup(IconName, ppszIcon);
+ }
+ STDMETHODIMP GetToolTip(IShellItemArray *psiItemArray, LPWSTR *ppszInfotip)
+ {
+ CStringW HelpText(MAKEINTRESOURCEW(IDS_HELPTEXT));
+ return SHStrDup(HelpText, ppszInfotip);
+ }
+ STDMETHODIMP GetCanonicalName(GUID *pguidCommandName)
+ {
+ *pguidCommandName = CLSID_ZipFolderExtractAllCommand;
+ return S_OK;
+ }
+ STDMETHODIMP GetState(IShellItemArray *psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE
*pCmdState)
+ {
+ *pCmdState = ECS_ENABLED;
+ return S_OK;
+ }
+ STDMETHODIMP Invoke(IShellItemArray *psiItemArray, IBindCtx *pbc)
+ {
+ CMINVOKECOMMANDINFO cm = { sizeof(cm), 0 };
+ cm.lpVerb = EXTRACT_VERBA;
+ cm.nShow = SW_SHOW;
+ return m_pZipObject->InvokeCommand(&cm);
+ }
+ STDMETHODIMP GetFlags(EXPCMDFLAGS *pFlags)
+ {
+ *pFlags = ECF_DEFAULT;
+ return S_OK;
+ }
+ STDMETHODIMP EnumSubCommands(IEnumExplorerCommand **ppEnum)
+ {
+ DbgPrint("%s\n", __FUNCTION__);
+ return E_NOTIMPL;
+ }
+
+public:
+ DECLARE_NOT_AGGREGATABLE(CExplorerCommand)
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(CExplorerCommand)
+ COM_INTERFACE_ENTRY_IID(IID_IExplorerCommand, IExplorerCommand)
+ END_COM_MAP()
+};
+
+
+class CEnumExplorerCommand :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IEnumExplorerCommand
+{
+private:
+ bool m_bFirst;
+ CComPtr<IContextMenu> m_pZipObject;
+
+public:
+
+ CEnumExplorerCommand()
+ :m_bFirst(true)
+ {
+ }
+
+ STDMETHODIMP Initialize(IContextMenu* zipObject)
+ {
+ m_pZipObject = zipObject;
+ return S_OK;
+ }
+
+ // *** IEnumExplorerCommand methods ***
+ STDMETHODIMP Next(ULONG celt, IExplorerCommand **pUICommand, ULONG *pceltFetched)
+ {
+ if (!pUICommand)
+ return E_POINTER;
+
+ if (pceltFetched)
+ *pceltFetched = 0;
+ if (m_bFirst && celt)
+ {
+ m_bFirst = false;
+ celt--;
+ HRESULT hr = ShellObjectCreatorInit<CExplorerCommand>(m_pZipObject,
IID_PPV_ARG(IExplorerCommand, pUICommand));
+ if (SUCCEEDED(hr))
+ {
+ if (pceltFetched)
+ *pceltFetched = 1;
+ }
+ return hr;
+ }
+ return S_FALSE;
+ }
+ STDMETHODIMP Skip(ULONG celt)
+ {
+ if (m_bFirst)
+ {
+ m_bFirst = false;
+ return S_OK;
+ }
+ return S_FALSE;
+ }
+ STDMETHODIMP Reset()
+ {
+ m_bFirst = true;
+ return S_OK;
+ }
+ STDMETHODIMP Clone(IEnumExplorerCommand **ppenum)
+ {
+ return E_NOTIMPL;
+ }
+
+public:
+ DECLARE_NOT_AGGREGATABLE(CEnumExplorerCommand)
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(CEnumExplorerCommand)
+ COM_INTERFACE_ENTRY_IID(IID_IEnumExplorerCommand, IEnumExplorerCommand)
+ END_COM_MAP()
+};
+
+class CExplorerCommandProvider :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IExplorerCommandProvider
+{
+private:
+ CComPtr<IContextMenu> m_pZipObject;
+
+public:
+ STDMETHODIMP Initialize(IContextMenu* zipObject)
+ {
+ m_pZipObject = zipObject;
+ return S_OK;
+ }
+
+ // *** IExplorerCommandProvider methods ***
+ STDMETHODIMP GetCommands(IUnknown *punkSite, REFIID riid, void **ppv)
+ {
+ return ShellObjectCreatorInit<CEnumExplorerCommand>(m_pZipObject, riid,
ppv);
+ }
+ STDMETHODIMP GetCommand(REFGUID rguidCommandId, REFIID riid, void **ppv)
+ {
+ UNIMPLEMENTED;
+ *ppv = NULL;
+ return E_NOTIMPL;
+ }
+
+public:
+ DECLARE_NOT_AGGREGATABLE(CExplorerCommandProvider)
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(CExplorerCommandProvider)
+ COM_INTERFACE_ENTRY_IID(IID_IExplorerCommandProvider, IExplorerCommandProvider)
+ END_COM_MAP()
+};
+
+
+HRESULT _CExplorerCommandProvider_CreateInstance(IContextMenu* zipObject, REFIID riid,
LPVOID * ppvOut)
+{
+ return ShellObjectCreatorInit<CExplorerCommandProvider>(zipObject, riid,
ppvOut);
+}
diff --git a/dll/shellext/zipfldr/CFolderViewCB.cpp
b/dll/shellext/zipfldr/CFolderViewCB.cpp
new file mode 100644
index 0000000000..2a46f6576a
--- /dev/null
+++ b/dll/shellext/zipfldr/CFolderViewCB.cpp
@@ -0,0 +1,54 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: IShellFolderViewCB implementation
+ * COPYRIGHT: Copyright 2017 David Quintana (gigaherz(a)gmail.com)
+ * Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+class CFolderViewCB :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IShellFolderViewCB
+{
+public:
+
+ virtual ~CFolderViewCB()
+ {
+ }
+
+ // *** IShellFolderViewCB methods ***
+ STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ /* TODO: Handle SFVM_GET_WEBVIEW_CONTENT to add tasks */
+ switch (uMsg)
+ {
+ case SFVM_DEFVIEWMODE:
+ {
+ FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*)lParam;
+ *pViewMode = FVM_DETAILS;
+ return S_OK;
+ }
+ case SFVM_COLUMNCLICK:
+ return S_FALSE;
+ case SFVM_BACKGROUNDENUM:
+ return S_OK;
+ }
+
+ return E_NOTIMPL;
+ }
+
+public:
+ DECLARE_NOT_AGGREGATABLE(CFolderViewCB)
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(CFolderViewCB)
+ COM_INTERFACE_ENTRY_IID(IID_IShellFolderViewCB, IShellFolderViewCB)
+ END_COM_MAP()
+};
+
+HRESULT _CFolderViewCB_CreateInstance(REFIID riid, LPVOID * ppvOut)
+{
+ return ShellObjectCreator<CFolderViewCB>(riid, ppvOut);
+}
diff --git a/dll/shellext/zipfldr/CMakeLists.txt b/dll/shellext/zipfldr/CMakeLists.txt
new file mode 100644
index 0000000000..d491f1d7c2
--- /dev/null
+++ b/dll/shellext/zipfldr/CMakeLists.txt
@@ -0,0 +1,50 @@
+
+set_cpp(WITH_RUNTIME WITH_EXCEPTIONS)
+if(NOT MSVC)
+ # HACK: this should be enabled globally!
+ add_compile_flags_language("-std=c++11" "CXX")
+endif()
+
+remove_definitions(-D_WIN32_WINNT=0x502)
+add_definitions(-D_WIN32_WINNT=0x600)
+
+add_definitions(
+ -D_ATL_NO_EXCEPTIONS)
+
+include_directories(
+ ${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib
+ ${REACTOS_SOURCE_DIR}/sdk/lib/atl
+ ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/contrib)
+
+spec2def(zipfldr.dll zipfldr.spec ADD_IMPORTLIB)
+
+
+list(APPEND SOURCE
+ zipfldr.cpp
+ zippidl.cpp
+ zippidl.hpp
+ IZip.hpp
+ CExplorerCommand.cpp
+ CEnumZipContents.cpp
+ CFolderViewCB.cpp
+ CZipEnumerator.hpp
+ CZipExtract.cpp
+ CZipFolder.hpp
+ Debug.cpp
+ zipfldr.spec
+ precomp.h
+ resource.h)
+
+add_library(zipfldr SHARED
+ ${SOURCE}
+ ${ZLIB_SOURCE}
+ zipfldr.rc
+ ${CMAKE_CURRENT_BINARY_DIR}/zipfldr.def)
+
+
+set_module_type(zipfldr win32dll UNICODE)
+target_link_libraries(zipfldr minizip zlib atlnew uuid)
+add_importlibs(zipfldr oleaut32 ole32 shlwapi comctl32 shell32 user32 advapi32 msvcrt
kernel32 ntdll)
+add_pch(zipfldr precomp.h SOURCE)
+add_cd_file(TARGET zipfldr DESTINATION reactos/system32 FOR all)
+
diff --git a/dll/shellext/zipfldr/CZipEnumerator.hpp
b/dll/shellext/zipfldr/CZipEnumerator.hpp
new file mode 100644
index 0000000000..2c36aac2ea
--- /dev/null
+++ b/dll/shellext/zipfldr/CZipEnumerator.hpp
@@ -0,0 +1,94 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: CZipEnumerator
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+struct CZipEnumerator
+{
+private:
+ CComPtr<IZip> m_Zip;
+ bool m_First;
+ CAtlList<CStringA> m_Returned;
+public:
+ CZipEnumerator()
+ :m_First(true)
+ {
+ }
+
+ bool initialize(IZip* zip)
+ {
+ m_Zip = zip;
+ return reset();
+ }
+
+ bool reset()
+ {
+ unzFile uf = m_Zip->getZip();
+ m_First = true;
+ if (unzGoToFirstFile(uf) != UNZ_OK)
+ return false;
+ m_Returned.RemoveAll();
+ return true;
+ }
+
+ bool next_unique(const char* prefix, CStringA& name, bool& folder,
unz_file_info64& info)
+ {
+ size_t len = strlen(prefix);
+ CStringA tmp;
+ while (next(tmp, info))
+ {
+ if (!_strnicmp(tmp, prefix, len))
+ {
+ int pos = tmp.Find('/', len);
+ if (pos < 0)
+ {
+ name = tmp.Mid(len);
+ folder = false;
+ }
+ else
+ {
+ name = tmp.Mid(len, pos - len);
+ folder = true;
+ }
+ tmp = name.MakeLower();
+
+ POSITION it = m_Returned.Find(tmp);
+ if (!name.IsEmpty() && !it)
+ {
+ m_Returned.AddTail(tmp);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool next(CStringA& name, unz_file_info64& info)
+ {
+ int err;
+
+ unzFile uf = m_Zip->getZip();
+ if (!m_First)
+ {
+ err = unzGoToNextFile(uf);
+ if (err == UNZ_END_OF_LIST_OF_FILE)
+ {
+ return false;
+ }
+ }
+ m_First = false;
+
+ err = unzGetCurrentFileInfo64(uf, &info, NULL, 0, NULL, 0, NULL, 0);
+ if (err == UNZ_OK)
+ {
+ PSTR buf = name.GetBuffer(info.size_filename);
+ err = unzGetCurrentFileInfo64(uf, NULL, buf, name.GetAllocLength(), NULL, 0,
NULL, 0);
+ name.ReleaseBuffer(info.size_filename);
+ name.Replace('\\', '/');
+ }
+ return err == UNZ_OK;
+ }
+};
+
diff --git a/dll/shellext/zipfldr/CZipExtract.cpp b/dll/shellext/zipfldr/CZipExtract.cpp
new file mode 100644
index 0000000000..5fbebefbb7
--- /dev/null
+++ b/dll/shellext/zipfldr/CZipExtract.cpp
@@ -0,0 +1,481 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: Zip extraction
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+class CZipExtract :
+ public IZip
+{
+ CStringW m_Filename;
+ CStringW m_Directory;
+ unzFile uf;
+public:
+ CZipExtract(PCWSTR Filename)
+ :uf(NULL)
+ {
+ m_Filename = Filename;
+ m_Directory = m_Filename;
+ PWSTR Dir = m_Directory.GetBuffer();
+ PathRemoveExtensionW(Dir);
+ m_Directory.ReleaseBuffer();
+ }
+
+ ~CZipExtract()
+ {
+ if (uf)
+ {
+ DPRINT1("WARNING: uf not closed!\n");
+ Close();
+ }
+ }
+
+ void Close()
+ {
+ if (uf)
+ unzClose(uf);
+ uf = NULL;
+ }
+
+ // *** IZip methods ***
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
+ {
+ if (riid == IID_IUnknown)
+ {
+ *ppvObject = this;
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return 2;
+ }
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ return 1;
+ }
+ STDMETHODIMP_(unzFile) getZip()
+ {
+ return uf;
+ }
+
+ class CConfirmReplace : public CDialogImpl<CConfirmReplace>
+ {
+ private:
+ CStringA m_Filename;
+ public:
+ enum DialogResult
+ {
+ Yes,
+ YesToAll,
+ No,
+ Cancel
+ };
+
+ static DialogResult ShowDlg(HWND hDlg, PCSTR FullPath)
+ {
+ PCSTR Filename = PathFindFileNameA(FullPath);
+ CConfirmReplace confirm(Filename);
+ INT_PTR Result = confirm.DoModal(hDlg);
+ switch (Result)
+ {
+ case IDYES: return Yes;
+ case IDYESALL: return YesToAll;
+ default:
+ case IDNO: return No;
+ case IDCANCEL: return Cancel;
+ }
+ }
+
+ CConfirmReplace(const char* filename)
+ {
+ m_Filename = filename;
+ }
+
+ LRESULT OnInitDialog(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled)
+ {
+ CenterWindow(GetParent());
+
+ HICON hIcon = LoadIcon(NULL, IDI_EXCLAMATION);
+ SendDlgItemMessage(IDC_EXCLAMATION_ICON, STM_SETICON, (WPARAM)hIcon);
+
+ /* Our CString does not support FormatMessage yet */
+ CStringA message(MAKEINTRESOURCE(IDS_OVERWRITEFILE_TEXT));
+ CHeapPtr<CHAR, CLocalAllocator> formatted;
+
+ DWORD_PTR args[2] =
+ {
+ (DWORD_PTR)m_Filename.GetString(),
+ NULL
+ };
+
+ ::FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ message, 0, 0, (LPSTR)&formatted, 0, (va_list*)args);
+
+ ::SetDlgItemTextA(m_hWnd, IDC_MESSAGE, formatted);
+ return 0;
+ }
+
+ LRESULT OnButton(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
+ {
+ EndDialog(wID);
+ return 0;
+ }
+
+ public:
+ enum { IDD = IDD_CONFIRM_FILE_REPLACE };
+
+ BEGIN_MSG_MAP(CConfirmReplace)
+ MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
+ COMMAND_ID_HANDLER(IDYES, OnButton)
+ COMMAND_ID_HANDLER(IDYESALL, OnButton)
+ COMMAND_ID_HANDLER(IDNO, OnButton)
+ COMMAND_ID_HANDLER(IDCANCEL, OnButton)
+ END_MSG_MAP()
+ };
+
+
+ class CExtractSettingsPage : public CPropertyPageImpl<CExtractSettingsPage>
+ {
+ private:
+ CZipExtract* m_pExtract;
+
+ public:
+ CExtractSettingsPage(CZipExtract* extract)
+
:CPropertyPageImpl<CExtractSettingsPage>(MAKEINTRESOURCE(IDS_WIZ_TITLE))
+ ,m_pExtract(extract)
+ {
+ m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_WIZ_DEST_TITLE);
+ m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_WIZ_DEST_SUBTITLE);
+ m_psp.dwFlags |= PSP_USETITLE | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
+ }
+
+ int OnSetActive()
+ {
+ SetDlgItemTextW(IDC_DIRECTORY, m_pExtract->m_Directory);
+ ::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE); /* Not supported for now
*/
+ GetParent().CenterWindow(::GetDesktopWindow());
+ return 0;
+ }
+
+ int OnWizardNext()
+ {
+ ::EnableWindow(GetDlgItem(IDC_BROWSE), FALSE);
+ ::EnableWindow(GetDlgItem(IDC_DIRECTORY), FALSE);
+ ::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE);
+
+ if (!m_pExtract->Extract(m_hWnd, GetDlgItem(IDC_PROGRESS)))
+ {
+ /* Extraction failed, do not go to the next page */
+ SetWindowLongPtr(DWLP_MSGRESULT, -1);
+
+ ::EnableWindow(GetDlgItem(IDC_BROWSE), TRUE);
+ ::EnableWindow(GetDlgItem(IDC_DIRECTORY), TRUE);
+ ::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE); /* Not supported for
now */
+
+ return TRUE;
+ }
+ return 0;
+ }
+
+ struct browse_info
+ {
+ HWND hWnd;
+ LPCWSTR Directory;
+ };
+
+ static INT CALLBACK s_BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lp, LPARAM
pData)
+ {
+ if (uMsg == BFFM_INITIALIZED)
+ {
+ browse_info* info = (browse_info*)pData;
+ CWindow dlg(hWnd);
+ dlg.SendMessage(BFFM_SETSELECTION, TRUE, (LPARAM)info->Directory);
+ dlg.CenterWindow(info->hWnd);
+ }
+ return 0;
+ }
+
+ LRESULT OnBrowse(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
+ {
+ BROWSEINFOW bi = { m_hWnd };
+ WCHAR path[MAX_PATH];
+ bi.pszDisplayName = path;
+ bi.lpfn = s_BrowseCallbackProc;
+ bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS |
BIF_RETURNONLYFSDIRS;
+ CStringW title(MAKEINTRESOURCEW(IDS_WIZ_BROWSE_TITLE));
+ bi.lpszTitle = title;
+
+ browse_info info = { m_hWnd, m_pExtract->m_Directory.GetString() };
+ bi.lParam = (LPARAM)&info;
+
+ CComHeapPtr<ITEMIDLIST> pidl;
+ pidl.Attach(SHBrowseForFolderW(&bi));
+
+ WCHAR tmpPath[MAX_PATH];
+ if (pidl && SHGetPathFromIDListW(pidl, tmpPath))
+ {
+ m_pExtract->m_Directory = tmpPath;
+ SetDlgItemTextW(IDC_DIRECTORY, m_pExtract->m_Directory);
+ }
+ return 0;
+ }
+
+ LRESULT OnPassword(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
+ {
+ return 0;
+ }
+
+ public:
+ enum { IDD = IDD_PROPPAGEDESTIONATION };
+
+ BEGIN_MSG_MAP(CCompleteSettingsPage)
+ COMMAND_ID_HANDLER(IDC_BROWSE, OnBrowse)
+ COMMAND_ID_HANDLER(IDC_PASSWORD, OnPassword)
+ CHAIN_MSG_MAP(CPropertyPageImpl<CExtractSettingsPage>)
+ END_MSG_MAP()
+ };
+
+
+ class CCompleteSettingsPage : public CPropertyPageImpl<CCompleteSettingsPage>
+ {
+ private:
+ CZipExtract* m_pExtract;
+
+ public:
+ CCompleteSettingsPage(CZipExtract* extract)
+
:CPropertyPageImpl<CCompleteSettingsPage>(MAKEINTRESOURCE(IDS_WIZ_TITLE))
+ , m_pExtract(extract)
+ {
+ m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_WIZ_COMPL_TITLE);
+ m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_WIZ_COMPL_SUBTITLE);
+ m_psp.dwFlags |= PSP_USETITLE | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
+ }
+
+
+ int OnSetActive()
+ {
+ SetWizardButtons(PSWIZB_FINISH);
+ CStringW Path = m_pExtract->m_Directory;
+ PWSTR Ptr = Path.GetBuffer();
+ RECT rc;
+ ::GetWindowRect(GetDlgItem(IDC_DESTDIR), &rc);
+ HDC dc = GetDC();
+ PathCompactPathW(dc, Ptr, rc.right - rc.left);
+ ReleaseDC(dc);
+ Path.ReleaseBuffer();
+ SetDlgItemTextW(IDC_DESTDIR, Path);
+ CheckDlgButton(IDC_SHOW_EXTRACTED, BST_CHECKED);
+ return 0;
+ }
+ BOOL OnWizardFinish()
+ {
+ if (IsDlgButtonChecked(IDC_SHOW_EXTRACTED) == BST_CHECKED)
+ {
+ ShellExecuteW(NULL, L"explore", m_pExtract->m_Directory,
NULL, NULL, SW_SHOW);
+ }
+ return FALSE;
+ }
+
+ public:
+ enum { IDD = IDD_PROPPAGECOMPLETE };
+
+ BEGIN_MSG_MAP(CCompleteSettingsPage)
+ CHAIN_MSG_MAP(CPropertyPageImpl<CCompleteSettingsPage>)
+ END_MSG_MAP()
+ };
+
+
+ void runWizard()
+ {
+ PROPSHEETHEADERW psh = { sizeof(psh), 0 };
+ psh.dwFlags = PSH_WIZARD97 | PSH_HEADER;
+ psh.hInstance = _AtlBaseModule.GetResourceInstance();
+
+ CExtractSettingsPage extractPage(this);
+ CCompleteSettingsPage completePage(this);
+ HPROPSHEETPAGE hpsp[] =
+ {
+ extractPage.Create(),
+ completePage.Create()
+ };
+
+ psh.phpage = hpsp;
+ psh.nPages = _countof(hpsp);
+
+ PropertySheetW(&psh);
+ }
+
+ bool Extract(HWND hDlg, HWND hProgress)
+ {
+ unz_global_info64 gi;
+ uf = unzOpen2_64(m_Filename.GetString(), &g_FFunc);
+ int err = unzGetGlobalInfo64(uf, &gi);
+ if (err != UNZ_OK)
+ {
+ DPRINT1("ERROR, unzGetGlobalInfo64: 0x%x\n", err);
+ return false;
+ }
+
+ CZipEnumerator zipEnum;
+ if (!zipEnum.initialize(this))
+ {
+ DPRINT1("ERROR, zipEnum.initialize\n");
+ return false;
+ }
+
+ CWindow Progress(hProgress);
+ Progress.SendMessage(PBM_SETRANGE32, 0, gi.number_entry);
+ Progress.SendMessage(PBM_SETPOS, 0, 0);
+
+ BYTE Buffer[2048];
+ CStringA BaseDirectory = m_Directory;
+ CStringA Name;
+ unz_file_info64 Info;
+ int CurrentFile = 0;
+ bool bOverwriteAll = false;
+ while (zipEnum.next(Name, Info))
+ {
+ bool is_dir = Name.GetLength() > 0 && Name[Name.GetLength()-1] ==
'/';
+
+ char CombinedPath[MAX_PATH * 2] = { 0 };
+ PathCombineA(CombinedPath, BaseDirectory, Name);
+ CStringA FullPath = CombinedPath;
+ FullPath.Replace('/', '\\'); /* SHPathPrepareForWriteA
does not handle '/' */
+ DWORD dwFlags = SHPPFW_DIRCREATE | (is_dir ? SHPPFW_NONE :
SHPPFW_IGNOREFILENAME);
+ HRESULT hr = SHPathPrepareForWriteA(hDlg, NULL, FullPath, dwFlags);
+ if (FAILED_UNEXPECTEDLY(hr))
+ {
+ return false;
+ }
+ CurrentFile++;
+ if (is_dir)
+ continue;
+
+ const char* password = NULL;
+ /* FIXME: Process password, if required and not specified, prompt the user
*/
+ err = unzOpenCurrentFilePassword(uf, password);
+ if (err != UNZ_OK)
+ {
+ DPRINT1("ERROR, unzOpenCurrentFilePassword: 0x%x\n", err);
+ return false;
+ }
+
+ HANDLE hFile = CreateFileA(FullPath, GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_FILE_EXISTS)
+ {
+ bool bOverwrite = bOverwriteAll;
+ if (!bOverwriteAll)
+ {
+ CConfirmReplace::DialogResult Result =
CConfirmReplace::ShowDlg(hDlg, FullPath);
+ switch (Result)
+ {
+ case CConfirmReplace::YesToAll:
+ bOverwriteAll = true;
+ case CConfirmReplace::Yes:
+ bOverwrite = true;
+ break;
+ case CConfirmReplace::No:
+ break;
+ case CConfirmReplace::Cancel:
+ return false;
+ }
+ }
+
+ if (bOverwrite)
+ {
+ hFile = CreateFileA(FullPath, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ dwErr = GetLastError();
+ }
+ }
+ else
+ {
+ unzCloseCurrentFile(uf);
+ continue;
+ }
+ }
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ unzCloseCurrentFile(uf);
+ DPRINT1("ERROR, CreateFileA: 0x%x (%s)\n", dwErr,
bOverwriteAll ? "Y" : "N");
+ return false;
+ }
+ }
+
+ do
+ {
+ err = unzReadCurrentFile(uf, Buffer, sizeof(Buffer));
+
+ if (err < 0)
+ {
+ DPRINT1("ERROR, unzReadCurrentFile: 0x%x\n", err);
+ break;
+ }
+ else if (err > 0)
+ {
+ DWORD dwWritten;
+ if (!WriteFile(hFile, Buffer, err, &dwWritten, NULL))
+ {
+ DPRINT1("ERROR, WriteFile: 0x%x\n", GetLastError());
+ break;
+ }
+ if (dwWritten != (DWORD)err)
+ {
+ DPRINT1("ERROR, WriteFile: dwWritten:%d err:%d\n",
dwWritten, err);
+ break;
+ }
+ }
+
+ } while (err > 0);
+
+ /* Update Filetime */
+ FILETIME LastAccessTime;
+ GetFileTime(hFile, NULL, &LastAccessTime, NULL);
+ FILETIME LocalFileTime;
+ DosDateTimeToFileTime((WORD)(Info.dosDate >> 16), (WORD)Info.dosDate,
&LocalFileTime);
+ FILETIME FileTime;
+ LocalFileTimeToFileTime(&LocalFileTime, &FileTime);
+ SetFileTime(hFile, &FileTime, &LastAccessTime, &FileTime);
+
+ /* Done.. */
+ CloseHandle(hFile);
+
+ if (err)
+ {
+ unzCloseCurrentFile(uf);
+ DPRINT1("ERROR, unzReadCurrentFile2: 0x%x\n", err);
+ return false;
+ }
+ else
+ {
+ err = unzCloseCurrentFile(uf);
+ if (err != UNZ_OK)
+ {
+ DPRINT1("ERROR(non-fatal), unzCloseCurrentFile: 0x%x\n",
err);
+ }
+ }
+ Progress.SendMessage(PBM_SETPOS, CurrentFile, 0);
+ }
+
+ Close();
+ return true;
+ }
+};
+
+
+void _CZipExtract_runWizard(PCWSTR Filename)
+{
+ CZipExtract extractor(Filename);
+ extractor.runWizard();
+}
+
diff --git a/dll/shellext/zipfldr/CZipFolder.hpp b/dll/shellext/zipfldr/CZipFolder.hpp
new file mode 100644
index 0000000000..fa98673ff2
--- /dev/null
+++ b/dll/shellext/zipfldr/CZipFolder.hpp
@@ -0,0 +1,625 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: Main class
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+
+EXTERN_C HRESULT WINAPI SHCreateFileExtractIconW(LPCWSTR pszPath, DWORD dwFileAttributes,
REFIID riid, void **ppv);
+
+
+struct FolderViewColumns
+{
+ int iResource;
+ DWORD dwDefaultState;
+ int cxChar;
+ int fmt;
+};
+
+static FolderViewColumns g_ColumnDefs[] =
+{
+ { IDS_COL_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 25, LVCFMT_LEFT
},
+ { IDS_COL_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 20, LVCFMT_LEFT
},
+ { IDS_COL_COMPRSIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 10, LVCFMT_RIGHT
},
+ { IDS_COL_PASSWORD, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 10, LVCFMT_LEFT
},
+ { IDS_COL_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 10, LVCFMT_RIGHT
},
+ { IDS_COL_RATIO, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 10, LVCFMT_LEFT
},
+ { IDS_COL_DATE_MOD, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, 15, LVCFMT_LEFT
},
+};
+
+
+class CZipFolder :
+ public CComCoClass<CZipFolder, &CLSID_ZipFolderStorageHandler>,
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IShellFolder2,
+ //public IStorage,
+ public IContextMenu,
+ public IShellExtInit,
+ //public IPersistFile,
+ public IPersistFolder2,
+ public IZip
+{
+ CStringW m_ZipFile;
+ CStringA m_ZipDir;
+ CComHeapPtr<ITEMIDLIST> m_CurDir;
+ unzFile m_UnzipFile;
+
+public:
+ CZipFolder()
+ :m_UnzipFile(NULL)
+ {
+ }
+
+ ~CZipFolder()
+ {
+ Close();
+ }
+
+ void Close()
+ {
+ if (m_UnzipFile)
+ unzClose(m_UnzipFile);
+ m_UnzipFile = NULL;
+ }
+
+ // *** IZip methods ***
+ STDMETHODIMP_(unzFile) getZip()
+ {
+ if (!m_UnzipFile)
+ {
+ m_UnzipFile = unzOpen2_64(m_ZipFile, &g_FFunc);
+ }
+
+ return m_UnzipFile;
+ }
+
+ // *** IShellFolder2 methods ***
+ STDMETHODIMP GetDefaultSearchGUID(GUID *pguid)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
+ {
+ if (!pcsFlags || iColumn >= _countof(g_ColumnDefs))
+ return E_INVALIDARG;
+ *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState;
+ return S_OK;
+ }
+ STDMETHODIMP GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT
*pv)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ // Adapted from CFileDefExt::GetFileTimeString
+ BOOL _GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
+ {
+ SYSTEMTIME st;
+
+ if (!FileTimeToSystemTime(lpFileTime, &st))
+ return FALSE;
+
+ size_t cchRemaining = cchResult;
+ LPWSTR pwszEnd = pwszResult;
+ int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st,
NULL, pwszEnd, cchRemaining);
+ if (cchWritten)
+ --cchWritten; // GetDateFormatW returns count with terminating zero
+ else
+ return FALSE;
+ cchRemaining -= cchWritten;
+ pwszEnd += cchWritten;
+
+ StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd,
&cchRemaining, 0);
+
+ cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd,
cchRemaining);
+ if (cchWritten)
+ --cchWritten; // GetTimeFormatW returns count with terminating zero
+ else
+ return FALSE;
+
+ return TRUE;
+ }
+ STDMETHODIMP GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
+ {
+ if (iColumn >= _countof(g_ColumnDefs))
+ return E_FAIL;
+
+ psd->cxChar = g_ColumnDefs[iColumn].cxChar;
+ psd->fmt = g_ColumnDefs[iColumn].fmt;
+
+ if (pidl == NULL)
+ {
+ return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(),
g_ColumnDefs[iColumn].iResource);
+ }
+
+ PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
+ if (curpidl->mkid.cb != 0)
+ {
+ DPRINT1("ERROR, unhandled PIDL!\n");
+ return E_FAIL;
+ }
+
+ const ZipPidlEntry* zipEntry = _ZipFromIL(pidl);
+ if (!zipEntry)
+ return E_INVALIDARG;
+
+ WCHAR Buffer[100];
+ bool isDir = zipEntry->ZipType == ZIP_PIDL_DIRECTORY;
+ switch (iColumn)
+ {
+ case 0: /* Name, ReactOS specific? */
+ return GetDisplayNameOf(pidl, 0, &psd->str);
+ case 1: /* Type */
+ {
+ SHFILEINFOA shfi;
+ DWORD dwAttributes = isDir ? FILE_ATTRIBUTE_DIRECTORY :
FILE_ATTRIBUTE_NORMAL;
+ ULONG_PTR firet = SHGetFileInfoA(zipEntry->Name, dwAttributes, &shfi,
sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME);
+ if (!firet)
+ return E_FAIL;
+ return SHSetStrRet(&psd->str, shfi.szTypeName);
+ }
+ case 2: /* Compressed size */
+ case 4: /* Size */
+ {
+ ULONG64 Size = iColumn == 2 ? zipEntry->CompressedSize :
zipEntry->UncompressedSize;
+ if (!StrFormatByteSizeW(Size, Buffer, _countof(Buffer)))
+ return E_FAIL;
+ return SHSetStrRet(&psd->str, Buffer);
+ }
+ case 3: /* Password */
+ if (isDir)
+ return SHSetStrRet(&psd->str, L"");
+ return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(),
zipEntry->Password ? IDS_YES : IDS_NO);
+ case 5: /* Ratio */
+ {
+ int ratio = 0;
+ if (zipEntry->UncompressedSize && !isDir)
+ ratio = 100 -
(int)((zipEntry->CompressedSize*100)/zipEntry->UncompressedSize);
+ StringCchPrintfW(Buffer, _countof(Buffer), L"%d%%", ratio);
+ return SHSetStrRet(&psd->str, Buffer);
+ }
+ case 6: /* Date */
+ {
+ if (isDir)
+ return SHSetStrRet(&psd->str, L"");
+ FILETIME ftLocal;
+ DosDateTimeToFileTime((WORD)(zipEntry->DosDate>>16),
(WORD)zipEntry->DosDate, &ftLocal);
+ if (!_GetFileTimeString(&ftLocal, Buffer, _countof(Buffer)))
+ return E_FAIL;
+ return SHSetStrRet(&psd->str, Buffer);
+ }
+ }
+
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+
+ // *** IShellFolder methods ***
+ STDMETHODIMP ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
+ {
+ return _CEnumZipContents_CreateInstance(this, dwFlags, m_ZipDir,
IID_PPV_ARG(IEnumIDList, ppEnumIDList));
+ }
+ STDMETHODIMP BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid,
LPVOID *ppvOut)
+ {
+ if (riid == IID_IShellFolder)
+ {
+ CStringA newZipDir = m_ZipDir;
+ PCUIDLIST_RELATIVE curpidl = pidl;
+ while (curpidl->mkid.cb)
+ {
+ const ZipPidlEntry* zipEntry = _ZipFromIL(curpidl);
+ if (!zipEntry)
+ {
+ return E_FAIL;
+ }
+ newZipDir += zipEntry->Name;
+ newZipDir += '/';
+
+ curpidl = ILGetNext(curpidl);
+ }
+ return ShellObjectCreatorInit<CZipFolder>(m_ZipFile, newZipDir,
m_CurDir, pidl, riid, ppvOut);
+ }
+ DbgPrint("%s(%S) UNHANDLED\n", __FUNCTION__, guid2string(riid));
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid,
LPVOID *ppvOut)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE
pidl2)
+ {
+ const ZipPidlEntry* zipEntry1 = _ZipFromIL(pidl1);
+ const ZipPidlEntry* zipEntry2 = _ZipFromIL(pidl2);
+
+ if (!zipEntry1 || !zipEntry2)
+ return E_INVALIDARG;
+
+ int result = 0;
+ if (zipEntry1->ZipType != zipEntry2->ZipType)
+ result = zipEntry1->ZipType - zipEntry2->ZipType;
+ else
+ result = stricmp(zipEntry1->Name, zipEntry2->Name);
+
+ if (!result && zipEntry1->ZipType == ZIP_PIDL_DIRECTORY)
+ {
+ PCUIDLIST_RELATIVE child1 = ILGetNext(pidl1);
+ PCUIDLIST_RELATIVE child2 = ILGetNext(pidl2);
+
+ if (child1->mkid.cb && child2->mkid.cb)
+ return CompareIDs(lParam, child1, child2);
+ else if (child1->mkid.cb)
+ result = 1;
+ else if (child2->mkid.cb)
+ result = -1;
+ }
+
+ return MAKE_COMPARE_HRESULT(result);
+ }
+ STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
+ {
+ static const GUID UnknownIID = // {93F81976-6A0D-42C3-94DD-AA258A155470}
+ {0x93F81976, 0x6A0D, 0x42C3, {0x94, 0xDD, 0xAA, 0x25, 0x8A, 0x15, 0x54, 0x70}};
+ if (riid == IID_IShellView)
+ {
+ SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
+ CComPtr<IShellFolderViewCB> pcb;
+
+ HRESULT hr = _CFolderViewCB_CreateInstance(IID_PPV_ARG(IShellFolderViewCB,
&pcb));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ sfvparams.psfvcb = pcb;
+ hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
+
+ return hr;
+ }
+ if (riid == IID_IExplorerCommandProvider)
+ {
+ return _CExplorerCommandProvider_CreateInstance(this, riid, ppvOut);
+ }
+ if (UnknownIID != riid)
+ DbgPrint("%s(%S) UNHANDLED\n", __FUNCTION__, guid2string(riid));
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD
*rgfInOut)
+ {
+ if (!rgfInOut || !cidl || !apidl)
+ return E_INVALIDARG;
+
+ *rgfInOut = 0;
+
+ //static DWORD dwFileAttrs = SFGAO_STREAM | SFGAO_HASPROPSHEET | SFGAO_CANDELETE
| SFGAO_CANCOPY | SFGAO_CANMOVE;
+ //static DWORD dwFolderAttrs = SFGAO_FOLDER | SFGAO_DROPTARGET |
SFGAO_HASPROPSHEET | SFGAO_CANDELETE | SFGAO_STORAGE | SFGAO_CANCOPY | SFGAO_CANMOVE;
+ static DWORD dwFileAttrs = SFGAO_STREAM;
+ static DWORD dwFolderAttrs = SFGAO_FOLDER | SFGAO_HASSUBFOLDER |
SFGAO_BROWSABLE;
+
+
+ while (cidl > 0 && *apidl)
+ {
+ const ZipPidlEntry* zipEntry = _ZipFromIL(*apidl);
+
+ if (zipEntry)
+ {
+ if (zipEntry->ZipType == ZIP_PIDL_FILE)
+ *rgfInOut |= dwFileAttrs;
+ else
+ *rgfInOut |= dwFolderAttrs;
+ }
+ else
+ {
+ *rgfInOut = 0;
+ }
+
+ apidl++;
+ cidl--;
+ }
+
+ *rgfInOut &= ~SFGAO_VALIDATE;
+ return S_OK;
+ }
+ static HRESULT CALLBACK ZipFolderMenuCallback(IShellFolder *psf, HWND hwnd,
IDataObject *pdtobj,
+ UINT uMsg, WPARAM wParam, LPARAM
lParam)
+ {
+ switch (uMsg)
+ {
+ case DFM_MERGECONTEXTMENU:
+ DPRINT1("FIXME: Add menu items for DFM_MERGECONTEXTMENU\n");
+ return S_OK;
+ case DFM_INVOKECOMMAND:
+ case DFM_INVOKECOMMANDEX:
+ case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
+ return S_FALSE;
+ }
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
+ {
+ if ((riid == IID_IExtractIconA || riid == IID_IExtractIconW) && cidl ==
1)
+ {
+ const ZipPidlEntry* zipEntry = _ZipFromIL(*apidl);
+ if (zipEntry)
+ {
+ CComHeapPtr<WCHAR> pathW;
+
+ int len = MultiByteToWideChar(CP_ACP, 0, zipEntry->Name, -1, NULL,
0);
+ pathW.Allocate(len);
+ MultiByteToWideChar(CP_ACP, 0, zipEntry->Name, -1, pathW, len);
+
+ DWORD dwAttributes = (zipEntry->ZipType == ZIP_PIDL_DIRECTORY) ?
FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
+ return SHCreateFileExtractIconW(pathW, dwAttributes, riid, ppvOut);
+ }
+ }
+ else if (riid == IID_IContextMenu && cidl >= 0)
+ {
+ const ZipPidlEntry* zipEntry = _ZipFromIL(*apidl);
+ if (zipEntry)
+ {
+ HKEY keys[1] = {0};
+ int nkeys = 0;
+ if (zipEntry->ZipType == ZIP_PIDL_DIRECTORY)
+ {
+ LSTATUS res = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Folder",
0, KEY_READ | KEY_QUERY_VALUE, keys);
+ if (res != ERROR_SUCCESS)
+ return E_FAIL;
+ nkeys++;
+ }
+ return CDefFolderMenu_Create2(NULL, hwndOwner, cidl, apidl, this,
ZipFolderMenuCallback, nkeys, keys, (IContextMenu**)ppvOut);
+ }
+ }
+ else if (riid == IID_IDataObject && cidl >= 1)
+ {
+ return CIDLData_CreateFromIDArray(m_CurDir, cidl, apidl,
(IDataObject**)ppvOut);
+ }
+
+ DbgPrint("%s(%S) UNHANDLED\n", __FUNCTION__ , guid2string(riid));
+ return E_NOINTERFACE;
+ }
+ STDMETHODIMP GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
+ {
+ if (!pidl)
+ return S_FALSE;
+
+ PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
+ if (curpidl->mkid.cb != 0)
+ {
+ DPRINT1("ERROR, unhandled PIDL!\n");
+ return E_FAIL;
+ }
+
+ const ZipPidlEntry* zipEntry = _ZipFromIL(pidl);
+ if (!zipEntry)
+ return E_FAIL;
+
+ return SHSetStrRet(strRet, (LPCSTR)zipEntry->Name);
+ }
+ STDMETHODIMP SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD
dwFlags, PITEMID_CHILD *pPidlOut)
+ {
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+ }
+ //// IStorage
+ //STDMETHODIMP CreateStream(LPCOLESTR pwcsName, DWORD grfMode, DWORD reserved1, DWORD
reserved2, IStream **ppstm);
+ //STDMETHODIMP OpenStream(LPCOLESTR pwcsName, void *reserved1, DWORD grfMode, DWORD
reserved2, IStream **ppstm);
+ //STDMETHODIMP CreateStorage(LPCOLESTR pwcsName, DWORD grfMode, DWORD dwStgFmt, DWORD
reserved2, IStorage **ppstg);
+ //STDMETHODIMP OpenStorage(LPCOLESTR pwcsName, IStorage *pstgPriority, DWORD grfMode,
SNB snbExclude, DWORD reserved, IStorage **ppstg);
+ //STDMETHODIMP CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude,
IStorage *pstgDest);
+ //STDMETHODIMP MoveElementTo(LPCOLESTR pwcsName, IStorage *pstgDest, LPCOLESTR
pwcsNewName, DWORD grfFlags);
+ //STDMETHODIMP Commit(DWORD grfCommitFlags);
+ //STDMETHODIMP Revert();
+ //STDMETHODIMP EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3,
IEnumSTATSTG **ppenum);
+ //STDMETHODIMP DestroyElement(LPCOLESTR pwcsName);
+ //STDMETHODIMP RenameElement(LPCOLESTR pwcsOldName, LPCOLESTR pwcsNewName);
+ //STDMETHODIMP SetElementTimes(LPCOLESTR pwcsName, const FILETIME *pctime, const
FILETIME *patime, const FILETIME *pmtime);
+ //STDMETHODIMP SetClass(REFCLSID clsid);
+ //STDMETHODIMP SetStateBits(DWORD grfStateBits, DWORD grfMask);
+ //STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag);
+
+ // *** IContextMenu methods ***
+ STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved, LPSTR
pszName, UINT cchMax)
+ {
+ if (idCmd != 0)
+ return E_INVALIDARG;
+
+ switch (uFlags)
+ {
+ case GCS_VERBA:
+ return StringCchCopyA(pszName, cchMax, EXTRACT_VERBA);
+ case GCS_VERBW:
+ return StringCchCopyW((LPWSTR)pszName, cchMax, EXTRACT_VERBW);
+ case GCS_HELPTEXTA:
+ {
+ CStringA helpText(MAKEINTRESOURCEA(IDS_HELPTEXT));
+ return StringCchCopyA(pszName, cchMax, helpText);
+ }
+ case GCS_HELPTEXTW:
+ {
+ CStringW helpText(MAKEINTRESOURCEA(IDS_HELPTEXT));
+ return StringCchCopyW((LPWSTR)pszName, cchMax, helpText);
+ }
+ case GCS_VALIDATEA:
+ case GCS_VALIDATEW:
+ return S_OK;
+ }
+
+ return E_INVALIDARG;
+ }
+ STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici)
+ {
+ if (!pici || pici->cbSize != sizeof(*pici))
+ return E_INVALIDARG;
+
+ if (pici->lpVerb == MAKEINTRESOURCEA(0) || (HIWORD(pici->lpVerb) &&
!strcmp(pici->lpVerb, EXTRACT_VERBA)))
+ {
+ BSTR ZipFile = m_ZipFile.AllocSysString();
+ InterlockedIncrement(&g_ModuleRefCnt);
+
+ DWORD tid;
+ HANDLE hThread = CreateThread(NULL, 0, s_ExtractProc, ZipFile, NULL,
&tid);
+ if (hThread)
+ {
+ CloseHandle(hThread);
+ return S_OK;
+ }
+ }
+ return E_INVALIDARG;
+ }
+ STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT
idCmdLast, UINT uFlags)
+ {
+ int Entries = 0;
+
+ CStringW menuText(MAKEINTRESOURCEW(IDS_MENUITEM));
+
+ InsertMenuW(hmenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
+ Entries++;
+ InsertMenuW(hmenu, indexMenu++, MF_BYPOSITION | MF_STRING, idCmdFirst++,
menuText);
+ Entries++;
+
+ return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Entries);
+ }
+
+ // *** IShellExtInit methods ***
+ STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY
hkeyProgID)
+ {
+ FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stg;
+
+ HRESULT hr = pDataObj->GetData(&etc, &stg);
+ if (FAILED_UNEXPECTEDLY(hr))
+ {
+ return hr;
+ }
+ hr = E_FAIL;
+ HDROP hdrop = (HDROP)GlobalLock(stg.hGlobal);
+ if (hdrop)
+ {
+ UINT uNumFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
+ if (uNumFiles == 1)
+ {
+ WCHAR szFile[MAX_PATH * 2];
+ if (DragQueryFileW(hdrop, 0, szFile, _countof(szFile)))
+ {
+ CComHeapPtr<ITEMIDLIST> pidl;
+ hr = SHParseDisplayName(szFile, NULL, &pidl, 0, NULL);
+ if (!FAILED_UNEXPECTEDLY(hr))
+ {
+ hr = Initialize(pidl);
+ }
+ }
+ else
+ {
+ DbgPrint("Failed to query the file.\r\n");
+ }
+ }
+ else
+ {
+ DbgPrint("Invalid number of files: %d\r\n", uNumFiles);
+ }
+ GlobalUnlock(stg.hGlobal);
+ }
+ else
+ {
+ DbgPrint("Could not lock stg.hGlobal\r\n");
+ }
+ ReleaseStgMedium(&stg);
+ return hr;
+
+ }
+
+ //// IPersistFile
+ ////STDMETHODIMP GetClassID(CLSID *pclsid);
+ //STDMETHODIMP IsDirty();
+ //STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode);
+ //STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember);
+ //STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName);
+ //STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName);
+
+ //// *** IPersistFolder2 methods ***
+ STDMETHODIMP GetCurFolder(LPITEMIDLIST * pidl)
+ {
+ *pidl = ILClone(m_CurDir);
+ return S_OK;
+ }
+
+ // *** IPersistFolder methods ***
+ STDMETHODIMP Initialize(LPCITEMIDLIST pidl)
+ {
+ WCHAR tmpPath[MAX_PATH];
+
+ if (SHGetPathFromIDListW(pidl, tmpPath))
+ {
+ m_ZipFile = tmpPath;
+ m_CurDir.Attach(ILClone(pidl));
+ return S_OK;
+ }
+ DbgPrint("%s() => Unable to parse pidl\n", __FUNCTION__);
+ return E_INVALIDARG;
+ }
+
+ // *** IPersist methods ***
+ STDMETHODIMP GetClassID(CLSID *lpClassId)
+ {
+ DbgPrint("%s\n", __FUNCTION__);
+ return E_NOTIMPL;
+ }
+
+
+ STDMETHODIMP Initialize(PCWSTR zipFile, PCSTR zipDir, PCUIDLIST_ABSOLUTE curDir,
PCUIDLIST_RELATIVE pidl)
+ {
+ m_ZipFile = zipFile;
+ m_ZipDir = zipDir;
+
+ m_CurDir.Attach(ILCombine(curDir, pidl));
+ return S_OK;
+ }
+ static DWORD WINAPI s_ExtractProc(LPVOID arg)
+ {
+ CComBSTR ZipFile;
+ ZipFile.Attach((BSTR)arg);
+
+ _CZipExtract_runWizard(ZipFile);
+
+ InterlockedDecrement(&g_ModuleRefCnt);
+ return 0;
+ }
+
+public:
+ DECLARE_NO_REGISTRY() // Handled manually because this object is exposed via
multiple clsid's
+ DECLARE_NOT_AGGREGATABLE(CZipFolder)
+
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(CZipFolder)
+ COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
+ COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
+// COM_INTERFACE_ENTRY_IID(IID_IStorage, IStorage)
+ COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
+ COM_INTERFACE_ENTRY_IID(IID_IShellExtInit, IShellExtInit)
+ //COM_INTERFACE_ENTRY_IID(IID_IPersistFile, IPersistFile)
+ COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
+ COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
+ COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
+ END_COM_MAP()
+};
+
diff --git a/dll/shellext/zipfldr/Debug.cpp b/dll/shellext/zipfldr/Debug.cpp
new file mode 100644
index 0000000000..35580771db
--- /dev/null
+++ b/dll/shellext/zipfldr/Debug.cpp
@@ -0,0 +1,44 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: Zip extraction
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+static bool GetInterfaceName(const WCHAR* InterfaceString, WCHAR* buf, size_t size)
+{
+ WCHAR LocalBuf[100];
+ DWORD dwType = 0, dwDataSize = size * sizeof(WCHAR);
+
+ if (!SUCCEEDED(StringCchPrintfW(LocalBuf, _countof(LocalBuf),
L"Interface\\%s", InterfaceString)))
+ return false;
+
+ return SHRegGetValueW(HKEY_CLASSES_ROOT, LocalBuf, NULL, RRF_RT_REG_SZ, &dwType,
buf, &dwDataSize) == ERROR_SUCCESS;
+}
+
+WCHAR* guid2string(REFCLSID iid)
+{
+ static WCHAR buf[2][300];
+ static int idx = 0;
+
+ idx ^= 1;
+
+ LPOLESTR tmp;
+ HRESULT hr = ProgIDFromCLSID(iid, &tmp);
+ if (SUCCEEDED(hr))
+ {
+ wcscpy(buf[idx], tmp);
+ CoTaskMemFree(tmp);
+ return buf[idx];
+ }
+ StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
+ if (GetInterfaceName(buf[idx], buf[idx], _countof(buf[idx])))
+ {
+ return buf[idx];
+ }
+ StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
+
+ return buf[idx];
+}
diff --git a/dll/shellext/zipfldr/IZip.hpp b/dll/shellext/zipfldr/IZip.hpp
new file mode 100644
index 0000000000..bb16e50fb4
--- /dev/null
+++ b/dll/shellext/zipfldr/IZip.hpp
@@ -0,0 +1,12 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: IZip
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+struct IZip : public IUnknown
+{
+ virtual STDMETHODIMP_(unzFile) getZip() PURE;
+};
+
diff --git a/dll/shellext/zipfldr/precomp.h b/dll/shellext/zipfldr/precomp.h
new file mode 100644
index 0000000000..01b92991d5
--- /dev/null
+++ b/dll/shellext/zipfldr/precomp.h
@@ -0,0 +1,62 @@
+#ifndef ZIPFLDR_PRECOMP_H
+#define ZIPFLDR_PRECOMP_H
+
+#define COBJMACROS
+#define COM_NO_WINDOWS_H
+#define NTOS_MODE_USER
+
+#include <windef.h>
+#include <winbase.h>
+#include <shlobj.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlcoll.h>
+#include <atlstr.h>
+#include <rosdlgs.h>
+#include <shlwapi.h>
+#include <shellapi.h>
+#include <strsafe.h>
+// debug.h needs this:
+#define NTSTATUS LONG
+#include <reactos/debug.h>
+#include <shellutils.h>
+
+
+
+#define EXTRACT_VERBA "extract"
+#define EXTRACT_VERBW L"extract"
+
+EXTERN_C const GUID CLSID_ZipFolderStorageHandler;
+EXTERN_C const GUID CLSID_ZipFolderSendTo;
+EXTERN_C const GUID CLSID_ZipFolderContextMenu;
+EXTERN_C const GUID CLSID_ZipFolderRightDragHandler;
+EXTERN_C const GUID CLSID_ZipFolderDropHandler;
+
+EXTERN_C const GUID CLSID_ZipFolderExtractAllCommand;
+
+extern LONG g_ModuleRefCnt;
+
+
+#define Win32DbgPrint(file, line, warn, func) DbgPrint("(%s:%d) " warn, file,
line, func)
+WCHAR* guid2string(REFCLSID iid);
+
+
+#include "minizip/unzip.h"
+#include "minizip/ioapi.h"
+
+extern zlib_filefunc64_def g_FFunc;
+
+#include "resource.h"
+
+#include "zippidl.hpp"
+#include "IZip.hpp"
+
+HRESULT _CEnumZipContents_CreateInstance(IZip* zip, DWORD flags, const char* prefix,
REFIID riid, LPVOID * ppvOut);
+HRESULT _CExplorerCommandProvider_CreateInstance(IContextMenu* zipObject, REFIID riid,
LPVOID * ppvOut);
+HRESULT _CFolderViewCB_CreateInstance(REFIID riid, LPVOID * ppvOut);
+void _CZipExtract_runWizard(PCWSTR Filename);
+
+#include "CZipEnumerator.hpp"
+#include "CZipFolder.hpp"
+
+#endif /* ZIPFLDR_PRECOMP_H */
diff --git a/dll/shellext/zipfldr/res/zipfldr.ico b/dll/shellext/zipfldr/res/zipfldr.ico
new file mode 100644
index 0000000000..04f98dcb2f
Binary files /dev/null and b/dll/shellext/zipfldr/res/zipfldr.ico differ
diff --git a/dll/shellext/zipfldr/res/zipfldr.rgs b/dll/shellext/zipfldr/res/zipfldr.rgs
new file mode 100644
index 0000000000..82cfc8dfae
--- /dev/null
+++ b/dll/shellext/zipfldr/res/zipfldr.rgs
@@ -0,0 +1,54 @@
+HKCR
+{
+ NoRemove CLSID
+ {
+ '{E88DCCE0-B7B3-11d1-A9F0-00AA0060FA31}' = s 'CompressedFolder'
+ {
+ InprocServer32 = s '%MODULE%' { val ThreadingModel = s
'Apartment' }
+ ProgId = s 'CompressedFolder'
+ ShellFolder
+ {
+ val Attributes = d '0x200001a0'
+ val UseDropHandler = s ''
+ }
+ }
+ '{b8cdcb65-b1bf-4b42-9428-1dfdb7ee92af}' = s 'Compressed (zipped)
Folder Menu'
+ {
+ InprocServer32 = s '%MODULE%' { val ThreadingModel = s
'Apartment' }
+ }
+ }
+ NoRemove Applications
+ {
+ 'zipfldr.dll' { val NoOpenWith = s '' }
+ }
+ NoRemove CompressedFolder
+ {
+ FriendlyTypeName = s '%MODULE%,-10195'
+ CLSID = s '{E88DCCE0-B7B3-11d1-A9F0-00AA0060FA31}'
+ DefaultIcon = s '%MODULE%'
+
+ NoRemove Shell
+ {
+ NoRemove Open
+ {
+ command = s 'rundll32.exe zipfldr.dll,RouteTheCall %%L'
+ val BrowserFlags = d '0x10'
+ val ExplorerFlags = d '0x10'
+ }
+ }
+
+ NoRemove shellex
+ {
+ NoRemove ContextMenuHandlers
+ {
+ ForceRemove '{b8cdcb65-b1bf-4b42-9428-1dfdb7ee92af}' = s
'Compressed (zipped) Folder Menu'
+ {
+ }
+ }
+ }
+ }
+ NoRemove '.zip' = s 'CompressedFolder'
+ {
+ val 'Content Type' = s 'application/x-zip-compressed'
+ }
+}
diff --git a/dll/shellext/zipfldr/resource.h b/dll/shellext/zipfldr/resource.h
new file mode 100644
index 0000000000..10cadc742a
--- /dev/null
+++ b/dll/shellext/zipfldr/resource.h
@@ -0,0 +1,58 @@
+#pragma once
+
+/* registry stuff */
+#define IDR_ZIPFLDR 8000
+
+
+/* Dialogs */
+
+#define IDD_PROPPAGEDESTIONATION 1000
+#define IDC_DIRECTORY 1001
+#define IDC_BROWSE 1002
+#define IDC_PASSWORD 1003
+#define IDC_PROGRESS 1004
+
+#define IDD_PROPPAGECOMPLETE 1100
+#define IDC_DESTDIR 1101
+#define IDC_SHOW_EXTRACTED 1102
+
+#define IDD_CONFIRM_FILE_REPLACE 1200
+#define IDYESALL 1202
+#define IDC_EXCLAMATION_ICON 1205
+#define IDC_MESSAGE 1206
+
+
+/* Strings */
+#define IDS_COL_NAME 100
+#define IDS_COL_TYPE 101
+#define IDS_COL_COMPRSIZE 102
+#define IDS_COL_PASSWORD 103
+#define IDS_COL_SIZE 104
+#define IDS_COL_RATIO 105
+#define IDS_COL_DATE_MOD 106
+#define IDS_YES 107
+#define IDS_NO 108
+
+
+/* Wizard titles */
+#define IDS_WIZ_TITLE 8000
+#define IDS_WIZ_DEST_TITLE 8001
+#define IDS_WIZ_DEST_SUBTITLE 8002
+#define IDS_WIZ_COMPL_TITLE 8003
+#define IDS_WIZ_COMPL_SUBTITLE 8004
+
+#define IDS_WIZ_BROWSE_TITLE 8010
+
+/* Questions */
+#define IDS_OVERWRITEFILE_TEXT 9000
+
+
+/* Context menu / ExplorerCommand strings */
+#define IDS_MENUITEM 10039
+#define IDS_HELPTEXT 10041
+#define IDS_FRIENDLYNAME 10195
+
+
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
diff --git a/dll/shellext/zipfldr/zipfldr.cpp b/dll/shellext/zipfldr/zipfldr.cpp
new file mode 100644
index 0000000000..27a354e0a1
--- /dev/null
+++ b/dll/shellext/zipfldr/zipfldr.cpp
@@ -0,0 +1,117 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: zipfldr entrypoint
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+HMODULE g_hModule = NULL;
+LONG g_ModuleRefCnt = 0;
+
+#include <initguid.h>
+
+DEFINE_GUID(CLSID_ZipFolderStorageHandler, 0xe88dcce0, 0xb7b3, 0x11d1, 0xa9, 0xf0, 0x00,
0xaa, 0x00, 0x60, 0xfa, 0x31);
+DEFINE_GUID(CLSID_ZipFolderSendTo, 0x888dca60, 0xfc0a, 0x11cf, 0x8f, 0x0f, 0x00,
0xc0, 0x4f, 0xd7, 0xd0, 0x62);
+DEFINE_GUID(CLSID_ZipFolderContextMenu, 0xb8cdcb65, 0xb1bf, 0x4b42, 0x94, 0x28, 0x1d,
0xfd, 0xb7, 0xee, 0x92, 0xaf);
+DEFINE_GUID(CLSID_ZipFolderRightDragHandler,0xbd472f60, 0x27fa, 0x11cf, 0xb8, 0xb4, 0x44,
0x45, 0x53, 0x54, 0x00, 0x00);
+DEFINE_GUID(CLSID_ZipFolderDropHandler, 0xed9d80b9, 0xd157, 0x457b, 0x91, 0x92, 0x0e,
0x72, 0x80, 0x31, 0x3b, 0xf0);
+
+/* IExplorerCommand: Extract All */
+DEFINE_GUID(CLSID_ZipFolderExtractAllCommand, 0xc3d9647b, 0x8fd9, 0x4ee6, 0x8b, 0xc7,
0x82, 0x7, 0x80, 0x9, 0x10, 0x5a);
+
+
+class CZipFldrModule : public CComModule
+{
+public:
+};
+
+
+BEGIN_OBJECT_MAP(ObjectMap)
+ OBJECT_ENTRY(CLSID_ZipFolderStorageHandler, CZipFolder)
+ OBJECT_ENTRY(CLSID_ZipFolderContextMenu, CZipFolder)
+END_OBJECT_MAP()
+
+CZipFldrModule gModule;
+
+
+#include "minizip/ioapi.h"
+#include "minizip/iowin32.h"
+
+zlib_filefunc64_def g_FFunc;
+
+static void init_zlib()
+{
+ fill_win32_filefunc64W(&g_FFunc);
+}
+
+EXTERN_C
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hInstance);
+ g_hModule = hInstance;
+ gModule.Init(ObjectMap, hInstance, NULL);
+ init_zlib();
+ break;
+ }
+
+ return TRUE;
+}
+
+STDAPI DllCanUnloadNow()
+{
+ if (g_ModuleRefCnt)
+ return S_FALSE;
+ return gModule.DllCanUnloadNow();
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ return gModule.DllGetClassObject(rclsid, riid, ppv);
+}
+
+STDAPI DllRegisterServer()
+{
+ HRESULT hr;
+
+ hr = gModule.DllRegisterServer(FALSE);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ hr = gModule.UpdateRegistryFromResource(IDR_ZIPFLDR, TRUE, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ return S_OK;
+}
+
+STDAPI DllUnregisterServer()
+{
+ HRESULT hr;
+
+ hr = gModule.DllUnregisterServer(FALSE);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ hr = gModule.UpdateRegistryFromResource(IDR_ZIPFLDR, FALSE, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ return S_OK;
+}
+
+EXTERN_C
+BOOL WINAPI
+RouteTheCall(
+ IN HWND hWndOwner,
+ IN HINSTANCE hInstance,
+ IN LPWSTR lpNamedPipeName,
+ IN INT Show)
+{
+ UNIMPLEMENTED;
+ return FALSE;
+}
diff --git a/dll/shellext/zipfldr/zipfldr.rc b/dll/shellext/zipfldr/zipfldr.rc
new file mode 100644
index 0000000000..0b577b5f3e
--- /dev/null
+++ b/dll/shellext/zipfldr/zipfldr.rc
@@ -0,0 +1,88 @@
+#include <windef.h>
+#include <winuser.h>
+
+#include "resource.h"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+1 ICON "res/zipfldr.ico"
+
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Zip Shell Extension"
+#define REACTOS_STR_INTERNAL_NAME "zipfldr"
+#define REACTOS_STR_ORIGINAL_FILENAME "zipfldr.dll"
+#include <reactos/version.rc>
+
+#include <reactos/manifest_dll.rc>
+
+IDR_ZIPFLDR REGISTRY "res/zipfldr.rgs"
+
+
+
+IDD_PROPPAGEDESTIONATION DIALOGEX 0, 0, 235, 156
+STYLE DS_SHELLFONT | WS_CHILD | WS_DISABLED | WS_CAPTION
+CAPTION "Select a Destination"
+FONT 8, "MS Shell Dlg", 400, 0, 0x0
+BEGIN
+ LTEXT "Select the destionation directory",IDC_STATIC,6,12,174,8
+ EDITTEXT IDC_DIRECTORY,6,24,222,12,ES_AUTOHSCROLL
+ PUSHBUTTON "Browse...",IDC_BROWSE,174,42,54,14
+ PUSHBUTTON "Password",IDC_PASSWORD,174,66,54,14
+ LTEXT "Extracting...",IDC_STATIC,6,114,42,8
+ CONTROL
"",IDC_PROGRESS,"msctls_progress32",WS_BORDER,6,126,222,6
+END
+
+
+IDD_PROPPAGECOMPLETE DIALOGEX 0, 0, 235, 156
+STYLE DS_SHELLFONT | WS_CHILD | WS_DISABLED | WS_CAPTION
+CAPTION "Extraction Complete"
+FONT 8, "MS Shell Dlg", 400, 0, 0x0
+BEGIN
+ LTEXT "The files have been extracted to the following
directory:",IDC_STATIC,6,12,222,18
+ LTEXT "Target dir",IDC_DESTDIR,6,36,222,8
+ CONTROL "Show extracted
files",IDC_SHOW_EXTRACTED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,66,81,10
+ LTEXT "Press finish to continue.",IDC_STATIC,6,84,174,8
+END
+
+
+IDD_CONFIRM_FILE_REPLACE DIALOGEX 0, 0, 273, 56
+STYLE DS_MODALFRAME | DS_SHELLFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Confirm File Replace"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "&Yes",IDYES,6,36,62,14
+ PUSHBUTTON "Cancel",IDCANCEL,204,36,62,14
+ PUSHBUTTON "Yes &To All",IDYESALL,72,36,62,14
+ PUSHBUTTON "&No",IDNO,138,36,62,14
+ ICON "",IDC_EXCLAMATION_ICON,6,6,24,22
+ LTEXT "",IDC_MESSAGE,36,6,228,24
+END
+
+
+
+STRINGTABLE
+BEGIN
+ IDS_COL_NAME "Name"
+ IDS_COL_TYPE "Type"
+ IDS_COL_COMPRSIZE "Compressed size"
+ IDS_COL_PASSWORD "Password"
+ IDS_COL_SIZE "Size"
+ IDS_COL_RATIO "Ratio"
+ IDS_COL_DATE_MOD "Date modified"
+ IDS_YES "Yes"
+ IDS_NO "No"
+
+ IDS_WIZ_TITLE "Extraction Wizard"
+ IDS_WIZ_DEST_TITLE "Select a Destination"
+ IDS_WIZ_DEST_SUBTITLE "The files from the zip archive will be extracted to the
location specified."
+ IDS_WIZ_COMPL_TITLE "Extraction Complete"
+ IDS_WIZ_COMPL_SUBTITLE "The files from the zip archive have been
extracted."
+ IDS_WIZ_BROWSE_TITLE "Select the place where you want to extract the selected
items."
+
+ IDS_OVERWRITEFILE_TEXT "This folder already contains a file called
'%1'.\nDo you want to replace it?"
+
+ IDS_MENUITEM "Extract &All..."
+ IDS_HELPTEXT "Extracts folder contents"
+ IDS_FRIENDLYNAME "Compressed (zipped) Folder"
+
+END
diff --git a/dll/shellext/zipfldr/zipfldr.spec b/dll/shellext/zipfldr/zipfldr.spec
new file mode 100644
index 0000000000..8b689b5e12
--- /dev/null
+++ b/dll/shellext/zipfldr/zipfldr.spec
@@ -0,0 +1,5 @@
+@ stdcall -private DllCanUnloadNow()
+@ stdcall -private DllGetClassObject(ptr ptr ptr)
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
+@ stdcall RouteTheCall(ptr ptr wstr long)
\ No newline at end of file
diff --git a/dll/shellext/zipfldr/zippidl.cpp b/dll/shellext/zipfldr/zippidl.cpp
new file mode 100644
index 0000000000..1e1eb9f57f
--- /dev/null
+++ b/dll/shellext/zipfldr/zippidl.cpp
@@ -0,0 +1,42 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: zip pidl handling
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+#include "precomp.h"
+
+LPITEMIDLIST _ILCreate(ZipPidlType Type, LPCSTR lpString, unz_file_info64& info)
+{
+ int cbData = sizeof(ZipPidlEntry) + strlen(lpString);
+ ZipPidlEntry* pidl = (ZipPidlEntry*)SHAlloc(cbData + sizeof(WORD));
+ if (!pidl)
+ return NULL;
+
+ pidl->cb = cbData;
+ pidl->MagicType = 'z';
+ pidl->ZipType = Type;
+
+ if (Type != ZIP_PIDL_DIRECTORY)
+ {
+ pidl->CompressedSize = info.compressed_size;
+ pidl->UncompressedSize = info.uncompressed_size;
+ pidl->DosDate = info.dosDate;
+ pidl->Password = info.flag & 1;
+ }
+
+ strcpy(pidl->Name, lpString);
+ *(WORD*)((char*)pidl + cbData) = 0;
+
+ return (LPITEMIDLIST)pidl;
+}
+
+
+const ZipPidlEntry* _ZipFromIL(LPCITEMIDLIST pidl)
+{
+ const ZipPidlEntry* zipPidl = (const ZipPidlEntry*)pidl;
+ if (zipPidl->MagicType == 'z')
+ return zipPidl;
+ return NULL;
+}
diff --git a/dll/shellext/zipfldr/zippidl.hpp b/dll/shellext/zipfldr/zippidl.hpp
new file mode 100644
index 0000000000..8c81158525
--- /dev/null
+++ b/dll/shellext/zipfldr/zippidl.hpp
@@ -0,0 +1,34 @@
+/*
+ * PROJECT: ReactOS Zip Shell Extension
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: zip pidl handling
+ * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+
+enum ZipPidlType
+{
+ ZIP_PIDL_DIRECTORY,
+ ZIP_PIDL_FILE
+};
+
+#include <pshpack1.h>
+struct ZipPidlEntry
+{
+ WORD cb;
+ BYTE MagicType;
+ ZipPidlType ZipType;
+
+ ULONG64 CompressedSize;
+ ULONG64 UncompressedSize;
+ ULONG DosDate;
+ BYTE Password;
+
+ char Name[1];
+};
+#include <poppack.h>
+
+
+LPITEMIDLIST _ILCreate(ZipPidlType Type, LPCSTR lpString, unz_file_info64& info);
+const ZipPidlEntry* _ZipFromIL(LPCITEMIDLIST pidl);
+