You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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);
        // --- 新增核心逻辑结束 ---
}

注意事项

  1. 类型扩展:可根据实际DLL函数的类型需求,扩展type_map中的类型条目。
  2. 内存管理:存入字典的指针内存需由脚本层或library类的unload方法负责释放,避免内存泄漏。
  3. 参数数量:示例中仅实现了最多3个参数的调用,需自行扩展到10个参数以满足需求。

内容的提问来源于stack exchange,提问作者super technology

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:37:35