Создание ZIP-файла в Windows (XP / 2003) в C / C ++ - PullRequest
11 голосов
/ 23 сентября 2008

Я ищу способ создания ZIP-файла из папки в Windows C / C ++ API. Я могу найти способ сделать это в VBScript с помощью метода Shell32.Application CopyHere, и я нашел учебное пособие, объясняющее, как это сделать также в C #, но ничего для C API (тоже хорошо, C ++, проект уже использует MFC).

Буду очень признателен, если кто-нибудь сможет поделиться примером кода C, который может успешно создать zip-файл в Windows XP / 2003. Если это не удастся, если кто-то найдет надежные документы или учебник, это было бы здорово, так как поиск в MSDN не слишком активен. Я действительно надеюсь, что вам не придется отправлять стороннюю библиотеку для этого, потому что функциональность явно есть, я просто не могу понять, как получить к ней доступ. Поиски в Google не приносят ничего полезного, только дразнящие кусочки информации. Мы надеемся, что кто-то в сообществе разобрался с этим и может поделиться им для потомков!

Ответы [ 7 ]

13 голосов
/ 23 сентября 2008

Как отмечалось в других комментариях, это будет работать только с уже созданным 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, но обратите внимание, что функция ZipAdd (), вызываемая с помощью ZIP_FOLDER, не является рекурсивной - она ​​просто создает папку в архиве. Чтобы рекурсивно сжать архив, вам нужно использовать функцию AddFolderContent (). Например, чтобы создать C: \ Sample.zip и добавить в него папку C: \ Temp, используйте следующее:

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

Важное замечание: функция AddFolderContent () не функционирует, поскольку включена в библиотеку XZip. Он вернется в структуру каталогов, но не сможет добавить файлы в zip-архив из-за ошибки в путях, переданных в ZipAdd (). Чтобы использовать эту функцию, вам нужно отредактировать источник и изменить эту строку:

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)
7 голосов
/ 23 сентября 2008

Мы используем XZip для этой цели. Это бесплатно, поставляется как исходный код C ++ и работает хорошо.

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

4 голосов
/ 23 октября 2008

Приведенный выше код для создания пустого 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.

4 голосов
/ 23 сентября 2008

РЕДАКТИРОВАТЬ: Этот ответ старый, но я не могу удалить его, потому что он был принят. Смотрите следующий

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);
2 голосов
/ 23 сентября 2008

На этом сайте появился быстрый поиск в Google: http://www.example -code.com / vcpp / vcUnzip.asp , в котором приведен очень короткий пример распаковки файла с использованием загружаемой библиотеки. Есть много других доступных библиотек. Другой пример доступен в Code Project, озаглавленном Zip and Unzip MFC-способом , в котором приведен полный пример графического интерфейса. Если вы хотите сделать это с .NET, то всегда есть классы под System.Compression.

Существует также библиотека из 7 Zip http://www.7 -zip.org / sdk.html . Это включает источник для нескольких языков и примеры.

1 голос
/ 23 сентября 2008

Я не думаю, что MFC или стандартные API C / C ++ для Windows предоставляют интерфейс для встроенной функции zip.

0 голосов
/ 23 сентября 2008

Вы всегда можете статически связываться с бесплатной zip-библиотекой, если не хотите отправлять другую библиотеку ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...