本程序参考:http://www.cnblogs.com/liping13599168/archive/2011/03/31/2000320.html
但是本程序需要说明一个非常重要的问题:
就是注意你注意DLL文件和调用它的位数上一定要一致;否则会出现错误;
本人使用的是X64架构的系统;所以这个要遵守;
首先创建一个C++解决方案;其次在下面的选项里面选择win32项目,这个一定注意; 不要选控制台或者MFC程序;
然后再程序设置中选择DLL;其他默认即可;
最后得到了下面的界面;
我们可以看到这里有一些文件,其中dllmain.cpp作为定义DLL应用程序的入口点,它的作用跟exe文件有个main或者WinMain入口函数是一样的,它就是作为DLL的一个入口函数,实际上它是个可选的文件。它是在静态链接时或动态链接时调用LoadLibrary和FreeLibrary时都会被调用。
以下是要注意的部分了:一定要对下面的Debug部分进行更改;这一点就是X64配合的主要步骤;
配置管理器更改如下图;
然后再CppDemo.cpp中输入以下代码:
//以下是我写的程序; extern "C" __declspec(dllexport) int Sub(int x, int y) { return x - y; } extern "C" __declspec(dllexport) int Multiply(int x, int y) { return x * y; } extern "C" __declspec(dllexport) int Add(int x, int y) { return x + y; } extern "C" __declspec(dllexport) int Divide(int x, int y) { return x / y; }
extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。而被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。
__declspec(dllexport)的目的是为了将对应的函数放入到DLL动态库中。
extern "C" __declspec(dllexport)加起来的目的是为了使用DllImport调用非托管C++的DLL文件。因为使用DllImport只能调用由C语言函数做成的DLL。
下面创建C#控制台程序;
赋值CppDemo中的dll文件到 C#程序目录的X64的运行目录中
并且添加类:
public class CPPDLL { [DllImport("CppDemo.dll")] public static extern int Add(int x,int y); [DllImport("CppDemo.dll")] public static extern int Sub(int x, int y); [DllImport("CppDemo.dll")] public static extern int Multiply(int x, int y); [DllImport("CppDemo.dll")] public static extern int Divide(int x, int y); }
然后再在主程序中写入:
int result = CPPDLL.Add(10, 20); Console.WriteLine("10 + 20 = {0}", result); result = CPPDLL.Sub(30, 12); Console.WriteLine("30 - 12 = {0}", result); result = CPPDLL.Multiply(5, 4); Console.WriteLine("5 * 4 = {0}", result); result = CPPDLL.Divide(30, 5); Console.WriteLine("30 / 5 = {0}", result); Console.ReadLine();
运行结果如下:
下面说说类调用:
首先在CppDemo中添加一个UserInfo这个类:
其中,UserInfo.h 中添加下面程序;
class UserInfo { private: char* m_Name; int m_Age; public: UserInfo(char* name, int age) { m_Name = name; m_Age = age; } virtual ~UserInfo(){ } //这里的virtual是留待以后实现的意思; int GetAge() { return m_Age; } char* GetName() { return m_Name; } };
UserInfo.cpp的内容如下:
#include "stdafx.h" #include "malloc.h" #include "UserInfo.h" typedef struct { char name[32]; int age; } User;//定义一个结构体命名为为User; UserInfo* userInfo;//声明一个指向UserInfo对象的指针; //下面这个是接口,返回一个指针指向的地址; //注意:代码中的User*是个指针,返回也是一个对象指针,这样做为了防止方法作用域结束后的局部变量的释放。 extern "C" __declspec(dllexport) User* Create(char* name, int age) { //malloc 向系统申请分配指定size个字节的内存空间。 //返回类型是 void* 类型。void* 表示未确定类型的指针。 User* user = (User*)malloc(sizeof(User));//分配给user一块内存;再用User进行格式化; userInfo = new UserInfo(name, age); //复制; strcpy(user->name, userInfo->GetName()); user->age = userInfo->GetAge(); return user; }
然后再在我们之前建立的C#程序中添加:
在项目中CPPDLL类中添加代码:
//类调用程序块; [DllImport("CppDemo.dll")] public static extern IntPtr Create(string name, int age); //IntPtr是表示平台特定指针,使用时指定; //StructLayout特性允许我们控制Structure语句块的元素在内存中的排列方式, //以及当这些元素被传递给外部DLL时,运行库排列这些元素的方式。 [StructLayout(LayoutKind.Sequential)] //控制结构体字段的物理布局;括号中的意思是按出现顺序依次布局; public struct User { //MarshalAs属性指示如何在托管代码和非托管代码之间封送数据。 //下面说明两个字段的数据占的能存大小,第一个是字符类型;第二个是32位int类型; //它有两个参数,所以能管到下面两行; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string Name; public int Age; }
在主程序中添加如下代码:
//下面是类的调用 IntPtr ptr = CPPDLL.Create("Shawn", 25); //转换指针为User类型;因为返回的是一个object,所以要格式化成User; CPPDLL.User user = (CPPDLL.User)Marshal.PtrToStructure(ptr, typeof(CPPDLL.User)); Console.WriteLine("Name: {0}, Age: {1}", user.Name, user.Age);
注意对C#程序进行重新编译
运行结果如下;