VS2019动态链接库

必须安装此项

  

VC动态链接库的分类:

Visual C++支持三种DLL,它们分别是Non-MFCDLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。

非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;

MFC规则DLL包含一个继承自CWinApp的类,但其无消息循环;可以被其他语言调用

MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。

动态链接库中的导出接口可以使用Dependencies工具进行查看 

微软官方有提供depends,可以查看exe文件的依赖库,仅适用于winxp/win7/win8,但是不能用于win10,会卡死报错.

推荐Dependencies,可以应用在win7/win8/win10,不支持winxp

Dependencies使用

软件下载:

链接:https://pan.baidu.com/s/1OBoZfom1yRrJl4EI0WhaLA     提取码:6666

查看dll文件中的函数:

 

 查看exe执行文件用的函数和dll文件:

 

非MFCDLL 

创建项目

 

framework.h文件

该文件在编译Dll时需要将所有在你的项目中需要使用的头文件包含进去

 

关于dllmain.cpp

DLL程序入口点函数DllMain:

仅导出资源的DLL可以没有DllMain函数

 

 

 参数1:HMODULE hModule  指向DLL本身的实例句柄

参数2:DWORD  ul_reason_for_call  指明了DLL被调用的原因,可以有以下4个取值:

    DLL_PROCESS_ATTACH:当DLL被进程 <<第一次>> 调用时,导致DllMain函数被调用,同时ul_reason_for_call的值为DLL_PROCESS_ATTACH,

    如果同一个进程后来再次调用此DLL时,操作系统只会增加DLL的使用次数,不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。

    DLL_PROCESS_DETACH:当DLL被从进程的地址空间解除映射时,系统调用了它的DllMain,传递的ul_reason_for_call值是DLL_PROCESS_DETACH。

    ★如果进程的终结是因为调用了TerminateProcess,系统就不会用DLL_PROCESS_DETACH来调用DLL的DllMain函数。

    这就意味着DLL在进程结束前没有机会执行任何清理工作。

    DLL_THREAD_ATTACH:当进程创建一线程时,系统查看当前映射到进程地址空间中的所有DLL文件映像,

    并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。 新创建的线程负责执行这次的DLL的DllMain函数,

    只有当所有的DLL都处理完这一通知后,系统才允许线程开始执行它的线程函数。

    DLL_THREAD_DETACH:如果线程调用了ExitThread来结束线程(线程函数返回时,系统也会自动调用ExitThread),

    系统查看当前映射到进程空间中的所有DLL文件映像,并用DLL_THREAD_DETACH来调用DllMain函数,通知所有的DLL去执行线程级的清理工作。

    注意:如果线程的结束是因为系统中的一个线程调用了TerminateThread,系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数

参数3:保留,目前没什么意义

 

DLL有2种导出函数方式:

一种是导出函数,供被DLL调用的程序使用;

另外一种是供DLL内部调用使用,不提供导出 

DLL导出函数的方式:*.def 文件或 __declspec(dllexport) 关键字

*.def文件函数导出方式: 

1.添加def文件

 

 2.文件中的第一个语句必须是 LIBRARY 语句。此语句将 .def 文件标识为属于 DLL。LIBRARY 语句的后面是 DLL 的名称。链接器将此名称放到 DLL 的导入库中

 3.

EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。

注释语句,在语句前面加分号 “;”

4.创建函数实现cpp文件

  

 注意:要加入预编译头文件

5.CTRL+B  编译

 

_declspec(dllexport) 函数导出方式: 

 1.为了规范创建头文件

 

#pragma once

extern "C" _declspec(dllexport) int dec(int a, int b);//声明导出函数

 

 2.函数的实现

  

 3.编译 CTRL+B

 

实例工程下载:

链接:https://pan.baidu.com/s/1UueQ8mdVjxqZRLt2XlUVww
提取码:6666

 

 

 

函数的调用  

隐式调用

隐式链接的特点是由编译器完成对DLL的加载和程序结束时对DLL的卸载工作,

如果程序结束时如果还有其他应用程序使用该DLL,那么系统会使DLL的使用计数减1,

当DLL的使用计数降为0时,会将DLL从内存中删除 

优缺点:隐式链接DLL的方法简单实用,但缺少灵活性

使用方法:使用隐式链接DLL开发时,首先,需要将DLL的引入库文件(*.lib--编译生成DLL时,会一起生成的)与应用程序进行静态链接,

因为引入库文件包含DLL的各种输出资源,如导出函数,导出类等信息,这些信息指向DLL的函数指针等等,EXE执行时,

DLL被 “自动” 加载,EXE退出时DLL被 “自动” 卸载。

隐式链接也有两种方法:一种是通过代码,一种是通过属性设置

通过属性设置 

 

通过代码

1.在h文件中加入代码

#pragma comment(lib,"D:\\bb\\yinsi\\Dll1.lib")

 

#pragma comment(lib,"Dll1.lib")

也可以使用默认目录:把dll拷贝到工程文件目录

 【lib的链接有编译器完成,所以绝对路径,还是相对路径无所谓,程序运行时不需要】

 

2.对导入函数加声明

_declspec(dllimport) int add(int a, int b);

3.把dll文件拷贝到exe所在的目录 

  

 

显式调用 

显示链接方式是完全由编程者用API 加载和卸载DLL,编程者可以决定何时加载DLL,加载哪个DLL,何时卸载DLL,卸载哪个DLL等。
★优缺点:显示链接方式充分体现了DLL的灵活性,是比较常用的调用DLL方式。但是与静态链接相比稍微复杂了些

使用方法:
LoadLibrary(...):该 API 用于加载指定的DLL;
GetProcAddress(...):该 API 用于获取DLL中导出函数的指针, 即导出函数的入口点;
FreeLibrary(...):该 API 用于卸载指定的DLL

注:如果程序中多次调用LoadLibrary(...)加载同一DLL时,在卸载的时候也要调用相应次数的FreeLibrary(...)进行卸载 

typedef int(*p)(int a, int b); //定义函数指针
void CyDlg::OnBnClickedButton1()
{
    
    //HMODULE hmod=LoadLibrary(_T("D:\\bb\\y\\Debug\\Dll1.dll"));//加载动态链接库
    HMODULE hmod = LoadLibrary(_T("Dll1.dll"));//加载动态链接库
    /*
    参数:LPCTSTR lpFileName  模块名
    返回值:如果函数成功,则返回值是模块的句柄
            如果函数失败,则返回值为NULL
    */
    if (!hmod) {
        AfxMessageBox(_T("动态链接库加载失败"));
        ExitProcess(0);
    }

     p pf = (p)GetProcAddress(hmod,"add");//获取函数首地址
    /*
    参数1:HMODULE hModule    DLL模块句柄
    参数2:LPCSTR lpProcName   函数名,   注意:只能是ANSI
    返回值:如果函数调用成功,返回值是DLL中的输出函数地址
            如果函数调用失败,返回值是NULL

    */

     if (!pf) {
         AfxMessageBox(_T("add函数首地址加载失败"));
         ExitProcess(0);
     }


    int c = pf(10,20);//函数的使用

    BOOL b2=FreeLibrary(hmod);//释放动态链接库
    /*
    参数:HMODULE hLibModule  DLL模块句柄
    返回值:如果该函数成功,则返回值为非零值。
            如果函数失败,则返回值为零
    */



}

说明:

使用LoadLibrary显式链接,在这个函数的参数中可以指定DLL文件的完整路径。如果不指定路径,Windows将遵循如下的搜索顺序来定位DLL:

①EXE文件所在的目录:
②进程的当前工作目录:
③Windows系统目录:例如:C:\WINDOWS\system32
④Windows目录:例如:C:\WINDOWS
⑤环境变量的目录:我的电脑-->属性-->高级-->环境变量

 
●进程的当前工作目录:使用函数SetCurrentDirectory(...)设置的路径,或者从父进程继承而来的路径,使用GetCurrentDirectory(...)得到。
●EXE文件所在的目录:本EXE文件所在的绝对路径, 使用GetModuleFileName得到。
两者可能不同。

▲GetProcAddress函数可以有两种方式取得DLL导出函数的入口点:
例如:
GetProcAddress(hMod, "add"); //按照函数名称方式;
GetProcAddress(hMod, MAKEINTRESOURCEA(1)); //按照导出函数序号方式;

MFC规则DLL 

1、MFC规则DLL特点:
●DLL内部可以使用MFC类库;
●可以被其他所有支持DLL技术的语言所调用。

2、MFC规则DLL的入口点函数:
默认情况下DLL的入口点函数都是DllMain,MFC规则DLL也不例外,但是因为是支持MFC的,所以在MFC规则DLL中,DllMain函数已经被MFC所封装,所以在你的工程中是看不到DllMain函数的,就好像在MFC对话框工程中你找不到WinMain函数一样的,不过也有一些补救的技巧,就是InitInstance()与ExitInstance()函数,当进程初始化调用DLL时,DLL中会默认调用那个InitInstance()函数,当EXE退出时DLL会调用ExitInstance()函数,但是当有新线程时,处于程序安全性考虑则没有什么好的办法进行处理。

“规则的”意味着它不同于MFC扩展DLL,在MFC规则DLL的内部虽然可以使用MFC,但是其与应用程序的接口不能是MFC。而MFC扩展DLL与应用程序的接口可以是MFC,可以从MFC扩展DLL中导出一个MFC类的派生类

3、MFC规则DLL分为两类:
●静态链接到MFC的规则DLL:与MFC库静态链接,会将MFC类库的代码直接编译生成到DLL文件中,在调用这种DLL的接口时,MFC使用DLL的资源,因此,不需要模块状态的切换,但是缺点就是使用这种方式生成的DLL文件大小比较大。

●动态链接到MFC的规则DLL:可以和调用DLL的EXE同时动态链接到MFC库,在这种情况下,MFC使用主应用程序(即:EXE程序)的资源句柄来加载资源模版,这样,当DLL和应用程序中存在相同ID的资源时,就要进行模块的切换,以便MFC能够找到正确的资源模版。

创建项目(动态链接到MFC的规则DLL) 

重载函数(添加ExitInstance函数)

点击类视图-->选中工程APP

 点击属性页-->点击重写

 添加ExitInstance函数

添加对话框资源 

右击工程-->添加-->资源

 

在CPP文件中写资源对话框显示的导出函数 

 

 

 

 

 

 

 

 

调用MFC规则Dll

调用方法与非MFCDLL调用方法相同 

 

MFC规则DLL的模块状态切换 

void ShowDLLDlg() {
    AFX_MANAGE_STATE(AfxGetStaticModuleState());//模块状态切换--方法一
    CDialog dlg(IDD_DIALOG1);
    dlg.DoModal();
}

注意:代码加在DLL导出函数中

 

void ShowDLLDlg() {
    //模块状态切换--方法二
    HINSTANCE hSaveInst = AfxGetResourceHandle();//获取当前资源模块句柄
    //说明:获取exe的实例句柄,为了后面恢复
    AfxSetResourceHandle(theApp.m_hInstance);//设置程序目前要使用的资源模块句柄
    /*
    说明:把dll的实例句柄设置为当前程序要使用的实例句柄
    
    参数:HINSTANCE hInstResource  模块句柄
    
    */
    CDialog dlg(IDD_DIALOG1);
    dlg.DoModal();
    AfxSetResourceHandle(hSaveInst);
    //说明:把exe的实例句柄设置为当前程序要使用的实例句柄


}

注意:代码加在DLL导出函数中

 

void CMFCcallDlg::OnBnClickedButton2()
{
    //模块状态切换-方法三
    
    HINSTANCE hExeInst = GetModuleHandle(NULL);
    //获取本进程的实例句柄
    HINSTANCE hDLLInst = GetModuleHandle(_T("MFCdll.dll"));
    //获取动态链接库的模块句柄
    ASSERT(hExeInst && hDLLInst);//条件为真继续执行,条件为假中断执行
    AfxSetResourceHandle(hDLLInst);
    //说明:把dll的实例句柄设置为当前程序要使用的实例句柄
    
    ShowDLLDlg();

    AfxSetResourceHandle(hExeInst);
    //说明:把exe的实例句柄设置为当前程序要使用的实例句柄

}

注意:代码加在EXE程序中(调用函数的程序中)

 

MFC规则dll教程视频:

https://www.bilibili.com/video/BV1yd4y1Z7SA/

 

 

 

 

 

 

 

posted @ 2023-02-23 08:26  天子骄龙  阅读(331)  评论(0编辑  收藏  举报