2008-09-23 1 views
11

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

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

+1

Как создать сжатую папку в Windows с помощью API Win32: http://ixmx.wordpress.com/2009/09/16/how-to-create-a-compress-folder-in-windows-using-win32 -api/ – anno 2010-09-08 14:44:31

+0

Вы можете использовать Shell32.CopyHere с C или C++, просто вызовите необходимые API win32 для создания экземпляра COM-объекта. – Motes 2013-12-03 18:43:34

ответ

4

EDIT: Этот ответ старый, но я не могу удалить его, потому что он был принят. Смотрите следующий один

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

----- ORIGIN ОТВЕТ -----

Существует пример кода, чтобы сделать это здесь

[EDIT: Ссылка теперь сломана]

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

Убедитесь, что вы читали о том, как обрабатывать наблюдения за нить, чтобы закончить.

Edit: Из комментариев, этот код работает только на существующий почтовый файл, но @Simon при условии, этот код, чтобы создать пустой файл почтового индекса

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); 
+0

Я раньше нашел этот пример кода, но на самом деле он не работает как есть. В конце концов, я получил его для компиляции и запуска после некоторой настройки и подобных работ, если файл ZIP уже существует. Если никто другой не придумает более полный ответ, я соглашусь с этим. – Jay 2008-09-23 02:18:17

+1

Создайте пустой почтовый индекс с помощью: FILE * f = fopen («путь», «wb»); fwrite ("\ x80 \ x75 \ 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); – 2008-09-23 03:26:41

1

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

0

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

2

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

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

13

Как уже отмечались в комментариях, это будет работать только на уже созданный 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; 

} 

я решил не идти по этому пути, несмотря на получение пола-функциональный кода, так как после проведения дальнейшего расследования, она появляется папкой :: метод 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) 
3

Приведенный выше код, чтобы создать пустой почтовый файл поврежден, а комментарии, но я смог заставить его работать. Я открыл пустой почтовый индекс в шестнадцатеричном редакторе и отметил несколько отличий. Вот мой модифицированный пример:

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.