前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Windows平台LoadLibrary加载动态库搜索路径的问题

Windows平台LoadLibrary加载动态库搜索路径的问题

作者头像
24K纯开源
发布2019-11-23 08:39:14
4.4K1
发布2019-11-23 08:39:14
举报
文章被收录于专栏:24K纯开源24K纯开源

一、背景

在给Adobe Premiere/After Effects等后期制作软件开发第三方插件的时候,我们总希望插件依赖的动态库能够脱离插件的位置,单独存储到另外一个地方。这样一方面可以与其他程序共享这些动态库,还能保证插件安装时非常的清爽。就Adobe Premiere Pro/After Effects来说,插件文件是放到C:\Program Files\Adobe\Common\Plug-ins\7.0\MediaCore(Windows平台)的。这个是PremiereProAfterEffects的公共插件目录,二者在启动的时候都会尝试去这个位置加载插件。与此同时,我们希望自己开发的插件所依赖的动态库放到另外的位置,另外也希望插件显示链接的动态库能够尽量少。因为如果是显式链接的话,这些插件依赖的动态库必须和插件保存在同一个位置。不然插件找不到这些依赖文件就会加载失败的。当然,我们也可以在环境变量里面增加一条路径,但是这容易污染环境变量,或者与其他的程序库产生冲突。LoadLibrary在这个时候就产生作用了。LoadLibrary通过将指定路径的动态库加载到当前的调用进程,然后获取其导出的函数就可以正常使用了。对于像第三方插件这样的应用场景,LoadLibrary可以说是个不错的实现方式。但是正因此也有个弊端,我们无法使用工具得知其的依赖库。

二、使用实例

我们在给Adobe Premiere Pro开发的一款插件中,正是使用了这种方法: (1)首先从注册表中获取到我们插件依赖的动态库文件所在的位置:

 1 bool GetInstallationPath(std::string& result) {
 2     DWORD data_type;
 3     CHAR value[1024];
 4     PVOID pv_data = value;
 5     DWORD size = sizeof(value);
 6     auto err = RegGetValue(HKEY_CLASSES_ROOT, "test_app\\plugin", "install_location", RRF_RT_ANY, &data_type, pv_data, &size);
 7     if (err == ERROR_SUCCESS) {
 8         std::string filepath(value); 
 9         std::regex_replace(std::back_inserter(result), filepath.begin(), filepath.end(), std::regex("[\\\\/]+[^\\\\/]+$"), "");
10         return true;
11     }
12     return false;
13 }

(2)通过调用LoadLibrary来加载指定的依赖库

std::string    dirname;
if (!GetInstallationPath(dirname)) {
    return false;
}  
SetDllDirectory(dirname.c_str());
insmedia_dll.handle = LoadLibrary("core.dll");

如上述代码所示,我们的插件唯一依赖的动态库叫core.dll。而core.dll文件存放的位置记录在注册表中。程序先从注册表中获取core.dll所在的文件夹,然后设置到DLL的搜索路径中。最后再调用LoadLibrary加载它。在最初开发及发布后,插件运行的很好。然而,在Adobe发布Premiere Pro CC 2020之后,插件就不工作了。这是为啥呢?根据过往的经验来看,插件加载不上只有一个原因:依赖的动态库缺失或者是加载错了版本。那么,我们就来看看到底是哪个依赖加载错了导致插件加载失败呢?通过在WinDBG里面调试看到了如下的差异:

看上图很显然,我们的插件在加载ffmpeg的库文件时,先找到了PremierePro安装根目录里面的版本了。而PremierePro使用的ffmpeg版本显然跟我们不一样。正是因为这两个库的版本不对,导致我们的插件加载失败了。那么,LoadLibrary这种方法显然还是存在一些Bug了。我们的core.dll还依赖OpenCV、ffmpeg等第三方库。看MSDN的解释是,LoadLibrary会先从调用进程的目录下搜索动态库的依赖。这样的行为显然不是我们想要的。这个时候,我们还有个选择:使用LoadLibraryEx。具体的使用方法仍然一样,只不过传给LoadLibraryEx的第一个参数是我们要加载的动态库的绝对路径:

 1 std::string    dirname;
 2 if (!GetInstallationPath(dirname)) {
 3     return false;
 4 }  
 5  
 6 std::string absolute_path = dirname + "\\InsMedia.dll";
 7 insmedia_dll.handle = LoadLibraryEx(absolute_path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
 8 if (!insmedia_dll.handle) {
 9     return false;
10 }

注意到第三个参数为LOAD_WITH_ALTERED_SEARCH_PATH,通过指定LOAD_WITH_ALTERED_SEARCH_PATH,让系统DLL搜索顺序从DLL所在目录开始。这样就能够保证加载动态库的时候优先加载我们打包的动态库。从而避免因为动态库加载错误导致插件失败。

从上图可以看到,所有依赖的动态库都变成了我们自己提供的库文件了,插件也能正常加载了。完美!

三、参考链接

1. https://blog.csdn.net/cuglifangzheng/article/details/50580279 2. https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-11-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
  • 二、使用实例
  • 三、参考链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档