Converting from VB6 string data to .NET byte array

I am writing a C # application that reads data from a SQL database generated by VB6 code. The data is an array of singles. I am trying to convert them to float []

Below is the VB6 code that writes data to the database (cannot change this code):

  Set fso = New FileSystemObject
  strFilePath = "c:\temp\temp.tmp"

  ' Output the data to a temporary file
  intFileNr = FreeFile
  Open strFilePath For Binary Access Write As #intFileNr
  Put #intFileNr, , GetSize(Data, 1)
  Put #intFileNr, , GetSize(Data, 2)
  Put #intFileNr, , Data
  Close #intFileNr

  ' Read the data back AS STRING
  Open strFilePath For Binary Access Read As #intFileNr
  strData = String$(LOF(intFileNr), 32)
  Get #intFileNr, 1, strData
  Close #intFileNr

  Call Field.AppendChunk(strData)

      

As you can see, the data is put into a temporary file, then read as a VB6 String and written to the database (dbLongBinary type string)

I've tried the following:

Executing BlockCopy

byte[] source = databaseValue as byte[];
float [,] destination = new float[BitConverter.ToInt32(source, 0), BitConverter.ToInt32(source, 4)];
Buffer.BlockCopy(source, 8, destination, 0, 50 * 99 * 4);

      

The problem is converting VB6 binary to string. The VB6 char string is 2 bytes wide and I don't know how to convert it back to binary that I can process.

Below is the dump of the temp file that the VB6 code generates: alt text http://robbertdam.nl/share/dump%20of%20text%20file%20generated%20by%20VB6.png

And here is the data dump when I read it from the database at (= VB6 line): alt text http://robbertdam.nl/share/dump%20of%20database%20field.png

+1


a source to share


6 answers


One possible way:

  • Read the data as System.Char [], which is Unicode like VB BSTR.
  • Convert it to ASCII byte array via Encoding.ASCII.GetBytes (). Effectively this removes all alternating 0s.
  • Copy this ASCII byte array into your final floating point array.

Something like that:



char[] destinationAsChars = new char[BitConverter.ToInt32(source, 0)* BitConverter.ToInt32(source, 4)];
byte[] asciiBytes = Encoding.ASCII.GetBytes(destinationAsChars);
float[] destination = new float[notSureHowLarge];
Buffer.BlockCopy(asciiBytes, 0, destination, 0, asciiBytes.Length);

      

The destination should now contain the original floats. CAVEAT: Not sure if the internal VB6 Singles format is binary compatible with the internal System.Float format. If not, all bets are disabled.

+3


a source


As you probably know, this is very bad coding at the end of VB6. What it is trying to do is make single data - the same as float in C # - as a String. But while there are better ways to do this, this is a really bad idea to start with.

The main reason is that reading binary data in VB6 BSTR will convert data from 8-bit bytes to 16-bit characters using the current code page. Thus, this can lead to different results in the database depending on which mode it is operating in. (!)

So when you read it back from the DB, if you don't specify the same codepage that is used when writing, you will get different floats, possibly even invalid ones.

This will help you see sample data in both binary (single) and DB (string) hexadecimal form to make sure this is happening.

From a later post:

This is not really "bad" VB6 code.



This is because it takes binary data into the string domain, which violates the basic rule of modern VB encoding. This is why the Byte datatype exists. If you ignore this, you may end up with unresolvable data when the database you are creating crosses locale boundaries.

What it does is store the array in a compact binary format and store it as a "chunk" in the database. There are many good reasons for this.

Of course, it has a good reason for it (although your definition of "compact" is different than usual). The ends are in order: the chosen means are not.

In the OP:

You probably cannot change what you are given as input, so above all is mostly academic. If there is still time to change the method used to create blocks, we suggest methods that do not include strings.

While applying any solution provided, do your best to avoid strings and if you cannot decode them using a custom code page that matches the one that created them.

+1


a source


This is the solution I got from the above answer.

Reading the file as unicode char[]

and then re-encoding to my standard system encoding created readable files.

internal void FixBytes()
{
    //Convert the bytes from VB6 style BSTR to standard byte[].

    char[] destinationAsChars = 
    System.Text.Encoding.Unicode.GetString(File).ToCharArray();

    byte[] asciiBytes =  Encoding.Default.GetBytes(destinationAsChars);
    byte[] newFile = new byte[asciiBytes.Length];
    Buffer.BlockCopy(asciiBytes,0, newFile, 0, asciiBytes.Length);
    File = newFile;
}

      

+1


a source


Can you clarify what the contents of the file are (like an example)? Or as binary (possibly hexadecimal) or character? If the data is a VB6 string, you will need to use float.Parse () to read it. .NET strings are also 2 bytes per character, but when loading from a file, you can control this with Encoding

.

0


a source


This is not really "bad" VB6 code. What it does is store the array in a compact binary format and store it as a "chunk" in the database. There are many good reasons for this.

The reason that the VB6 code saving it to disk and reading it is because VB6 doesn't have native support for reading and writing files in memory only. This is a general algorithm when you want to create a chunk of binary data and use it somewhere else as a database field.

This is not a problem related to this in .NET. The code I have is in VB.NET, so you'll have to convert it to C #.

Changed to handle bytes and unicode issue.

Public Function DataArrayFromDatabase(ByVal dbData As byte()) As Single(,)
    Dim bData(Ubound(dbData)/2) As Byte
    Dim I As Long
    Dim J As Long

    J=0
    For I = 1 To Ubound(dbData) step 2
        bData(J) = dbData(I)
        J=1
    Next I

    Dim sM As New IO.MemoryStream(bData)
    Dim bR As IO.BinaryReader = New IO.BinaryReader(sM)
    Dim Dim1 As Integer = bR.ReadInt32
    Dim Dim2 As Integer = bR.ReadInt32
    Dim newData(Dim1, Dim2) As Single

    For I = 0 To Dim2
        For J = 0 To Dim1
            newData(J, I) = bR.ReadSingle
        Next
    Next

    bR.Close()
    sM.Close()
    Return newData
End Function

      

The key trick is to read the data just as if you were in VB6. We have the ability to use MemoryStreams in .NET, so it's pretty easy.

First, we skip every other byte to avoid Unicode padding.

Then we create a memystream from a byte array. The BinaryReader is then initialized with a MemoryStream.

We read in the first dimension of a VB6 Long array or .NET Int32 We read in the second dimension of a VB6 Long array or .NET Int32

Read loops are built in reverse order according to the size of the array. Dim2 is the outer loop and Dim1 is the inner loop. The reason for this is that VB6 stores arrays in binary format.

Return newData and you have successfully restored the original array created in VB6!

Now you can try a math trick. Two dimensions are 4 bytes / characters, each array element is 4 bytes / characters. But for long term maintainability, I find using byte manipulation with memistreams much more explicit. It takes a little more code, but is much clearer when you revisit it 5 years later.

0


a source


We first skip every byte until we exclude the Unicode padding.

Hmmm ... if that were the right strategy, then every other column in the dump row dump would be composed of nothing but zeros. But a quick scan of the former shows that it is not. There are actually many non-zero bytes in these columns. Can we let them just give them up?

This shows that the conversion to Unicode caused by the use of strings does not just add padding, but changes the nature of the data. What you call complement is a coincidence of the fact that the ASCII range (binary 00-7F) maps to the same Unicode range. But this is not the case for binary 80-FF.

Take a look at the first stored value, which is the original byte value 94 9A 27 3A. When converted to Unicode, they DO NOT become 94 00 97 00 27 00 3A 00. They become 1D 20 61 01 27 00 3A 00.

Dropping every other byte gives you 1D 61 27 3A - not the original 94 9A 27 3A.

0


a source







All Articles