1
votes

I am trying to port a program from VB6 to C# that reads in a binary file and parses it. I get no compile time errors or warnings however when I try to run it, before it even enters Main() it throws the exception

System.TypeLoadException was unhandled
  Message=Could not load type 'Conversion.DataStructures.ClientOld' from assembly
     'SandboxConsole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because
     it contains an object field at offset 1 that is incorrectly aligned or overlapped
     by a non-object field.
  Source=SandboxConsole
  TypeName=Conversion.DataStructures.ClientOld
  StackTrace:
       at sandbox.Program.Main(String[] args)
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

Here is a sample of the old VB6 code

Private Type SrcClientOld
    Active As String * 1            '0
    titleLength As Byte             '1
    title As String * 8             '2
    lastNameLength As Byte          '10
    LastName As String * 25         '11
    (...)
    AddedBy As String * 3           '369
    junk7 As String * 22            '372
End Type                            '394

And here is my C# code I wrote

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Pack = 1)]
struct ClientOld
{
    [FieldOffset(0)]
    public byte Active;

    [FieldOffset(1)]
    [MarshalAs(UnmanagedType.AnsiBStr)]
    public string Title;

    [FieldOffset(10)]
    [MarshalAs(UnmanagedType.AnsiBStr)]
    public string LastName;

    (...)

    [FieldOffset(368)]
    [MarshalAs(UnmanagedType.AnsiBStr)]
    public string AddedBy;

    [FieldOffset(372)]
    [MarshalAs(UnmanagedType.LPArray, SizeConst = 22)]
    public byte[] Unknown7;
}

After some googleing I thought that it was that I was missing the Pack = 1 but adding that did not solve my issue.

Any other suggestions on what to do?

EDIT:

The first charater is one byte long, here is a hex dump of the first record in the file

A.Dr.......Smith....................
41 03 44 72 2E 00 00 00 00 00 05 53 6D 69 74 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|  |  |                       |  ^LastName
|  |  ^title                  ^lastNameLength
|  ^titleLength
^Active

EDIT2: Changing my code to the following to strip out all other possible errors it is still giving me the same exception

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Pack = 1)]
struct ClientOld
{
    [FieldOffset(0)]
    public byte Active;

    [FieldOffset(1)]
    [MarshalAs(UnmanagedType.AnsiBStr)]
    public string Title;
}

I tried both fieldoffset(1) and 2 and neither work.

4
Can you post the VB6 Len(SrcClientOld) of the struct? Possibly with only the first two members? - Marino Šimić
@Marino Šimić Len returns 394 for the entire structure for every record. In the VB6 code it would record the entire fixed width block for the record and then when it was actually pulling data from the struct it would do something like Left(recClient.LastName, recClient.lastNameLength) - Scott Chamberlain
Hmm, why are you declaring what used to be a String type as a byte now? Specifically, the first member of the structure, Active. - Cody Gray♦
@Cody Gray If you look at the data structure Active is a one byte charator. In VB6 you can just use a string array to read it, but in C# you need to convert it to a byte. - Scott Chamberlain
I looked at the data structure... The problem is, a byte is not equivalent to a single character. You're getting your data types mixed up. You can't convert strings (or individual characters within) to a byte. You need to declare it either as a char or a string. - Cody Gray♦

4 Answers

1
votes

I think that

Active As String * 1

translates to

Char

which in a Unicode language is not 1 byte, so the next field offset should not be 1, but probably 7 if I understood the following correctly:.

Explanation:

Here is the key to understanding strings: when we write the code:

Dim str As String str = "help" we are not defining a Unicode character array per se. We are defining a member of a data type called BSTR, which is short for Basic String. A BSTR is, in fact, a pointer to a null-terminated Unicode character array that is preceeded by a 4-byte length field.

Edit:

By your dump it seems that the second member should have [FieldOffset(2)] because if not it would overlap on the previous member. (edit false alarm, I have seen a 00 where there is a 03).

0
votes

From what I see, you confused the declarations of "Active" and "TitleLength" which seems to be skipped completely..

0
votes

Try using an IntPtr instead of a string, and then call Marshall.PtrToStringAnsi

You will need to retain the exact layout of the old code in order for this conversion to work. The binary is going to be stored in the format that it was last saved in, so you have to reproduce it exactly, even if that means using bytes and byte arrays everywhere.

If you want to use your new layout instead, you can create a version with the old layout, load them up copy values over to your new format and then save them back out with the new format. From then on you can load them up with your new layout instead. With that scenario your options are endless for your new format.