C# 调用C++ 接口 中 托管转非托管

156次阅读
没有评论

共计 5123 个字符,预计需要花费 13 分钟才能阅读完成。

一、C#调用DLL文件时参数对应表

Wtypes.h 非托管    非托管 C 语言类型        托管类名                                    说明
HANDLE              void*                   System.IntPtr                               32 位
BYTE                unsigned char           System.Byte                                 8 位
SHORT               short                   System.Int16                                16 位
WORD                unsigned short          System.UInt16                               16 位
INT                 int                     System.Int32                                32 位
UINT                unsigned int            System.UInt32                               32 位
LONG                long                    System.Int32                                32 位
BOOL                long                    System.Int32                                32 位
DWORD               unsigned long           System.UInt32                               32 位
ULONG               unsigned long           System.UInt32                               32 位
CHAR                char                    System.Char                                 用 ANSI 修饰。
LPSTR               char*                   System.String 或 System.StringBuilder       用 ANSI 修饰。
LPCSTR              Const char*             System.String 或 System.StringBuilder       用 ANSI 修饰。
LPWSTR              wchar_t*                System.String 或 System.StringBuilder       用 Unicode 修饰。
LPCWSTR             Const wchar_t*          System.String 或 System.StringBuilder       用 Unicode 修饰。
FLOAT               Float                   System.Single                               32 位
DOUBLE              Double                  System.Double                               64 位

二、结构体转换

C#转换的结构体需要用LayoutKind.Sequential这个属性修饰,因为在C/C++的结构体内存是顺序布局的,所以需要C#转换的结构体内存也按照顺序布局,这样传递指针时dll内部不会出错。
例如:
C的结构体声明为

struct demobuf

C#中的结构体声明为

[StructLayout(LayoutKind.Sequential)]
public struct DemoBuf

C#转换的结构体成员需要用public修饰符,如果不添加public修饰符,C#成员变量默认是保护的,在其它方法内定义这个结构体就不能随便访问修改其成员变量。并且在C的结构体中对其内部成员变量的访问权限只能是public,C++中允许public/protected/private。
C的结构体为

struct demobuf
{
  int a;
  int b;
  bool c;
}

C#的结构体为

[StructLayout(LayoutKind.Sequential)]
public struct DemoBuf
{
  public int a;
  public int b;
  public bool c;
}

当转换的结构体成员中包含数组时,需要获取转换数组的大小,用到MarshalAs属性。
C的结构体为

struct demobuf
{
  int a;
  int b;
  bool c;
  int arr[9];
  char ch[9];
}
[StructLayout(LayoutKind.Sequential)]
public struct DemoBuf
{
  public int a;
  public int b;
  public bool c;
  [MarshalAs(UnmanagedType.ByValArray,SizeConst=9)]
  public int[] arr;
  [MarshalAs(UnmanagedType.ByValTStr,SizeConst=9)]
  public char[] ch;
}

 

经常用到的比如说:

GCHandle[] hArr = new GCHandle[4];
hArr = GCHandle.Alloc(Bytes,GCHandleType.Pinned);
hArr[i].AddrOfPinnedObject();

IntPtr Ptr = IntPtr.Zero;
Ptr = Marshal.AllocHGlobal(size);
Marshal.StructToPtr(struct,Ptr,true);

或者指针数组
Int64 Addr = IntPtr.ToInt64();
IntPtr pstIndex = new IntPtr(Addr);
Marshal.StructToPtr(struct,pstIndex,true);
pstIndex = pstIndex + Marshal.SizeOf(struct);

二、C#调用C++编写的DLL函数, 以及各种类型的参数传递

  1. 如果函数只有传入参数,比如:
//C++中的输出函数 
int __declspec(dllexport) test(const int N) 
{ 
    return N+10; 
} 
对应的C#代码为:
C# Code Copy Code To Clipboard
[DllImport("test.dll", EntryPoint = "#1")] 
public static extern int test(int m); 
 
private void button1_Click(object sender, EventArgs e) 
{ 
textBox1.Text= test(10).ToString(); 
} 


2.如果函数有传出参数,比如:
//C++
void __declspec(dllexport) test(const int N, int& Z) 
{ 
    Z=N+10; 
} 
对应的C#代码:
C# Code Copy Code To Clipboard
[DllImport("test.dll", EntryPoint = "#1")] 
public static extern double test(int m, ref int n); 
 
private void button1_Click(object sender, EventArgs e) 
{ 
int N = 0; 
test1(10, ref N); 
textBox1.Text= N.ToString(); 
} 



3. 带传入数组:

void __declspec(dllexport) test(const int N, const int n[], int& Z) 
{ 
    for (int i=0; i<N; i++) 
    { 
        Z+=n[i]; 
    } 
} 
C#代码:

[DllImport("test.dll", EntryPoint = "#1")] 
public static extern double test(int N, int[] n, ref int Z); 
 
private void button1_Click(object sender, EventArgs e) 
{ 
    int N = 0; 
    int[] n; 
    n = new int[10]; 
    for (int i = 0; i < 10; i++) 
    { 
        n[i] = i; 
    } 
    test(n.Length, n, ref N); 
    textBox1.Text= N.ToString(); 
    } 
    

4. 带传出数组:
C++不能直接传出数组,只传出数组指针,

void __declspec(dllexport) test(const int M, const int n[], int *N) 
{ 
    for (int i=0; i<M; i++) 
    { 
        N[i]=n[i]+10; 
    } 
} 
对应的C#代码:

[DllImport("test.dll", EntryPoint = "#1")] 
public static extern void test(int N, int[] n, [MarshalAs(UnmanagedType.LPArray,SizeParamIndex=1)] int[] Z); 
 
private void button1_Click(object sender, EventArgs e) 
{ 
    int N = 1000; 
    int[] n, Z; 
    n = new int[N];Z = new int[N]; 
    for (int i = 0; i < N; i++) 
    { 
        n[i] = i; 
    } 
    test(n.Length, n, Z); 
    for (int i=0; i<Z.Length; i++) 
    { 
        textBox1.AppendText(Z[i].ToString()+"n"); 
    } 
}

这里声明函数入口时,注意这句 [MarshalAs(UnmanagedType.LPArray,SizeParamIndex=1)] int[] Z
在C#中数组是直接使用的,而在C++中返回的是数组的指针,这句用来转化这两种不同的类型.
关于MarshalAs的参数用法以及数组的Marshaling,可以参见这篇转帖的文章: http://www.kycis.com/blog/read.php?21

  1. 传出字符数组:
C++定义:
1.  void __declspec(dllexport) test(int i, double &a, double &b, char t[5])  
C#对应声明:

  [DllImport("dll.dll", EntryPoint = "test")]  
  public static extern void test(int i, ref double a, ref double b, [Out, MarshalAs(UnmanagedType.LPArray)] char[] t);   
              char[] t = new char[5];  
              test(i, ref a, ref b, t);

字符数组的传递基本与4相似,只是mashalAs 时前面加上Out。

三、
1.普通传值,如下面代码中MotionDetect的第4个参数;
2.传引用,MotionDetect的第3个参数,nNum传进动态库后赋值再传回来;
3.引用传一个结构体,MotionDetect的第2个参数,这里其实是传一个结构体的数组,具体像加ref的传参还真不会;
4.传一个有分配内存的变量,需要用到GCHandle,因为C#是可以自动回收内存的,而GCHandle在这里的作用就是把它的内存空间Pin住,传递给C++动态库后再手动回收资源。

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class D : MonoBehaviour 
{
    struct Color_32
    {
        public byte r;
        public byte g;
        public byte b;
        public byte a;
    }
    Color_32[]  imageDataResult;
    
    GCHandle pixelsHandle;
    
    [DllImport("MotionDetectionDll")]
    private static extern bool MotionDetect( System.IntPtr colors, Color_32[] imageDataResult, ref int nNum, int nChannels );
    
    void Start()
    {
        imageDataResult = new Color_32[128*128];
    }
    
    void Update () 
    {
        int nNum = 0;
        pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
        bool wfDf = MotionDetect( pixelsHandle.AddrOfPinnedObject(), imageDataResult, ref nNum, 4 );
        pixelsHandle.Free();
    }
}
C++中是这么写的
//头文件中多加个结构体定义
#include "stdafx.h"
struct Color_32
{
    byte r;
    byte g;
    byte b;
    byte a;
};
extern "C" _declspec(dllexport) bool MotionDetect ( char* imageData, Color_32 imageDataResult[], int *nNum, int nChannels );

// CPP文件中
extern "C" _declspec(dllexport) bool MotionDetect ( char* imageData, Color_32 imageDataResult[], int *nNum, int nChannels )
{
    IplImage* imgSrc = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, 4);
    if(!imgSrc)
    {
        return false;
    }
    cvSetData( imgSrc, imageData, imgSrc->widthStep );
    
    ......
    
    for ( int i=0; i< ......; i++ )
    {
        imageDataResult[i].r = ......;
        imageDataResult[i].g = ......;
        imageDataResult[i].b = ......;
        imageDataResult[i].a = ......;
    }
    
    ......
    
    *nNum = 5;
    nChannels = 4;
}

 

正文完
 
admin
版权声明:本站原创文章,由 admin 2022-07-19发表,共计5123字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码