一、为什么需要dll
代码复用是提高软件开发 效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架, 如ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点 比较多,总结起来有4点。
暴露了源代码;容易与程序员的“普通”代码发生命名冲突;多份拷贝,造成存储浪费;更新功能模块比较困难。
实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。
在Windows操作系 统中有两种可执行文件,其后缀名分别为.exe和.dll。它们的区别在于,.exe文件可被独立的装载于内存中运行;.dll文件却不能,它只能被其它 进程调用。然而无论什么格式,它们都是二进制文件。上面说到的“二进制级别”的代码复用,可以使用.dll来实现。
与白盒复用相 比,.dll很大程度上弥补了上述4大缺陷。.dll是二进制文件,因此隐藏了源代码;如果采用“显式调用”(后边将会提到),一般不会发生命名冲突;由 于.dll是动态链接到应用程序中去的,它并不会在链接生成程序时被原原本本拷贝进去;.dll文件相对独立的存在,因此更新功能模块是可行的。
说明:实现“黑盒复用”的途径不只dll一种,静态链接库甚至更高级的COM组件都是。本文只对dll进行讨论。
二、C语言制作dll文件
1. 启动vs2010,?
2.创建dll工程
a. 文件 -> 新建 -> ?项目 -> win32控制台应用程序 —>?
b. 输入工程名称, 比如 dll, 点击确定
c. 点击下一步, 在“应用程序设置界面” 勾选 dll 和空项目, 点击完成按钮
d. 视图 -> 解决方案资源管理器 ->右键点击头文件, 添加->新建项,这里用dll.h,
e.?右键点击"源文件",添加->新建项,这里我们添加dll.c,到此dll工程搭建完毕.?
3. dll.h
#ifndef AXLPLUGIN_H
#define AXLPLUGIN_H
/**/
#ifdef _WINDOWS
#define DLL_DECLARE __declspec(dllexport)
#else
#define DLL_DECLARE
#endif
DLL_DECLARE int Min(int a, int b);
/* 把所有的函数声明都列在这里 */
#endif
4. dll.c
#include "dll.h"
#include
/*根据需要添加相应的头文件*/
DLL_DECLARE int Min(int a, int b)
{
if (a >= b)
return b;
else
return a;
}
/* 把所有声明的函数都在这里实现*/
5. 生成dll lib 文件
点击build -> build dll
这样,dll 和 lib 文件就生成了。
5. dll文件的使用
试验证明dll.dll文件要和dll.lib以及dll.h文件一起使用 ?
三 ,在程序中加载dll
新建一个测试用的"Win32应用程序" ?操作:
?a.文件->新建->项目->Win32控制台应用程序.
? ?b.输入工程名称,这里我们用test_dll,点击确定按钮. ?
???????c.点击下一步,在"应用程序设置界面设置"控制台应用程序"和"空项目",点击完成按钮.??
???????d.将工程dll目录里的dll.h/dll.dll/dll.lib拷贝到工程test_dll目录里。??
???????e.视图->解决方案资源管理器,右键点击"头文件",添加->新建项,这里咱们用dll.h??
?????????右键点击"源文件",添加->新建项,这里我们添加test_dll.c,右键点击“资源文件”,??
?????????添加->"现有项",选择dll.lib,到此test_dll工程搭建完毕. ??
3.??编辑test_dll.c文件,内容如下?
#include "dll.h"
#include
int main()
{
printf("Min(2, 4) = %d\n", Min(2, 4));
printf("Min(5, 2) = %d\n", Min(5, 2));
return 0;
}
四,dll和test_dll工程的目录结构 ?
../test_dll/
│ test_dll.sdf
│ test_dll.sln
│ test_dll_dir.txt
├─Debug
│ test_dll.exe
│ test_dll.ilk
│ test_dll.pdb
├─ipch
│ └─test_dll-eb5063a1
│ test_dll-c06c53e7.ipch
└─test_dll
│ dll.dll
│ dll.h
│ dll.lib
│ test_dll.c
│ test_dll.vcxproj
│ test_dll.vcxproj.filters
│ test_dll.vcxproj.user
└─Debug
cl.command.1.tlog
CL.read.1.tlog
CL.write.1.tlog
link-cvtres.read.1.tlog
link-cvtres.write.1.tlog
link.3004-cvtres.read.1.tlog
link.3004-cvtres.write.1.tlog
link.3004.read.1.tlog
link.3004.write.1.tlog
link.command.1.tlog
link.read.1.tlog
link.write.1.tlog
mt.command.1.tlog
mt.read.1.tlog
mt.write.1.tlog
rc.command.1.tlog
rc.read.1.tlog
rc.write.1.tlog
test_dll.Build.CppClean.log
test_dll.exe.embed.manifest
test_dll.exe.embed.manifest.res
test_dll.exe.intermediate.manifest
test_dll.lastbuildstate
test_dll.log
test_dll.obj
test_dll_manifest.rc
vc100.idb
vc100.pdb
?
转载于:https://www.gsm-guard.net/wangjz/p/4808803.html
1.首先创建生成dll的项目:打开VS2010,两种途经建立dll项目,基于MFC DLL的和基于Win32控制台应用程序的,这里选择基于Win32控制台建立。
a.文件--新建--项目(项目命名为myAPI)--Visual C++ --Win32--选择Win32控制台应用程序;
b.接下来下一步一直到如下图,程序类型选择DLL,如无特殊需要选择空项目完成,项目建立完毕;
2.定义头文件:为项目添加头文件myAPI.h 内部添加如下代码,
#ifndef _DLL_API
#define _DLL_API _declspec(dllexport)
#else
#define _DLL_API _declspec(dllimport)
#endif
_DLL_API int ADD(int a,int b);
内部定义一个ADD()函数接口,如需添加其他函数接口,可以接着继续定义,如
_DLL_API int MINUS(int a,int b);
_DLL_API int otherfunc(int,int,int);
3.定义源文件:为项目添加相应的源文件myAPI.cpp 之后便是在该源文件中对函数进行定义,内部添加如下代码,
#include "myAPI.h"
??
在VS2010上使用C#调用非托管C++生成的DLL文件(图文讲解)
背景?
???? 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件的时候,通过C#来开发应用软件时,就需要利用DllImport特性进行方法调用。本篇文章将引导你快速理解这个调用的过程。
步骤
1. 创建一个CSharpInvokeCPP的解决方案:
2. 创建一个C++的动态库项目:
3. 在应用程序设置中,选择“DLL”,其他按照默认选项:
最后点击完成,得到如图所示项目:
????? 我们可以看到这里有一些文件,其中dllmain.cpp作为定义DLL应用程序的入口点,它的作用跟exe文件有个main或者WinMain入口函数是一样的,它就是作为DLL的一个入口函数,实际上它是个可选的文件。它是在静态链接时或动态链接时调用LoadLibrary和FreeLibrary时都会被调用。详细内容可以参考(DllMain详解_许振坪的专栏-CSDN博客_dllmain)。
4. 现在我们打开CSharpInvokeCPP.CPPDemo.cpp文件:
现在我们加入以下内容:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// CSharpInvokeCPP.CPPDemo.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
extern
"C"
__declspec
(
dllexport
)
int
Add(
int
x,
int
y)
{
????
return
x + y;
}
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
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。
5. 编译项目程序,最后在Debug目录生成CSharpInvokeCPP.CPPDemo.dll和CSharpInvokeCPP.CPPDemo.lib
我们用反编译工具PE Explorer查看下该DLL里面的方法:
可以发现对外的公共函数上包含这四种“加减乘除”方法。
6. 现在来演示下如何利用C#项目来调用非托管C++的DLL,首先创建C#控制台应用程序:
7. 在CSharpInvokeCSharp.CSharpDemo项目上新建一个CPPDLL类,编写以下代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public
class
CPPDLL
{
????
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
????
public
static
extern
int
Add(
int
x,
int
y);
????
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
????
public
static
extern
int
Sub(
int
x,
int
y);
????
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
????
public
static
extern
int
Multiply(
int
x,
int
y);
????
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
????
public
static
extern
int
Divide(
int
x,
int
y);
}
DllImport作为C#中对C++的DLL类的导入入口特征,并通过static extern对extern “C”进行对应。
8. 另外,记得把CPPDemo中生成的DLL文件拷贝到CSharpDemo的bin目录下,你也可以通过设置【项目属性】->【配置属性】->【常规】中的输出目录:
这样编译项目后,生成的文件就自动输出到CSharpDemo中了。
9. 然后在Main入口编写测试代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static
void
Main(
string
[] args)
{
????
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();
}
运行结果:
方法得到调用。
10. 以上的方法只能通过静态方法对于C++中的函数进行调用。那么怎样通过静态方法去调用C++中一个类对象中的方法呢?现在我在CPPDemo项目中添加一个头文件userinfo.h:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class
UserInfo {
private
:
????
char
* m_Name;
????
int
m_Age;
public
:
????
UserInfo(
char
* name,
int
age)
????
{
????????
m_Name = name;
????????
m_Age = age;
????
}
????
virtual
~UserInfo(){ }
????
int
GetAge() {
return
m_Age; }
????
char
* GetName() {
return
m_Name; }
};
在CSharpInvokeCPP.CPPDemo.cpp中,添加一些代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "malloc.h"
#include "userinfo.h"
typedef
struct
{
????
char
name[32];
????
int
age;
} User;?
UserInfo* userInfo;
extern
"C"
__declspec
(
dllexport
) User* Create(
char
* name,
int
age)???
{??
????
User* user = (User*)
malloc
(
sizeof
(User));
????
userInfo =
new
UserInfo(name, age);
????
strcpy
(user->name, userInfo->GetName());?
????
user->age = userInfo->GetAge();
????
return
user;
}
这里声明一个结构,包括name和age,这个结构是用于和C#方面的结构作个映射。
注意:代码中的User*是个指针,返回也是一个对象指针,这样做为了防止方法作用域结束后的局部变量的释放。
strcpy是个复制char数组的函数。
11. 在CSharpDemo项目中CPPDLL类中补充代码:
?
1
2
3
4
5
6
7
8
9
10
11
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
public
static
extern
IntPtr Create(
string
name,
int
age);
[StructLayout(LayoutKind.Sequential)]
public
struct
User
{
????
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
????
public
string
Name;
????
public
int
Age;
}
其中这里的结构User就和C++中的User对应。
12. 在Program.cs中补充代码:
?
1
2
3
IntPtr ptr = CPPDLL.Create(
"李平"
, 27);
"#ff0000" >CPPDLL.User user = (CPPDLL.User)Marshal.PtrToStructure(ptr, typeof (CPPDLL.User));
Console.WriteLine(
"Name: {0}, Age: {1}"
, www.gsm-guard.net, user.Age);
注意:红色字体部分,这里结构指针首先转换成IntPtr句柄,然后通过Marshal.PtrToStructrue转换成你所需要的结构。
运行结果:
最后附上我的源代码:CSharpInvokeCPP.rar,希望对大家有所帮助:)
zhuanzi?? ?https://www.gsm-guard.net/liping13599168/archive/2011/03/31/2000320.html