LoadLibrary无法加载.dll解决思路

动态载入DLL所需要的三个函数详解(LOADLIBRARY,GETPROCADDRESS,FREELIBRARY
  • dll, lib, h的区别
  1. dll是动态库, 将其链接进工程, 一般用LoadLibrary, GetProcAddress调用里面导出的函数(api文档);
  2. lib是静态库, 将其链接进工程, 调用时需要包含一个头文件, 具体实现放在lib里了;
  3. h头文件, 实现放在.c或.cpp里
  • 静态链接库LIB和动态链接库DLL的区别
  1. 什么是静态连接库,什么是动态链接库 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接 库。静态链接库与静态链接库调用规则总体比较如下。

    对于静态链接库(比较简单): 首先,静态链接库的使用需要库的开发者提供生成库的.h头文件和.lib文件。

    生成库的.h头文件中的声明格式如下: extern “C” 函数返回类型 函数名(参数表); 在调用程序的.cpp源代码文件中如下: #include “…\lib.h” #pragma comment(lib,"…\debug\libTest.lib") //指定与静态库一起链接

  2. 因为静态链接库是将全部指令都包含入调用程序生成的EXE文件中。因此如果用的是静态链接库,那么也就不存在“导出某个函数提供给用户使用”的情况,要想用就得全要!要不就都别要!?

    对于动态链接库: 动态链接库的使用需要库的开发者提供生成的.lib文件和.dll文件。或者只提供dll文件。 首先我们必须先注意到DLL内的函数分为两种: (1)DLL 导出函数,可供应用程序调用; (2)DLL 内部函数,只能在 DLL 程序使用,应用程序无法调用它们。 因此调用程序若想调用DLL中的某个函数就要以某种形式或方式指明它到底想调用哪一个函数。

  • 动态载入.dll
    动态载入方式是指在编译之前并不知道将会调用哪些 DLL 函数, 完全是在运行过程中根据需要决定应调用哪些函数。

    方法是:用 LoadLibrary 函数加载动态链接库到内存,用 GetProcAddress函数动态获得 DLL 函数的入口地址。当一个 DLL 文件用 LoadLibrary 显式加载后,在任何时刻均可以通过调用 FreeLibrary 函数显式地从内存中把它给卸载。

    动态调用使用的 Windows API 函数主要有 3 个, 分别是 LoadLibrary、 GetProcAddress 和FreeLibrary。

LoadLibrary 函数

注:Delphi 中还提供了 SafeLoadLibrary 函数,它封装了 Loadlibrary 函数,可以装载由 Filename 参数指定的 WindowsDLL或 Linux 共享对象。它简化了DLL的装载并且使装载更加安全。

function LoadLibrary(LibFileName : PChar): Thandle;

[功能]:加载由参数 LibFileName 指定的 DLL 文件。

[说明]:参数 LibFileName 指定了要装载的 DLL 文件名,如果 LibFileName 没有包含一个路径,系统将按照:当前目录、Windows 目录、Windows 系统目录、包含当前任务可执行文件的目录、列在 PATH 环境变量中的目录等顺序查找文件。

如果函数操作成功,将返回装载 DLL 库模块的实例句柄,否则,将返回一个错误代码,错误代码的定义如引文[1]所示。

GetProcAddress 函数

[格式]:
function GetProcAddress(Module:Thandle; ProcName:PChar): TfarProc;

[功能]:返回参数 Module 指定的模块中,由参数 ProcName 指定的过程或函数的入口地址。

[说明]:参数 Module 包含被调用函数的 DLL 句柄,这个值由 LoadLibrary 返回, ProcName
是指向含有函数名的以 nil 结尾的字符串指针,或者可以是函数的次序值,但大多数情况下,用函数名是一种更稳妥的选择。如果该函数执行成功,则返回 DLL 中由参数 ProcName 指定的过程或函数的入口地址,否则返回 nil 。

FreeLibrary 函数

[格式]:
procedure FreeLibrary(Module: Thandle);
[说明]:将由参数 Module 指定的 DLL 文件从内存中卸载 1 次。

[说明]:Module 为 DLL 库的句柄。这个值由 LoadLibrary 返回。由于 DLL 在内存中只装载一次,因此调用 FreeLibrary 首先使 DLL 的引用计数减 1,如果计数减为 0 则卸载该 DLL。

[注意]:每调用一次 LoadLibrary 函数就应调用一次 FreeLibrary 函数,以保证不会有多余的库模块在应用程序结束后仍留在内存中,否则导致内存泄漏。

加载dll不成功解决思路

用LoadLibrary和GetProcAddress加载DLL时,可能会碰到LoadLibrary无法正确加载DLL,返回值总是NULL的情况。一般而言,可按下面的思路解决:

(1)检查dll的路径是否正确。

     可先用绝对路径试一下,若可以,那说明相对路径设置错了。简单的测试当面目录的方法就是随便写一个文件,比如std::ofstream outfile("test.txt"),然后看输出的test.txt文件在哪,就知道当前目录是哪了,然后依据它设置相对目录。

(2)检查LoadLibrary输入参数字符格式是否正确

     LoadLibrary的输入参数,即DLL的路径,要求是LPCTSTR类型。一般而言,如果你用

HINSTANCE hFaceDll =LoadLibrary("…/bin/dllTest.dll");
这样的字符格式加载,那么会出现“无法将const char [] 转换为LPCTSTR”的错误。当然,也有可能没报错,但是却不能正确加载,这时候就应在路径字符串前加"_T",转换一下格式:

HINSTANCE hFaceDll = LoadLibrary(_T("…/bin/dllTest.dll")) ;
(3)检查具体的错误

如果经过上面两步,还是不能加载,那肯定就是其他错误了。你可以自制一个很小的库(比如里面就放个很小的整数求和函数啥的),加载一下,看下能不能成功,如果你自制的可以正确加载,但目的dll却不能,那就是目的dll本身的问题。这时候就应该进一步找更具体的问题了。

   在LoadLibrary加载语句后面紧跟std::cout<<GetLastError()<<endl;一句,调用GetLastError()函数看具体的错误代码。

一般而言,最有可能的错误代码就是“126”,查找GetLastError的错误代码列表,知道错误“126”是指错误“ 找不到指定的模块 ”,这说明你要加载的dll本身还依赖于其他一些文件,找到这些依赖的文件(一般为一些dll),放到编译器可以找到的目录(或在附加库目录里设置下这些dll的路径),编译,一般就可以通过了。当然,若是其他错误代码,那就要具体问题具体分析了。
GetLastError()返回值"193", 则需考虑DLL的一致性问题。也就是exe版本必须要和dll版本在debug/release上是一致的,x64或者win32可能也需要保持一致。

附注: 查找dll依赖文件的方法

方法一:用vs自带的命令

dumpbin -imports MyDll.dll
其中"MyDLL.dll"为你要分析的dll文件名。具体操作方式为:打开VS自带的"Visula Studio命令提示"命令行工具,切换到要分析的dll文件所在目录下(也可不切换而用全路径),执行上述命令即可。为了方便浏览,最好在命令后面接重定向符“>”定位到一个文本文件中。如:

dumpbin -imports MyDll.dll > result.txt
方法二:下载工具Dependency Walker (http://www.dependencywalker.com/) 解压后运行depends.exe,打开要分析的dll,软件会自动分析,目标dll依赖的文件不存在的话,会在左侧列表中用问号标出,并且下面也有错误提示。可依据错误提示去找这些依赖文件。另外,有时候,虽然你系统有依赖文件(比如opencv的一些库),但却检测到没有,很可能是因为你没设置相应环境变量或者没有注册对应dll到注册表。

[1] https://www.cnblogs.com/westsoft/p/5936092.html
[2] https://www.cnblogs.com/15157737693zsp/p/4179438.html
[3] https://blog.csdn.net/gcola007/article/details/80079092

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值