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 表达式。尝试使用固定语句。
求助??????
提前谢谢你!
【问题讨论】:
你为什么使用unsafe
和fixed
?我不明白你为什么需要这样做。对我来说,这看起来像是一个非常常规的 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# 结构中的另一个数组