[ZIPFLDR] Initial implementation.
authorMark Jansen <mark.jansen@reactos.org>
Fri, 29 Dec 2017 22:45:02 +0000 (23:45 +0100)
committerMark Jansen <mark.jansen@reactos.org>
Sat, 7 Apr 2018 13:29:59 +0000 (15:29 +0200)
Icon by Jared Smudde
CORE-7684

19 files changed:
dll/shellext/CMakeLists.txt
dll/shellext/zipfldr/CEnumZipContents.cpp [new file with mode: 0644]
dll/shellext/zipfldr/CExplorerCommand.cpp [new file with mode: 0644]
dll/shellext/zipfldr/CFolderViewCB.cpp [new file with mode: 0644]
dll/shellext/zipfldr/CMakeLists.txt [new file with mode: 0644]
dll/shellext/zipfldr/CZipEnumerator.hpp [new file with mode: 0644]
dll/shellext/zipfldr/CZipExtract.cpp [new file with mode: 0644]
dll/shellext/zipfldr/CZipFolder.hpp [new file with mode: 0644]
dll/shellext/zipfldr/Debug.cpp [new file with mode: 0644]
dll/shellext/zipfldr/IZip.hpp [new file with mode: 0644]
dll/shellext/zipfldr/precomp.h [new file with mode: 0644]
dll/shellext/zipfldr/res/zipfldr.ico [new file with mode: 0644]
dll/shellext/zipfldr/res/zipfldr.rgs [new file with mode: 0644]
dll/shellext/zipfldr/resource.h [new file with mode: 0644]
dll/shellext/zipfldr/zipfldr.cpp [new file with mode: 0644]
dll/shellext/zipfldr/zipfldr.rc [new file with mode: 0644]
dll/shellext/zipfldr/zipfldr.spec [new file with mode: 0644]
dll/shellext/zipfldr/zippidl.cpp [new file with mode: 0644]
dll/shellext/zipfldr/zippidl.hpp [new file with mode: 0644]

index 56fabe1..e11df36 100644 (file)
@@ -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 (file)
index 0000000..f135fe7
--- /dev/null
@@ -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@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 (file)
index 0000000..1e634f8
--- /dev/null
@@ -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@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 (file)
index 0000000..2a46f65
--- /dev/null
@@ -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@gmail.com)
+ *              Copyright 2017 Mark Jansen (mark.jansen@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 (file)
index 0000000..d491f1d
--- /dev/null
@@ -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 (file)
index 0000000..2c36aac
--- /dev/null
@@ -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@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 (file)
index 0000000..5fbebef
--- /dev/null
@@ -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@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 (file)
index 0000000..fa98673
--- /dev/null
@@ -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@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 (file)
index 0000000..3558077
--- /dev/null
@@ -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@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 (file)
index 0000000..bb16e50
--- /dev/null
@@ -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@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 (file)
index 0000000..01b9299
--- /dev/null
@@ -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 (file)
index 0000000..04f98dc
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 (file)
index 0000000..82cfc8d
--- /dev/null
@@ -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 (file)
index 0000000..10cadc7
--- /dev/null
@@ -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 (file)
index 0000000..27a354e
--- /dev/null
@@ -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@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 (file)
index 0000000..0b577b5
--- /dev/null
@@ -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 (file)
index 0000000..8b689b5
--- /dev/null
@@ -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 (file)
index 0000000..1e1eb9f
--- /dev/null
@@ -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@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 (file)
index 0000000..8c81158
--- /dev/null
@@ -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@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);
+