首页 > 编程语言 >C++ 快速加载 Dll 里的 API

C++ 快速加载 Dll 里的 API

时间:2023-12-14 11:57:30浏览次数:42  
标签:set sentry C++ Dll instance API dll options sdk

最近项目里要重新编写程序加载器,也就是编译出一个可执行文件,在 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;
}

   

 

标签:set,sentry,C++,Dll,instance,API,dll,options,sdk
From: https://www.cnblogs.com/strive-sun/p/17900895.html

相关文章

  • navicat链接oracle时报错,检查是否是oci.dll库不匹配的问题
     1:安装Oracle数据库,安装时类型选择共享服务器,不要选专享服务器。2:确定Oracle,Navicat,OracleClient的位数,确保你的oracle数据库的位数与navicat位数一致,即:32v32,64v643:http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html,在这个页面下载和......
  • ASP.NET WebApi(.Net Framework) 应用CacheManager
    ASP.NETWebApi(.NetFramework)应用CacheManager,内存+Redis1,WebApi版本选.net4.6.2以上版本2,nuget包Unity(4.0.0.1)Unity.AspNet.WebApi(4.0.0.1)CacheManager.CoreCacheManager.Microsoft.Extensions.Caching.MemoryCacheManager.Microsoft.Extensions.ConfigurationCacheMa......
  • 如何在 Eolink Apikit 中发起 TCP/UDP 文档测试
    TCP/UDP是两种常用的网络传输协议。TCP协议提供可靠的连接,而UDP协议提供不可靠的连接。TCP协议是面向连接的协议,在建立连接之前,客户端和服务器需要先握手。握手完成后,客户端和服务器之间就会建立一个可靠的连接。在连接建立之后,客户端和服务器可以通过该连接进行数据传输。T......
  • API 设计错误
    缺乏一致性:API设计中的一个常见错误是缺乏连贯的结构。命名约定、数据格式和错误处理方面的不一致可能会导致尝试集成API的开发人员感到困惑。要解决此问题,请为命名、格式设置和响应错误建立清晰且一致的准则。一致性不仅简化了使用,还改善了整体用户体验。文档不充分:文档不......
  • C++基础 -6- 二维数组,数组指针
    ———————二维数组,数组指针——————— ......
  • C++ Qt开发:Slider滑块条组件
    Qt是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍Slider滑块条组件的常用方法及灵活运用。当涉及到C++Qt开发中的Slider滑块条组件时,你可能会用到......
  • C++学习笔记九:值,常量,常表达式和常初始化
    1.值:Literal:Datathatisdirectlyrepresentedincodewithoutgoingthroughsomeothervariablestoredinmemory.值:直接在代码中表示的数据,无需通过内存中存储的其他变量。值不在内存中进行存储,而是直接在二进制的可执行文件中产生。1.1整型//Literaltypes:ua......
  • 如何判断lib和dll是32位还是64位?答案是使用微软的dumpbin工具,后面讲了如何使用gcc生成
    为什么我会考虑这个问题呢?因为我在使用java去调用一个c的lib库的时候,弹出以下警告:D:\work\ideaworkpaces\jdk21Test001\src\main\java\lib\hello.lib:%1不是有效的Win32应用程序。atjava.base/jdk.internal.loader.NativeLibraries.load(NativeMethod)然后我就在想是不是......
  • 算法战斗第二天C++2
    A.NextRound"Contestantwhoearnsascoreequaltoorgreaterthanthek-thplacefinisher'sscorewilladvancetothenextround,aslongasthecontestantearnsapositivescore..."—anexcerptfromcontestrules.Atotalofnpartic......
  • 支撑阻力指标,庄家成本价是可靠的支撑位(无未来,DLL加密)
    本指标依据庄家的成本价设计的,庄家成本价是可靠的支撑位。底层逻辑:庄家是有内幕的,庄家能在价格低位时抄底,庄家控股时,庄家不会让散户获取低价的筹码,所以当股价到达到支撑位时,会有比较大的反弹。庄家也会有出错的时候,在非融券的股票情况下,有以下三种方式:一、庄家极限拉高,可参考*ST......