在 Windows (XP/2003) 上使用 C/C++ 创建 ZIP 文件

发布于 2024-07-05 17:21:01 字数 395 浏览 3 评论 0原文

我正在寻找一种从 Windows C/C++ API 中的文件夹创建 ZIP 文件的方法。 我可以使用 Shell32.Application CopyHere 方法在 VBScript 中找到执行此操作的方法,并且我找到了一个教程,解释了如何在 C# 中执行此操作,但没有针对 C API 的说明(C++ 也很好,项目已经使用 MFC)。

如果有人可以分享一些可以在 Windows XP/2003 上成功创建 zip 文件的示例 C 代码,我将非常感激。 如果做不到这一点,如果有人能找到可靠的文档或教程那就太好了,因为 MSDN 搜索的结果并不多。 我真的希望避免为此提供第三方库,因为功能显然已经存在,我只是不知道如何访问它。 谷歌搜索没有发现任何有用的东西,只是一些诱人的信息。 希望社区中有人能解决这个问题并可以分享给后代!

I am looking for a way to create a ZIP file from a folder in Windows C/C++ APIs. I can find the way to do this in VBScript using the Shell32.Application CopyHere method, and I found a tutorial explaining how to do it in C# also, but nothing for the C API (C++ is fine too, project already uses MFC).

I'd be really grateful if anyone can share some sample C code that can successfully create a zip file on Windows XP/2003. Failing that, if someone can find solid docs or a tutorial that would be great, since MSDN searches don't turn up much. I'm really hoping to avoid having to ship a third-party lib for this, because the functionality is obviously there, I just can't figure out how to access it. Google searches turn up nothing useful, just tantalizing bits and pieces of information. Here's hoping someone in the community has sorted this out and can share it for posterity!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

情魔剑神 2024-07-12 17:21:01

正如评论中其他地方所指出的,这仅适用于已创建的 Zip 文件。 该内容还必须不存在于 zip 文件中,否则将显示错误。 这是我能够根据接受的答案创建的工作示例代码。 您需要链接到 shell32.lib 和 kernel32.lib(用于 CreateToolhelp32Snapshot)。

#include <windows.h>
#include <shldisp.h>
#include <tlhelp32.h>
#include <stdio.h>

int main(int argc, TCHAR* argv[])
{
    DWORD strlen = 0;
    char szFrom[] = "C:\\Temp",
         szTo[] = "C:\\Sample.zip";
    HRESULT hResult;
    IShellDispatch *pISD;
    Folder *pToFolder = NULL;
    VARIANT vDir, vFile, vOpt;
    BSTR strptr1, strptr2;

    CoInitialize(NULL);

    hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);

    if  (SUCCEEDED(hResult) && pISD != NULL)
    {
        strlen = MultiByteToWideChar(CP_ACP, 0, szTo, -1, 0, 0);
        strptr1 = SysAllocStringLen(0, strlen);
        MultiByteToWideChar(CP_ACP, 0, szTo, -1, strptr1, strlen);

        VariantInit(&vDir);
        vDir.vt = VT_BSTR;
        vDir.bstrVal = strptr1;
        hResult = pISD->NameSpace(vDir, &pToFolder);

        if  (SUCCEEDED(hResult))
        {
            strlen = MultiByteToWideChar(CP_ACP, 0, szFrom, -1, 0, 0);
            strptr2 = SysAllocStringLen(0, strlen);
            MultiByteToWideChar(CP_ACP, 0, szFrom, -1, strptr2, strlen);

            VariantInit(&vFile);
            vFile.vt = VT_BSTR;
            vFile.bstrVal = strptr2;

            VariantInit(&vOpt);
            vOpt.vt = VT_I4;
            vOpt.lVal = 4;          // Do not display a progress dialog box

            hResult = NULL;
            printf("Copying %s to %s ...\n", szFrom, szTo);
            hResult = pToFolder->CopyHere(vFile, vOpt); //NOTE: this appears to always return S_OK even on error
            /*
             * 1) Enumerate current threads in the process using Thread32First/Thread32Next
             * 2) Start the operation
             * 3) Enumerate the threads again
             * 4) Wait for any new threads using WaitForMultipleObjects
             *
             * Of course, if the operation creates any new threads that don't exit, then you have a problem. 
             */
            if (hResult == S_OK) {
                //NOTE: hard-coded for testing - be sure not to overflow the array if > 5 threads exist
                HANDLE hThrd[5]; 
                HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL ,0);  //TH32CS_SNAPMODULE, 0);
                DWORD NUM_THREADS = 0;
                if (h != INVALID_HANDLE_VALUE) {
                    THREADENTRY32 te;
                    te.dwSize = sizeof(te);
                    if (Thread32First(h, &te)) {
                        do {
                            if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) ) {
                                //only enumerate threads that are called by this process and not the main thread
                                if((te.th32OwnerProcessID == GetCurrentProcessId()) && (te.th32ThreadID != GetCurrentThreadId()) ){
                                    //printf("Process 0x%04x Thread 0x%04x\n", te.th32OwnerProcessID, te.th32ThreadID);
                                    hThrd[NUM_THREADS] = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
                                    NUM_THREADS++;
                                }
                            }
                            te.dwSize = sizeof(te);
                        } while (Thread32Next(h, &te));
                    }
                    CloseHandle(h);

                    printf("waiting for all threads to exit...\n");
                    //Wait for all threads to exit
                    WaitForMultipleObjects(NUM_THREADS, hThrd , TRUE , INFINITE);

                    //Close All handles
                    for ( DWORD i = 0; i < NUM_THREADS ; i++ ){
                        CloseHandle( hThrd[i] );
                    }
                } //if invalid handle
            } //if CopyHere() hResult is S_OK

            SysFreeString(strptr2);
            pToFolder->Release();
        }

        SysFreeString(strptr1);
        pISD->Release();
    }

    CoUninitialize();

    printf ("Press ENTER to exit\n");
    getchar();
    return 0;

}

尽管获得了半功能代码,我还是决定不走这条路,因为经过进一步调查,似乎Folder::CopyHere()方法实际上并不尊重传递给它的vOptions,这意味着你不能强迫它覆盖文件或不向用户显示错误对话框。

鉴于此,我也尝试了另一张海报提到的 XZip 库。 该库可以很好地创建 Zip 存档,但请注意,使用 ZIP_FOLDER 调用的 ZipAdd() 函数不是递归的 - 它只是在存档中创建一个文件夹。 为了递归地压缩存档,您需要使用 AddFolderContent() 函数。 例如,要创建 C:\Sample.zip 并向其中添加 C:\Temp 文件夹,请使用以下命令:

HZIP newZip = CreateZip("C:\\Sample.zip", NULL, ZIP_FILENAME);
BOOL retval = AddFolderContent(newZip, "C:", "temp");

重要提示:AddFolderContent() 函数无法像 XZip 库中包含的那样起作用。 它将递归到目录结构中,但由于传递给 ZipAdd() 的路径中存在错误,因此无法将任何文件添加到 zip 存档中。 为了使用此功能,您需要编辑源代码并将此行:更改

if (ZipAdd(hZip, RelativePathNewFileFound, RelativePathNewFileFound, 0, ZIP_FILENAME) != ZR_OK)

为以下内容:

ZRESULT ret;
TCHAR real_path[MAX_PATH] = {0};
_tcscat(real_path, AbsolutePath);
_tcscat(real_path, RelativePathNewFileFound);
if (ZipAdd(hZip, RelativePathNewFileFound, real_path, 0, ZIP_FILENAME) != ZR_OK)

As noted elsewhere in the comments, this will only work on a already-created Zip file. The content must also not already exist in the zip file, or an error will be displayed. Here is the working sample code I was able to create based on the accepted answer. You need to link to shell32.lib and also kernel32.lib (for CreateToolhelp32Snapshot).

#include <windows.h>
#include <shldisp.h>
#include <tlhelp32.h>
#include <stdio.h>

int main(int argc, TCHAR* argv[])
{
    DWORD strlen = 0;
    char szFrom[] = "C:\\Temp",
         szTo[] = "C:\\Sample.zip";
    HRESULT hResult;
    IShellDispatch *pISD;
    Folder *pToFolder = NULL;
    VARIANT vDir, vFile, vOpt;
    BSTR strptr1, strptr2;

    CoInitialize(NULL);

    hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);

    if  (SUCCEEDED(hResult) && pISD != NULL)
    {
        strlen = MultiByteToWideChar(CP_ACP, 0, szTo, -1, 0, 0);
        strptr1 = SysAllocStringLen(0, strlen);
        MultiByteToWideChar(CP_ACP, 0, szTo, -1, strptr1, strlen);

        VariantInit(&vDir);
        vDir.vt = VT_BSTR;
        vDir.bstrVal = strptr1;
        hResult = pISD->NameSpace(vDir, &pToFolder);

        if  (SUCCEEDED(hResult))
        {
            strlen = MultiByteToWideChar(CP_ACP, 0, szFrom, -1, 0, 0);
            strptr2 = SysAllocStringLen(0, strlen);
            MultiByteToWideChar(CP_ACP, 0, szFrom, -1, strptr2, strlen);

            VariantInit(&vFile);
            vFile.vt = VT_BSTR;
            vFile.bstrVal = strptr2;

            VariantInit(&vOpt);
            vOpt.vt = VT_I4;
            vOpt.lVal = 4;          // Do not display a progress dialog box

            hResult = NULL;
            printf("Copying %s to %s ...\n", szFrom, szTo);
            hResult = pToFolder->CopyHere(vFile, vOpt); //NOTE: this appears to always return S_OK even on error
            /*
             * 1) Enumerate current threads in the process using Thread32First/Thread32Next
             * 2) Start the operation
             * 3) Enumerate the threads again
             * 4) Wait for any new threads using WaitForMultipleObjects
             *
             * Of course, if the operation creates any new threads that don't exit, then you have a problem. 
             */
            if (hResult == S_OK) {
                //NOTE: hard-coded for testing - be sure not to overflow the array if > 5 threads exist
                HANDLE hThrd[5]; 
                HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL ,0);  //TH32CS_SNAPMODULE, 0);
                DWORD NUM_THREADS = 0;
                if (h != INVALID_HANDLE_VALUE) {
                    THREADENTRY32 te;
                    te.dwSize = sizeof(te);
                    if (Thread32First(h, &te)) {
                        do {
                            if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) ) {
                                //only enumerate threads that are called by this process and not the main thread
                                if((te.th32OwnerProcessID == GetCurrentProcessId()) && (te.th32ThreadID != GetCurrentThreadId()) ){
                                    //printf("Process 0x%04x Thread 0x%04x\n", te.th32OwnerProcessID, te.th32ThreadID);
                                    hThrd[NUM_THREADS] = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
                                    NUM_THREADS++;
                                }
                            }
                            te.dwSize = sizeof(te);
                        } while (Thread32Next(h, &te));
                    }
                    CloseHandle(h);

                    printf("waiting for all threads to exit...\n");
                    //Wait for all threads to exit
                    WaitForMultipleObjects(NUM_THREADS, hThrd , TRUE , INFINITE);

                    //Close All handles
                    for ( DWORD i = 0; i < NUM_THREADS ; i++ ){
                        CloseHandle( hThrd[i] );
                    }
                } //if invalid handle
            } //if CopyHere() hResult is S_OK

            SysFreeString(strptr2);
            pToFolder->Release();
        }

        SysFreeString(strptr1);
        pISD->Release();
    }

    CoUninitialize();

    printf ("Press ENTER to exit\n");
    getchar();
    return 0;

}

I have decided not to go this route despite getting semi-functional code, since after further investigation, it appears the Folder::CopyHere() method does not actually respect the vOptions passed to it, which means you cannot force it to overwrite files or not display error dialogs to the user.

In light of that, I tried the XZip library mentioned by another poster as well. This library functions fine for creating a Zip archive, but note that the ZipAdd() function called with ZIP_FOLDER is not recursive - it merely creates a folder in the archive. In order to recursively zip an archive you will need to use the AddFolderContent() function. For example, to create a C:\Sample.zip and Add the C:\Temp folder to it, use the following:

HZIP newZip = CreateZip("C:\\Sample.zip", NULL, ZIP_FILENAME);
BOOL retval = AddFolderContent(newZip, "C:", "temp");

Important note: the AddFolderContent() function is not functional as included in the XZip library. It will recurse into the directory structure but fails to add any files to the zip archive, due to a bug in the paths passed to ZipAdd(). In order to use this function you'll need to edit the source and change this line:

if (ZipAdd(hZip, RelativePathNewFileFound, RelativePathNewFileFound, 0, ZIP_FILENAME) != ZR_OK)

To the following:

ZRESULT ret;
TCHAR real_path[MAX_PATH] = {0};
_tcscat(real_path, AbsolutePath);
_tcscat(real_path, RelativePathNewFileFound);
if (ZipAdd(hZip, RelativePathNewFileFound, real_path, 0, ZIP_FILENAME) != ZR_OK)
猛虎独行 2024-07-12 17:21:01

编辑:这个答案很旧,但我无法删除它,因为它已被接受。 请参阅下一篇

https://stackoverflow.com/a/121720/3937

----- 原始答案 -- ---

这里有执行此操作的示例代码

[编辑:链接现已损坏]

http://www.eggheadcafe.com/software/aspnet/31056644/using-shfileoperation-to.aspx

确保您阅读了如何处理线程完成的监视。

编辑:从注释来看,此代码仅适用于现有的 zip 文件,但 @Simon 提供了此代码来创建空白 zip文件

FILE* f = fopen("path", "wb");
fwrite("\x50\x4B\x05\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 22, 1, f);
fclose(f);

EDIT: This answer is old, but I cannot delete it because it was accepted. See the next one

https://stackoverflow.com/a/121720/3937

----- ORIGINAL ANSWER -----

There is sample code to do that here

[EDIT: Link is now broken]

http://www.eggheadcafe.com/software/aspnet/31056644/using-shfileoperation-to.aspx

Make sure you read about how to handle monitoring for the thread to complete.

Edit: From the comments, this code only works on existing zip file, but @Simon provided this code to create a blank zip file

FILE* f = fopen("path", "wb");
fwrite("\x50\x4B\x05\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 22, 1, f);
fclose(f);
对你而言 2024-07-12 17:21:01

为此,我们使用 XZip。 它是免费的,以 C++ 源代码形式提供,并且运行良好。

http://www.codeproject.com/KB/cpp/xzipunzip.aspx

We use XZip for this purpose. It's free, comes as C++ source code and works nicely.

http://www.codeproject.com/KB/cpp/xzipunzip.aspx

祁梦 2024-07-12 17:21:01

快速 Google 搜索找到了这个网站:http://www.example-code。 com/vcpp/vcUnzip.asp 其中有一个非常简短的示例,用于使用可下载库解压缩文件。 还有很多其他库可用。 代码项目上提供了另一个示例,标题为 Zip and以MFC方式解压,里面有完整的gui示例。 如果您想使用 .NET 来完成此操作,那么 System.Compression 下总是有类。

还有 7-Zip 库 http://www.7-zip.org/sdk。 html。 这包括多种语言的源代码和示例。

A quick Google search came up with this site: http://www.example-code.com/vcpp/vcUnzip.asp which has a very short example to unzip a file using a downloadable library. There are plenty of other libraries available. Another example is availaible on Code Project entitled Zip and Unzip in the MFC way which has an entire gui example. If you want to do it with .NET then there is always the classes under System.Compression.

There is also the 7-Zip libarary http://www.7-zip.org/sdk.html. This includes source for several languages, and examples.

野稚 2024-07-12 17:21:01

正如注释所述,上面创建空 zip 文件的代码已损坏,但我能够让它工作。 我在十六进制编辑器中打开了一个空的 zip,并注意到了一些差异。 这是我修改后的示例:

FILE* f = fopen("path", "wb"); 
fwrite("\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 22, 1, f);
fclose(f);

这对我有用。 然后我就可以打开压缩文件夹。 未使用 winzip 等 3rd 方应用程序进行测试。

The above code to create an empty zip file is broken, as the comments state, but I was able to get it to work. I opened an empty zip in a hex editor, and noted a few differences. Here is my modified example:

FILE* f = fopen("path", "wb"); 
fwrite("\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 22, 1, f);
fclose(f);

This worked for me. I was able to then open the compressed folder. Not tested with 3rd party apps such as winzip.

纸伞微斜 2024-07-12 17:21:01

我不认为 MFC 或 Windows 标准 C/C++ API 提供内置 zip 功能的接口。

I do not think that MFC or the Windows standard C/C++ APIs provide an interface to the built in zip functionality.

情域 2024-07-12 17:21:01

如果您不想发布另一个库,您始终可以静态链接到免费软件 zip 库...

You could always statically link to the freeware zip library if you don't want to ship another library...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文