在AngelScript中解析DLL函数签名并实现参数转换与调用
AngelScript调用DLL函数:参数处理与结果存储实现方案
下面是针对你的需求的具体实现方案,覆盖参数类型转换、指针内存分配、函数调用及结果存入CScriptDictionary的全流程:
一、核心依赖与类型映射
首先定义类型映射表,将解析出的DLL函数类型字符串(如int、float*)对应到C++的类型元信息,方便后续转换处理:
#include <unordered_map> #include <typeinfo> #include <CScriptDictionary.h> // 存储类型的大小、是否为指针等元信息 struct TypeInfo { size_t size; bool is_pointer; const std::type_info* type; }; // 基础类型映射,可根据需求扩展更多类型 std::unordered_map<std::string, TypeInfo> type_map = { {"void", {0, false, nullptr}}, {"int", {sizeof(int), false, &typeid(int)}}, {"float", {sizeof(float), false, &typeid(float)}}, {"int*", {sizeof(int*), true, &typeid(int*)}}, {"float*", {sizeof(float*), true, &typeid(float*)}}, {"char*", {sizeof(char*), true, &typeid(char*)}} };
二、参数处理:转换与指针内存分配
遍历解析出的参数列表,同时读取AngelScript传入的参数,区分值类型和指针类型分别处理:
// 存储传递给DLL的参数地址 std::vector<void*> dll_args; // 存储分配的指针内存,后续存入字典 std::vector<void*> allocated_ptrs; asIScriptContext* ctx = asGetActiveContext(); for (size_t i = 0; i < last.size(); ++i) { const std::string& param_type = last[i]; auto type_iter = type_map.find(param_type); if (type_iter == type_map.end()) { ctx->SetException(("不支持的参数类型: " + param_type).c_str()); // 清理已分配内存 for (void* ptr : allocated_ptrs) free(ptr); return; } const TypeInfo& info = type_iter->second; if (info.is_pointer) { // 为指针参数分配内存 void* ptr = malloc(info.size); if (!ptr) { ctx->SetException("指针参数内存分配失败"); for (void* p : allocated_ptrs) free(p); return; } allocated_ptrs.push_back(ptr); dll_args.push_back(ptr); } else { // 值类型直接取AngelScript参数的地址 void* arg_addr = gen->GetArgAddress(i + 1); // 索引0是函数签名,参数从1开始 dll_args.push_back(arg_addr); } }
三、动态调用DLL函数
根据参数数量匹配对应签名的函数指针,调用目标DLL函数:
void* return_val = nullptr; const std::string& return_type = first[0]; bool has_return = return_type != "void"; // 处理最多10个参数的情况,可按需扩展 try { switch (dll_args.size()) { case 0: if (has_return) return_val = reinterpret_cast<void*(*)(void)>(address)(); else reinterpret_cast<void(*)(void)>(address)(); break; case 1: if (has_return) return_val = reinterpret_cast<void*(*)(void*)>(address)(dll_args[0]); else reinterpret_cast<void(*)(void*)>(address)(dll_args[0]); break; case 2: if (has_return) return_val = reinterpret_cast<void*(*)(void*, void*)>(address)(dll_args[0], dll_args[1]); else reinterpret_cast<void(*)(void*, void*)>(address)(dll_args[0], dll_args[1]); break; case 3: if (has_return) return_val = reinterpret_cast<void*(*)(void*, void*, void*)>(address)(dll_args[0], dll_args[1], dll_args[2]); else reinterpret_cast<void(*)(void*, void*, void*)>(address)(dll_args[0], dll_args[1], dll_args[2]); break; // 继续扩展到10个参数... default: ctx->SetException("参数数量过多(最多支持10个)"); for (void* ptr : allocated_ptrs) free(ptr); return; } } catch (...) { ctx->SetException("DLL函数调用崩溃"); for (void* ptr : allocated_ptrs) free(ptr); return; }
四、将结果存入CScriptDictionary
创建字典对象,分别存入返回值和分配的指针参数:
// 创建字典实例 CScriptDictionary* dict = CScriptDictionary::Create(); // 存入返回值到key=0 if (has_return && return_val != nullptr) { auto rt_iter = type_map.find(return_type); if (rt_iter != type_map.end()) { const TypeInfo& rt_info = rt_iter->second; if (!rt_info.is_pointer) { if (return_type == "int") dict->Set("0", *(int*)return_val); else if (return_type == "float") dict->Set("0", *(float*)return_val); // 扩展其他值类型的处理 } else { // 指针类型存入地址(脚本层需自行管理内存释放) dict->Set("0", (asINT64)return_val); } } } // 存入分配的指针参数到key=1~10 for (size_t i = 0; i < allocated_ptrs.size(); ++i) { dict->Set(std::to_string(i + 1).c_str(), (asINT64)allocated_ptrs[i]); } // 将字典设置为函数返回值 gen->SetReturnObject(dict);
五、完整整合后的library_call函数
将上述代码整合到你的现有逻辑中,注意异常情况下的内存清理:
void library_call(asIScriptGeneric* gen) { #undef GetObject//For WinAPI asIScriptContext* ctx = asGetActiveContext(); library* lib_obj = (library*)gen->GetObject(); void* ref = gen->GetArgAddress(0); std::string func_name= *static_cast<std::string*>(ref); //Function signature parser: std::vector<std::string> tokens; std::string token; for (char c : func_name) { if (c == ' ' || c == '(' || c == ')' || c == ',' || c == ';') { if (!token.empty()) { tokens.push_back(token); token.clear(); } } else { token += c; } } // First array: Return type and function name std::vector<std::string> first; first.push_back(tokens[0]); // Return type first.push_back(tokens[1]); // Function name // Second array: Parameters std::vector<std::string> last; for (size_t i = 2; i < tokens.size(); ++i) { if (!tokens[i].empty()) { last.push_back(tokens[i]); } } FARPROC address = GetProcAddress(lib_obj->lib, first[1].c_str()); if (address == NULL) { const char* name = first[1].c_str(); std::string message = "Signature parse error: GetProcAddress failed for '" + std::string(name) + "'"; ctx->SetException(message.c_str()); return; } // --- 新增核心逻辑开始 --- std::unordered_map<std::string, TypeInfo> type_map = { {"void", {0, false, nullptr}}, {"int", {sizeof(int), false, &typeid(int)}}, {"float", {sizeof(float), false, &typeid(float)}}, {"int*", {sizeof(int*), true, &typeid(int*)}}, {"float*", {sizeof(float*), true, &typeid(float*)}}, {"char*", {sizeof(char*), true, &typeid(char*)}} }; std::vector<void*> dll_args; std::vector<void*> allocated_ptrs; // 处理参数 for (size_t i = 0; i < last.size(); ++i) { const std::string& param_type = last[i]; auto type_iter = type_map.find(param_type); if (type_iter == type_map.end()) { ctx->SetException(("不支持的参数类型: " + param_type).c_str()); for (void* ptr : allocated_ptrs) free(ptr); return; } const TypeInfo& info = type_iter->second; if (info.is_pointer) { void* ptr = malloc(info.size); if (!ptr) { ctx->SetException("指针参数内存分配失败"); for (void* p : allocated_ptrs) free(p); return; } allocated_ptrs.push_back(ptr); dll_args.push_back(ptr); } else { void* arg_addr = gen->GetArgAddress(i + 1); dll_args.push_back(arg_addr); } } // 调用DLL函数 void* return_val = nullptr; const std::string& return_type = first[0]; bool has_return = return_type != "void"; try { switch (dll_args.size()) { case 0: if (has_return) return_val = reinterpret_cast<void*(*)(void)>(address)(); else reinterpret_cast<void(*)(void)>(address)(); break; case 1: if (has_return) return_val = reinterpret_cast<void*(*)(void*)>(address)(dll_args[0]); else reinterpret_cast<void(*)(void*)>(address)(dll_args[0]); break; case 2: if (has_return) return_val = reinterpret_cast<void*(*)(void*, void*)>(address)(dll_args[0], dll_args[1]); else reinterpret_cast<void(*)(void*, void*)>(address)(dll_args[0], dll_args[1]); break; case 3: if (has_return) return_val = reinterpret_cast<void*(*)(void*, void*, void*)>(address)(dll_args[0], dll_args[1], dll_args[2]); else reinterpret_cast<void(*)(void*, void*, void*)>(address)(dll_args[0], dll_args[1], dll_args[2]); break; // 继续扩展到10个参数... default: ctx->SetException("参数数量过多(最多支持10个)"); for (void* ptr : allocated_ptrs) free(ptr); return; } } catch (...) { ctx->SetException("DLL函数调用崩溃"); for (void* ptr : allocated_ptrs) free(ptr); return; } // 存入CScriptDictionary CScriptDictionary* dict = CScriptDictionary::Create(); if (has_return && return_val != nullptr) { auto rt_iter = type_map.find(return_type); if (rt_iter != type_map.end()) { const TypeInfo& rt_info = rt_iter->second; if (!rt_info.is_pointer) { if (return_type == "int") dict->Set("0", *(int*)return_val); else if (return_type == "float") dict->Set("0", *(float*)return_val); } else { dict->Set("0", (asINT64)return_val); } } } for (size_t i = 0; i < allocated_ptrs.size(); ++i) { dict->Set(std::to_string(i + 1).c_str(), (asINT64)allocated_ptrs[i]); } gen->SetReturnObject(dict); // --- 新增核心逻辑结束 --- }
注意事项
- 类型扩展:可根据实际DLL函数的类型需求,扩展
type_map中的类型条目。 - 内存管理:存入字典的指针内存需由脚本层或
library类的unload方法负责释放,避免内存泄漏。 - 参数数量:示例中仅实现了最多3个参数的调用,需自行扩展到10个参数以满足需求。
内容的提问来源于stack exchange,提问作者super technology
相关产品推荐
相关产品推荐

