Decompress a gz file using winapi

-1

How do I use microsoft's Compression API to deflate a .gz file in windows? I am using VS 2019, C++.

Even though I am looking for a gz decompress, I started testing only with a zip file when I ran into problems. I figure the MS Compression API should at least work with zip files. But I have yet to get it to work. I tested this with a zip file that winrar created and can decompress fine.

I tried to use this example https://docs.microsoft.com/en-us/windows/win32/cmpapi/using-the-compression-api-in-buffer-mode but the second call to Decompress function produces the error "Decompress failed with error 605: The specified buffer contains ill-formed data".

I tried all 4 algorithms provided in the API and got the same result for each.

Debug shows that the DecompressedBufferSize is never populated by the Decompress function. This could be a result of the zip file format not being supported by the Microsoft Compression API? I tried all 4 decompress formats they provided and the createDecompressor and Decompress (first) functions always returns success.

Here's the code I used in vs 2019:

#include <Windows.h>
#include <stdio.h>
#include <compressapi.h>
void wmain(_In_ int argc, _In_ WCHAR *argv[])
{ 
DECOMPRESSOR_HANDLE Decompressor    = NULL;
PBYTE CompressedBuffer              = NULL;
PBYTE DecompressedBuffer            = NULL;
HANDLE InputFile                    = INVALID_HANDLE_VALUE;
HANDLE DecompressedFile             = INVALID_HANDLE_VALUE;    
BOOL DeleteTargetFile               = TRUE;    
BOOL Success;
SIZE_T DecompressedBufferSize, DecompressedDataSize;
DWORD InputFileSize, ByteRead, ByteWritten;
ULONGLONG StartTime, EndTime;
LARGE_INTEGER FileSize;    
double TimeDuration;

if (argc != 3) 
{
    wprintf(L"Usage:\n\t%s <compressed_file_name> <decompressed_file_name>\n", argv[0]);
    return;
}

//  Open input file for reading, existing file only.
InputFile = CreateFile(
    argv[1],                  //  Input file name, compressed file
    GENERIC_READ,             //  Open for reading
    FILE_SHARE_READ,          //  Share for read
    NULL,                     //  Default security
    OPEN_EXISTING,            //  Existing file only
    FILE_ATTRIBUTE_NORMAL,    //  Normal file
    NULL);                    //  No template

if (InputFile == INVALID_HANDLE_VALUE)
{
    wprintf(L"Cannot open \t%s\n", argv[1]);
    goto done;
}

//  Get compressed file size.
Success = GetFileSizeEx(InputFile, &FileSize);
if ((!Success)||(FileSize.QuadPart > 0xFFFFFFFF))
{
    wprintf(L"Cannot get input file size or file is larger than 4GB.\n");        
    goto done;
}
InputFileSize = FileSize.LowPart;

//  Allocation memory for compressed content.
CompressedBuffer = (PBYTE)malloc(InputFileSize);
if (!CompressedBuffer)
{
    wprintf(L"Cannot allocate memory for compressed buffer.\n");
    goto done;
}

//  Read compressed content into buffer.
Success = ReadFile(InputFile, CompressedBuffer, InputFileSize, &ByteRead, NULL);
if ((!Success) || (ByteRead != InputFileSize))
{
    wprintf(L"Cannot read from \t%s\n", argv[1]);
    goto done;
}

//  Open an empty file for writing, if exist, destroy it.
DecompressedFile = CreateFile(
    argv[2],                  //  Decompressed file name
    GENERIC_WRITE|DELETE,     //  Open for writing
    0,                        //  Do not share
    NULL,                     //  Default security
    CREATE_ALWAYS,            //  Create a new file, if exists, overwrite it.
    FILE_ATTRIBUTE_NORMAL,    //  Normal file
    NULL);                    //  No template

if (DecompressedFile == INVALID_HANDLE_VALUE)
{
    wprintf(L"Cannot create file \t%s\n", argv[2]);
    goto done;
}

//  Create an XpressHuff decompressor.
Success = CreateDecompressor(
    COMPRESS_ALGORITHM_XPRESS_HUFF, //  Compression Algorithm
    NULL,                           //  Optional allocation routine
    &Decompressor);                 //  Handle

if (!Success)
{
    wprintf(L"Cannot create a decompressor: %d.\n", GetLastError());
    goto done;
}

//  Query decompressed buffer size.
Success = Decompress(
    Decompressor,                //  Compressor Handle
    CompressedBuffer,            //  Compressed data
    InputFileSize,               //  Compressed data size
    NULL,                        //  Buffer set to NULL
    0,                           //  Buffer size set to 0
    &DecompressedBufferSize);    //  Decompressed Data size

//  Allocate memory for decompressed buffer.
if (!Success)
{
    DWORD ErrorCode = GetLastError();

    // Note that the original size returned by the function is extracted 
    // from the buffer itself and should be treated as untrusted and tested
    // against reasonable limits.
    if (ErrorCode != ERROR_INSUFFICIENT_BUFFER) 
    {
        wprintf(L"Cannot decompress data: %d.\n",ErrorCode);
        goto done;
    }

    DecompressedBuffer = (PBYTE)malloc(DecompressedBufferSize);
    if (!DecompressedBuffer)
    {
        wprintf(L"Cannot allocate memory for decompressed buffer.\n");
        goto done;
    }           
}
    
StartTime = GetTickCount64();

//  Decompress data and write data to DecompressedBuffer.
Success = Decompress(
    Decompressor,               //  Decompressor handle
    CompressedBuffer,           //  Compressed data
    InputFileSize,              //  Compressed data size
    DecompressedBuffer,         //  Decompressed buffer
    DecompressedBufferSize,     //  Decompressed buffer size
    &DecompressedDataSize);     //  Decompressed data size

if (!Success)
{
    wprintf(L"Cannot decompress data: %d.\n", GetLastError());
    goto done;
}

EndTime = GetTickCount64();

//  Get decompression time.
TimeDuration = (EndTime - StartTime)/1000.0;

//  Write decompressed data to output file.
Success = WriteFile(
    DecompressedFile,       //  File handle
    DecompressedBuffer,     //  Start of data to write
    DecompressedDataSize,   //  Number of byte to write
    &ByteWritten,           //  Number of byte written
    NULL);                  //  No overlapping structure
if ((ByteWritten != DecompressedDataSize) || (!Success))
{
    wprintf(L"Cannot write decompressed data to file.\n");
    goto done;
}

wprintf(
    L"Compressed size: %d; Decompressed Size: %d\n",
    InputFileSize,
    DecompressedDataSize);
wprintf(L"Decompression Time(Exclude I/O): %.2f seconds\n", TimeDuration);
wprintf(L"File decompressed.\n");

DeleteTargetFile = FALSE;

done:
if (Decompressor != NULL)
{
    CloseDecompressor(Decompressor);
}

if (CompressedBuffer) 
{
    free(CompressedBuffer);
}

if (DecompressedBuffer)
{
    free(DecompressedBuffer);
}

if (InputFile != INVALID_HANDLE_VALUE)
{
    CloseHandle(InputFile);
}

if (DecompressedFile != INVALID_HANDLE_VALUE)
{
    //  Compression fails, delete the compressed file.
    if (DeleteTargetFile)
    {
        FILE_DISPOSITION_INFO fdi;
        fdi.DeleteFile = TRUE;      //  Marking for deletion
        Success = SetFileInformationByHandle(
            DecompressedFile,
            FileDispositionInfo,
            &fdi,
            sizeof(FILE_DISPOSITION_INFO));    
        if (!Success) {
            wprintf(L"Cannot delete corrupted decompressed file.\n");
        }
    }
    CloseHandle(DecompressedFile);
}
}

update: Thanks for the answers guys. For anyone else googling this error, this might be the only page that specifically mentions error 605 with the compression API. Part of the issue is that a corrupt buffer is so generic. I think you are right, the problem is that the compression API doesn't seem to be able to handle zip files. Easy as that. I will continue to look for alternatives.

c++
winapi
asked on Stack Overflow Sep 9, 2020 by kbaud • edited Sep 15, 2020 by kbaud

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0