C#调用C++动态库数据类型转换问题

C#调用C++比较常见的两种方式,一种是调用C++dll提供的接口,另一种直接调用C++提供的类,使用上更灵活一些。
其他的方式我也不知道了。
这里暂时只讲第一种方式,这种方式用得比较多。

传入参数

传入数值类型的参数

这个就比较直接了,没什么变化,接口是int型,调用处也是int型,这种大家都有的类型传递起来就比较方便。
举个最简单的例子:
.h文件中

extern "C" __declspec(dllexport) bool setInt(int a);

.cpp文件中:

bool setInt(int a)
{
    cout << a << endl;
    return true;
}

.cs文件中:

[DllImport("MyDll.dll")]
static extern bool setInt(int a);
int a = 1;
setInt(a);

传入字符串类型的参数

c++中使用char*参数类型,在C#中可以选择使用string或者StringBuilder
接口声明:

extern "C" __declspec(dllexport) bool setString(char* a);

定义:

bool setString(char* a)
{
    cout << a << endl;
    return true;
}

调用处:

[DllImport("MyDll.dll")]
static extern bool setString(StringBuilder a);

[DllImport("MyDll.dll")]
static extern bool setString(string a);
string b = "ceshi";
setString(b);
StringBuilder sb = new StringBuilder("shiyixia");
setString(sb);

传入结构体类型的参数

结构体的传递需要使用指针,接口处可以定义为void*类型的参数,也可以使用结构体指针,C#处传递参用IntPtr类型,传指针都是用这个类型就可以了。
结构体定义:

typedef struct __RESULT_STRUCT__
{
    char name[256];  // 这边注意长度要和接口调用处统一起来
    int x;
    int y;
    int width;
    int height;
    double confidence;
} RESULT_STRUCT, *P_RESULT_STRUCT;

接口声明:

extern "C" __declspec(dllexport) bool setStruct(P_RESULT_STRUCT d1);

定义:

bool setStruct(P_RESULT_STRUCT d)
{
    cout << d->name << endl;
    cout << d->x << endl;
    cout << d->y << endl;
    cout << d->width << endl;
    cout << d->height << endl;
    cout << d->confidence << endl;
    return false;
}

调用处:

[StructLayout(LayoutKind.Sequential)]
private struct Result
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string name;
    public int x;
    public int y;
    public int width;
    public int height;
    public double confidence;
}

[DllImport("MyDll.dll")]
static extern bool setStruct(IntPtr result);
Result d = new Result
{
    name = "测试一下name",
    x = 101,
    y = 102,
    width = 103,
    height = 104,
    confidence = 0.55
};
IntPtr dPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Result)));
Marshal.StructureToPtr(d, dPtr, false);
setStruct(dPtr);
Marshal.FreeHGlobal(dPtr);

传入数组类型的参数

传入元素为数值型的数组

数组的传递需要使用指针,接口处都可以定义为void*类型的参数,也可以定义为具体的类型的指针,C#处传递参数用IntPtr类型。
int型数组举例,其他都是差不多的:
接口声明:

extern "C" __declspec(dllexport) bool getArray(void* data, int len);

定义:

bool getArray(void* data, int len)
{
    int* d = (int*)data;
    for (int i = 0; i < len; i++)
    {
	cout << d[i] << endl;
    }
    return true;
}

调用处:

int[] c = new int[] { 1, 2, 3, 4 };
IntPtr cPtr = Marshal.AllocHGlobal(sizeof(int) * c.Length);
Marshal.Copy(c, 0, cPtr, c.Length);
getArray(cPtr, c.Length);
Marshal.FreeHGlobal(cPtr);
[DllImport("MyDll.dll")]
static extern bool getArray(IntPtr data, int len);

传入结构体数组

和传入单个的结构体也差不多,就是把结构体一个个按顺序排排好。
接口声明:

extern "C" __declspec(dllexport) bool setStructArray(P_RESULT_STRUCT d2, int len);

定义:

bool setStructArray(P_RESULT_STRUCT d2, int len)
{
    for (int i = 0; i < len; i++)
    {
        RESULT_STRUCT s = d2[i];
        cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << endl;
        cout << s.name << endl;
        cout << s.x << endl;
        cout << s.y << endl;
        cout << s.width << endl;
        cout << s.height << endl;
        cout << s.confidence << endl;
        cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<" << endl;
    }
    return true;
}

调用处:

Result e = new Result
{
    name = "测试两下name",
    x = 201,
    y = 202,
    width = 203,
    height = 204,
    confidence = 0.66
};

Result[] f = new Result[] { d, e };
IntPtr g = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Result)) * f.Length);
Marshal.StructureToPtr(f[0], g, false);
Marshal.StructureToPtr(f[1], g + Marshal.SizeOf(typeof(Result)), false);
setStructArray(g, f.Length);
Marshal.FreeHGlobal(g);
[DllImport("MyDll.dll")]
static extern bool setStructArray(IntPtr result, int len);

传出参数

传出数值型的参数

接口定义处参数类型加&,调用处用ref
接口声明:

extern "C" __declspec(dllexport) bool getInt(int& a);

定义:

bool getInt(int& a)
{
    a = 202;
    return true;
}

调用:

int h = 0;
getInt(ref h);
Console.WriteLine(h);
[DllImport("MyDll.dll")]
static extern bool getInt(ref int a);

传出字符串类型的参数

参数使用char*,调用处使用StringBuilder
接口声明:

extern "C" __declspec(dllexport) bool getString(char* a);

定义:

bool getString(char* a)
{
    string b = "ceshi测试";
    strcpy_s(a, b.length() + 1, b.c_str());
    return true;
}

调用:

[DllImport("MyDll.dll")]
static extern bool getString(StringBuilder sb);
StringBuilder i = new StringBuilder(1024);
getString(i);
Console.WriteLine(i);

传出结构体类型的参数

接口声明:

extern "C" __declspec(dllexport) bool getStruct(P_RESULT_STRUCT data);

定义:

bool getStruct(P_RESULT_STRUCT data)
{
    char a[123] = "这是新的结构体";
    strcpy_s(data->name, a);
    data->x = 122;
    data->y = 123;
    data->width = 124;
    data->height = 125;
    data->confidence = 0.98;
    return true;
}

调用:

[DllImport("MyDll.dll")]
static extern bool getStruct(IntPtr result);
IntPtr k = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Result)));
getStruct(k);
Result l = (Result)Marshal.PtrToStructure(k, typeof(Result));
Console.WriteLine("{0},{1},{2},{3},{4},{5}", l.name, l.x, l.y, l.width, l.height, l.confidence);

传出数组类型的参数

传出数值型数组

接口声明:

extern "C" __declspec(dllexport) bool getIntArray(int* data, int& len);

定义:

bool getIntArray(int* data, int& len)
{
    len = 10;
    for (int i = 0; i < len; i++)
    {
        data[i] = i;
    }
    return true;
}

调用:

[DllImport("MyDll.dll")]
static extern bool getIntArray(IntPtr data, ref int len);
IntPtr m = Marshal.AllocHGlobal(sizeof(int) * 100);
int n = 0;
getIntArray(m, ref n);
int[] p = new int[n];
Marshal.Copy(m, p, 0, n);
for (int o = 0; o < n; o++)
    Console.WriteLine(p[o]);

传出字符串型数组

未完待续

传出结构体型数组

未完待续