Delphi: how to call ActiveX Functions that return an OleVariant containing a Byte Array

Some Chilkat ActiveX functions return binary data as a Variant containing a byte array.  If the Chilkat function fails, it returns an OleVariant containing an empty byte array. The following code snippet is incorrect and will cause Delphi to raise an EVariantBadIndexError exception if an empty OleVariant is returned.

// This is incorrect:
mybuffer : array of byte;
// ...
// Do not directly assign a variable declared as "array of byte" to the OleVariant returned 
// by the Chilkat function.
mybuffer := socket.ReceiveBytes();

The correct technique is to do the following:

myVariant: OleVariant;
myByteArray : array of byte;
uBound : Integer;
numBytes : Integer;

// ....

    myVariant := socket.ReceiveBytes();

    // Check the upper bound of the array contained within the Variant.
    // If it equals -1, then it is empty which means the Chilkat function call failed.
    uBound := VarArrayHighBound( myVariant, 1 );
    if (uBound < 0) then
    begin
        Memo1.Lines.Add(socket.LastErrorText);
        Exit;
    end;
    // The number of bytes in the byte array equals uBound+1
    numBytes := uBound+1;
    // Now that we know the variant is non-empty, it is safe to access it as an "array of byte"
    myByteArray := myVariant;

VB6 Variant vs Byte Array

In Visual Basic 6.0, a Variant containing a byte array is different than a byte array.
For example, examine this code:

    ' Assume mime is a ChilkatMime object...
    Set mimePart = mime.GetPart(1)
    
    thefile = FreeFile()
    'Get the attachment filename
    FileName = mimePart.FileName
    'Get the attachment
    Dim mBody As Variant
    mBody = mimePart.GetBodyBinary
    If Len(mBody) > 0 Then
        Open FileName For Binary As #thefile
            Put #thefile, , mBody
        Close #thefile
    End If

The result is not what you expected. There’s a mysterious extra 12 bytes prepended to the content that is saved. Why? Because you saved the Variant structure as well as the contents of the Variant.
This is what you really wanted:

    ' Assume mime is a ChilkatMime object...
    Set mimePart = mime.GetPart(1)
    
    thefile = FreeFile()
    'Get the attachment filename
    FileName = mimePart.FileName
    'Get the attachment
    Dim mBody() As Byte
    mBody = mimePart.GetBodyBinary
    If UBound(mBody) > 0 Then
        Open FileName For Binary As #thefile
            Put #thefile, , mBody
        Close #thefile
    End If

In the above code fragment, mBody is a byte array. There is no Variant structure encapsulating the data. The content of the MIME body is saved exactly as you expected.

Note: There is an implicit conversion happening in this statement:

mBody = mimePart.GetBodyBinary

GetBodyBinary is a function that returns a Variant. However, mBody is declared as an array of Byte. Therefore, the VB6 runtime is converting the Variant to a byte array. In other words, it’s removing the Variant wrapping so-to-speak…

Read and Write Binary Files in VB6, VBScript, ASP

The Chilkat FileAccess ActiveX is a freeware component that may be used to read and write binary files in VBScript, ASP, VB6, etc. Here are some sample code fragments:

VBScript

' Download the Chilkat FileAccess ActiveX (freeware) from here:
' http://www.chilkatsoft.com/download/FileAccess.zip
set fac = CreateObject("Chilkat.FileAccess")

' Read an entire file as binary data (returns Variant containing byte array)
byteData = fac.ReadEntireFile("dude.gif")

' Save bytes to a file:
success = fac.WriteEntireFile("out.gif", byteData)
if (success = 0) then
    MsgBox "Failed to write file."
end if

ASP

<%

' Download the Chilkat FileAccess ActiveX (freeware) from here:
' http://www.chilkatsoft.com/download/FileAccess.zip
set fac = Server.CreateObject("Chilkat.FileAccess")

' Read the entire binary file into a Variant containing a byte array
fileData = fac.ReadEntireFile(Server.MapPath("data/dude.gif"))

' Write the entire byte array to a file:
success = fac.WriteEntireFile(Server.MapPath("data/out.gif"), fileData)
if (success = 0) then
    Response.Write "Failed."
else
    Response.Write "Success."
end if

%>

Visual Basic 6.0

' Download the Chilkat FileAccess ActiveX (freeware) from here:
' http://www.chilkatsoft.com/download/FileAccess.zip

' Add a reference to Chilkat FileAccess
Dim fac As New CkFileAccess

Dim fileData() As Byte
fileData = fac.ReadEntireFile("dude.gif")

Dim success As Long
success = fac.WriteEntireFile("out.gif", fileData)
If (success = 0) Then
    MsgBox "Failed to write file."
End If

Here are some example for reading/writing binary files in C#, VB.NET, and C/C++:

C/C++

FILE *fp = fopen("dude.gif","rb");
// How big is the file?
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
unsigned char *fileData = new unsigned char[size];
// Position to the start of the file:
fseek(fp,0,SEEK_SET);
fread(fileData,1,size,fp);
fclose(fp);

fp = fopen("out.gif","wb");
fwrite(fileData,1,size,fp);
fclose(fp);

C#

byte[] fileData = System.IO.File.ReadAllBytes("dude.gif");
System.IO.File.WriteAllBytes("out.gif", fileData);

VB.NET

Dim fileData As Byte()
fileData = System.IO.File.ReadAllBytes("dude.gif")
System.IO.File.WriteAllBytes("out.gif", fileData)