C# PInvoke结构/函数的奇数错误
我目前正在为C++ API编写一个C包装,但是一个特定的结构和一个依赖于这个结构的函数在调试时给了非常奇怪的错误。 C++结构:C# PInvoke结构/函数的奇数错误,c#,memory,struct,pinvoke,marshalling,C#,Memory,Struct,Pinvoke,Marshalling,我目前正在为C++ API编写一个C包装,但是一个特定的结构和一个依赖于这个结构的函数在调试时给了非常奇怪的错误。 C++结构: typedef struct { unsigned __int handle; char name[80]; unsigned int unique_ID; } DeviceInfo; 然后是此功能: int __stdcall get_device_info(DeviceInfo di[], const int leng
typedef struct
{
unsigned __int handle;
char name[80];
unsigned int unique_ID;
} DeviceInfo;
然后是此功能:
int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);
结构和函数按如下方式导入:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]
public String name;
public UInt32 unique_ID;
}
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(ref DeviceInfo di, int length_of_di_array, ref numValidDevices);
此结构和函数的预期用途只是从板Im访问中获取一些设备信息。目前我没有访问C++中的函数体,所以只能假设它工作100%(C++中的工作很好)。
问题是,当我使用该函数运行一个结构数组时,它会输出我要查找的数据,但在运行时也会开始失败,给我提供各种错误窗口
C#代码:
预期产出:
Handle: 3126770193
Name: DEVICE_A
Unique ID: 12345678
IntPtr输出:
Handle: 3126770193
Name:
Unique ID: 1145128264
奇怪的是,使用IntPtr不会导致上述任何错误,并且运行良好。
这使我相信问题是将C++ char封送到字符串中,但我不确定问题是否在于封送处理、内存管理(没有一个)?或者我完全不理解的问题。
如果您能提供任何反馈,我将不胜感激。我已经被难住了好几个星期了……这里有些东西不对劲。我不清楚该如何调用这个函数 特别是,本宣言:
int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);
与您的使用方式不匹配:
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(ref DeviceInfo di, const int length_of_di_array, int* p_numValidDevices);
...
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data
}
还是你想让整个阵列通过一次
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
rc = get_device_info(ref di[0], 16, ref numValidDevices); //accesses each device element and returns the data
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
Console.WriteLine(...);
}
您得到的异常表明您正在pinvoking的非托管代码正在破坏垃圾收集堆。这不是crystal为什么,但你没有给pinvoke marshaller多少机会去做正确的事情。它无法正确固定阵列。首先正确声明函数,它需要一个数组,因此声明一个:
[DllImportAttribute("MyC++API.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
DeviceInfo[] di,
int length_of_di_array,
out int p_numValidDevices
);
您对DeviceInfo的第一个声明是正确的,第二个声明是错误的,因为字符串不是指针。因此,正如下面的回复中指出的,我有两个问题:
1.我没有正确调用我的DllImport,我的方法是尝试将一个输出拼凑在一起,在这样做的过程中,我把内存分配给了结构数组。
2.我试图将一个输出拼凑在一起,把代码搞得更糟(试图将DeviceInfo数组di作为单个元素di[number]而不是作为一个整体传入)
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
公共结构设备信息
{
公共UInt32手柄;
[MarshalAsAttribute(UnmanagedType.ByValTStr,SizeConst=80)]
公共字符串名称;
公共UInt32唯一标识;
}
[DllImportAttribute(“MyC++API.dll”,EntryPoint=“获取设备信息”,CallingConvention=CallingConvention.StdCall)]
公共静态外部输入获取设备信息(
[In,Out][marshallas(UnmanagedType.LPArray,SizeParamIndex=1)]设备信息[]di,
_di_数组的int-length_,
参考int p_numValidDevices);
静态void Main()
{
int numValidDevices=0;
DeviceInfo[]di=新的DeviceInfo[16];
获取设备信息(di,16,参考有效设备);
对于(int i=0;i
您正在传递一个引用,但它需要一个数组。还要确保C中的sizeof
=sizeof
在C中。我怀疑该名称声明错误。请改为使用byte[]并应用值数组大小常量。您应该发布答案,而不是编辑问题。你愿意这么做吗?@David Heffernan啊,谢谢你的帮助(我知道我迟到了),编辑了帖子并发布了答案。你完全正确,我重新评估了我对该函数的使用情况,并将我的DllImport更改为你所拥有的,它成功了!非常感谢。(澄清被添加为对原始帖子的编辑)是的,我在做一些黑客代码,试图强制输出某种类型的内容。
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 1, ref numValidDevices); //accesses each device element and returns the data
}
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
rc = get_device_info(ref di[0], 16, ref numValidDevices); //accesses each device element and returns the data
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
Console.WriteLine(...);
}
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
int length_of_di_array,
ref int p_numValidDevices);
[DllImportAttribute("MyC++API.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
DeviceInfo[] di,
int length_of_di_array,
out int p_numValidDevices
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]
public String name;
public UInt32 unique_ID;
}
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
int length_of_di_array,
ref int p_numValidDevices);
static void Main()
{
int numValidDevices = 0;
DeviceInfo[] di = new DeviceInfo[16];
get_device_info(di, 16, ref numValidDevices);
for (int i = 0; i < numValidDevices; ++i)
{
Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);
}
Console.ReadLine();
API_close();
}