c#将大型结构编组到C DLL
本文关键字:DLL 大型 结构 | 更新日期: 2023-09-27 18:08:55
我试图访问一些DLL导出的函数(用于访问一些特定的硬件)。使用DLLImport和简单数据类型访问函数是有效的。但是对于一些函数,我需要传递一些非常大的结构。为了克服自动编组的大小限制,我想到使用IntPtr。
typedef struct
{
double value[MemDepth];
double offset[MemDepth];
unsigned long start;
BOOL active;
} RegisterData;
typedef struct
{
RegisterData a,b,c,d,e,f,g,h;
BOOL active;
BOOL test;
} Register;
我就是这样把它们放在c#里的:
[StructLayout(LayoutKind.Sequential)]
public struct RegisterData
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
public double[] value;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
public double[] offset;
UInt32 start;
[MarshalAs(UnmanagedType.U1)]
bool active;
}
[StructLayout(LayoutKind.Sequential)]
public struct Register
{
RegisterData a,b,c,d,e,f,g,h;
[MarshalAs(UnmanagedType.U1)]
bool active;
[MarshalAs(UnmanagedType.U1)]
bool test;
}
我试图调用的函数在DLL中看起来如下:
long ResetControl(Register* control);
我翻译成:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 Reset(IntPtr control);
然后我尝试像这样调用函数:
public Int32 lib_Reset(ref Register control)
{
int size = System.Runtime.InteropServices.Marshal.SizeOf(control);
//IntPtr ptr = System.Runtime.InteropServices.Marshal.FreeHGlobal(size);
IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(size);
System.Console.WriteLine("requested size of: " + size); // shows correct
System.Runtime.InteropServices.Marshal.StructureToPtr(control, ptr, false);
System.Console.WriteLine("mem done");
Int32 ret = Reset(ptr);
System.Console.WriteLine("called reset");
System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, control);
System.Console.WriteLine("transfered back");
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptr);
//System.Runtime.InteropServices.Marshal.FreeHGlobal(ptr);
System.Console.WriteLine("freeed mem");
ptr = IntPtr.Zero;
return ret;
}
这将导致调用dll重置函数时出现"堆已损坏"。我已经尝试了不同的方法,但都不起作用。
编辑:我现在通过使用IntPtr方法清理和重建项目使其工作。它不能直接使用"ref"。当使用"ref"时,我总是得到:
Cannot marshal 'parameter #1': Internal limitation: structure is too complex or too large.
我现在执行以下操作,将结构体转换为指针,然后再转换回来:
public Int32 lib_Reset(ref Register control)
{
int size = Marshal.SizeOf(control);
IntPtr ptr = Marshal.FreeHGlobal(size);
Marshal.StructureToPtr(control, ptr, false);
Int32 ret = Reset(ptr);
control = (Register)Marshal.PtrToStructure(ptr, typeof(control));
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
return ret;
}
这样我可以调用Reset,但似乎函数不做任何事情。
Register reg = new Register();
reg.active = true;
System.Console.WriteLine(reg.active); // .active = true
lib_Reset(ref reg); // should set .active = false
System.Console.WriteLine(reg.active); // .active = true
Reset函数应该初始化Register结构体。因此,它将active设置为false。但是在上面显示的代码中,active的值没有改变。我测试了相同的整数值。它也没有改变。
我希望这里有人能给我指出访问数据的正确方向。
谢谢你,托拜厄斯
现在它工作了。清理和重建项目成功了。它现在正在使用IntPtr方法。但是Reset函数不影响结构中的值。
从DLL的代码我知道元素应该初始化为一些值。但是从调用前到调用后,元素的值不会改变:
Register reg = new Register();
reg.active = true;
System.Console.WriteLine(reg.active); // .active = true
lib_Reset(ref reg); // should set .active = false
System.Console.WriteLine(reg.active); // .active = true
其他元素也一样。从结构到指针的转换,然后从指针到结构的转换是否有问题?
如果我取一个整数类型的值,并且在调用之前将其设置为4,那么调用后它将是4。