Reputation: 121719
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.
Upvotes: 0
Views: 6316
Reputation: 121719
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)
.
Upvotes: 0
Reputation: 264
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.
Upvotes: 2
Reputation: 2028
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.
Upvotes: 1