开发者

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 t开发者_StackOverflowo 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.


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.


Try

Encoding.ASCII.GetString(name, 1, name[0]);


The string stored in the original format is not "null terminated" ( C style string).

The original format is 'char count Then Count chars => 0x6 = char count, A = 0 S = 1 H = 2 T = 3 O = 4 N = 5.

You try to read Chars until a null char is encountered. But there are no null char, it's not a null terminated string. You have to set a custom data caster for this or to convert the database.


I found the answer here: Fixed size buffer cannot be directly used from "this" object

Solution:

    [StructLayout(LayoutKind.Sequential, Pack=4)]
    public unsafe struct tBowler_Rec
    {
        public tGender gender;
        public byte bowler_num;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
        public byte[] name;
        ...
        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();
            }
        }

Vladimir was absolutely on the right track: the fundamental problem was that I needed to treat this Delphi array as a value type, not a C# (reference type) array. The solution is "MarshalAs(UnmanagedType.ByValArray)"/

Thank you, all!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜