您的位置:首页 > 编程语言 > C语言/C++

C#项目调用C++的DLL程序

2016-04-21 19:06 661 查看
本程序参考:/article/4758607.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#程序进行重新编译

运行结果如下;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: