VS2015 创建及使用DLL,以及静态lib

创建DLL方法一

  1. 使用IDE创建Dll工程
    1.1 在既有解决方案中创建Dll工程使用 File->添加 dll项目
    1.2 需要新建解决方案时 使用File->新建 dll项目
    这里写图片描述
    是否需要导出符号,是否需要预编译头 可以自行决定
    使用导出符合可以 导出一个完整的Dll实例 如下
    这里写图片描述
    其中DllProject.h DllProject.cpp 是示例的头文件及其实现
    Dllmain.cpp 是dll的main函数入口

    【DllMain函数】
    Windows在加载DLL时,需要一个入口函数,就像控制台程序需要main函数一样。有的时候,DLL并没有提供DllMain函数,应用程序也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的默认DllMain函数版本,并不意味着DLL可以抛弃DllMain函数。
    根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数,这就说明不能在客户端直接调用DllMain函数,DllMain函数是自动被调用的。
    DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DllMain函数也被调用。参数ul_reason_for_call指明了调用DllMain的原因,有以下四种情况:
    DLL_PROCESS_ATTACH:当一个DLL被首次载入进程地址空间时,系统会调用该DLL的DllMain函数,传递的ul_reason_for_call参数值为DLL_PROCESS_ATTACH。这种情况只有首次映射DLL时才发生;
    DLL_THREAD_ATTACH:该通知告诉所有的DLL执行线程的初始化。当进程创建一个新的线程时,系统会查看进程地址空间中所有的DLL文件映射,之后用DLL_THREAD_ATTACH来调用DLL中的DllMain函数。要注意的是,系统不会为进程的主线程使用值DLL_THREAD_ATTACH来调用DLL中的DllMain函数;
    DLL_PROCESS_DETACH:当DLL从进程的地址空间解除映射时,参数ul_reason_for_call参数值为DLL_PROCESS_DETACH。当DLL处理DLL_PROCESS_DETACH时,DLL应该处理与进程相关的清理操作。如果进程的终结是因为系统中有某个线程调用了TerminateProcess来终结的,那么系统就不会用DLL_PROCESS_DETACH来调用DLL中的DllMain函数来执行进程的清理工作。这样就会造成数据丢失;
    DLL_THREAD_DETACH:该通知告诉所有的DLL执行线程的清理工作。注意的是如果线程的终结是使用TerminateThread来完成的,那么系统将不会使用值DLL_THREAD_DETACH来执行线程的清理工作,这也就是说可能会造成数据丢失,所以不要使用TerminateThread来终结线程。以上所有讲解在工程DLLMainDemo(工程下载)都有体现。


stdafx.h stdafx.cpp是预编译头文件及实现文件 targetver.h可以设置所支持的windows 平台版本 `DllProject.h文件解析如下`
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLLPROJECT_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// DLLPROJECT_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef DLLPROJECT_EXPORTS   //定义在 工程->属性->c/c++ 预处理器定义 为工程宏定义
#define DLLPROJECT_API __declspec(dllexport) //在dll项目中 为dllexport
#else
#define DLLPROJECT_API __declspec(dllimport)  //在使用该dll的项目中为 dllimport
#endif

// 此类是从 DllProject.dll 导出的
class DLLPROJECT_API CDllProject {
public:
    CDllProject(void);
    // TODO:  在此添加您的方法。
    static double Add(double a, double b);//添加的方法
};

extern DLLPROJECT_API int nDllProject; //非类变量示例

extern "C" DLLPROJECT_API int fnDllProject(void);//非类函数示例
`DllProject.cpp解析如下`
// DllProject.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
#include "DllProject.h"


// 这是导出变量的一个示例
DLLPROJECT_API int nDllProject=0;   //变量定义用DLLPROJECT_API限定

// 这是导出函数的一个示例。
DLLPROJECT_API int fnDllProject(void)
{
    return 42;
}

// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 DllProject.h
CDllProject::CDllProject()
{
    return;
}

//自定义方法
double CDllProject::Add(double a, double b) //类已限定了DLLPROJECT_API 此方法不用重复限定
{
    return a + b;
}
**不使用 导出符号属性** ![这里写图片描述](https://img-blog.csdn.net/20180603092239191?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpZ3Vhbmd4aWxneA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 自行添加头文件 实现文件 导出限定宏定义 等 **编译以上的dll工程可以得到dll库以及lib符号库(包含函数声明,变量声明等但不包含实现)**

创建DLL方法二 不常用

函数导出方式
在DLL的创建过程中,我使用的是_declspec( dllexport )方式导出函数的,其实还有另一种导出函数的方式,那就是使用导出文件(.def)。你可以在DLL工程中,添加一个Module-Definition File(.def)文件。.def文件为链接器提供了有关被链接器程序的导出、属性及其它方面的信息。
对于上面的例子,.def可以是这样的:

LIBRARY “DLLDemo2”
EXPORTS
Add @ 1 ;Export the Add function 因为指定了函数序号 所以extern “C”可以省略
Module-Definition File(.def)文件的格式如下:

LIBRARY语句说明.def文件对应的DLL;
EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号有一定的作用)。
使用def文件,生成了DLL
这里写图片描述
整体如下
这里写图片描述
以上dll工程 创建完毕,编译之后 dll lib库生成位置默认为 解决方案下的debug目录中


使用DLL库

在同一个解决方案下使用

  1. 添加引用 如下图
    这里写图片描述
    这里写图片描述
  2. 添加头文件 附加包含目录
    dll库头文件所在目录 此时引用头文件时,可以找到相应头文件
    这里写图片描述
    添加 附加包含目录
    这里写图片描述

  3. 使用库函数
    这里写图片描述

在不同的解决方案下使用dll库

静态使用dll库 需要头文件,导出库lib文件(生成dll时生成的文件与静态库lib同名但内容不同)以及dll文件
  1. 设置库头文件目录 附加包含目录 工程属性->C/C++->常规 附加包含目录 包含头文件时使用
  2. 设置l附加库目录 lib文件所在目录 工程属性->链接器->常规 附加库目录
  3. 设置依赖库 工程属性->链接器->输入->附加依赖项 具体到lib的全路径
    相当于在相应文件加入 #pragma comment(lib, “xxx\libconfig.lib”)
    #pragma comment(lib, “libconfig.lib”)//此时libconfig.lib需要和.h文件在同级目录下
    如下图 主要添加编译器 编译后的函数及变量 类等声明
    这里写图片描述
  4. dll动态库的设置 使得可以找到dll库 定义 方法如下
    方法1. 放置到解决方案下的 debug/release 目录下,与平台无关,可直接复制工程到别的电脑
    方法2. 放置到win32 system32 或者system目录下相当于放置在已有环境变量目录下 平台有关
    方法3. 设置dll动态库路径到 环境变量Path中 与平台有关 需要重启以使得环境变量生效
    方法4. 设置工作目录为 dll库所在目录 工作目录为程序启动后唯一的默认目录 文件读写就会自动寻找该目录,例如这种情况下,程序就会从工作目录中读取dll库,默认情况下工作目录为$(ProjectDir),所以 同方法1的原理相同,也因为如此,该目录作为默认目录会被放入别的文件
    选择当前工程,右击”属性” -> “配置属性” -> “调试”,在”工作目录”设置dll的路径
    优点:跨平台(拷贝到其他平台不用改动)
    缺点:目录不能很干净,因为生成的文件会自动放到这个目录下
    这里写图片描述
    方法5. 创建一个文件夹lib,里面放入所有需要的dll文件,右击”属性” -> “生成事件” -> “预先生成事件”,输入命令”copy .\lib*.dll ..\Debug\”,在预处理的时候把dll文件拷贝到Debug目录下。
    这里写图片描述
    以上均可以使得项目找到dll库的定义
动态使用dll库 只需要Dll 不需要lib文件以及头文件

按照上述方法,放置好dll文件,之后LoadLibrary载入dll
getProcAddress获取dll 接口 使用函数指针指向该接口,使用函数指针指向该指针
释放dll FreeLibray(HMODULE)
方法1:显示连接


#include "stdafx.h"
#include<iostream>
#include<Windows.h>

int main()
{
    HMODULE hMoudle = LoadLibrary(_T("DllProject.dll"));  //载入dll
    if (hMoudle == NULL || INVALID_HANDLE_VALUE == hMoudle)
    {
        FreeLibrary(hMoudle); //释放dll
        return -1;
    }
    double a = 5, b = 10;
    typedef double(*TYPE_ADD)(double, double);//函数指针
    typedef int(*TYPE_FUN)(void);
    //特别注意 C与C++风格的函数签名 是不同的,C++风格的签名与参数有关
    TYPE_FUN Add = (TYPE_FUN)GetProcAddress(hMoudle, "fnDllProject");
    if (NULL == Add)
    {
        return -1;
        FreeLibrary(hMoudle);
    }
    std::cout << Add()<< std::endl;
    system("pause");
    FreeLibrary(hMoudle);
    return 0;
}

方法2:def文件定义输出时,可以使用EXPORTS 输出序号来获取函数

#include <windows.h>
#include <iostream>
using namespace std;
typedef int (*AddFunc)(int a, int b);
int main(int argc, char *argv[])
{
      HMODULE hDll = LoadLibrary("DLLDemo2.dll");
      if (hDll != NULL)
      {
            AddFunc add = (AddFunc)GetProcAddress(hDll, MAKEINTRESOURCE(1)); //使用EXPORTS定义函数序号1
            if (add != NULL)
            {
                  cout<<add(2, 3)<<endl;
            }
            FreeLibrary(hDll);
      }
}

查看Dll接口

vs自带dumpbin 查看
这里写图片描述

Dependency Walker工具查看该DLL
https://www.cnblogs.com/ring1992/p/6003248.html

生成与使用静态库lib

https://blog.csdn.net/wzhwhust/article/details/53321532

  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值