This is my module file for using zlibwapi.dll in VB6. To use this code, simply copy the text in the code box at the bottom of this post, and paste it into an empty module in VB6. Note that you must have the DLL file in question in either the windows\system32 folder (windows\syswow64 on x64 Windows), or in the folder where your VB6 project files are for the project you are working on (the same folder where your EXE file will be compiled to). Normally Zlib's only easy to use compression/decompression functions are compress, compress2, and uncompress. Unfortunately those functions expect the compressed data to exist within the a Zlib container (has a 2 byte header, and a 4 byte footer that is an Adler32 checksum of the uncompressed data). However, a number of various file formats expect raw "deflate" data to be in use (I believe that the Zip file format is one), without any Zlib container surrounding the compressed data. Deflate is the name of the algorithm that Zlib uses. Now Zlib does have functions for directly accessing raw deflate streams, but they are VERY difficult to use, and require initializing special structures associated with the streams, requiring a massive amount of overhead in any program implementing it. Zlib also has builtin functions for working with GZip files directly, but what if you want to handle an in-memory copy of a GZip container? Well once again, you can use the stream commands for that (and again use a HUGE amount of overhead in writing what could otherwise be a very simple program).
That's where my module comes in. It completely gets around the need for stream handling, by ultimately always using the compress2, uncompress, compressBound, and crc32 Zlib methods, and then handling the container formats as needed directly in VB6 code (and also using the Windows API CopyMemory method where needed). It contains methods for handling not only Zlib containers, but also raw deflate streams, and GZip containers. And it does it all using memory. The methods for the raw deflate streams work by calling the Zlib functions, and then adding or removing the Zlib container from the compressed data as needed. Of course, when it recreates the Zlib container, it doesn't have access to the uncompressed data until it decompresses it, so there's no way for it to recreate the Adler32 checksum, and without the correct checksum the Zlib decompressor returns an error, even though it does correctly decompress the data. As a result, error checking for decompression of a raw deflate stream is impossible, and therefore the Inflate method (Inflate is what they call decompressing Deflated data), is a "sub" rather than a "function", as it can't return any usable error, as otherwise it would always be signalling that it failed. I recommend that if you use raw deflate streams, that you use some other error checking method outside of the compression functions, such as storing a checksum or CRC separately (either in the header of your file, or in a separate file that your program will also load in addition to the file containing compressed data). My GZip compress and decompress functions call my Inflate and Deflate methods, and add or remove the GZip container from the data as needed. GZip uses CRC32 rather than a checksum, and since it can check for errors, the decompress method for GZip once again is a function. I have verified that my GZip compress function generates a valid GZip container, by saving it to a file and then opening it in the program 7Zip. My Zlib functions are included just to simplify the use of Zlib, as no special preprocessing or postprocessing of container formats is required here. These simplify handling of Zlib containers, by using byte arrays, rather than arbitrary data, so you don't need to know the size of the data that's being fed to it. These functions internally automatically determine the size of the input data by using the UBound VB6 function on the arrays. The only thing you will need to know is upon decompressing a Zlib stream or a raw Deflate stream, you will will need to know the original uncompressed size. This can be determined easily by your own use of the UBound function in your own code, and then this info can be saved into whatever structure or file format you use to pass information to and from this program. Only difference is with a GZip container, which already stores the original uncompressed size as part of the container (it's a 4byte Long value, which is the last 4 bytes of the 8byte footer at the end of the container, according to the official specs for GZip).
All my functions use the Boolean type for the return value, and output True for success, and False for failure. All input and output data are byte arrays. All byte arrays are to be 1D arrays, with the first index at 0 (zero). My GZip functions also handle a stored filename. For compressing, supplying a filename is optional. For decompressing, even if you don't have a filename stored, since it is passed byref, a filename variable MUST be supplied, even if it's only acting as a dummy/filler variable if you have no intent to use that info. All other optional fields that may be present in a GZip container are ignored by my decompression function, and are simply skipped if they are present. If the header indicates they exist they do get processed to find their length, but only for the purpose of skipping them to get to the deflate stream, as no info stored in them is returned by my GZip decompress function. Likewise , the only optional field that can be saved by my GZip compress function is the filename field.
That's where my module comes in. It completely gets around the need for stream handling, by ultimately always using the compress2, uncompress, compressBound, and crc32 Zlib methods, and then handling the container formats as needed directly in VB6 code (and also using the Windows API CopyMemory method where needed). It contains methods for handling not only Zlib containers, but also raw deflate streams, and GZip containers. And it does it all using memory. The methods for the raw deflate streams work by calling the Zlib functions, and then adding or removing the Zlib container from the compressed data as needed. Of course, when it recreates the Zlib container, it doesn't have access to the uncompressed data until it decompresses it, so there's no way for it to recreate the Adler32 checksum, and without the correct checksum the Zlib decompressor returns an error, even though it does correctly decompress the data. As a result, error checking for decompression of a raw deflate stream is impossible, and therefore the Inflate method (Inflate is what they call decompressing Deflated data), is a "sub" rather than a "function", as it can't return any usable error, as otherwise it would always be signalling that it failed. I recommend that if you use raw deflate streams, that you use some other error checking method outside of the compression functions, such as storing a checksum or CRC separately (either in the header of your file, or in a separate file that your program will also load in addition to the file containing compressed data). My GZip compress and decompress functions call my Inflate and Deflate methods, and add or remove the GZip container from the data as needed. GZip uses CRC32 rather than a checksum, and since it can check for errors, the decompress method for GZip once again is a function. I have verified that my GZip compress function generates a valid GZip container, by saving it to a file and then opening it in the program 7Zip. My Zlib functions are included just to simplify the use of Zlib, as no special preprocessing or postprocessing of container formats is required here. These simplify handling of Zlib containers, by using byte arrays, rather than arbitrary data, so you don't need to know the size of the data that's being fed to it. These functions internally automatically determine the size of the input data by using the UBound VB6 function on the arrays. The only thing you will need to know is upon decompressing a Zlib stream or a raw Deflate stream, you will will need to know the original uncompressed size. This can be determined easily by your own use of the UBound function in your own code, and then this info can be saved into whatever structure or file format you use to pass information to and from this program. Only difference is with a GZip container, which already stores the original uncompressed size as part of the container (it's a 4byte Long value, which is the last 4 bytes of the 8byte footer at the end of the container, according to the official specs for GZip).
All my functions use the Boolean type for the return value, and output True for success, and False for failure. All input and output data are byte arrays. All byte arrays are to be 1D arrays, with the first index at 0 (zero). My GZip functions also handle a stored filename. For compressing, supplying a filename is optional. For decompressing, even if you don't have a filename stored, since it is passed byref, a filename variable MUST be supplied, even if it's only acting as a dummy/filler variable if you have no intent to use that info. All other optional fields that may be present in a GZip container are ignored by my decompression function, and are simply skipped if they are present. If the header indicates they exist they do get processed to find their length, but only for the purpose of skipping them to get to the deflate stream, as no info stored in them is returned by my GZip decompress function. Likewise , the only optional field that can be saved by my GZip compress function is the filename field.
Code:
Private Declare Function crc32 Lib "zlibwapi.dll" (ByVal OldCRC As Long, ByRef Data As Any, ByVal DataLen As Long) As Long
Private Declare Function compress2 Lib "zlibwapi.dll" (ByRef Dest As Byte, ByRef DestLen As Long, ByRef Src As Byte, ByVal SrcLen As Long, ByVal CompLevel As Long) As Long
Private Declare Function uncompress Lib "zlibwapi.dll" (ByRef Dest As Byte, ByRef DestLen As Long, ByRef Src As Byte, ByVal SrcLen As Long) As Long
Private Declare Function compressBound Lib "zlibwapi.dll" (ByVal SrcLen As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Public Function ZlibCompress(ByRef Dest() As Byte, ByRef Src() As Byte, Optional ByVal CompLevel As Long = 9) As Boolean
Dim SrcLen As Long
Dim DestLen As Long
Dim ErrorNum As Long
SrcLen = UBound(Src) + 1
DestLen = compressBound(SrcLen)
ReDim Dest(DestLen - 1)
ErrorNum = compress2(Dest(0), DestLen, Src(0), SrcLen, CompLevel)
If ErrorNum Then Exit Function
ReDim Preserve Dest(DestLen - 1)
ZlibCompress = True
End Function
Public Function ZlibDecompress(ByRef Dest() As Byte, ByRef Src() As Byte, ByVal UncompLen As Long) As Boolean
Dim SrcLen As Long
Dim DestLen As Long
Dim ErrorNum As Long
SrcLen = UBound(Src) + 1
DestLen = UncompLen
ReDim Dest(DestLen - 1)
ErrorNum = uncompress(Dest(0), DestLen, Src(0), SrcLen)
If ErrorNum Then Exit Function
ReDim Preserve Dest(DestLen - 1)
ZlibDecompress = True
End Function
Public Function Deflate(ByRef Dest() As Byte, ByRef Src() As Byte, Optional ByVal CompLevel As Long = 9) As Boolean
Dim ZlibCompData() As Byte
Dim Success As Boolean
Success = ZlibCompress(ZlibCompData, Src, CompLevel)
If Success = False Then Exit Function
ReDim Dest(UBound(ZlibCompData) - 6)
CopyMemory Dest(0), ZlibCompData(2), UBound(Dest) + 1
Deflate = True
End Function
Public Sub Inflate(ByRef Dest() As Byte, ByRef Src() As Byte, ByVal UncompLen As Long)
Dim ZlibCompData() As Byte
Dim CheckSumInput As Long
Dim n As Long
ReDim ZlibCompData(UBound(Src) + 6)
ZlibCompData(0) = &H78
ZlibCompData(1) = &H80
CheckSumInput = &H7880&
For n = 0 To 31
If (CheckSumInput Or n) Mod 31 = 0 Then
ZlibCompData(1) = ZlibCompData(1) Or n
Exit For
End If
Next n
CopyMemory ZlibCompData(2), Src(0), UBound(ZlibCompData) + 1
ZlibDecompress Dest(), ZlibCompData(), UncompLen
End Sub
Public Function GzipCompress(ByRef Dest() As Byte, ByRef Src() As Byte, Optional ByVal CompLevel As Long = 9, Optional ByVal FileName As String) As Boolean
Const HeaderLen As Long = 10
Const FooterLen As Long = 8
Dim DeflatedData() As Byte
Dim DeflateLen As Long
Dim FNameBytes() As Byte
Dim FNameLen As Long
Dim CRC As Long
Dim UncompLen As Long
Dim Success As Boolean
Success = Deflate(DeflatedData, Src, CompLevel)
If Success = False Then Exit Function
DeflateLen = UBound(DeflatedData) + 1
FNameBytes() = StrConv(FileName, vbFromUnicode)
FNameLen = Len(FileName)
If FNameLen > 0 Then
FNameLen = FNameLen + 1
ReDim Preserve FNameBytes(FNameLen - 1)
End If
UncompLen = UBound(Src) + 1
CRC = crc32(0, Src(0), UncompLen)
ReDim Dest(HeaderLen + FNameLen + DeflateLen + FooterLen - 1)
Dest(0) = 31
Dest(1) = 139
Dest(2) = 8
If FNameLen Then
Dest(3) = 8
CopyMemory Dest(HeaderLen), FNameBytes(0), FNameLen
End If
If CompLevel < 5 Then Dest(8) = 4 Else Dest(8) = 2
Dest(9) = 0
CopyMemory Dest(HeaderLen + FNameLen), DeflatedData(0), DeflateLen
CopyMemory Dest(HeaderLen + FNameLen + DeflateLen), CRC, 4
CopyMemory Dest(HeaderLen + FNameLen + DeflateLen + 4), UncompLen, 4
GzipCompress = True
End Function
Public Function GzipDecompress(ByRef Dest() As Byte, ByRef Src() As Byte, ByRef FileName As String) As Boolean
Const HeaderLen As Long = 10
Const ID1 As Byte = 31
Const ID2 As Byte = 139
Const CM As Byte = 8
Const FooterLen As Long = 8
Dim DataPtr As Long
Dim SrcLen As Long
Dim FLG As Byte
Dim XLEN As Integer
Dim DeflatedData() As Byte
Dim DeflateLen As Long
Dim TempStr As String
Dim FNameLen As Long
Dim FCommentLen As Long
Dim LenBeforeData As Long
Dim UncompLen As Long
Dim CRC As Long
Dim CRC2 As Long
SrcLen = UBound(Src) + 1
LenBeforeData = HeaderLen
If Src(0) <> ID1 Then Exit Function
If Src(1) <> ID2 Then Exit Function
If Src(2) <> CM Then Exit Function
FLG = Src(3)
If FLG And 2 Then LenBeforeData = LenBeforeData + 2
If FLG And 4 Then
CopyMemory XLEN, Src(HeaderLen), 2
LenBeforeData = LenBeforeData + 2 + XLEN
DataPtr = HeaderLen + 2 + XLEN
Else
DataPtr = HeaderLen
End If
If (FLG And 8) Or (FLG And 16) Then
Do Until Src(DataPtr) = 0
TempStr = TempStr & Chr$(Src(DataPtr))
DataPtr = DataPtr + 1
Loop
If FLG And 8 Then
FNameLen = Len(TempStr) + 1
FileName = Left$(TempStr, FNameLen - 1)
LenBeforeData = LenBeforeData + FNameLen
If FLG And 16 Then
DataPtr = DataPtr + 1
TempStr = ""
Do Until Src(DataPtr) = 0
TempStr = TempStr & Chr$(Src(DataPtr))
DataPtr = DataPtr + 1
Loop
FCommentLen = Len(TempStr) + 1
LenBeforeData = LenBeforeData + FCommentLen
End If
Else
FCommentLen = Len(TempStr) + 1
LenBeforeData = LenBeforeData + FCommentLen
End If
End If
DeflateLen = SrcLen - LenBeforeData - 8
ReDim DeflatedData(DeflateLen - 1)
CopyMemory CRC, Src(LenBeforeData + DeflateLen), 4
CopyMemory UncompLen, Src(LenBeforeData + DeflateLen + 4), 4
CopyMemory DeflatedData(0), Src(LenBeforeData), DeflateLen
ReDim Dest(UncompLen - 1)
Inflate Dest(), DeflatedData(), UncompLen
CRC2 = crc32(0, Dest(0), UncompLen)
If CRC2 <> CRC Then Exit Function
GzipDecompress = True
End Function