C# 错误:不能使用包含在未固定表达式中的固定大小缓冲区

Posted

技术标签:

【中文标题】C# 错误:不能使用包含在未固定表达式中的固定大小缓冲区【英文标题】:C# error: cannot use fixed size buffers contained in unfixed expressions 【发布时间】:2011-09-26 04:23:23 【问题描述】:

我正在努力使用 C# 程序从数据库中读取二进制记录。这些记录是用 Borland Delphi 创建的。这是一个例子:

// Delphi 记录定义 tBowler_Rec = 记录 民众 性别:tGender; Bowler_num:字节; 名称:tString32; 首字母:字符串[3]; ... // 对应的C#定义(非托管代码) [StructLayout(LayoutKind.Sequential, Pack=4)] 公共不安全结构 tBowler_Rec 公共 tGender 性别; 公共字节 Bowler_num; 公共固定字节名称[32]; 公共固定字节首字母[3]; ...

我实际上能够从 SQL Server 数据库中读取这个二进制结构,并在 Visual Studio 调试器中查看数据。耶!我可以毫无问题地访问“gender”和“bowler_num”等字段。耶!

问:如何将“名称”转换为 C# 字符串?

示例名称是“ASHTON”。在内存中是这样的:

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

这是我尝试访问它的方式:


[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();
        
    

我收到此错误:

错误:您不能使用包含在 unfixed 表达式。尝试使用固定语句。

求助??????

提前谢谢你!

【问题讨论】:

你为什么使用unsafefixed?我不明白你为什么需要这样做。对我来说,这看起来像是一个非常常规的 P/Invoke。 检查这个问题bytes.com/topic/c-sharp/answers/584049-fixed-byte-array-string TString32 是 Delphi 中的自定义类型,它的声明是什么?我猜是 String[32]。 您确定要在 Delphi 和 C# 之间发送 string[N] 实例吗? \0x6ASHTON\0x0\0x0 显然是很好的 ol' Turbo Pascal 字符串,其中长度字节最多可添加 255 个字符字节。 【参考方案1】:

由于我对 Delphi 不是很熟悉,所以我无法在 tString32 字段上给你一个直接的答案。好像是UnmanagedType.AnsiBStr。

如果是这样的话,我会选择这样的:

[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;

看看我是如何进行initials 编组的。如果 tString 不是 AnsiBStr,这将是从 name 编组字符的更好方法。

我还想表明我已经从结构声明中删除了固定和不安全的指令,因为这对于您尝试做的事情不是必需的。

【讨论】:

嗨 - 你是对的 - 我绝对想要“MarshalAs (UnmanagedType.ByValArray)。核心问题是“引用类型”(.Net 数组和类都是引用类型的示例)和“值类型”(如 .Net 结构......这里是非托管数组类型)。再次感谢您的帮助! @paul 虽然您已将此标记为您的答案,但我不相信 Delphi 短字符串是与 .net 互操作的好选择。事实上,我认为它们在今天几乎是一个糟糕的选择。 好像 Bstr 是一个“不错的选择” ;)?请记住我原来的帖子:我正在从数据库中读取二进制记录(在 Delphi 中创建)。那就是问题所在。 StructLayout(顺序和显式,取决于记录)、MarshalAs() 和属性访问器的组合是解决方案:) 我没有说BSTR 是这里的解决方案。它不是。您希望 array [0..N-1] of char 使用 delphi 大小,string 使用合适的 MarshalAs 在 C# 端。 Delphi shortstring 绝对不适合互操作。 我想你是说我应该修改 Delphi 代码。我不能:(【参考方案2】:

试试

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

【讨论】:

这是正确的,它考虑了第一个字节是长度,这是Delphi中典型的ShortString数据类型。 Paulsm 在这里 - 否决票不是我,本。除了“GetString()”之外,我选择了不同的路径,但我感谢反馈。谢谢!【参考方案3】:

以原始格式存储的字符串不是“空终止”(C 样式字符串)。

原来的格式是'char count Then Count chars => 0x6 = 字符数,A = 0 S = 1 H = 2 T = 3 O = 4 N = 5。

您尝试读取 Chars,直到遇到空字符。但是没有空字符,它不是以空字符结尾的字符串。您必须为此设置自定义数据转换或转换数据库。

【讨论】:

很好地观察到它的长度前缀。但是您不需要自定义转换器,.NET 为此提供了 ASCIIEncoding 类。【参考方案4】:

我在这里找到了答案: Fixed size buffer cannot be directly used from "this" object

解决方案:

    [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 绝对正确:根本问​​题是我需要将此 Delphi 数组视为值类型,而不是 C#(引用类型)数组。解决方案是“MarshalAs(UnmanagedType.ByValArray)”/

谢谢大家!

【讨论】:

以上是关于C# 错误:不能使用包含在未固定表达式中的固定大小缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

为啥固定大小的缓冲区只能是原始类型?

C# WinForm的SplitContainer控件固定Panel大小

无法将固定大小的字节数组从结构复制到 C# 结构中的另一个数组

C# 中具有固定大小数组的连续分层结构内存?

如何在 C# struct 中声明和使用固定大小的字符缓冲区

c# WINFORM窗体如何设置才可以不能随意拖动大小