如何从非托管 DLL 访问包含动态数组的 C# 结构体?

4
在我的C代码中,我有一个包含许多未知大小数组的结构体,在一个非托管dll(C代码)中。我需要将这个结构体的一个实例的数据转换为C#,稍后再将其发送回非托管C代码。
一旦到达C#,我不需要操作这些数据,只需持有它/存储它一段时间(因此它可以保留在一个字节数组中)。
我不想使用关键字“unsafe”,因为这是一个大项目,而这只是一个小片段,我不想那样编译。
我尝试将其作为lpArray进行转换,一切看起来都很好,但当我查看返回C#之后的内容时,它总是为空。这种类型的转换方式对于各种类型的动态数组都适用,但不适用于结构体。
在网上搜索未果,更复杂的情况比我的还要多,但如果有人见过这样的链接,请在这里发布,我会非常感激!
谢谢。
-更新:下面是我的代码大致结构:
C#:
[DllImport("mydll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern int W_Thread_Connect_NET(
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 100)] string IPAddress, 
    int DevicePort,
    [MarshalAs(UnmanagedType.LPArray)] byte[] connectionHandle
);

//and call it like this, with an empty struc to be populated by c (can this be done? it is comming back without the data):
byte[] myStrucParam= new byte[100];
int result = W_Thread_Connect_NET(myStrucParam, myParam1, myParam2, ...);

c:

 typedef struct myStructDef{
     char* myArray1, 
     char* myArray2,
     int myInt1,
     ...
 } mystrucObj, *pMystrucObj;

//method that i am wanting to marshal the struct as a paramter here..
 MYDLL_DLLIMPORT int APIENTRY W_Thread_Connect_NET(pMystrucObj strucReturn_handle, char * IPAddress, int DevicePort, ...)
    {
      //(omitted)
    }

3
请展示结构体的C声明和接收该结构体的本地函数的C声明。然后我们可以为您展示所需的PInvoke。 - David Heffernan
谢谢David - 我添加了一些类似于我正在处理的代码。当然,我不能在这里粘贴实际的代码,因为它很大而且杂乱,并且因为我不允许分享它。不过,如果有其他信息需要包含以帮助指导我找到答案或更多信息的链接,请告诉我! - Chris
结构体的起源在哪里?C字符串是如何分配的? - David Heffernan
@Chris 我不认为Hans有任何负面意思。我确信他并不想编译这段代码,他可以从阅读中解决这些问题。Hans是你想要向他寻求建议的人,他真的非常有知识! - David Heffernan
1
好的,谢谢大家。 - Chris
显示剩余2条评论
1个回答

2
你说C#代码不需要操作结构体,这使得解决问题变得很简单。你可以将结构体指针视为一个不透明指针,即IntPtr
首先,你需要在本地代码中添加一个新函数:
pMystrucObj CreateStruct(void)
{
    pMystrucObj res = malloc(sizeof(*res));
    return res;
}

然后在你的C#代码中,你可以这样调用它:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr CreateStruct();

现在这样声明 W_Thread_Connect_NET:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern int W_Thread_Connect_NET(
    IntPtr theStructPtr,
    string IPAddress, 
    int DevicePort,
    ....
);

把它们全部这样调用:

IntPtr theStructPtr = CreateStruct();
int res = W_Thread_Connect_NET(theStructPtr, IPAddress, DevicePort, ...);

当然,你会想要添加另一个函数名为DestroyStruct来在完成使用结构体后释放其内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接