文章来源:http://www.cnblogs.com/houkai/archive/2013/06/05/3119513.html

代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点比较多,总结起来有4点。
暴露了源代码;多份拷贝,造成存储浪费;
容易与程序员的“普通”代码发生命名冲突;
更新功能模块比较困难,不利于问题的模块化实现;
实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。
说明:实现“黑盒复用”的途径不只dll一种,静态链接库甚至更高级的COM组件都是。

2.dll的创建

参考程序原文:http://msdn.microsoft.com/zh-cn/library/ms235636.aspx
新建“Win32项目”,选择应用程序类型为"DLL”,其他默认。添加头文件testdll.h

//testdll.h
#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
namespace MathFuncs
{
// This class is exported from the testdll.dll
class MyMathFuncs
{
public:
// Returns a + b
static TESTDLL_API double Add(double a, double b); // Returns a - b
static TESTDLL_API double Subtract(double a, double b); // Returns a * b
static TESTDLL_API double Multiply(double a, double b); // Returns a / b
// Throws const std::invalid_argument& if b is 0
static TESTDLL_API double Divide(double a, double b);
};
}

当定义了符号TESTDLL_EXPORTS,TESTDLL_API被设置为 __declspec(dllexport) 修饰符,This modifier enables the function to be exported by the DLL so that it can be used by other applications。若未定义则TESTDLL_API被设置为__declspec(dllimport),This modifier enables the compiler to optimize the importing of the function from the DLL for use in other applications。当DLL项目生成时,TESTDLL_EXPORTS默认是定义的,所以默认设置的是__declspec(dllexport) 修饰符。
添加cpp文件

// testdll.cpp : 定义 DLL 应用程序的导出函数。
#include "stdafx.h"
#include "testdll.h"
#include <stdexcept>
using namespace std; namespace MathFuncs
{
double MyMathFuncs::Add(double a, double b)
{
return a + b;
} double MyMathFuncs::Subtract(double a, double b)
{
return a - b;
} double MyMathFuncs::Multiply(double a, double b)
{
return a * b;
} double MyMathFuncs::Divide(double a, double b)
{
if (b == )
{
throw invalid_argument("b cannot be zero!");
}
return a / b;
}
}

编译就会生成对应的dll文件,同时也会生成对应的lib文件。
注意:a.DLL中导出函数的声明有两种方式:在函数声明中加上__declspec(dllexport);采用模块定义(.def)文件声明。详见:http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html
b.对于C文件创建dll时或者想使用C编译器创建dll时,建议使用 extern “C” 标志,参见extern "C"的简单解析

3.dll的调用

应用程序使用DLL可以采用两种方式:一种是隐式链接(调用),另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。VS在VC\bin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。两种的对比详见:http://blog.sina.com.cn/s/blog_53004b4901009h3b.html
隐式链接采用静态加载的方式,比较简单,需要.h、.lib、.dll三件套。新建“控制台应用程序”或“空项目”。配置如下:
项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件testdll.h所在的目录
项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件testdll.lib所在的目录
项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“testdll.lib”(若有多个 lib 则以空格隔开)
添加cpp文件

//mydll.cpp
#include <iostream>
#include "testdll.h"
using namespace std; int main()
{
double a = 7.4;
int b = ; cout << "a + b = " <<
MathFuncs::MyMathFuncs::Add(a, b) << endl;
cout << "a - b = " <<
MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
cout << "a * b = " <<
MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
cout << "a / b = " <<
MathFuncs::MyMathFuncs::Divide(a, b) << endl; try
{
cout << "a / 0 = " <<
MathFuncs::MyMathFuncs::Divide(a, ) << endl;
}
catch (const invalid_argument &e)
{
cout << "Caught exception: " << e.what() << endl;
}
return ;
}

现在可以编译通过了,但是程序运行就报错,还需要将testdll.dll复制到当前项目生成的可执行文件所在的目录。
显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。
新建项目,不需要特殊配置,添加cpp文件

/*
*作者:侯凯
*说明:显式调用DLL
*日期:2013-6-5
*/
#include<Windows.h> //加载的头文件
#include<iostream>
using namespace std; int main()
{
typedef double (*pAdd)(double a, double b);
typedef double (*pSubtract)(double a, double b); HMODULE hDLL = LoadLibrary("testdll.dll"); //加载dll文件
if(hDLL != NULL)
{
pAdd fp1 = pAdd(GetProcAddress(hDLL, MAKEINTRESOURCE())); //得到dll中的第一个函数
if(fp1 != NULL)
{
cout<<fp1(2.5, 5.5)<<endl;
}
else
{
cout<<"Cannot Find Function "<<"add"<<endl;
}
pSubtract fp2 = pSubtract(GetProcAddress(hDLL, "?Subtract@MyMathFuncs@MathFuncs@@SANNN@Z")); //得到dll中标示为"?..."的函数,C++编译器考虑了函数的参数
if(fp2 != NULL)
{
cout<<fp2(5.5, 2.5)<<endl;
}
else
{
cout<<"Cannot Find Function "<<"Subtract"<<endl;
}
FreeLibrary(hDLL);
}
else
{
std::cout<<"Cannot Find "<<"testdll"<<std::endl;
}
return ;
}

显式调用的问题:在DLL文件中,dll工程中函数名称在编译生成DLL的过程中发生了变化(C++编译器),在DLL文件中称变化后的字符为“name标示”。GetProcAddress中第二个参数可以由DLL文件中函数的顺序获得,或者直接使用DLL文件中的”name标示”,这个标示可以通过Dumpbin.exe小程序查看。如果C++编译器下,想让函数名更规范(和原来工程中一样),具体方法详见:http://blog.csdn.net/btwsmile/article/details/6676802
当然,为了让函数名更规范,最常用的方式是:创建dll过程中使用C编译器来编译函数,这样DLL文件中的函数名和原dll工程中的函数名就一致了。

4.更一般的显式调用

为了解决上部分最后的问题,可以使用 extern “C” 为dll工程中的函数建立C连接,简单的示例工程如下。
在DLL创建的工程中,添加cpp文件

/*
*作者:侯凯
*说明:创建dll,使用C接口——C编译器生成的dll中函数的"name标示"仍为addfun
*日期:2013-6-5
*/
// cdll.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h" #ifdef __cplusplus // if used by C++ code
extern "C" { // we need to export the C interface
#endif __declspec(dllexport) int addfun(int a, int b)
{
return a+b;
} #ifdef __cplusplus
}
#endif

编译即可生成DLL文件。在dll调用工程中,添加cpp文件

/*
*作者:侯凯
*说明:显式调用dll
*日期:2013-6-5
*/
#include <windows.h>
#include <iostream>
using namespace std; void main()
{
typedef int(*FUNA)(int,int);
HMODULE hMod = LoadLibrary("cdll.dll");//dll路径
if (hMod)
{
FUNA addfun = (FUNA)GetProcAddress(hMod, TEXT("addfun"));//直接使用原工程函数名
if (addfun != NULL)
{
cout<<addfun(, )<<endl;
}
else
{
cout<<"ERROR on GetProcAddress"<<endl;
}
FreeLibrary(hMod);
}
else
cout<<"ERROR on LoadLibrary"<<endl;
}

运行,这样便可以调用dll的函数了。再进一步,上述dll文件如果通过隐式调用,利用.dll、.lib文件,调用函数应为

//隐式链接
#include <iostream>
#pragma comment(lib,"cdll.lib")
using namespace std; extern "C" _declspec(dllimport) int addfun(int a,int b);
//载入addfun函数,这里起到了.h文件的作用
//dll中使用C编译器 故这里需要extern "C" 如果dll中无extern "C"
//此处为:_declspec(dllimport) int addfun(int a,int b);
void main()
{
cout<<addfun(,)<<endl;
}

VC++创建、调用dll的方法步骤的更多相关文章

  1. 在ASP中调用DLL的方法

    .net的dll已经不是严格意义上的动态连接库了,而是一个类或者类库.它是不能直接在ASP.VB等其它的应用环境中使用的.   我们可以通过COM包装器(COM callable wrapper (C ...

  2. 在vs2013下手把手创建/调用dll

    body { font: 16px } 参考了大佬的文章 首先,体会一下静态编译: 创建Win32Project,选DLL,添加一个.h和.cpp文件 点击生成解决方案,然后去debug目录下拷贝.l ...

  3. C++ 生成 dll 和调用 dll 的方法实例(转)

    1)生成dll 建立两个文件 xxx.h , xxx.cpp xxx.h内容如下: #ifdef BUILD_XXX_DLL#define EXPORT __declspec(dllexport)#e ...

  4. VC动态调用DLL

    1. //函数指针声明 typedef int (_stdcall MYDLLFUN)(char* _pcOut, /*INOUT*/int *_piOutBufLen, char* _pcIn, i ...

  5. 自然语言处理工具python调用hanlp的方法步骤

    Python调用hanlp的方法此前有分享过,本篇文章分享自“逍遥自在017”的博客,个别处有修改,阅读时请注意! 1.首先安装jpype 首先各种坑,jdk和python 版本位数必须一致,我用的是 ...

  6. linux创建进程fork的方法步骤

    fork创建进程 函数原型如下 #include// 必须引入头文件,使用fork函数的时候,必须包含这个头文件,否则,系统找不到fork函数 pid_t fork(void); //void代表没有 ...

  7. VS2010创建动态链接库(DLL)的方法

    1.第一步创建WIN32项目,选择DLL 2.第二步,创建你自己的DLL CPP文件和头文件,下面以两个简单的加减法函数为例子导出 然后编译生成即可.DLL文件在Debug或Release目录中 .d ...

  8. VC静态调用DLL(lib)

    1. #pragma comment(lib, "libxml2.lib")#pragma comment(lib, "iconv.lib")#pragma c ...

  9. vc导出调用dll的两种方式

    一.stdcall 1. #define  DLLEXPORT _declspec(dllexport) _stdcall, int DLLEXPORT func(const char *peer,u ...

随机推荐

  1. 初识Less(2015年05月23日)

    因为最近在研究Bootstrap,然后才了解到Less,听说Less很强大,又听说Bootstrap+Less会更搭,所以就决定也顺带了解下Less的相关知识. come  on...... 一.简介 ...

  2. spark集成hive遭遇mysql check失败的问题

    问题: spark集成hive,启动spark-shell或者spark-sql的时候,报错: INFO MetaStoreDirectSql: MySQL check failed, assumin ...

  3. Android之布局

    Android中的布局分为六种,分别是相对布局.线性布局.表格布局.网格布局.帧布局.绝对布局,良好的布局设计对UI界面至关重要,下面先来看看先相对布局. 相对布局(RelativeLayout): ...

  4. php对UTF8字体串进行单字分割返回数组

    在网上查了很多字符串分割方法,都无法正确对UTF8字符串进行正确分割返回单个字符的数组.经过对FTU8编码的分析写出了下面的方法对UTF8进行分割.本人测试可用.本方法只支持UTF8编码的,其它编码转 ...

  5. JAVA 基础加强学习笔记

    一.面向对象 (一)继承  1.继承的好处: (1) 提高了代码的复用性. (2) 让类与类之间产生了关系,提供了另一个特征多态的前提. 注意: 子类中所有的构造函数都会默认访问父类中的空参数的构造函 ...

  6. 第五十三篇、OC利用AFN上传视频到服务器

    整体思路已经清楚,拿到视频资源,先转为mp4,写进沙盒,然后上传,上传成功后删除沙盒中的文件. 本地拍摄的视频,上传到服务器: //视频转换为MP4 //转码操作... _hud.mode = MBP ...

  7. PHP学习笔记 - 进阶篇(11)

    PHP学习笔记 - 进阶篇(11) 数据库操作 PHP支持哪些数据库 PHP通过安装相应的扩展来实现数据库操作,现代应用程序的设计离不开数据库的应用,当前主流的数据库有MsSQL,MySQL,Syba ...

  8. PHP学习笔记 - 入门篇(3)

    PHP学习笔记 - 入门篇(3) 常量 什么是常量 什么是常量?常量可以理解为值不变的量(如圆周率):或者是常量值被定义后,在脚本的其他任何地方都不可以被改变.PHP中的常量分为自定义常量和系统常量 ...

  9. 基于 HTML5 的数据存储

    以前想做个静态网页APP.最初的思路是用本地文件存储数据,后来发现在手机上运行时,文件无法找到. 经过了长达几个月的搜索(实际也就几天),没有找到合适的方法. 就在绝望的时候,无意间搜到基于HTML5 ...

  10. CAF(C++ actor framework)使用随笔(使用类去构建actor和使用的一些思路)

    Class-based actorsA class-based actor is a subtype of event_based_actor and must implement the pure ...