如何使用指针作为参数在 C# 中包装C++ DLL?
How to wrap C++ DLL in C# with pointers as parameters?
我正在尝试从 C# 程序中的 C++ DLL 文件中调用一些函数。但是当涉及到指针时,我陷入了困境。有人能指出我正确的方向吗?
下面是包含目标函数的C++头文件:
#pragma once
#ifdef STCL_DEVICES_DLL
#define STCL_DEVICES_EXPORT extern "C" _declspec(dllexport)
#else
#define STCL_DEVICES_EXPORT extern "C" _declspec(dllimport)
#endif
enum SD_ERR
{
SD_ERR_OK = 0,
SD_ERR_FAIL,
SD_ERR_DLL_NOT_OPEN,
SD_ERR_INVALID_DEVICE, //device with such index doesn't exist
SD_ERR_FRAME_NOT_SENT,
};
#pragma pack (1)
struct LaserPoint
{
WORD x;
WORD y;
byte colors[6];
};
struct DeviceInfo
{
DWORD maxScanrate;
DWORD minScanrate;
DWORD maxNumOfPoints;
char type[32];
};
//////////////////////////////////////////////////////////////////////////
///Must be called when starting to use
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int OpenDll();
//////////////////////////////////////////////////////////////////////////
///Search for .NET devices (Moncha.NET now)
///Must be called after OpenDll, but before CreateDeviceList!
///In pNumOfFoundDevs can return number of found devices (optional)
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SearchForNETDevices(DWORD* pNumOfFoundDevs);
//////////////////////////////////////////////////////////////////////////
///All devices will be closed and all resources deleted
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT void CloseDll();
//////////////////////////////////////////////////////////////////////////
///Creates new list of devices - previous devices will be closed
///pDeviceCount returns device count
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int CreateDeviceList(DWORD* pDeviceCount);
//////////////////////////////////////////////////////////////////////////
///Returns unique device name
///deviceIndex is zero based device index
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int GetDeviceIdentifier(DWORD deviceIndex, WCHAR** ppDeviceName);
//////////////////////////////////////////////////////////////////////////
///Send frame to device, frame is in following format:
///WORD x
///WORD y
///byte colors[6]
///so it's 10B point (=> dataSize must be numOfPoints * 10)
///scanrate is in Points Per Second (pps)
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SendFrame(DWORD deviceIndex, byte* pData, DWORD numOfPoints, DWORD scanrate);
//////////////////////////////////////////////////////////////////////////
///Returns true in pCanSend if device is ready to send next frame
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int CanSendNextFrame(DWORD deviceIndex, bool* pCanSend);
//////////////////////////////////////////////////////////////////////////
///Send DMX if device supports it - pDMX must be (!!!) 512B long
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SendDMX(DWORD deviceIndex, byte* pDMX);
//////////////////////////////////////////////////////////////////////////
///Send blank point to position x, y
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SendBlank(DWORD deviceIndex, WORD x, WORD y);
//////////////////////////////////////////////////////////////////////////
///Get device info
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int GetDeviceInfo(DWORD deviceIndex, DeviceInfo* pDeviceInfo);
这是我到目前为止的 C# 测试类:
#region Usings
using System;
using System.Runtime.InteropServices;
#endregion
namespace MonchaTestSDK {
class Program {
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int OpenDll();
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SearchForNETDevices(DWORD* pNumOfFoundDevs);
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void CloseDll();
public static void Main(string[] args) {
OpenDll();
CloseDll();
}
}
}
OpenDll()
和CloseDll()
工作正常。但是第一个问题发生在SearchForNETDevices
函数的DWORD*
参数上:
The type or namespace name 'DWORD' could not be found (are you missing a using directive or an assembly reference?
Pointers and fixed size buffers may only be used in an unsafe context.
Cannot take the address of, get the size of, or declare a pointer to a managed type ('DWORD').
此外,我必须如何处理其他函数参数,例如 WCHAR** 以及结构DeviceInfo
和LaserPoint
?
这是我到目前为止提出的 C# 解决方案。唯一剩下的问题是GetDeviceInfo(...)
函数调用,如以下问题中所述:
using System;
using System.Threading;
using System.Runtime.InteropServices;
namespace MonchaTestSDK {
public class Program {
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int OpenDll();
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern void CloseDll();
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int SearchForNETDevices(ref UInt32 pNumOfFoundDevs);
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int CreateDeviceList(ref UInt32 pDeviceCount);
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int GetDeviceIdentifier(UInt32 deviceIndex, out IntPtr ppDeviceName);
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int SendFrame(UInt32 deviceIndex, LaserPoint[] pData, UInt32 numOfPoints, UInt32 scanrate);
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int CanSendNextFrame(UInt32 deviceIndex, ref bool pCanSend);
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int SendBlank(UInt32 deviceIndex, UInt16 x, UInt16 y);
[DllImport("..\..\dll\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // FAILS
public static extern int GetDeviceInfo(UInt32 deviceIndex, ref DeviceInfo pDeviceInfo);
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct LaserPoint {
public UInt16 x;
public UInt16 y;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] colors;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DeviceInfo {
public UInt32 maxScanrate;
public UInt32 minScanrate;
public UInt32 maxNumOfPoints;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string type;
}
public static void Main(string[] args) {
Console.WriteLine("Moncha SDKn");
OpenDll();
Console.WriteLine("StclDevices.dll is open.");
UInt32 deviceCount1 = 0;
int r1 = SearchForNETDevices(ref deviceCount1);
Console.WriteLine("SearchForNETDevices() [" + r1+"]: "+deviceCount1);
UInt32 deviceCount2 = 0;
int r2 = CreateDeviceList(ref deviceCount2);
Console.WriteLine("CreateDeviceList() ["+r2+"]: "+deviceCount2);
IntPtr pString;
int r3 = GetDeviceIdentifier(0, out pString);
string devname = Marshal.PtrToStringUni(pString);
Console.WriteLine("GetDeviceIdentifier() ["+r3+"]: "+devname);
DeviceInfo pDevInfo = new DeviceInfo();
int r4 = GetDeviceInfo(0, ref pDevInfo);
Console.WriteLine("GetDeviceInfo() ["+r4+"]: ");
Console.WriteLine(" - min: "+pDevInfo.minScanrate);
Console.WriteLine(" - max: " + pDevInfo.maxScanrate);
Console.WriteLine(" - points: " + pDevInfo.maxNumOfPoints);
Console.WriteLine(" - type: " + pDevInfo.type);
const UInt32 numOfPts = 600;
do {
while(!Console.KeyAvailable) {
for(UInt16 j=0; j< deviceCount1; j++) {
bool canSend = false;
CanSendNextFrame(j, ref canSend);
if(!canSend) {
continue;
}
LaserPoint[] points = new LaserPoint[numOfPts];
float t = (Environment.TickCount % 2000) / 2000.0f;
for(int i=0; i<numOfPts; i++) {
points[i].x = (UInt16)(16384 + 32768 * i / numOfPts);
points[i].y = (UInt16)(16384 + 32768 * t);
points[i].colors = new byte[6];
points[i].colors[0] = 255;
points[i].colors[1] = 0;
points[i].colors[2] = 0;
points[i].colors[3] = 0;
points[i].colors[4] = 0;
points[i].colors[5] = 0;
}
SendFrame(j, points, numOfPts, 30000);
}
Thread.Sleep(5);
}
} while(Console.ReadKey(true).Key != ConsoleKey.Escape);
Thread.Sleep(100);
CloseDll();
}
}
}
相关文章:
- 如何在c++17中制作一个模板包装器/装饰器
- std::vector的包装器,使数组的结构看起来像结构的数组
- 如何在c++迭代器类型中包装std::chrono
- 是否可以用"iostream"包装现有的TCP/OOpenSSL会话
- 用pybind11包装C++抽象类时出错
- 为左值和右值的包装器实现C++范围
- C结构,其指针将被包装在unique_ptr中
- 如何包装第三方DLL在R中使用
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 将 N-arg 函数包装到另一个函数中
- 元组由 Swig 生成的 Python 包装器返回,用于C++向量
- 包装一个对象并假装它是一个 int
- 使用 Python Extension API 包装复杂C++类
- 外壳包装器句柄/执行交互式命令管道C++ UNIX
- 包装C++类时不完整的类型 GLFWwindow
- 将函数包装器转换为 std::function
- C++函数包装器来捕获某些信号
- 创建包装升压适配器的自定义范围类
- 如何包装(撰写)增强 hana 地图并访问括号运算符(运算符 [])?
- 使用运算符*重载包装矩阵乘法