chuyiBKY

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

关于C#调用C++的Dll接口

首先,制作一个普通的Dll(非COM的),API到处接口声明定义如下:

>>>>>> C++

.h: _declspec(dllexport) void mymath(int* a);

.cpp:

 void mymath(int* a)

{

    *a = 1010;

    return;

}

我们制作一个Dll(假定名字"ExportFun.dll"),此时的Dll C++/C程序调用没有问题,就是动态Dll使用的那几个办法。

问题是,如何让C#来调用呢?

-------------------------------------------

>>>>>> C#

using System.Runtime.InteropServices;   //使用运行时Interop

namespace TestSharp
{
    class Program
    {

        //API 声明,使用DllImport,在C#程序运行时会载入Dll

        //同时,声明要使用的接口
        [DllImport("ExportFun.dll", EntryPoint = "mymath")]
        private static extern void mymath(IntPtr a);  //与C++声明不太一致


        static void Main(string[] args)
        {

            IntPtr pb = Marshal.AllocHGlobal(0);
            mymath(pb);
            int bb = Marshal.ReadInt32(pb);
            Console.WriteLine(bb.ToString());

        }

    }
}

-----------------------------------------------

这样C#Code就可以调用C++的Dll接口了,其中,基本数据类型没有问题,关键是指针,句柄什么的。

如上,C#声明mymath的参数不是int*, 因为C#是托管类代码,int*是非托管类的,所以这类类型都要

用IntPtr代替。

然后,就是由于Dll操作的是Native的Code,所以C#调用这个接口时,是要做一些转换操作,那就是

Marshal。 首先,用Marshal分配一块Native的内存,然后传入给mymath(),此时Dll中对内存操作就

OK了,然后再用Marshal读取这个内存位置的数据,就可以得到结果1010了。

 

 

好了,这是一个比较简单的数据类型,如果是结构体呢?

我们要注意,.Net与Native对数据在内存上的排列不太一样。

假如,我定义一个结构体

>>>> C++ 

.h

struct MYSTRUCT_T
{
    int a;
    int b;
    char c;
};

_declspec(dllexport)  MYSTRUCT_T* mymath3();

.cpp

MYSTRUCT_T* mymath3()
{
    MYSTRUCT_T* pT = new MYSTRUCT_T();

    pT->a = 500;
    pT->b = 600;
    pT->c = 'A';

    return pT;
}

一个比较简单的结构体类型,接口函数返回该结构体指针

-----------------------------------------------

>>>> C#

[StructLayout(LayoutKind.Explicit)] 
public struct myStruct_t

{
    [FieldOffset(0)]     //就是起始地址,所以位移为0
    public int a;        
    [FieldOffset(4)]     //相对于起始地址,位移一个int,32位机上是4个字节
    public int b;        
    [FieldOffset(8)]     //相对于起始地址,位移2个int,就是8个字节
    public char c;
}

 

[DllImport("ExportFun.dll", EntryPoint = "mymath3")]
private static extern IntPtr mymath3();

 

IntPtr pT = mymath3();
myStruct_t mt = new myStruct_t();
mt = (myStruct_t)Marshal.PtrToStructure(pT, mt.GetType());

 

在这里我们看到了,在C#中一定要在重新定义一下接口中的结构体,同时要指定各成员的排列情况(假定C++Dll是在32位机上编译的)。

然后,同样,mymath3()的返回值类型声明为IntPtr,使用时直接调用mymath3(),此时,pT指向的内存地址是Native的,非托管类型。然后,定义一个C#的结构体变量,之后调用Marshal.PtrToStructure操作,将Native上的结构体数据内容拷贝至托管数据mt上。

所以,C#定义结构体时要指定Native上的内存的排列,否则无法将有效数据Copy至托管类变量上。

 

当然,如果结构体char c后面还有成员变量,那就要注意内存对齐的问题了。(至少默认情况下编译器时会进行内存对齐的,这个问题我之前的文章提到过了)

还有复杂的数据,以后再添加了。

posted on 2015-11-11 10:18  chuyiBKY  阅读(123)  评论(0)    收藏  举报