如何用指针作为参数在C#中包装C ++ DLL?

时间:2018-04-30 15:16:18

标签: c# c++ dll interop dllimport

我试图在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()工作正常。但第一个问题出现在DWORD*函数的SearchForNETDevices参数:

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 **和结构DeviceInfoLaserPoint

1 个答案:

答案 0 :(得分:0)

这是我到目前为止提出的C#解决方案。唯一剩下的问题是the question here中描述的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 SDK\n");

            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();

        }

    }
}