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