本程序参考: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#程序进行重新编译
运行结果如下;