【发布时间】:2011-07-29 13:47:18
【问题描述】:
我目前正在为 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 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);
这个结构和函数的预期用途只是从我正在访问的板上获取一些设备信息。目前我无法访问 C++ 中的函数体,所以我只能假设它 100% 工作(在 C++ 中工作正常)。
问题是,当我使用该函数遍历结构数组时,它会输出我正在寻找的数据,但也会在运行时开始失败,给我各种错误窗口。
C#代码:
static void Main()
{
int numValidDevices = 0; //initialize variable
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
Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);
}
Console.ReadLine(); //stops console from closing prematurely
API_close(); //custom close function from the C++ API
}
调试时出错(信息仍然显示): “在 System.dll 中发生了“System.Threading.ThreadStateException”类型的未处理异常
附加信息:线程尚未启动。”
“在 mscorlib.dll 中发生了“System.ExecutionEngineException”类型的未处理异常”
调试时出错(信息未显示,程序无法执行): "在 mscorlib.dll 中发生了类型为 'System.AccessViolationException' 的未处理异常
附加信息:试图读取或写入受保护的内存。这通常表明其他内存已损坏。”
关闭控制台窗口时: “‘0x7c9113c0’处的指令引用了‘0x00000000’处的内存。无法‘写入’内存。” (有时说“读”而不是“写”)。
我对 PInvoke 做了很多研究,发现了 Microsoft InteropAssistant application,各种堆栈溢出文章,如 this one,和 this post 似乎更接近于我正在做的事情,但我仍在深入研究如何使用 Marshal.CoTaskMemAlloc/Free,看看它是否会做任何事情......
到目前为止,我的结构和函数是正确的,我尝试将结构更改为使用 IntPtr,但这不会返回 di.name 值,并且 di.unique_ID 变得乱码(奇怪的是 di.句柄保持有效)
C#代码:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
IntPtr p_name;
public String name { get { return Marshal.PtrToStringAnsi(p_name); } }
public UInt32 unique_ID;
}
预期输出:
Handle: 3126770193
Name: DEVICE_A
Unique ID: 12345678
IntPtr 输出:
Handle: 3126770193
Name:
Unique ID: 1145128264
奇怪的是,使用 IntPtr 不会导致上述错误,并且运行良好。 这让我相信问题在于将 C++ char 编组为字符串,但我不确定问题是否在于编组、内存管理(没有?)或我没有完全理解的东西。
非常感谢任何和所有反馈,我已经为此困扰了好几个星期了......
【问题讨论】:
-
您正在传递一个引用,但它需要一个数组。
-
还要确保 C# 中的
sizeof== C 中的sizeof。我怀疑该名称被声明为错误。改用 byte[] 并应用值数组大小常量。 -
您应该发布答案,而不是编辑问题。你愿意这样做吗?
-
@David Heffernan 啊,感谢您的提醒(我知道我来晚了),编辑了帖子并发布了答案。
标签: c# memory struct pinvoke marshalling