请帮忙看看C#调用非托管DLL中的函数无法从结构指针中返回值的问题

langmafeng 2005-03-26 11:46:49
在VC++中结构定义:
typedef struct
{
int nDataNum; // 数据个数
double *pdData; // 存放数据数组
} PDData;

在VC++中函数定义:
int Test(..., PDData *pOutputData, ...) {}

在C#中相应结构定义:
[StructLayout(LayoutKind.Sequential)]
public class PDData
{
public int nDataNum;

public double[] pdData;

public PDData(double[] pdData)
{
this.pdData = pdData;
this.nDataNum = pdData.Length;
}
}

在C#中外部方法定义:
[DllImport("TestDll.dll")]
public static extern int PredictData(
...,
[MarshalAs(UnmanagedType.LPStruct)]
PDData pOutputData,
...);


在C#中调用该方法:(MAX_OUTPUT_DATA_NUM为常量)
PDData pOutputData = new PDData(new double[MAX_OUTPUT_DATA_NUM]);
Test(..., pOutputData, ...);

在VC++中对该Dll进行测试完全正确,pdData数组中都有值输出,但在C#中按上面的方法调用该Dll时也可以正常运行,也可以得到函数返回值,但pdData数组中没有值输出。

我还尝试以下几种方法:
在C#中将PDData定义成struct,并定义外部方法将pOutputData参数定义成ref;
在C#中定义外部方法将pOutputData参数定义成ref;
在C#中定义结构时将double[] pdData标识为[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_R8)];
在C#中定义结构时将double[] pdData标识为[MarshalAs(UnmanagedType.ByValArray, SizeConst=MAX_OUTPUT_DATA_NUM, ArraySubType=UnmanagedType.R8)];
在C#中定义结构时将double[] pdData标识为[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.R8, SizeConst=MAX_OUTPUT_DATA_NUM)];
...

但这些方法运行时都是出错,不是报内存不足就是报未将对象引用到实例,无比郁闷,请大家帮忙看看是什么地方出的问题,为什么结构中的数组的值传不回来?或者大家帮忙找个C#中调用非托管Dll中的带有返回对象参数的例子,谢谢
...全文
293 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
yezie 2005-03-27
  • 打赏
  • 举报
回复
帮你up
蒋晟 2005-03-27
  • 打赏
  • 举报
回复
在C++里面调用不需要pin
// marshal_embedded_pointer.cpp
// compile with: /clr
#include <iostream>

using namespace System;
using namespace System::Runtime::InteropServices;
using namespace stdcli::language;

// unmanaged struct, but visible to both
struct ListStruct
{
int count;
double* item;
};


#pragma unmanaged

void UnmanagedTakesListStruct(ListStruct list)
{
printf("[unmanaged] count = %d\n", list.count);
for (int i=0; i<list.count; i++)
{
printf("array[%d] = %f\n", i, list.item[i]);
}

}

#pragma managed

int main()
{
ListStruct list;
list.count = 10;
list.item = new double[list.count];

Console::WriteLine("[managed] count = {0}", list.count);
Random^ r = gcnew Random();
for (int i=0; i<list.count; i++)
{
list.item[i] = r->NextDouble() * 100.0;
Console::WriteLine("array[{0}] = {1}", i, list.item[i]);
}

UnmanagedTakesListStruct( list );
delete list.item;

return 0;
}

在托管里面给pdData赋初值的时候应该用pin指针钉住托管堆上的数据或者在传统堆上分配数据。
参考http://msdn2.microsoft.com/library/23acw07k.aspx
haoco 2005-03-27
  • 打赏
  • 举报
回复
up
zr1982930 2005-03-27
  • 打赏
  • 举报
回复
不在公司,帮顶!
速马 2005-03-27
  • 打赏
  • 举报
回复
如果改成这样定义的话:
typedef struct
{
int nDataNum; // 数据个数
double pdData[10]; // 存放数据数组
} PDData;

C#里面这样对应:
[StructLayout(LayoutKind.Sequential)]
struct DData
{
public int nDataNum;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
public double[] pdData;
}
速马 2005-03-27
  • 打赏
  • 举报
回复
typedef struct
{
int nDataNum; // 数据个数
double *pdData; // 存放数据数组
} PDData;

这里有问题吧?PDData不是指针,怎么以P开头了?有歧义 :-)
还有在struct里面定义的数组,最好是定长的,比如double pdData[10]

在C#里面你可以这样定义等价的结构:
struct DData
{
public int nDataNum;
public IntPtr pdData;
}

然后使用Marshal.Copy方法把数据从指针复制到本地数组
langmafeng 2005-03-27
  • 打赏
  • 举报
回复
谢谢Sunmast和其他的朋友,将数组设为定长后问题解决

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧