OBS-扩展模块DLL
+ -

OBS扩展模块DLL-加载模块

2024-06-20 12 0

设置了一系列的模块的路径之后,需要在这些路径下搜索模块dll,并将其加载到内存中。
模块的加载入口位于:OBSBasic::OBSInit(),其函数调用关系如下:

  • OBSBasic::OBSInit()
    • obs_load_all_modules2(&mfi); //加载模块,失败信息存储在struct obs_module_failure_info mfi;
    • obs_log_loaded_modules(); //仅是打印划块的文件名
    • obs_post_load_modules(); //如果模块的post_load函数存在,则调用

从这里来看,具有三个函数,但只有obs_load_all_modules2是真正的加载模块,其余2个一个是打印日志,一个是有post_load函数的调用。

obs_load_all_modules2

void obs_load_all_modules2(struct obs_module_failure_info *mfi)
{
/
    struct fail_info fail_info = {0};
    memset(mfi, 0, sizeof(*mfi));

    //线程调试统计信息
    profile_start(obs_load_all_modules2_name);

    //执行回调load_all_callback
    obs_find_modules2(load_all_callback,, &fail_info);

#ifdef _WIN32 //只32位系统,符号库信息
    profile_start(reset_win32_symbol_paths_name);
    reset_win32_symbol_paths();
    profile_end(reset_win32_symbol_paths_name);
#endif
    profile_end(obs_load_all_modules2_name);

    mfi->count = fail_info.fail_count;
    mfi->failed_modules =strlist_split(fail_info.fail_modules.array, ';', false);
    dstr_free(&fail_info.fail_modules);
}

对于模块中的每一个路径,调用find_modules_in_path查找模块,并执行load_all_callback加载。

void obs_find_modules2(obs_find_module_callback2_t callback, void *param)
{
    if (!obs)
        return;

    for (size_t i = 0; i < obs->module_paths.num; i++) {
        struct obs_module_path *omp = obs->module_paths.array + i;
        find_modules_in_path(omp, callback, param);
    }
}

find_modules_in_path函数用于查找该路径下的所有模块,并执行回调函数callback=load_all_callback

static void find_modules_in_path(struct obs_module_path *omp,
                 obs_find_module_callback2_t callback,
                 void *param)
{
    struct dstr search_path = {0};
    char *module_start;
    bool search_directories = false;
    os_glob_t *gi;

    dstr_copy(&search_path, omp->bin);

//通过路径最后的"%module%" 标识是模块类型
    module_start = strstr(search_path.array, "%module%");
    if (module_start) {
        //取除后面的"%module%" 字会串
        dstr_resize(&search_path, module_start - search_path.array);
        search_directories = true;
    }

    //后面加上/,路径结束符
    if (!dstr_is_empty(&search_path) && dstr_end(&search_path) != '/')
        dstr_cat_ch(&search_path, '/');

    //最终变为/*.dll
    dstr_cat_ch(&search_path, '*');
    if (!search_directories)
        dstr_cat(&search_path, get_module_extension());

    //调用os_glob搜索路径下的dll,并返回到gi中
    if (os_glob(search_path.array, 0, &gi) == 0)
    {
        //对于每一个文件,调用process_found_module
        for (size_t i = 0; i < gi->gl_pathc; i++) 
        {
            if (search_directories == gi->gl_pathv[i].directory)
                process_found_module(omp,
                                        gi->gl_pathv[i].path,
                                        search_directories,
                                        callback,
                                        param);
        }

        os_globfree(gi);
    }

    dstr_free(&search_path);
}

process_found_module中调用回调函数。

static void process_found_module(struct obs_module_path *omp, const char *path,
                 bool directory,
                 obs_find_module_callback2_t callback,
                 void *param)
{
    struct obs_module_info2 info;
    struct dstr name = {0};
    struct dstr parsed_bin_path = {0};
    const char *file;
    char *parsed_data_dir;
    bool bin_found = true;

    file = strrchr(path, '/');
    file = file ? (file + 1) : path;

    if (strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
        return;

    dstr_copy(&name, file);
    char *ext = strrchr(name.array, '.');
    if (ext)
        dstr_resize(&name, ext - name.array);

    if (!directory) {
        dstr_copy(&parsed_bin_path, path);
    } else {
        bin_found = parse_binary_from_directory(&parsed_bin_path,omp->bin, name.array);
    }

    parsed_data_dir = make_data_directory(name.array, omp->data);

    if (parsed_data_dir && bin_found) {
        info.bin_path = parsed_bin_path.array;//dll路径
        info.data_path = parsed_data_dir; //配置文件路径
        info.name = name.array; //dll名称

        //调用回调函数
        callback(param, &info);
    }

    bfree(parsed_data_dir);
    dstr_free(&name);
    dstr_free(&parsed_bin_path);
}

回调函数load_all_callback

static void load_all_callback(void *param, const struct obs_module_info2 *info)
{
    struct fail_info *fail_info = param;
    obs_module_t *module;

    bool is_obs_plugin;
    bool can_load_obs_plugin;

    get_plugin_info(info->bin_path, &is_obs_plugin, &can_load_obs_plugin);

    if (!is_obs_plugin) {
        blog(LOG_WARNING, "Skipping module '%s', not an OBS plugin", info->bin_path);
        return;
    }

    if (!is_safe_module(info->name)) {
        blog(LOG_WARNING, "Skipping module '%s', not on safe list",
             info->name);
        return;
    }

    if (!can_load_obs_plugin) {
        blog(LOG_WARNING,
             "Skipping module '%s' due to possible "
             "import conflicts",
             info->bin_path);
        goto load_failure;
    }

    int code = obs_open_module(&module, info->bin_path, info->data_path);
    switch (code) {
    case MODULE_MISSING_EXPORTS:
        blog(LOG_DEBUG,
             "Failed to load module file '%s', not an OBS plugin",
             info->bin_path);
        return;
    case MODULE_FILE_NOT_FOUND:
        blog(LOG_DEBUG,
             "Failed to load module file '%s', file not found",
             info->bin_path);
        return;
    case MODULE_ERROR:
        blog(LOG_DEBUG, "Failed to load module file '%s'",
             info->bin_path);
        goto load_failure;
    case MODULE_INCOMPATIBLE_VER:
        blog(LOG_DEBUG,
             "Failed to load module file '%s', incompatible version",
             info->bin_path);
        goto load_failure;
    case MODULE_HARDCODED_SKIP:
        return;
    }

    if (!obs_init_module(module))
        free_module(module);

    UNUSED_PARAMETER(param);
    return;

load_failure:
    if (fail_info) {
        dstr_cat(&fail_info->fail_modules, info->name);
        dstr_cat(&fail_info->fail_modules, ";");
        fail_info->fail_count++;
    }
}

除过一大堆的错误处理,主要其实还是get_plugin_info函数的调用,用于返回DLL的信息。如果是插件,则调用obs_open_module。

get_plugin_info函数返回的参数很有意思。

    bool is_obs_plugin;
    bool can_load_obs_plugin;
    get_plugin_info(info->bin_path, &is_obs_plugin, &can_load_obs_plugin);

get_plugin_info函数的代码具体过程,通过文件映射将dll映射到内存中:

  • is_obs_plugin:通过判断导出表中的函数中是否有obs_module_load判断是否obs插件
  • can_load_obs_plugin:排除掉qt的dll

obs_open_module

dll校验是OBS插件后。调用obs_open_module加载模块插件。

//obs-module.c
    int code = obs_open_module(&module, info->bin_path, info->data_path);

obs_open_module函数的执行流程如下:

  • 使用os_dlopen函数LoadLibrary
  • 使用load_module_exports函数通过GetProcessAddress获取dll导出的函数指针。

    struct obs_module {
      char *mod_name; //没有扩展名dll的文件名
      const char *file;//文件名 xxx.dll
      char *bin_path; //完整的dll的PathName
      char *data_path;//配置文件ini路径
      void *module;  //LoadLibrary返回的HMODULE实例
      bool loaded;  //obs_init_module函数中调用bool (*load)(void);的返回值
    
      bool (*load)(void);//必须存在
      void (*unload)(void);
      void (*post_load)(void);
      void (*set_locale)(const char *locale);// 参数obs->locale
      bool (*get_string)(const char *lookup_string,  const char **translated_string);
      void (*free_locale)(void);
      uint32_t (*ver)(void);//必须存在
      void (*set_pointer)(obs_module_t *module);//必须存在
      const char *(*name)(void);
      const char *(*description)(void);
      const char *(*author)(void);
    
      struct obs_module *next;//链表,下一个。第一个存在obs->first_module;
    };
    

    导出的表函数指针与函数名的对应关系如下:

    static int load_module_exports(struct obs_module *mod, const char *path)
    {
      mod->load = os_dlsym(mod->module, "obs_module_load");
      if (!mod->load)
          return req_func_not_found("obs_module_load", path);
    
      mod->set_pointer = os_dlsym(mod->module, "obs_module_set_pointer");
      if (!mod->set_pointer)
          return req_func_not_found("obs_module_set_pointer", path);
    
      mod->ver = os_dlsym(mod->module, "obs_module_ver");
      if (!mod->ver)
          return req_func_not_found("obs_module_ver", path);
    
      /* optional exports */
      mod->unload = os_dlsym(mod->module, "obs_module_unload");
      mod->post_load = os_dlsym(mod->module, "obs_module_post_load");
      mod->set_locale = os_dlsym(mod->module, "obs_module_set_locale");
      mod->free_locale = os_dlsym(mod->module, "obs_module_free_locale");
      mod->name = os_dlsym(mod->module, "obs_module_name");
      mod->description = os_dlsym(mod->module, "obs_module_description");
      mod->author = os_dlsym(mod->module, "obs_module_author");
      mod->get_string = os_dlsym(mod->module, "obs_module_get_string");
      return MODULE_SUCCESS;
    }
    

obs_init_module初始化模块

obs_init_module主要是调用模块的回调函数load。

//obs-module.c
bool obs_init_module(obs_module_t *module)
{
    if (!module || !obs)
        return false;
    if (module->loaded)
        return true;

    const char *profile_name =    profile_store_name(obs_get_profiler_name_store(),   "obs_init_module(%s)", module->file);
    profile_start(profile_name);

    module->loaded = module->load();
    if (!module->loaded)
        blog(LOG_WARNING, "Failed to initialize module '%s'", module->file);

    profile_end(profile_name);
    return module->loaded;
}

模块对象的存储

加载的所有模块,使用struct obs_module表示,其使用链表存储在obs->first_module。如对于obs_post_load_modules的调用是这样的.

void obs_post_load_modules(void)
{
    for (obs_module_t *mod = obs->first_module; !!mod; mod = mod->next)
        if (mod->post_load)
            mod->post_load();
}

0 篇笔记 写笔记

作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!