如何将包含可变大小数组的结构封送到C#?

时间:2011-05-05 17:54:59

标签: c# .net c++ marshalling

如何编组此C ++类型?

ABS_DATA结构用于将任意长的数据块与长度信息相关联。 Data数组的声明长度为1,但实际长度由Length成员给出。

typedef struct abs_data {
  ABS_DWORD Length;
  ABS_BYTE Data[ABS_VARLEN];
} ABS_DATA;

我尝试了以下代码,但它无效。数据变量总是空的,我确定它中有数据。

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
    public struct abs_data
    {
        /// ABS_DWORD->unsigned int
        public uint Length;

        /// ABS_BYTE[1]
       [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 1)]
        public string Data;
    }

4 个答案:

答案 0 :(得分:6)

不可能编组包含可变长度数组的结构(但 可能将可变长度数组编组为函数参数)。您必须手动读取数据:

IntPtr nativeData = ... ;
var length = Marshal.ReadUInt32 (nativeData) ;
var bytes  = new byte[length] ;

Marshal.Copy (new IntPtr ((long)nativeData + 4), bytes, 0, length) ;

答案 1 :(得分:5)

如果保存的数据不是字符串,则不必将其存储在字符串中。除非原始数据类型是char*,否则我通常不会编组为字符串。否则byte[]应该这样做。

尝试:

[MarshalAs(UnmanagedType.ByValArray, SizeConst=[whatever your size is]]
byte[] Data;

如果您需要稍后将其转换为字符串,请使用:

System.Text.Encoding.UTF8.GetString(your byte array here). 

显然,您需要根据需要改变编码,但UTF-8通常就足够了。

我现在看到问题,你必须编组一个VARIABLE长度数组。 MarshalAs不允许这样做,并且必须通过引用发送数组。

如果数组长度是可变的,那么byte[]需要是一个IntPtr,所以你会使用,

IntPtr Data;

而不是

[MarshalAs(UnmanagedType.ByValArray, SizeConst=[whatever your size is]]
byte[] Data;

然后,您可以使用Marshal类访问基础数据。

类似的东西:

uint length = yourABSObject.Length;
byte[] buffer = new byte[length];

Marshal.Copy(buffer, 0, yourABSObject.Data, length);

你可能需要在完成后清理你的内存以避免泄漏,但我怀疑当你的SOSObject超出范围时GC会清理它。无论如何,这是清理代码:

Marshal.FreeHGlobal(yourABSObject.Data);

答案 2 :(得分:2)

你试图整理一些byte[ABS_VARLEN]的东西,好像它是长度为1的string。你需要弄清楚ABS_VARLEN常量是什么,并将数组编组为:< / p>

[MarshalAs(UnmanagedType.LPArray, SizeConst = 1024)]
public byte[] Data;

(1024有一个占位符;填写ASB_VARLEN的实际值。)

答案 3 :(得分:0)

在我看来,固定数组并获取其地址更简单,更有效。

假设您需要将abs_data传递给myNativeFunction(abs_data*)

public struct abs_data
{
    public uint Length;
    public IntPtr Data;
}

[DllImport("myDll.dll")]
static extern void myNativeFunction(ref abs_data data);

void CallNativeFunc(byte[] data)
{
    GCHandle pin = GCHandle.Alloc(data, GCHandleType.Pinned);

    abs_data tmp;
    tmp.Length = data.Length;
    tmp.Data = pin.AddrOfPinnedObject();

    myNativeFunction(ref tmp);

    pin.Free();
}