IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> VC++通过查看ReactOS开源代码,解决完整路径dll加载失败问题(附源码) -> 正文阅读

[C++知识库]VC++通过查看ReactOS开源代码,解决完整路径dll加载失败问题(附源码)

目录

1、问题实例

2、到cmd.exe中用regsvr32去手动注册控件

3、到ReactOS开源代码中去查看regsvr32的实现,找到解决问题的线索

3.1、ReactOS开源操作系统简介

3.2、使用Source Insight打开ReactOS源码,找到regsvr32.exe程序的代码

4、到微软MSDN上查看LOAD_WITH_ALTERED_SEARCH_PATH参数的含义


? ? ? ?在某些情况下我们需要调用LoadLibrary动态地加载dll库,去获取dll库中的接口。但有时会出现加载失败的情况,即使传入的是dll库的全路径,也会加载失败,我们在项目中已经不止一次遇到这样的问题了。今天我们就来看一下这个问题该如何解决。

1、问题实例

? ? ? ?我们通过代码去制作安装包,不再使用inno setupinstallshield等打包工具,安装包中的文件拷贝、读写注册表、注册控件等操作都是通过代码去完成的。在某次测试时发现,某个控件注册失败了,注册控件的代码如下所示:

void RegCtrl( LPCTSTR lpszDllPath )
{
?? ?if ( lpszDllPath == NULL )
?? ?{
?? ??? ?return;
?? ?}

?? ?CString strLog;
?? ?strLog.Format( _T("[RegCtrl] lpszDllPath: %s."), lpszDllPath );
?? ?WriteLog( strLog );

?? ?// 1、先将库动态加载起来
?? ?HINSTANCE hInstance = LoadLibrary( lpszDllPath )
?? ?if ( NULL == hInstance )
?? ?{
?? ??? ?strLog.Format( _T("[RegCtrl] load dll failed, GetLastError: %d."), GetLastError() );
?? ??? ?WriteLog( strLog );
?? ?}

? ? // 2、获取库中的DllRegisterServer函数接口,调用该接口去完成控件的注册
?? ?typedef HRESULT (*DllRegisterServerFunc)(void);
?? ?DllRegisterServerFunc dllRegisterServerFun = (DllRegisterServerFunc)GetProcAddress( hInstance, "DllRegisterServer" );
?? ?if ( dllRegisterServerFun != NULL )
?? ?{
?? ??? ?HRESULT hr = dllRegisterServerFun();
?? ??? ?strLog.Format( _T("[RegCtrl] DllRegisterServer return: %d"), hr );
?? ??? ?WriteLog( strLog );
?? ?}
?? ?else
?? ?{
?? ??? ?strLog.Format( _T("[RegCtrl] Get DllRegisterServer address failed. GetLastError: %d"), GetLastError() );
?? ??? ?WriteLog( strLog );
?? ?}

?? ?FreeLibrary( hInstance );
}

代码中通过dll文件的完整路径去加载控件dll,然后获取控件dll中的DllRegisterServer接口,调用DllRegisterServer接口完成控件的注册。

? ? ? ? 根据打印出来的日志,在调用LoadLibrary去加载控件dll库时就失败了,我们给LoadLibrary接口传入的是控件dll的完整路径,到这个路径中也是能找到这个控件dll文件的。这个就有点搞不懂了,明明传入的是控件dll完整路径,并且控件dll文件在路径中也是存在的,为啥还会出现dll库加载失败的情况呢?这个有点太诡异了!

2、到cmd.exe中用regsvr32去手动注册控件

? ? ? ?安装包程序无法加载该dll库,我们想到可以尝试到cmd窗口中,使用regsvr32去手动注册这个dll控件试试。打开cmd窗口,手动将dll文件拖入到cmd窗口中获取dll文件的路径,按下回车键去注册该控件:(此处以飞秋的ImageOle.dll控件为例来说明)

regsvr32 "C:\Program Files\feiq\GifDll\ImageOle.dll"

手动注册是能正常注册成功的。使用regsvr32去手动注册时,调用的是系统的regsvr32.exe程序,regsvr32.exe程序内部应该也需要去加载这个控件dll文件的,regsvr32.exe程序能成功将库加载起来的。难道regsvr32.exe程序加载库的方式,和我们安装包程序使用的方式不一样?

3、到ReactOS开源代码中去查看regsvr32的实现,找到解决问题的线索

? ? ? ?之前下载过开源操作系统ReactOS的源码,ReactOS中的系统库内部实现和Windows是很相像的,提供的系统API接口基本是一模一样的。我们时常会去查看ReactOS中API函数及底层库的内部实现,去了解Windows系统的内部实现,以辅助排查我们在开发过程中遇到的问题。

3.1、ReactOS开源操作系统简介

? ? ? ?ReactOS是一款基于 Windows NT 架构的类似于Windows XP系统的免费开源操作系统,旨在实现和 NT 与 Windows 操作系统二进制下的完全应用程序和驱动设备的兼容性,通过使用类似构架和提供完全公共接口。
? ? ? ?ReactOS一直在持续维护中,可以到reactos官网上找到ReactOS源码的下载地址,使用svn将ReactOS源码下载下来。

? ? ? ?ReactOS开源代码对于我们Windows软件开发人员来说非常有用,我们可以去查看API函数的内部实现,可以去查看系统exe的内部实现,可以去查看ReactOS系统内部任意模块的实现代码。ReactOS是比较接近Windows系统的,可以通过查看ReactOS的代码去大概地了解Windows系统的内部实现,对我们排查Windows软件的问题是很有好处的!

3.2、使用Source Insight打开ReactOS源码,找到regsvr32.exe程序的代码

? ? ? ?ReactOS源码中没有Visual Studio工程文件,无法使用Visual Studio打开查看源代码,可以Source Insight去查看源码。至于怎么使用Source Insight,可以参看我之前写的一篇关于Source Insight的文章:

使用Source Insight查看编辑源代码https://blog.csdn.net/chenlycly/article/details/124347857因为regsvr32是一个独立的exe,不是一个函数,所以需要找到该程序对应的.c源文件。于是尝试到文件列表中以regsvr32为关键字进行搜索,找到了regsvr32.c文件。在该文件中找到_tWinMain函数,在该main函数中看到了加载dll库文件的代码,如下所示:

代码中是调用LoadLibraryEx接口去加载dll库的,传入的参数为LOAD_WITH_ALTERED_SEARCH_PATH,并且也是获取dll控件库中的DllRegisterServer去进行注册的。

? ? ? ? regsvr32.exe使用这种方式去加载库文件应该是有它的道理的,于是我们也参照它的做法,把加载dll库的代码改成调用LoadLibraryEx,传入LOAD_WITH_ALTERED_SEARCH_PATH,即如下所示:

HINSTANCE hInstance = LoadLibraryEx( lpszDllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH )
if ( NULL == hInstance )
{
?? ?strLog.Format( _T("[RegCtrl] load dll failed, GetLastError: %d."), GetLastError() );
?? ?WriteLog( strLog );
}

改成上述代码后,就没再出现库加载失败的问题了,上述代码果然很有用啊!

? ? ? ?后来其他模块中也遇到库加载失败的问题,也更换成上述代码,结果后面再也没有出过问题了。这个通过库的绝对路径去加载库失败的问题,不是必现的,只是在个别机器上才会出问题。

4、到微软MSDN上查看LOAD_WITH_ALTERED_SEARCH_PATH参数的含义

? ? ? ?为了搞清楚LOAD_WITH_ALTERED_SEARCH_PATH参数的含义,我们到微软MSDN上查看LoadLibraryEx API函数的说明页面,找到了LOAD_WITH_ALTERED_SEARCH_PATH参数的说明:

LOAD_WITH_ALTERED_SEARCH_PATH:(0x00000008)

If this value is used and lpFileName specifies an absolute path, the system uses the alternate file search strategy discussed in the Remarks section to find associated executable modules that the specified module causes to be loaded. If this value is used and lpFileName specifies a relative path, the behavior is undefined.
If this value is not used, or if lpFileName does not specify a path, the system uses the standard search strategy discussed in the Remarks section to find associated executable modules that the specified module causes to be loaded.

This value cannot be combined with any LOAD_LIBRARY_SEARCH flag.

从上述描述文字得知,如果设置了LOAD_WITH_ALTERED_SEARCH_PATH参数,则系统会使用the alternate file search strategy搜索策略,那这个搜索策略到底是什么样的呢?

? ? ? ?在LoadLibraryEx函数的说明页面继续向下看,看到了"Dynamic-Link?Library?Search?Order"超链接,这是动态连接库加载顺序的详细说明页面。从页面中我们看到了,如果没设置LOAD_WITH_ALTERED_SEARCH_PATH参数,则使用Standard Search Order for Desktop Applications标准搜索顺序

1、The directory from which the application loaded.
2、The system directory. Use the GetSystemDirectory function to get the path of this directory.
3、The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
4、The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
5、The current directory.
6、The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.

? ? ? ? 如果设置了LOAD_WITH_ALTERED_SEARCH_PATH参数,则系统会使用Alternate Search Order for Desktop Applications搜索顺序

1、The directory specified by lpFileName.
2、The system directory. Use the GetSystemDirectory function to get the path of this directory.
3、The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
4、The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
5、The current directory.
6、The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.?? ?

所以我们最终找到了答案,当我们设置LOAD_WITH_ALTERED_SEARCH_PATH参数时,就会使用Alternate Search Order for Desktop Applications,会优先使用设置下来的完整路径去加载dll库的。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-27 11:09:20  更:2022-04-27 11:10:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 21:56:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码