首先:DLL技术是针对C的技术,虽然也支持C++,但是对C++的支持不够好。C++对应的是COM技术。

建议首先看一下Programming  Windows的21章,虽然没有讲MFC如何制作DLL,但是讲了一些很重要的基本概念。

教程地址:

http://blog.csdn.net/chenqiang35/article/details/3069382

http://oulehui.blog.163.com/blog/static/79614698201152423656383/

http://www.cnblogs.com/DxSoft/archive/2011/04/22/2024686.html

笔记:环境为Win7+VS2013

1.DLL工程的创建

1.1.Win32  DLL项目的创建

新建项目->VC++->Win32项目->下一步->DLL->完成

1.2.MFC DLL项目的创建

新建项目->VC++->MFC->MFC DLL->下一步->选择DLL类型->完成

2.第一个Win32 DLL的制作

2.1.创建Win32 DLL项目:AddXxx,注意Wizard自动生成了stdafx.h,AddXxx.cpp,dllmain.cpp,stdafx.cpp等文件,目前不去管他们,默认即可

2.2.添加Add.h和Add.cpp

Add.h

这里的DLLEXPORT宏是借鉴Programming Windows 5th的做法(作者用的是EXPORT,但是我发现和afx某个头文件中的宏定义冲突,所以我改为了DLLEXPORT),凡是DLL内部使用的符号,函数,则头文件中其声明之前不用DLLEXPORT宏,否则头文件中声明之前要加DLLEXPORT宏。

简单说,DLL模块的interface部分需要加DLLEXPORT声明,属于implementation的部分不应该加DLLEXPORT声明

 #pragma once

 #ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec (dllexport)
#else
#define DLLEXPORT __declspec (dllexport)
#endif DLLEXPORT int add(int a, int b);

Add.cpp

#include "stdafx.h"
#include "Add.h" int add(int x, int y)
{
return x + y;
}

注意,这里有第二种方法声明哪些部分是DLL的接口,就是用.def文件,即模块配置文件,这样你就用不着DLLEXPORT宏了,我比较偏爱这种方法(二者的比较:http://blog.chinaunix.net/uid-9681606-id-1998574.html)

把上面跟DLLEXPORT相关的部分全部删除,然后

添加->VC++->代码->模块定义文件->添加一个名为AddXxx的模块定义文件,内容如下

 LIBRARY "AddXxx"

 EXPORTS

 add @ 

.def文件的规则为:(更完整的建议看http://blog.163.com/hanyinlong@126/blog/static/99751486201363115639401/)

  (1)LIBRARY语句说明.def文件对应的DLL;

  (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);

  (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。

  由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态链接库,导出其中的add函数,并指定add函数的序号为1。

好,生成解决方案,生成了AddXxx.dll和AddXxx.lib

这里的AddXxx.lib是什么?是对应的静态库吗?不是。参考Programming Windows  5th,这玩意儿叫做import library,即IL,IL里面不包含你写的可执行代码,只包含一些其他信息。一般我们生成的静态库是object library(因为他本质上就是对obj文件进行了打包封装)叫做OL,OL里面是包含你写的可执行代码的。要说明IL是拿来干嘛的,就要说到DLL的两种加载方式。

假设你写了另一个项目叫做Test要用到AddXxx.dll,你有两种办法加载它:

一种是配置AddXxx.dll对应的头文件的搜索路径,然后在代码中调用LoadLibrary加载AddXxx.dll,用完了以后调用FreeLibrary卸载。这叫所谓的动态调用

另一种是配置好AddXxx.dll对应的头文件的搜索路径(包含目录)+AddXxx.dll以及AddXxx.lib的搜索路径(库目录)+链接器中配置好AddXxx.dll和AddXxx.lib(链接器/输入/附加倚赖项,延迟加载的DLL),从而程序在运行的时候自动加载之,程序结束的时候自动卸载。这叫所谓的静态调用

静态调用就要用到IL,上面提到了静态调用需要事先在项目里配置DLL,其实不仅要配置DLL,还要配置IL,IL里面包含是用来给linker提供DLL的信息从而linker可以把这些信息写到其他DLL或者EXE中,从而实现程序运行时自动加载相应的DLL,因此你在用的时候不需要任何显式加载DLL的代码,用起来就跟使用OL一样方便,这是静态调用的优点;其缺点在于没有动态调用灵活,因为动态调用可以更精确地控制什么时候加载DLL什么时候卸载DLL。

在编译阶段,无论是静态调用还是动态调用,都需要相应DLL的头文件;静态调用由于编译阶段就需要解析DLL中的符号(动态调用则不用,因为动态调用是用LoadLibrary这些API去加载),因此还需要IL的名称,位置,可以符号即其他信息从IL中提取出来放到生成的EXE中。

在运行阶段(或者调试的时候),只需要DLL文件就可以了。

编译阶段:

动态调用的代码示例(只需要配置好头文件的位置即可):

头文件位置的配置:项目属性包含目录中添加一项->E:\vs_wksp\PW_MFC_2ND\AddXxx\AddXxx;

#include "Add.h"

typedef int(*lpAddFun)(int, int);
HINSTANCE hDll;
lpAddFun addFun;
hDll = ::LoadLibrary(_T("E:\\vs_wksp\\PW_MFC_2ND\\AddXxx\\Debug\\AddXxx.dll"));
addFun = (lpAddFun)::GetProcAddress(hDll, "add");
if (addFun(, ) == )
MessageBox(_T("This feature is currently unimplemented. Sorry!"),
_T("Error"), MB_ICONINFORMATION | MB_OK);
FreeLibrary(hDll);

这样你就可以编译过了

静态调用的代码实例(不仅要配置好头文件的位置,还要配置好IL的位置以及IL的名称):

如上所述配置头文件的位置

配置IL的位置:项目属性,库目录添加一项->E:\vs_wksp\PW_MFC_2ND\AddXxx\Debug,这个目录是编译阶段用来搜索AddXxx.lib的位置的

配置IL的名称:项目属性,链接器,输入,附加依赖项,填上AddXxx.lib

#include "Add.h"

if (add(, ) == )
MessageBox(_T("This feature is currently unimplemented. Sorry!"),
_T("Error"), MB_ICONINFORMATION | MB_OK);

好,对于静态调用的代码,这样你也可以编译过了

对于链接器中的另一个选项,延迟加载,其含义是你指定DLL文件的名字,在这些DLL中的代码只有在真正被调用到的时候才会加载该DLL,如果必要的话,你可以加上(参考:http://blog.csdn.net/panda1987/article/details/5936770)

运行/调试阶段:

前面说过,此阶段只要DLL文件就行了。与IL不一样,IL是你在编译的时候有用,编译完就没用了,而DLL是你的EXE在运行的时候要用的,运行的时候DLL必须存在在如下位置,从而你的EXE能找到它。

配置DLL的搜索路径:

你的

1、把DLL文件与EXE放到同一个目录(亲测可行),或者直接用LoadLibrary用代码显式加载(可行)

你在调试EXE的时候,有两个办法,一个是手工把DLL拷贝到EXE所在目录,另一个是修改项目属性->配置属性->调试->工作目录->修改为DLL所在的目录

2、在IL指定的目录(不知道能不能行,反正我捣鼓半天没捣鼓明白到底怎么弄)

参考http://blog.csdn.net/h57020877/article/details/5943898,你在生成DLL之前,通过配置DLL项目,使得生成的IL中指明DLL文件所在的位置,从而编译EXE的时候,IL中的信息编译到EXE中,EXE运行的时候就会自动到指定目录去加载DLL

3、把DLL文件放到系统的SYSTEM32目录下(亲测没卵用,不知道什么情况)

参考http://bbs.csdn.net/topics/390192334,http://bbs.csdn.net/topics/350072027,http://bbs.csdn.net/topics/390499759

4、PATH环境变量指定的目录下(没试)

5、.local重定向(没仔细研究)

参考http://blog.csdn.net/wingeek/article/details/3621822

导出DLL中的全局变量

这里我使用DEF文件导出DLL中的一个函数一个全局变量,导入的时候用__declspec(dllimport)即可

创建一个workspace,名为DLLDEMO,把上面创建的AddXxx项目导入这个workspace,然后再在这个workspace中新建一个TestDLL项目用于测试AddXxx.dll,配置好TestDLL项目从而它可以包含AddXxx的头文件以及link到TestDLL的import library。

选择:项目->项目依赖项,让TestDLL依赖于AddXxx,这样在构建TestDLL的时候可以自动构建AddXxx(如果AddXxx没有保持最新的话)

在解决方案资源管理器中双击TestDLL,选择:项目->设为启动项目。这是因为TestDLL是整个程序的入口。调试的时候就从TestDLL生成的EXE启动

AddXxx跟上面一样,不去修改生成的stdafx.h,stdafx.cpp,dllmain.cpp等文件。添加AddXxx.h。代码如下:

AddXxx.h

 #pragma once

 #ifdef DLL_EXPORT
#define DLL_API
#else
#define DLL_API __declspec (dllimport)
#endif DLL_API extern int nGlobal; DLL_API int add(int a, int b);

因为使用DEF文件对DLL的API进行导出,所以没有使用__declspec(dllexport);用__declspec(dllimport)对DLL的API进行导入,所以这里定义了宏DLL_API。在编译DLL项目的时候,去preprocessor的选项中添加上DLL_EXPORT宏,在编译客户程序的时候则不添加DLL_EXPORT宏

AddXxx.cpp

 // AddXxx.cpp : 定义 DLL 应用程序的导出函数。
// #include "stdafx.h"
#include "AddXxx.h" int add(int x, int y)
{
return x + y + nGlobal;
} int nGlobal = ;

AddXxx.def

 LIBRARY AddXxx

 EXPORTS

 add @ 

 nGlobal DATA

TestDLL项目中客户程序关键代码:

     if (add(, ) == nGlobal++) {
CString info;
info.Format(_T("%d%s"), add(, ), _T("This feature is currently unimplemented. Sorry!"));
MessageBox(info,
_T("Error"), MB_ICONINFORMATION | MB_OK);
}

TestDLL的配置:

1、AddXxx的头文件路径(在项目属性的VC++目录中配置)

2、静态调用,所以配置AddXxx的import library,即AddXxx.lib(在项目属性的VC++目录中配置其路径,在链接器输入中配置AddXxx.lib)

什么时候用静态调用,什么时候用动态调用:https://msdn.microsoft.com/en-us/library/253b8k2c.aspx

什么时候用DEF文件,什么时候用__declspec(dllexport):https://msdn.microsoft.com/en-us/library/900axts6.aspx

由于项目AddXxx和TestDLL都是一个workspace的,所以输出的DLL文件和EXE文件是放在一个目录下的,因此EXE文件在运行时可以找到DLL文件(这样静态调用就能正确起作用)。

转:DLL教程的更多相关文章

  1. 创建dll教程

    先看我的总结: 总结: 1.头文件中如果想以C形式提供,要判断,如果定义了 _cplusplus, extern "c"{  这里写接口声明 } 2.接口声明中,要表明接口接入点的 ...

  2. Unity封装dll教程整理

    ///作者Unity3d师兄---LeroyYang 通过网上大神们的资料以及自己的整理,学习一下用vs2013简单的封装dll文件,方便接口模式下开发,使得逻辑层更为清晰. 操作步骤 1.打开vs2 ...

  3. [DLL] Dynamic link library (dll) 的编写和使用教程

    前一阵子,项目里需要导出一个DLL,但是导出之后输出一直不怎么对,改了半天才算改对...读了一些DLL教程,感觉之后要把现在的代码导出,应该还要花不少功夫...下面教程参照我读的3个教程写成,所以内容 ...

  4. Python调用C# Com dll组件实战

    之前公司有套C# AES加解密方案,但是方案加密用的是Rijndael类,而非AES的四种模式(ECB.CBC.CFB.OFB,这四种用的是RijndaelManaged类),Python下Crypt ...

  5. C输入输出函数与缓冲区

    #转 对C语言输入输出流和缓冲区的深入理解C语言缓冲区(缓存)详解缓冲区又称为缓存,它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的 ...

  6. VS2015 VB.Net利用QrCodeNet生成QR Code

    Step by step Create QR Code with QrCodeNet Step.1 新建項目 Step.2 下載QrCodeNet代碼,解壓\QrCodeNet\sourceCode\ ...

  7. VS2015 C#利用QrCodeNet生成QR Code

    Step by step Create QR Code with QrCodeNet Step.1 新建項目 Step.2 在窗口中拖入一個Button Step.3 下載QrCodeNet代碼,解壓 ...

  8. DLL编写教程(绝对经典之作)

    DLL编写教程 半年不能上网,最近网络终于通了,终于可以更新博客了,写点什么呢?决定最近写一个编程技术系列,其内容是一些通用的编程技术.例如DLL,COM,Socket,多线程等等.这些技术的特点就是 ...

  9. VisualSVN 5.1.5 破解版 手动破解教程 生成dll文件

    VisualSVN 5.1.5 破解版 手动破解教程 生成VisualSVN.Core.L.dll文件 附上本人用到的命令: ildasm "D:\Program Files (x86)\V ...

随机推荐

  1. Maven问题总结:could not resolve archetype xxxxxxx from any of the configured repositories

    错误提示 Eclipse中通过Archetype创建Maven项目时报错:Could not resolve archetype xxxxxxx from any of the configured ...

  2. Myeclipse 多版本共存

    Myeclipse2015 stable2.0 和Myeclipse10 共存的解决办法: 1. 准备内容: 1) myeclipse-2015-stable-2.0-offline-installe ...

  3. DS实验题 地鼠安家

    ★实验任务 fd是一个公认的美丽校园.一天,fd来了一群地鼠,编号为1到n,他们希望在这里定居.现在先由第一只地鼠往下打一个单位的距离,并且在那里安家.对于每一个已经安家的地鼠,如果他左下或右下没有邻 ...

  4. git 使用钩子直接推送到工作目录

    远端机器 $ mkdir /www/teacherapi  # 创建工作目录 $ cd /data/git $ git init teacherapi.git --bare --shared Init ...

  5. Yii源码阅读笔记(四)

    所有控制器action的基类yii\base\Action.php namespace yii\base;//定义的命名空间 use Yii //使用的命名空间 class Action extend ...

  6. Python实用工具包Scrapy安装教程

       对于想用每个想用Python开发网络爬虫的开发者来说,Scrapy无疑是一个极好的开源工具.今天安装之后觉得Scrapy的安装确实不易啊.所以在此博文一篇,往后来着少走弯路. 废话不多说了,如果 ...

  7. Levenshtein distance

    https://en.wikipedia.org/wiki/Levenshtein_distance 验证码识别 图片中的二维码截取

  8. 蓝牙的OBEX协议

    1.概述     OBEX为Object Exchange,用于在蓝牙设备间传数据对象,来源于红外定义的协议,后被蓝牙采用.OBEX在蓝牙协议层中的位置如下图(在之前的OBEX版本中,OBEX是通过R ...

  9. 利用快速排序原理找出数组中前n大的数

    #include <stdio.h> #include <stdint.h> #include <stdlib.h> #define MAX_SIZE 400001 ...

  10. Linux CentOS 编绎安装Python 3.5

    Linux CentOS 编绎安装Python 3.5 先决条件(若无安装,则不能编绎使用idle3):yum install tk-devel xz -d Python-3.5.0.tar.xzta ...