C++ 快速加载 Dll 里的 API
最近项目里要重新编写程序加载器,也就是编译出一个可执行文件,在 Windows 上是 .exe
为什么要程序加载器?
个人理解是,可执行文件大小最好是越小越好,功能都可以由 dll 文件执行
而程序加载器里最重要的是两个 win32 函数,分别是 LoadLibrary 和 GetProcAddress
前者是加载 dll 并返回 instance 句柄,后者是从 instance 里提取所需的函数
我们从主 dll 中提取 wWinMain 函数入口,再执行 wWinMain 函数就可以执行程序了
旧版本的程序加载器也是简单依靠这两个函数加载特定的函数,但是在一个线上问题的排查中,发现程序加载器提示的错误消息太少了(只能依靠 MessageBox),导致我们无法快速定位到问题
所以我们新引入 sentry 模块,这个模块我们已经用于主程序了,现在在程序加载器中我们打算提取 sentry 的部分函数,用于上传错误事件
由于要提取很多函数,所以重复写 LoadLibrary 和 GetProcAddress 就显得代码比较臃肿,故要写一套比较简单复用的加载 dll 逻辑
参考 Nim 的逻辑,我们改进了一下
// load_dll.h #include <map>
#include <string> #include <wtypes.h> class SDKInstance {
public:
SDKInstance();
virtual ~SDKInstance(); bool LoadSdkDll(const wchar_t* cur_module_dir,
const wchar_t* sdk_dll_file_name); void UnloadSdkDll(); void* GetFunction(const std::string& function_name) {
auto it = function_map_.find(function_name);
if (it != function_map_.end()) {
return it->second;
} void* function_ptr = ::GetProcAddress(instance_, function_name.c_str()); function_map_[function_name] = function_ptr;
return function_ptr;
} private:
HINSTANCE instance_; std::map<std::string, void*> function_map_;
}; #define SDK_GET_FUNC(function_ptr, suffix, sdk_instance) \
((function_ptr##suffix)sdk_instance->GetFunction(#function_ptr))
// load_dll.cpp
#include "load_dll.h"
SDKInstance::SDKInstance() { instance_ = nullptr; }
SDKInstance::~SDKInstance() {
UnloadSdkDll();
}
bool SDKInstance::LoadSdkDll(const wchar_t *cur_module_dir,
const wchar_t *sdk_dll_file_name) {
std::wstring dir(cur_module_dir);
dir.append(L"\\");
dir.append(sdk_dll_file_name);
instance_ = LoadLibraryW(dir.c_str());
if (instance_ == nullptr) {
return false;
}
return true;
}
void SDKInstance::UnloadSdkDll() {
if (instance_) {
FreeLibrary(instance_);
instance_ = nullptr;
}
function_map_.clear();
}
main 函数使用:
#include "load_dll.h" #define SENTRY_GET_FUNC(function_ptr, sdk_instance) \
SDK_GET_FUNC(function_ptr, _func, sdk_instance) typedef decltype(sentry_options_new)* sentry_options_new_func;
typedef decltype(sentry_options_set_handler_pathw)*
sentry_options_set_handler_pathw_func;
typedef decltype(sentry_options_set_dsn)* sentry_options_set_dsn_func;
typedef decltype(sentry_options_set_system_crash_reporter_enabled)*
sentry_options_set_system_crash_reporter_enabled_func;
typedef decltype(sentry_options_set_environment)*
sentry_options_set_environment_func;
typedef decltype(sentry_options_set_debug)* sentry_options_set_debug_func;
typedef decltype(sentry_init)* sentry_init_func;
typedef decltype(sentry_set_tag)* sentry_set_tag_func;
typedef decltype(sentry_value_new_message_event)*
sentry_value_new_message_event_func;
typedef decltype(sentry_capture_event)* sentry_capture_event_func; bool SendToSentry(PCWSTR dll_directory, PCWSTR current_version,
const std::string& log, sentry_level_t level,
SDKInstance* sdk_instance) {
if (sdk_instance == nullptr ||
!sdk_instance->LoadSdkDll(dll_directory, L"sentry.dll")) {
return false;
} sentry_options_t* options =
SENTRY_GET_FUNC(sentry_options_new, sdk_instance)(); auto crashpad_handler_path =
std::wstring(dll_directory) + L"\\crashpad_handler.exe";
SENTRY_GET_FUNC(sentry_options_set_handler_pathw, sdk_instance)
(options, crashpad_handler_path.c_str()); SENTRY_GET_FUNC(sentry_options_set_dsn, sdk_instance)(options, kSentryDsn);
SENTRY_GET_FUNC(sentry_options_set_system_crash_reporter_enabled,
sdk_instance)(options, 1); #if defined(NDEBUG)
SENTRY_GET_FUNC(sentry_options_set_environment, sdk_instance)
(options, "production");
#else
SENTRY_GET_FUNC(sentry_options_set_environment, sdk_instance)
(options, "staging");
SENTRY_GET_FUNC(sentry_options_set_debug, sdk_instance)(options, 1);
#endif auto ret = SENTRY_GET_FUNC(sentry_init, sdk_instance)(options);
if (ret != 0) {
return false;
} auto version = WideToUTF8(current_version);
SENTRY_GET_FUNC(sentry_set_tag, sdk_instance)("version", version.c_str()); auto event = SENTRY_GET_FUNC(sentry_value_new_message_event, sdk_instance)(
level, nullptr, log.c_str());
SENTRY_GET_FUNC(sentry_capture_event, sdk_instance)(event); return true;
} int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine,
_In_ int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance); ... sentry_instance = std::make_unique<SDKInstance>();
SendToSentry(dll_path.c_str(), current_version.c_str(),"Load xxx.dll failed", SENTRY_LEVEL_FATAL,
sentry_instance.get());
... return 1;
}
C++ 快速加载 Dll 里的 API的更多相关文章
- unity3d动态加载dll的API以及限制
Unity3D的坑系列:动态加载dll 一.使用限制 现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Androi ...
- 加载dll、lib库
2.是关于如何加载dll或lib库的.可以看这篇bog Qt调用dll中的功能函数点击打开链接 ************************************************** ...
- 加载dll、lib库(例子的代码很全)
是关于如何加载dll或lib库的.可以看这篇bog Qt调用dll中的功能函数点击打开链接 **************************************************** ...
- 动态加载dll的实现+远线程注入
1.在目标进程中申请内存 2.向目标进程内存中写入shellcode(没有特征,编码比较麻烦) 3.创建远线程执行shellcode 之前可以看到shellcode很难编写还要去依赖库,去字符串区等等 ...
- 使用c#封装海康SDK出现无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块
最近在研究网络摄像头的二次开发,测试了一款海康威视的网络摄像头,程序调试的时候,出现如题的报错. 调试随机自带的demo时,程序运行正常,但当把该程序引入到我自己的程序中时,就开始报错.根据开发软件包 ...
- 在内存中加载DLL
有个需求是把一个DLL作为数据打包到EXE中,运行的时候动态加载.但要求不是释放出来生成DLL文件加载. 花了一天时间做出来.效果还可以. 不过由于是直接分配内存加载DLL的.有一些小缺陷.例如遍历进 ...
- c#实现动态加载Dll(转)
c#实现动态加载Dll 分类: .net2009-12-28 13:54 3652人阅读 评论(1) 收藏 举报 dllc#assemblynullexceptionclass 原理如下: 1.利用反 ...
- 内存加载DLL
1.前言 目前很多敏感和重要的DLL(Dynamic-link library) 都没有提供静态版本供编译器进行静态连接(.lib文件),即使提供了静态版本也因为兼容性问题导致无法使用,而只提供DLL ...
- Unity3D的坑系列:动态加载dll
我现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Android平台是可以动态加载dll的,有了这个就可以实现代码 ...
- c# 如何进行动态加载dll
最近遇到了在c#中如何进行动态加载dll的话,搞定了,下面介绍一下自己的步骤. 1,新建dll. 打开vs,新建project->Class Library->项目名为testdll1.在 ...
随机推荐
- 【转帖】【笔记】python连接神通数据库
https://www.cnblogs.com/wyongbo/p/17054924.html python连接国产神州通用数据库. 一.准备 下载whl及dll: 链接: https://pan.b ...
- [转帖]并发控制- sched_yield 函数
函数说明 函数原型 #include <sched.h> int sched_yield(void); 1 2 sched_yield的作用是让出处理器,调用时会导致当前线程放弃CPU,进 ...
- Redisson/Jedis 线程数不足报错问题的思考
Redisson/Jedis 线程数不足报错问题的思考 背景 最近公司内总出现 Redis相关的错误 !-_-! 看我最近发的博客就可以看的出来. 这个错误提示其实是 两年前 清明节进行 压测时发现的 ...
- [转帖]NOHZ = ON如何影响Linux内核中的do_timer()?
https://www.jb51.cc/faq/897483.html 如何解决NOHZ = ON如何影响Linux内核中的do_timer()?? 首先,让我们了解什么是tickless kerne ...
- Linux时间戳转换成易读格式的方法
背景 最近一直在学习Redis相关的知识. 其中遇到了一个redis monitor的命令 但是这里有一个问题是: 原生命令查询出来的时间是Unix时间戳格式的. 不太好发现查看与进行对照. 所以今天 ...
- 庖丁解牛:最全babel-plugin-import源码详解
庖丁解牛:最全 babel-plugin-import 源码详解 序言:在用 babel-plugin 实现按需加载一文中笔者用作用域链思路实现了按需加载组件.此思路是统一式处理,进入 ImportD ...
- SPI在Java中的实现与应用 | 京东物流技术团队
1 SPI的概念 API API在我们日常开发工作中是比较直观可以看到的,比如在 Spring 项目中,我们通常习惯在写 service 层代码前,添加一个接口层,对于 service 的调用一般也都 ...
- 【发现一个小问题】坑爹的官方日志库`golang.org/x/exp/slog`,凭啥不让我设置debug级别日志
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 一个代码使用了官方的日志库"golang.org ...
- 【JS 逆向百例】DOM事件断点调试,某商盟登录逆向
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:某商盟登录 ...
- [LeetCode刷题记录]39 组合总和
题目描述 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合.candidates 中的数字可以无限制 ...