[SHELLEXT][ZIPFLDR] Implement ZIP creation (#2114)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Sun, 1 Dec 2019 12:20:53 +0000 (21:20 +0900)
committerGitHub <noreply@github.com>
Sun, 1 Dec 2019 12:20:53 +0000 (21:20 +0900)
This PR will implement *.ZFSendToTarget file type that will realize SendTo ZIP folder in future.
CORE-16495, CORE-12562

21 files changed:
dll/shellext/zipfldr/CMakeLists.txt
dll/shellext/zipfldr/CSendToZip.cpp [new file with mode: 0644]
dll/shellext/zipfldr/CSendToZip.hpp [new file with mode: 0644]
dll/shellext/zipfldr/CZipCreater.cpp [new file with mode: 0644]
dll/shellext/zipfldr/CZipCreater.hpp [new file with mode: 0644]
dll/shellext/zipfldr/lang/de-DE.rc
dll/shellext/zipfldr/lang/en-US.rc
dll/shellext/zipfldr/lang/et-EE.rc
dll/shellext/zipfldr/lang/fr-FR.rc
dll/shellext/zipfldr/lang/hi-IN.rc
dll/shellext/zipfldr/lang/it-IT.rc
dll/shellext/zipfldr/lang/ja-JP.rc
dll/shellext/zipfldr/lang/pl-PL.rc
dll/shellext/zipfldr/lang/ro-RO.rc
dll/shellext/zipfldr/lang/ru-RU.rc
dll/shellext/zipfldr/lang/sv-SE.rc
dll/shellext/zipfldr/lang/zh-CN.rc
dll/shellext/zipfldr/precomp.h
dll/shellext/zipfldr/res/zipfldr.rgs
dll/shellext/zipfldr/resource.h
dll/shellext/zipfldr/zipfldr.cpp

index 3c39dc4..a0244d6 100644 (file)
@@ -28,6 +28,8 @@ list(APPEND SOURCE
     CExplorerCommand.cpp
     CEnumZipContents.cpp
     CFolderViewCB.cpp
+    CSendToZip.cpp
+    CZipCreater.cpp
     CZipEnumerator.hpp
     CZipExtract.cpp
     CZipFolder.hpp
diff --git a/dll/shellext/zipfldr/CSendToZip.cpp b/dll/shellext/zipfldr/CSendToZip.cpp
new file mode 100644 (file)
index 0000000..9ec9bd5
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * PROJECT:   ReactOS Zip Shell Extension
+ * LICENSE:   GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:   SendTo handler
+ * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
+ *            Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
+ */
+
+#include "precomp.h"
+
+STDMETHODIMP
+CSendToZip::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt,
+                      DWORD *pdwEffect)
+{
+    m_pDataObject = pDataObj;
+
+    FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+    m_fCanDragDrop = SUCCEEDED(pDataObj->QueryGetData(&etc));
+
+    if (m_fCanDragDrop)
+        *pdwEffect &= DROPEFFECT_COPY;
+    else
+        *pdwEffect = DROPEFFECT_NONE;
+
+    return S_OK;
+}
+
+STDMETHODIMP CSendToZip::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+    if (m_fCanDragDrop)
+        *pdwEffect &= DROPEFFECT_COPY;
+    else
+        *pdwEffect = DROPEFFECT_NONE;
+
+    return S_OK;
+}
+
+STDMETHODIMP CSendToZip::DragLeave()
+{
+    m_fCanDragDrop = FALSE;
+    m_pDataObject.Release();
+    return S_OK;
+}
+
+STDMETHODIMP
+CSendToZip::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt,
+                 DWORD *pdwEffect)
+{
+    m_pDataObject = pDataObj;
+    *pdwEffect &= DROPEFFECT_COPY;
+
+    if (!pDataObj || !m_fCanDragDrop || !*pdwEffect)
+    {
+        DPRINT1("Drop failed: %d %d %d\n",
+                !pDataObj, !m_fCanDragDrop, !*pdwEffect);
+        *pdwEffect = 0;
+        DragLeave();
+        return E_FAIL;
+    }
+
+    STGMEDIUM stg;
+    FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+    HRESULT hr = pDataObj->GetData(&etc, &stg);
+    if (FAILED_UNEXPECTEDLY(hr))
+    {
+        *pdwEffect = 0;
+        DragLeave();
+        return E_FAIL;
+    }
+
+    HDROP hDrop = reinterpret_cast<HDROP>(stg.hGlobal);
+    UINT cItems = ::DragQueryFileW(hDrop, -1, NULL, 0);
+
+    CZipCreator *pCreater = CZipCreator::DoCreate();
+
+    for (UINT iItem = 0; iItem < cItems; ++iItem)
+    {
+        WCHAR szPath[MAX_PATH];
+        DragQueryFileW(hDrop, iItem, szPath, _countof(szPath));
+
+        pCreater->DoAddItem(szPath);
+    }
+
+    ::ReleaseStgMedium(&stg);
+
+    CZipCreator::runThread(pCreater);   // pCreater is deleted in runThread
+
+    DragLeave();
+    return hr;
+}
diff --git a/dll/shellext/zipfldr/CSendToZip.hpp b/dll/shellext/zipfldr/CSendToZip.hpp
new file mode 100644 (file)
index 0000000..1f3eb24
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * PROJECT:   ReactOS Zip Shell Extension
+ * LICENSE:   GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:   SendTo handler
+ * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
+ *            Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
+ */
+
+#ifndef CSENDTOZIP_HPP_
+#define CSENDTOZIP_HPP_
+
+class CSendToZip :
+    public CComCoClass<CSendToZip, &CLSID_ZipFolderSendTo>,
+    public CComObjectRootEx<CComMultiThreadModelNoCS>,
+    public IDropTarget,
+    public IPersistFile
+{
+    CComPtr<IDataObject> m_pDataObject;
+    BOOL m_fCanDragDrop;
+
+public:
+    CSendToZip() : m_fCanDragDrop(FALSE)
+    {
+        InterlockedIncrement(&g_ModuleRefCnt);
+    }
+
+    virtual ~CSendToZip()
+    {
+        InterlockedDecrement(&g_ModuleRefCnt);
+    }
+
+    // *** IShellFolder2 methods ***
+    STDMETHODIMP DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+    STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+    STDMETHODIMP DragLeave();
+    STDMETHODIMP Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+
+    // *** IPersistFile methods ***
+    STDMETHODIMP IsDirty()
+    {
+        return S_FALSE;
+    }
+    STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode)
+    {
+        return S_OK;
+    }
+    STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember)
+    {
+        return E_FAIL;
+    }
+    STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName)
+    {
+        return E_FAIL;
+    }
+    STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName)
+    {
+        return E_FAIL;
+    }
+
+    // *** IPersist methods ***
+    STDMETHODIMP GetClassID(CLSID *pclsid)
+    {
+        return E_FAIL;
+    }
+
+public:
+    DECLARE_NO_REGISTRY()   // Handled manually
+    DECLARE_NOT_AGGREGATABLE(CSendToZip)
+
+    DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+    BEGIN_COM_MAP(CSendToZip)
+        COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
+        COM_INTERFACE_ENTRY_IID(IID_IPersistFile, IPersistFile)
+        COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
+    END_COM_MAP()
+};
+
+#endif
diff --git a/dll/shellext/zipfldr/CZipCreater.cpp b/dll/shellext/zipfldr/CZipCreater.cpp
new file mode 100644 (file)
index 0000000..694128b
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * PROJECT:     ReactOS Zip Shell Extension
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     Create a zip file
+ * COPYRIGHT:   Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
+ *              Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
+ */
+
+#include "precomp.h"
+#include "atlsimpcoll.h"
+#include "minizip/zip.h"
+#include "minizip/iowin32.h"
+#include <process.h>
+
+static CStringW DoGetZipName(LPCWSTR filename)
+{
+    WCHAR szPath[MAX_PATH];
+    StringCbCopyW(szPath, sizeof(szPath), filename);
+    PathRemoveExtensionW(szPath);
+
+    CStringW ret = szPath;
+    ret += L".zip";
+
+    UINT i = 2;
+    while (PathFileExistsW(ret))
+    {
+        CStringW str;
+        str.Format(L" (%u).zip", i++);
+
+        ret = szPath;
+        ret += str;
+    }
+
+    return ret;
+}
+
+static CStringA DoGetAnsiName(LPCWSTR filename)
+{
+    CHAR buf[MAX_PATH];
+    WideCharToMultiByte(CP_ACP, 0, filename, -1, buf, _countof(buf), NULL, NULL);
+    return buf;
+}
+
+static CStringW DoGetBaseName(LPCWSTR filename)
+{
+    WCHAR szBaseName[MAX_PATH];
+    StringCbCopyW(szBaseName, sizeof(szBaseName), filename);
+    PathRemoveFileSpecW(szBaseName);
+    PathAddBackslashW(szBaseName);
+    return szBaseName;
+}
+
+static CStringA
+DoGetNameInZip(const CStringW& basename, const CStringW& filename)
+{
+    CStringW basenameI = basename, filenameI = filename;
+    basenameI.MakeUpper();
+    filenameI.MakeUpper();
+
+    CStringW ret;
+    if (filenameI.Find(basenameI) == 0)
+        ret = filename.Mid(basename.GetLength());
+    else
+        ret = filename;
+
+    ret.Replace(L'\\', L'/');
+
+    return DoGetAnsiName(ret);
+}
+
+static BOOL
+DoReadAllOfFile(LPCWSTR filename, CSimpleArray<BYTE>& contents,
+                zip_fileinfo *pzi)
+{
+    contents.RemoveAll();
+
+    HANDLE hFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ,
+                               NULL, OPEN_EXISTING,
+                               FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+    {
+        DPRINT1("%S: cannot open\n", filename);
+        return FALSE;
+    }
+
+    FILETIME ft, ftLocal;
+    ZeroMemory(pzi, sizeof(*pzi));
+    if (GetFileTime(hFile, NULL, NULL, &ft))
+    {
+        SYSTEMTIME st;
+        FileTimeToLocalFileTime(&ft, &ftLocal);
+        FileTimeToSystemTime(&ftLocal, &st);
+        pzi->tmz_date.tm_sec = st.wSecond;
+        pzi->tmz_date.tm_min = st.wMinute;
+        pzi->tmz_date.tm_hour = st.wHour;
+        pzi->tmz_date.tm_mday = st.wDay;
+        pzi->tmz_date.tm_mon = st.wMonth - 1;
+        pzi->tmz_date.tm_year = st.wYear;
+    }
+
+    const DWORD cbBuff = 0x7FFF;
+    LPBYTE pbBuff = reinterpret_cast<LPBYTE>(CoTaskMemAlloc(cbBuff));
+    if (!pbBuff)
+    {
+        DPRINT1("Out of memory\n");
+        CloseHandle(hFile);
+        return FALSE;
+    }
+
+    for (;;)
+    {
+        DWORD cbRead;
+        if (!ReadFile(hFile, pbBuff, cbBuff, &cbRead, NULL) || !cbRead)
+            break;
+
+        for (DWORD i = 0; i < cbRead; ++i)
+            contents.Add(pbBuff[i]);
+    }
+
+    CoTaskMemFree(pbBuff);
+    CloseHandle(hFile);
+
+    return TRUE;
+}
+
+static void
+DoAddFilesFromItem(CSimpleArray<CStringW>& files, LPCWSTR item)
+{
+    if (!PathIsDirectoryW(item))
+    {
+        files.Add(item);
+        return;
+    }
+
+    WCHAR szPath[MAX_PATH];
+    StringCbCopyW(szPath, sizeof(szPath), item);
+    PathAppendW(szPath, L"*");
+
+    WIN32_FIND_DATAW find;
+    HANDLE hFind = FindFirstFileW(szPath, &find);
+    if (hFind == INVALID_HANDLE_VALUE)
+        return;
+
+    do
+    {
+        if (wcscmp(find.cFileName, L".") == 0 ||
+            wcscmp(find.cFileName, L"..") == 0)
+        {
+            continue;
+        }
+
+        StringCbCopyW(szPath, sizeof(szPath), item);
+        PathAppendW(szPath, find.cFileName);
+
+        if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+            DoAddFilesFromItem(files, szPath);
+        else
+            files.Add(szPath);
+    } while (FindNextFileW(hFind, &find));
+
+    FindClose(hFind);
+}
+
+struct CZipCreatorImpl
+{
+    CSimpleArray<CStringW> m_items;
+
+    unsigned JustDoIt();
+};
+
+CZipCreator::CZipCreator() : m_pimpl(new CZipCreatorImpl)
+{
+    InterlockedIncrement(&g_ModuleRefCnt);
+}
+
+CZipCreator::~CZipCreator()
+{
+    InterlockedDecrement(&g_ModuleRefCnt);
+    delete m_pimpl;
+}
+
+static unsigned __stdcall
+create_zip_function(void *arg)
+{
+    CZipCreator *pCreater = reinterpret_cast<CZipCreator *>(arg);
+    return pCreater->m_pimpl->JustDoIt();
+}
+
+BOOL CZipCreator::runThread(CZipCreator *pCreater)
+{
+    unsigned tid = 0;
+    HANDLE hThread = reinterpret_cast<HANDLE>(
+        _beginthreadex(NULL, 0, create_zip_function, pCreater, 0, &tid));
+
+    if (hThread)
+    {
+        CloseHandle(hThread);
+        return TRUE;
+    }
+
+    DPRINT1("hThread == NULL\n");
+
+    CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE));
+    CStringW strText(MAKEINTRESOURCEW(IDS_CANTSTARTTHREAD));
+    MessageBoxW(NULL, strText, strTitle, MB_ICONERROR);
+
+    delete pCreater;
+    return FALSE;
+}
+
+void CZipCreator::DoAddItem(LPCWSTR pszFile)
+{
+    // canonicalize path
+    WCHAR szPath[MAX_PATH];
+    GetFullPathNameW(pszFile, _countof(szPath), szPath, NULL);
+
+    m_pimpl->m_items.Add(szPath);
+}
+
+enum CZC_ERROR
+{
+    CZCERR_ZEROITEMS = 1,
+    CZCERR_NOFILES,
+    CZCERR_CREATE,
+    CZCERR_READ
+};
+
+unsigned CZipCreatorImpl::JustDoIt()
+{
+    // TODO: Show progress.
+
+    if (m_items.GetSize() <= 0)
+    {
+        DPRINT1("GetSize() <= 0\n");
+        return CZCERR_ZEROITEMS;
+    }
+
+    CSimpleArray<CStringW> files;
+    for (INT iItem = 0; iItem < m_items.GetSize(); ++iItem)
+    {
+        DoAddFilesFromItem(files, m_items[iItem]);
+    }
+
+    if (files.GetSize() <= 0)
+    {
+        DPRINT1("files.GetSize() <= 0\n");
+
+        CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE));
+        CStringW strText;
+        strText.Format(IDS_NOFILES, static_cast<LPCWSTR>(m_items[0]));
+        MessageBoxW(NULL, strText, strTitle, MB_ICONERROR);
+
+        return CZCERR_NOFILES;
+    }
+
+    zlib_filefunc64_def ffunc;
+    fill_win32_filefunc64W(&ffunc);
+
+    CStringW strZipName = DoGetZipName(m_items[0]);
+    zipFile zf = zipOpen2_64(strZipName, APPEND_STATUS_CREATE, NULL, &ffunc);
+    if (zf == 0)
+    {
+        DPRINT1("zf == 0\n");
+
+        int err = CZCERR_CREATE;
+
+        CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE));
+        CStringW strText;
+        strText.Format(IDS_CANTCREATEZIP, static_cast<LPCWSTR>(strZipName), err);
+        MessageBoxW(NULL, strText, strTitle, MB_ICONERROR);
+
+        return err;
+    }
+
+    // TODO: password
+    const char *password = NULL;
+    int zip64 = 1; // always zip64
+    zip_fileinfo zi;
+
+    int err = 0;
+    CStringW strTarget, strBaseName = DoGetBaseName(m_items[0]);
+    for (INT iFile = 0; iFile < files.GetSize(); ++iFile)
+    {
+        const CStringW& strFile = files[iFile];
+
+        CSimpleArray<BYTE> contents;
+        if (!DoReadAllOfFile(strFile, contents, &zi))
+        {
+            DPRINT1("DoReadAllOfFile failed\n");
+            err = CZCERR_READ;
+            strTarget = strFile;
+            break;
+        }
+
+        unsigned long crc = 0;
+        if (password)
+        {
+            // TODO: crc = ...;
+        }
+
+        CStringA strNameInZip = DoGetNameInZip(strBaseName, strFile);
+        err = zipOpenNewFileInZip3_64(zf,
+                                      strNameInZip,
+                                      &zi,
+                                      NULL,
+                                      0,
+                                      NULL,
+                                      0,
+                                      NULL,
+                                      Z_DEFLATED,
+                                      Z_DEFAULT_COMPRESSION,
+                                      0,
+                                      -MAX_WBITS,
+                                      DEF_MEM_LEVEL,
+                                      Z_DEFAULT_STRATEGY,
+                                      password,
+                                      crc,
+                                      zip64);
+        if (err)
+        {
+            DPRINT1("zipOpenNewFileInZip3_64\n");
+            break;
+        }
+
+        err = zipWriteInFileInZip(zf, contents.GetData(), contents.GetSize());
+        if (err)
+        {
+            DPRINT1("zipWriteInFileInZip\n");
+            break;
+        }
+
+        err = zipCloseFileInZip(zf);
+        if (err)
+        {
+            DPRINT1("zipCloseFileInZip\n");
+            break;
+        }
+    }
+
+    zipClose(zf, NULL);
+
+    if (err)
+    {
+        DeleteFileW(strZipName);
+
+        CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE));
+
+        CStringW strText;
+        if (err < 0)
+            strText.Format(IDS_CANTCREATEZIP, static_cast<LPCWSTR>(strZipName), err);
+        else
+            strText.Format(IDS_CANTREADFILE, static_cast<LPCWSTR>(strTarget));
+
+        MessageBoxW(NULL, strText, strTitle, MB_ICONERROR);
+    }
+
+    return err;
+}
diff --git a/dll/shellext/zipfldr/CZipCreater.hpp b/dll/shellext/zipfldr/CZipCreater.hpp
new file mode 100644 (file)
index 0000000..52fb14f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * PROJECT:     ReactOS Zip Shell Extension
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     Create a zip file
+ * COPYRIGHT:   Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
+ *              Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
+ */
+#ifndef CZIPCREATER_HPP_
+#define CZIPCREATER_HPP_
+
+struct CZipCreatorImpl;
+
+class CZipCreator
+{
+public:
+    struct CZipCreatorImpl *m_pimpl;
+
+    virtual ~CZipCreator();
+
+    static CZipCreator* DoCreate()
+    {
+        return new CZipCreator();
+    }
+
+    virtual void DoAddItem(LPCWSTR pszFile);
+    static BOOL runThread(CZipCreator* pCreater);
+
+protected:
+    CZipCreator();
+};
+
+#endif
index 5925822..6f50cb7 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Änderungsdatum"
     IDS_YES "Ja"
     IDS_NO "Nein"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Extrahier-Assistent"
     IDS_WIZ_DEST_TITLE "Ziel auswählen"
index bba5be5..a73ad54 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Date modified"
     IDS_YES "Yes"
     IDS_NO "No"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Extraction Wizard"
     IDS_WIZ_DEST_TITLE "Select a Destination"
index c9a7cc0..eb2b028 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Kuupäeval muudetud"
     IDS_YES "Jah"
     IDS_NO "Ei"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Ekstraktimise visard"
     IDS_WIZ_DEST_TITLE "Vali sihtkoht"
index 19d4520..24decc1 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Date de modification"
     IDS_YES "Oui"
     IDS_NO "Non"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Assistant d'extraction"
     IDS_WIZ_DEST_TITLE "Choisir une destination"
index 7794f63..cf31cce 100644 (file)
@@ -55,6 +55,11 @@ BEGIN
     IDS_COL_DATE_MOD "तिथि संशोधित"
     IDS_YES "हाँ"
     IDS_NO "नहीं"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "निष्कर्षण विज़ार्ड"
     IDS_WIZ_DEST_TITLE "एक गंतव्य चुनें"
index 9f88643..d3572fa 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Data modificata"
     IDS_YES "Sì"
     IDS_NO "No"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Estrazione Guidata"
     IDS_WIZ_DEST_TITLE "Seleziona una Destinazione"
index 63564b7..157e671 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "変更日"
     IDS_YES "はい"
     IDS_NO "いいえ"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "展開ウィザード"
     IDS_WIZ_DEST_TITLE "展開先を選んで下さい"
index c4130cb..fcdb043 100644 (file)
@@ -67,6 +67,11 @@ BEGIN
     IDS_COL_DATE_MOD "Data modyfikacji"
     IDS_YES "Tak"
     IDS_NO "Nie"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Kreator wyodrębniania"
     IDS_WIZ_DEST_TITLE "Wybierz miejsce docelowe"
index 31642b0..cb15956 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Data modificată"
     IDS_YES "Da"
     IDS_NO "Nu"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Asistent de extracție"
     IDS_WIZ_DEST_TITLE "Selectați o Destinație"
index 89096a3..1791169 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Дата изменения"
     IDS_YES "Да"
     IDS_NO "Нет"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Мастер извлечения архивов"
     IDS_WIZ_DEST_TITLE "Укажите путь"
index a2f1c90..886dddf 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "Ändrad den"
     IDS_YES "Ja"
     IDS_NO "Nej"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "Extraheringsguiden"
     IDS_WIZ_DEST_TITLE "Välj ett mål"
index 27066ad..6d69c01 100644 (file)
@@ -68,6 +68,11 @@ BEGIN
     IDS_COL_DATE_MOD "修改日期"
     IDS_YES "是"
     IDS_NO "否"
+    IDS_ERRORTITLE "Compressed (zipped) Folders Error"
+    IDS_CANTSTARTTHREAD "Cannot start thread to compress files."
+    IDS_NOFILES "The specified directory '%s' is empty, so Compressed (zipped) Folders cannot add it to the archive."
+    IDS_CANTCREATEZIP "Failed to create a compressed folder '%s' (Error Code: %d)."
+    IDS_CANTREADFILE "Cannot read file '%s'."
 
     IDS_WIZ_TITLE "解压向导"
     IDS_WIZ_DEST_TITLE "选择一个目标文件夹"
index e25e2f1..ed61dc7 100644 (file)
@@ -78,5 +78,7 @@ eZipConfirmResponse _CZipAskReplace(HWND hDlg, const char* FullPath);
 
 #include "CZipEnumerator.hpp"
 #include "CZipFolder.hpp"
+#include "CZipCreater.hpp"
+#include "CSendToZip.hpp"
 
 #endif /* ZIPFLDR_PRECOMP_H */
index 82cfc8d..c57aeae 100644 (file)
@@ -12,10 +12,26 @@ HKCR
                 val UseDropHandler = s ''
             }
         }
-        '{b8cdcb65-b1bf-4b42-9428-1dfdb7ee92af}' = s 'Compressed (zipped) Folder Menu'
+        '{B8CDCB65-B1BF-4B42-9428-1DFDB7EE92AF}' = s 'Compressed (zipped) Folder Menu'
         {
             InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' }
         }
+        '{888DCA60-FC0A-11CF-8F0F-00C04FD7D062}' = s 'Compressed (zipped) Folder SendTo Target'
+        {
+            val EditFlags = d '0x01'
+            val FriendlyTypeName = s '%MODULE%,-10226'
+            val NeverShowExt = s ''
+            val NoOpen = s 'Drag Files onto this icon to compress them.'
+            DefaultIcon = s '%MODULE%' { }
+            InprocServer32 = s '%MODULE%'
+            {
+                val ThreadingModel = s 'Apartment'
+            }
+            ShellEx
+            {
+                DropHandler = s '{888DCA60-FC0A-11CF-8F0F-00C04FD7D062}' { }
+            }
+        }
     }
     NoRemove Applications
     {
@@ -24,7 +40,7 @@ HKCR
     NoRemove CompressedFolder
     {
         FriendlyTypeName = s '%MODULE%,-10195'
-        CLSID = s '{E88DCCE0-B7B3-11d1-A9F0-00AA0060FA31}'
+        CLSID = s '{E88DCCE0-B7B3-11D1-A9F0-00AA0060FA31}'
         DefaultIcon = s '%MODULE%'
 
         NoRemove Shell
@@ -41,7 +57,7 @@ HKCR
         {
             NoRemove ContextMenuHandlers
             {
-                ForceRemove '{b8cdcb65-b1bf-4b42-9428-1dfdb7ee92af}' = s 'Compressed (zipped) Folder Menu'
+                ForceRemove '{B8CDCB65-B1BF-4B42-9428-1DFDB7EE92AF}' = s 'Compressed (zipped) Folder Menu'
                 {
                 }
             }
@@ -52,3 +68,13 @@ HKCR
         val 'Content Type' = s 'application/x-zip-compressed'
     }
 }
+HKLM
+{
+    NoRemove Software
+    {
+        NoRemove Classes
+        {
+            '.ZFSendToTarget' = s 'CLSID\{888DCA60-FC0A-11CF-8F0F-00C04FD7D062}'
+        }
+    }
+}
index b14faeb..b294e82 100644 (file)
 #define IDS_COL_DATE_MOD    106
 #define IDS_YES             107
 #define IDS_NO              108
-
+#define IDS_ERRORTITLE      109
+#define IDS_CANTSTARTTHREAD 110
+#define IDS_NOFILES         111
+#define IDS_CANTCREATEZIP   112
+#define IDS_CANTREADFILE    113
 
 /* Wizard titles */
 #define IDS_WIZ_TITLE           8000
index 2b9e5b6..eecb1ac 100644 (file)
@@ -31,6 +31,7 @@ public:
 BEGIN_OBJECT_MAP(ObjectMap)
     OBJECT_ENTRY(CLSID_ZipFolderStorageHandler, CZipFolder)
     OBJECT_ENTRY(CLSID_ZipFolderContextMenu, CZipFolder)
+    OBJECT_ENTRY(CLSID_ZipFolderSendTo, CSendToZip)
 END_OBJECT_MAP()
 
 CZipFldrModule gModule;