如何将结构从 C# 传递到 C++ DLL?
How to pass struct from C# to C++ DLL?
好的,我的激光设备控制器的一系列问题还在继续...我想从我的 C# 代码调用 DLL 中的以下 C++ 函数:
extern "C" _declspec(dllimport) int SendFrame(DWORD deviceIndex, byte* pData, DWORD numOfPoints, DWORD scanrate);
指针pData
指向C++头文件中定义的激光点数组如下:
#pragma pack (1)
struct LaserPoint {
WORD x;
WORD y;
byte colors[6];
};
在 C# 端,我将函数导入定义如下:
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, ref byte[] pData, UInt32 numOfPoints, UInt32 scanrate);
... 和 LaserPoint
结构如下:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct LaserPoint {
public UInt16 x;
public UInt16 y;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] colors;
}
然后我这样调用 SendFrame
函数:
LaserPoint point = new LaserPoint();
point.x = 16384;
point.y = 32768;
point.colors = new byte[] {255, 0, 0, 0, 0, 0};
byte[] arr = point2array(points);
SendFrame(0, ref arr, 1, 30000);
这是我将 LaserPoint
结构实例转换为字节数组的函数:
private static byte[] point2array(object obj) {
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
但是我的激光设备没有接收到正确的输入,因为激光的行为很奇怪。在 C++ 项目中使用相同的代码效果很好。所以这个错误是在这个 C# 互操作性代码的某个地方。
有什么想法吗?
数组已经是引用类型,所以 ref byte[]
是一个双重间接寻址,类似于 byte**
— 去掉 ref
.
如果您只想在这里使用 LaserPoint 结构,您可以将函数定义为
[DllImport("..\..\dll\StclDevices.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, LaserPoint[] pData, UInt32 numOfPoints, UInt32 scanrate);
并摆脱复制,让运行时为您完成。
否则,正如 ildjarn 所说,摆脱 ref 应该可行,因为字节数组已经是引用(指针)类型。
有关如何执行此操作的示例,PInvoke wiki 已获得大多数 windows API 的结构和签名,因此虽然您的电话不会在那里,有很多类似的例子。
好的,我的激光设备控制器的一系列问题还在继续...我想从我的 C# 代码调用 DLL 中的以下 C++ 函数:
extern "C" _declspec(dllimport) int SendFrame(DWORD deviceIndex, byte* pData, DWORD numOfPoints, DWORD scanrate);
指针pData
指向C++头文件中定义的激光点数组如下:
#pragma pack (1)
struct LaserPoint {
WORD x;
WORD y;
byte colors[6];
};
在 C# 端,我将函数导入定义如下:
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, ref byte[] pData, UInt32 numOfPoints, UInt32 scanrate);
... 和 LaserPoint
结构如下:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct LaserPoint {
public UInt16 x;
public UInt16 y;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] colors;
}
然后我这样调用 SendFrame
函数:
LaserPoint point = new LaserPoint();
point.x = 16384;
point.y = 32768;
point.colors = new byte[] {255, 0, 0, 0, 0, 0};
byte[] arr = point2array(points);
SendFrame(0, ref arr, 1, 30000);
这是我将 LaserPoint
结构实例转换为字节数组的函数:
private static byte[] point2array(object obj) {
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
但是我的激光设备没有接收到正确的输入,因为激光的行为很奇怪。在 C++ 项目中使用相同的代码效果很好。所以这个错误是在这个 C# 互操作性代码的某个地方。
有什么想法吗?
数组已经是引用类型,所以 ref byte[]
是一个双重间接寻址,类似于 byte**
— 去掉 ref
.
如果您只想在这里使用 LaserPoint 结构,您可以将函数定义为
[DllImport("..\..\dll\StclDevices.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, LaserPoint[] pData, UInt32 numOfPoints, UInt32 scanrate);
并摆脱复制,让运行时为您完成。
否则,正如 ildjarn 所说,摆脱 ref 应该可行,因为字节数组已经是引用(指针)类型。
有关如何执行此操作的示例,PInvoke wiki 已获得大多数 windows API 的结构和签名,因此虽然您的电话不会在那里,有很多类似的例子。