Win32学习笔记(24)隐式链接

本文详细介绍了Windows环境下隐式链接DLL的过程,包括将DLL和LIB文件放入工程目录、添加#pragma comment(lib)、函数声明等步骤,并探讨了隐式链接的实现原理,涉及导入表(IAT)、DLLMain函数的执行情况。同时,讨论了DLL的优点,如节省内存,以及DllMain在DLL生命周期中的不同调用场景。

1.隐式链接

步骤一: 将.dll .lib文件放到工程目录下

在之前使用dll时我们根本没使用.lib文件,因为函数根本不在他里面。他不是静态链接库里的lib文件。

但是这里面有着辅助信息,所以我们要使用隐式链接的话就要使用他,来让编译器找到dll。

步骤二:将 #pragma comment(lib,"lib名.lib")添加到调用文件中

步骤三:加入函数声明(也可以把dll头文件放到目录下)

注意:

extern "C" _declspec(dllexport) ... ... ... ...

当我们使用上面这种方式的时候别忘了在函数声明时前面加上extern "C"

_declspec(dllimport) int add(int x, int y);

示例代码:

#include <stdio.h>
#include "StdAfx.h"
#include <Windows.h>
#pragma comment(lib,"Dll1.lib")
_declspec(dllimport) int add(int x, int y);
​
int main()
{
    
    int x = add(1, 2);
    printf("%d", x);
    
    return 0;
}

注意:我在尝试的时候出现一个(无法找到dllmain.dll,程序无法运行)的错误。因为我放到该目录下的dll名字为dll1.dll但是他说无法找到dllmain.dll所以我改了下名字就可以正常运行了。

输出:

 

那么可能会有疑问,编译器如何找到我们的dll文件呢?我们又没有把它加载到内存。原来是.lib文件里面存在对dll文件的描述信息,因此编译器可以找到。

这段代码看起来好像是静态链接库的实现方式,我们看一下汇编代码。

 

可以看到在这里调用函数时调用的是一个地址,这个地址就是我们要用的函数地址。(间接调用) 我们在使用静态库时调用的直接就是函数。


2.隐式链接的实现

下面我们具体看看这种隐式链接的方式怎么实现的。

可能大家学过PE文件的话可能会知道除了导出表还有导入表(IAT)上面描述了使用的dll的各种信息。我们打开PE加载器看一下exe文件(我们使用dll程序的exe)的导入表

 

 

可以看到里面导入了我们的dllmain.dll。还记载了用到了哪些函数

 

我们也可以点一下导出表,一般exe是没有导出表的(不过不是一定没用)。因为他不许要给别人提供函数,一般都是dll来提供。不过dll也有导入表,因为他也需要使用一些dll比如kernel32.dll。

 

(滴水视频上的直接截图过来了)


3.DLL的优点

之前在静态链接的时候说过,它的缺点就是dll的优点。

相信大家都对这个图很熟悉了。静态链接库是直接给进程复制一份代码放

进去,而dll文件就只有一份,使用时就映射到虚拟内存中,其实只有一份。那假如我们在使用时修改dll,这样会对其他进程产生影响呢?是不会的。我们在之前也说过,改的话会创建一个新的物理页,如图片右边。


4.DllMain函数

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,//句柄
  _In_ DWORD     fdwReason,//指示调用 DLL 入口点函数的原因代码(有四种情况会被调用,具体看文档)
  _In_ LPVOID    lpvReserved
);

exe和dll除了导入,导出表有些差异。还有就是他们的入口函数执行时间不同。如果是exe文件都会从main函数地方进行,这就是exe的入口函数。

而dll的入口函数是DllMain。它不像main函数只执行一次,他可能执行多次。

1.当第一次使用LoadLibraryA时会调用DllMain.传的参数为DLL_THREAD_ATTACH

2.当调用FreeLibrary时会调用DllMain穿的参数为DLL_PROCESS_DETACH

下面我们论证一下

dllmain.cpp:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "dllmain.h"
#include <Windows.h>
#include <stdio.h>
BOOL WINAPI DllMain(HANDLE hModele,
    DWORD fdwReason,
    LPVOID lpReserved
)
{
    switch (fdwReason)
    {
        //1. DLL_THREAD_ATTACH (LoadLibrary)
    case DLL_PROCESS_ATTACH:
        OutputDebugStringA("1.DLL_THREAD_ATTACH \n");
        //2. DLL_PROCESS_DETACH (FreeLibrary)
        break;
    case DLL_PROCESS_DETACH:
        OutputDebugStringA("2.DLL_PROCESS_DETACH \n");
        break;
    default:
        printf("error");
        break;
    }
}
#include <stdio.h>
#include "StdAfx.h"
#include <Windows.h>
typedef int ( *lpadd)(int x, int y);
lpadd myadd;
int main()
{
    HINSTANCE hModule = LoadLibraryA("Dll1.dll");//这里下断点
    FreeLibrary(hModule);
    return 0;
}

f11调试看到输出:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wzprabbit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值