OBS扩展模块DLL-加载模块
			 2024-06-20
			  53
			 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();
}
	
	
			
			OBS-扩展模块DLL
			




