现在非常的流行使用C++来完成底层的算法或是需要高运行速度的程序快。然后使用 C# 来调用。用C#来写界面和逻辑层。 这样即用到了 C++ 的运行速度又用到了C# 快速的开发优势。
下面讲一下如何在C#中调用 C++生成的dll :
// cppdll.cpp 因为这个DLL的目的就是为了让 C#去调用。所以这里不用 // 再去写个头文件了,写头文件主要是为了让 C++的程序去调。我们这里 // 是把函数的声明与定义都写在一起。 #include <iostream> using namespace std; // 把固定要使用的 “extern "C" __declspec(dllexport)”,放在函数、// 头,这是生成DLL函数所必须的,所以给他起个别名,简洁一些。 #define DLLEXPORT extern "C" __declspec(dllexport) // 写一个简单的 加法函数 add ,增加两个整数值,反加一个整数 DLLEXPORT int add(int i,int j){ return i + j; } // 再写一个无返回值的Print方法,他传入的是一个字符串 // 注意 这时字符串最好使用 char* 而不是 string. 不然可能会 // 报 “尝试访问受保护内存” 的错误。 DLLEXPORT void Print(char* str,int i) { cout << "Input the " << str << " and " << i << endl; }
上面的C++程序使用的是 全局的静态方法,这样比较方法调用。当然你也可以写一个类,让C#去调用的。
这是如何在C# 中,调用 上面写的那两个 C++ 函数 :
using System.Text; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; namespace Test { class Program { // CalingConvention 意思是“调用协定",主要是规定一下参数如 // 何进去栈,及是由调用方还是被调用方来释放资源。 [DllImport("cppdll.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int add(int i, int j); // 如果参数中有 字符串的情况下,还需要设置一下这个函数所使 // 用的字符集 CharSet = CharSet.Ansi [DllImport("cppdll.dll", CallingConvention = CallingConvention.Cdecl,CharSet = CharSet.Ansi)] public static extern void Print(string s,int i); static void Main(string[] args) { // C++ add Method Console.WriteLine(add(45, 567)); // C++ Print Method Print("May_H",757); Console.ReadKey(); } } }
参考 : http://www.soaspx.com/dotnet/csharp/csharp_20110406_7469.html
------------------------------------------ 2013/10/26 一些补充------------------------------------------
之前研究的是使用 C# 去调用 C++ 的函数,那么如何来调用C++的类呢. 如果按一般的解决方法,将类在C#中重声明一下,如果其中再有结构体,枚举等会是一个相当麻烦的过程,所以,我们还是使用 调用函数的方法.写一个接口方法,在接口方法中去创建类的指针, 将调用 C++类的操作写在这个接口函数中. 再用C#去调用. 就方便很多. 避开了很多的麻烦.
例如: 在 C++ 中写一个简单的 Person类,只有name ,age.
class Person {
public:
char* name;
int age;
// 构造函数
Person(char* name,int age);
~Person();
// 方法
void SayHi();
};
1. 创建一个 DLLInterface.cpp 文件,写一个 生成 Person 指针的方法:
#define EXPORTDLL extern "C" _declspec(dllexport)
EXPORTDLL void* PersonInit(char* name,int age) {
Person* per = new Person(name,age);
return per;
}
这样在 C# 中调用这个文件 ,就可以得到这个类的指针(int).
2. 根据我们的需要, 可以再写一些接口方法,比如调用它的SayHi 方法
EXPORTDLL void InvokeSayHi(Person* p) {
p->SayHi();
}
再与一个清理 这个生Person指针的方法.
EXPORTDLL void FreePerson(Person* p) {
delete p;
cout << "Free Over" << endl;
}
这些方法.都接收一个 Person 指针.
3.在C#中.. 引入这个 DLL 及声明接口方法:
// 这里使用 [MarshalAs(UnmanagedType.LPArray)]byte[] name,来传递字符串,(中文还不行.. 还得研究)
// 经过看网上的资料, 发现所有的指针都可以使用 IntPtr 在C# 中使用,包括Char*. 在C#向C++传字符串的时候使用 Marshal.StringToHGlobalAnsi("字符串"). 的方法传
// 递, 方便好用 所以指针都用 IntPtr就行了. Marshel 就是用到托管与非托管之间进行参数传递或转换的一个方法类.
[DllImport("cpp01.dll", EntryPoint = "PersonInit", CallingConvention = CallingConvention.Cdecl)]
extern static IntPtr PersonInit([MarshalAs(UnmanagedType.LPArray)]byte[] name, int age);
// 写成 IntPtr的形式: extern static IntPtr PersonInit(IntPtr name, int age);
[DllImport("cpp01.dll", EntryPoint = "InvokeSayHi", CallingConvention = CallingConvention.Cdecl)]
extern static void InvokeSayHi(IntPtr personPoint);
4.OK.. 现在我们就可以在C#中调用这个C++类了!
static void Main(string[] args) {
// 得到Person 的指针,由Int存储
int pp = PersonInit(System.Text.Encoding.ASCII.GetBytes("Mick"), 3456);
// 使用这个地址调用Person->SayHi 方法
InvokeSayHi(pp);
// 清理Person用到的内存
FreePerson(pp);
}