Создание ZIP-файла в Windows (XP/2003) в C/C++
Я ищу способ создания ZIP-файла из папки в Windows C/C++ API. Я могу найти способ сделать это в VBScript с помощью метода CopyHere Shell32.Application, и я нашел учебное пособие, объясняющее, как это сделать также в C#, но ничего для C API (все в порядке, проект уже использует MFC).
Я был бы очень признателен, если бы кто-нибудь смог поделиться примером кода C, который может успешно создать zip-файл в Windows XP/2003. В противном случае, если кто-то сможет найти надежные документы или учебник, это было бы замечательно, так как поиск в MSDN не слишком активен. Я действительно надеюсь избежать необходимости загружать стороннюю библиотеку для этого, потому что функциональность, очевидно, есть, я просто не могу понять, как получить к ней доступ. Поиски в Google не дают ничего полезного, а просто дразнят кусочки информации. Мы надеемся, что кто-то в сообществе разобрался с этим и может поделиться им для потомков!
7 ответов
РЕДАКТИРОВАТЬ: Этот ответ старый, но я не могу удалить его, потому что он был принят. Смотрите следующий
/questions/30573984/sozdanie-zip-fajla-v-windows-xp2003-v-cc/30573989#30573989
----- ОРИГИНАЛЬНЫЙ ОТВЕТ -----
Вот пример кода, чтобы сделать это здесь
[Редактировать: ссылка теперь не работает]
http://www.eggheadcafe.com/software/aspnet/31056644/using-shfileoperation-to.aspx
Убедитесь, что вы прочитали о том, как обрабатывать мониторинг для завершения потока.
Изменить: Из комментариев, этот код работает только на существующий файл ZIP, но @ Simon Buchan предоставил этот код для создания пустого файла 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);
Как отмечалось в других комментариях, это будет работать только с уже созданным 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)
Мы используем XZip для этой цели. Это бесплатно, поставляется как исходный код C++ и прекрасно работает.
Приведенный выше код для создания пустого 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.
На этом сайте был найден быстрый поиск в 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. Это включает в себя источник для нескольких языков и примеры.
Я не думаю, что MFC или стандартные API C / C++ для Windows предоставляют интерфейс для встроенной функции zip.
Вы всегда можете статически ссылаться на бесплатную zip-библиотеку, если не хотите отправлять другую библиотеку...