Creating Zip file using WINAPI
Приклад створення .zip файлу на Windows використовуючи тільки WINAPI на C++.
Було завдання створити архіви .zip, використовуючи лише Windows API без будь-яких бібліотек третьої сторони.
Цей код був написаний і перевірений з використанням Visual Studio 2015.
#include <comdef.h>
#include <comutil.h>
#pragma comment( lib, "comsuppw.lib" )
#include <Shldisp.h>
#include <tlhelp32.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <set>
#include <vector>
std::set<DWORD> getAllThreadIds()
{
auto processId = GetCurrentProcessId();
auto currThreadId = GetCurrentThreadId();
std::set<DWORD> thread_ids;
std::unique_ptr< void, decltype(&CloseHandle) > h(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0), CloseHandle);
if (h.get() != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(h.get(), &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 == processId) && (te.th32ThreadID != currThreadId))
{
thread_ids.insert(te.th32ThreadID);
}
}
te.dwSize = sizeof(te);
} while (Thread32Next(h.get(), &te));
}
}
return thread_ids;
}
template <class InputIterator>
HRESULT CopyItems(__in InputIterator first, __in InputIterator last, __in PCWSTR dest)
{
_COM_SMARTPTR_TYPEDEF(IShellDispatch, IID_IShellDispatch);
_COM_SMARTPTR_TYPEDEF(Folder, IID_Folder);
IShellDispatchPtr shell;
FolderPtr destFolder;
variant_t dirName, fileName, options;
HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&shell);
if (SUCCEEDED(hr))
{
dirName = dest;
hr = shell->NameSpace(dirName, &destFolder);
if (SUCCEEDED(hr))
{
auto existingThreadIds = getAllThreadIds();
options = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; //NOTE: same result as 0x0000
while (first != last)
{
fileName = *first;
wprintf(L"Copying %s to %s ...\n", *first, dest);
++first;
hr = destFolder->CopyHere(fileName, options); //NOTE: this appears to always return S_OK even on error
auto updatedThreadIds = getAllThreadIds();
std::vector<decltype(updatedThreadIds)::value_type> newThreadIds;
std::set_difference(updatedThreadIds.begin(), updatedThreadIds.end(), existingThreadIds.begin(), existingThreadIds.end(), std::back_inserter(newThreadIds));
std::vector<HANDLE> threads;
for (auto threadId : newThreadIds)
threads.push_back(OpenThread(SYNCHRONIZE, FALSE, threadId));
if (!threads.empty())
{
// Waiting for new threads to finish not more than 5 min.
WaitForMultipleObjects(threads.size(), &threads[0], TRUE, 5 * 60 * 1000);
for (size_t i = 0; i < threads.size(); i++)
CloseHandle(threads[i]);
}
}
}
}
return hr;
}
int main()
{
FILE* f = fopen("C:\\temp\\new file.zip", "wb");
// see Data in HKEY_CLASSES_ROOT\.zip\CompressedFolder\ShellNew
// same value here
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);
wchar_t* files[] = {
L"C:\\temp\\file1.ext",
L"C:\\temp\\file2.ext",
L"C:\\temp\\dir1"
};
{
CoInitialize(NULL);
CopyItems(std::cbegin(files), std::cend(files), L"C:\\temp\\new file.zip");
CoUninitialize();
}
return 0;
}