Search code examples
c#delphiinterop

C# error: cannot use fixed size buffers contained in unfixed expressions


I'm struggling with a C# program to read binary records from a database. The records were created with Borland Delphi. Here's an example:

// Delphi record definition
  tBowler_Rec = Record
  public
    gender          : tGender;
    bowler_num      : byte;
    name            : tString32;
    initials        : String[3];
    ...
// Corresponding C# definition (unmanaged code)
    [StructLayout(LayoutKind.Sequential, Pack=4)]
    public unsafe struct tBowler_Rec
    {
        public tGender gender;
        public byte bowler_num;
        public fixed byte name[32];
        public fixed byte initials[3];
        ...

I'm actually able to read this binary struct out of a SQL Server database and see the data in the Visual Studio debugger. Yay! I'm able to access fields like "gender" and "bowler_num" with no problem. Yay!

Q: How do I turn "name" into a C# string?

An example name is "ASHTON". It looks like this in memory:

\0x6ASHTON\0x0\0x0...

Here's how I'm trying to access it:


[StructLayout(LayoutKind.Sequential, Pack=4)]
public unsafe struct tBowler_Rec
{
    public tGender gender;
    public byte bowler_num;
    public fixed byte name[32];
    ...
    public string Name
    {
        get
        {
            StringBuilder sb = new StringBuilder();
            int ilen = name[0];
            for (int i = 1; i <= ilen; i++)
                sb.Append(name[i]);
            return sb.ToString();
        }
    }

I'm getting this error:

Error: You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement.


Solution

  • As I'm not very familiar with Delphi so I can't give you a straight answer on the tString32 field. It seems to be UnmanagedType.AnsiBStr.

    If this is the case, I would go for something like this:

    [StructLayout(LayoutKind.Sequential, Pack=4)]
    public struct tBowler_Rec
    {
        public tGender gender;
        public byte bowler_num;
        [MarshalAs(UnmanagedType.AnsiBStr)]
        public string name;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public char[] initials;
    

    Look also how I'm doing the initials marshaling. If tString is not an AnsiBStr, this would be a better way to marshal the characters from name.

    I also would like to show that I've removed the fixed and unsafe instructions from the struct declaration as this is not necessary for what you are trying to do.