From c7e6a9d04b08131fd1bc6b5ed70d1b9e4bf8140a Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Fri, 29 Dec 2017 23:45:02 +0100 Subject: [PATCH] [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(+) create mode 100644 dll/shellext/zipfldr/CEnumZipContents.cpp create mode 100644 dll/shellext/zipfldr/CExplorerCommand.cpp create mode 100644 dll/shellext/zipfldr/CFolderViewCB.cpp create mode 100644 dll/shellext/zipfldr/CMakeLists.txt create mode 100644 dll/shellext/zipfldr/CZipEnumerator.hpp create mode 100644 dll/shellext/zipfldr/CZipExtract.cpp create mode 100644 dll/shellext/zipfldr/CZipFolder.hpp create mode 100644 dll/shellext/zipfldr/Debug.cpp create mode 100644 dll/shellext/zipfldr/IZip.hpp create mode 100644 dll/shellext/zipfldr/precomp.h create mode 100644 dll/shellext/zipfldr/res/zipfldr.ico create mode 100644 dll/shellext/zipfldr/res/zipfldr.rgs create mode 100644 dll/shellext/zipfldr/resource.h create mode 100644 dll/shellext/zipfldr/zipfldr.cpp create mode 100644 dll/shellext/zipfldr/zipfldr.rc create mode 100644 dll/shellext/zipfldr/zipfldr.spec create mode 100644 dll/shellext/zipfldr/zippidl.cpp create mode 100644 dll/shellext/zipfldr/zippidl.hpp diff --git a/dll/shellext/CMakeLists.txt b/dll/shellext/CMakeLists.txt index 56fabe12999..e11df360013 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 00000000000..f135fe756fa --- /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@reactos.org) + */ + +#include "precomp.h" + +class CEnumZipContents : + public CComObjectRootEx, + 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(zip, flags, prefix, riid, ppvOut); +} + diff --git a/dll/shellext/zipfldr/CExplorerCommand.cpp b/dll/shellext/zipfldr/CExplorerCommand.cpp new file mode 100644 index 00000000000..1e634f81c81 --- /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@reactos.org) + */ + +#include "precomp.h" + +class CExplorerCommand : + public CComObjectRootEx, + public IExplorerCommand +{ +private: + CComPtr 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, + public IEnumExplorerCommand +{ +private: + bool m_bFirst; + CComPtr 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(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, + public IExplorerCommandProvider +{ +private: + CComPtr 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(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(zipObject, riid, ppvOut); +} diff --git a/dll/shellext/zipfldr/CFolderViewCB.cpp b/dll/shellext/zipfldr/CFolderViewCB.cpp new file mode 100644 index 00000000000..2a46f6576ad --- /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@gmail.com) + * Copyright 2017 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +class CFolderViewCB : + public CComObjectRootEx, + 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(riid, ppvOut); +} diff --git a/dll/shellext/zipfldr/CMakeLists.txt b/dll/shellext/zipfldr/CMakeLists.txt new file mode 100644 index 00000000000..d491f1d7c27 --- /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 00000000000..2c36aac2ea9 --- /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@reactos.org) + */ + +struct CZipEnumerator +{ +private: + CComPtr m_Zip; + bool m_First; + CAtlList 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 00000000000..5fbebefbb7f --- /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@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 + { + 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 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 + { + private: + CZipExtract* m_pExtract; + + public: + CExtractSettingsPage(CZipExtract* extract) + :CPropertyPageImpl(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 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) + END_MSG_MAP() + }; + + + class CCompleteSettingsPage : public CPropertyPageImpl + { + private: + CZipExtract* m_pExtract; + + public: + CCompleteSettingsPage(CZipExtract* extract) + :CPropertyPageImpl(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) + 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 00000000000..fa98673ff2a --- /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@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, + public CComObjectRootEx, + public IShellFolder2, + //public IStorage, + public IContextMenu, + public IShellExtInit, + //public IPersistFile, + public IPersistFolder2, + public IZip +{ + CStringW m_ZipFile; + CStringA m_ZipDir; + CComHeapPtr 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(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 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 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 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 00000000000..35580771db6 --- /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@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 00000000000..bb16e50fb4c --- /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@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 00000000000..01b92991d53 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// debug.h needs this: +#define NTSTATUS LONG +#include +#include + + + +#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 0000000000000000000000000000000000000000..04f98dcb2fdb9a65786e0d3652d24f9e67055f7f GIT binary patch literal 26070 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 h`odSd`6YVWZAh4c76m)mdkF-l1g`6mxmFhKwS literal 0 HcmV?d00001 diff --git a/dll/shellext/zipfldr/res/zipfldr.rgs b/dll/shellext/zipfldr/res/zipfldr.rgs new file mode 100644 index 00000000000..82cfc8dfaeb --- /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 00000000000..10cadc742ae --- /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 00000000000..27a354e0a1a --- /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@reactos.org) + */ + +#include "precomp.h" + +HMODULE g_hModule = NULL; +LONG g_ModuleRefCnt = 0; + +#include + +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 00000000000..0b577b5f3e5 --- /dev/null +++ b/dll/shellext/zipfldr/zipfldr.rc @@ -0,0 +1,88 @@ +#include +#include + +#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 + +#include + +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 00000000000..8b689b5e122 --- /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 00000000000..1e1eb9f57f0 --- /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@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 00000000000..8c81158525f --- /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@reactos.org) + */ + + +enum ZipPidlType +{ + ZIP_PIDL_DIRECTORY, + ZIP_PIDL_FILE +}; + +#include +struct ZipPidlEntry +{ + WORD cb; + BYTE MagicType; + ZipPidlType ZipType; + + ULONG64 CompressedSize; + ULONG64 UncompressedSize; + ULONG DosDate; + BYTE Password; + + char Name[1]; +}; +#include + + +LPITEMIDLIST _ILCreate(ZipPidlType Type, LPCSTR lpString, unz_file_info64& info); +const ZipPidlEntry* _ZipFromIL(LPCITEMIDLIST pidl); + -- 2.17.1