如何使用C++/CLI包装器将变量参数从托管传递到非托管

How do I pass variable arguments from managed to unmanaged with a C++/CLI Wrapper?

本文关键字:参数 变量 C++ 何使用 CLI 包装      更新时间:2023-10-16

要在托管域中实现params(变量参数)功能,我们在c++/cli中执行以下操作,例如:

funcManaged(int n, ...array<int>^ variableParams)

我对如何将其传递给接受变量参数的非托管域感到困惑。

funcUnmanaged(int n, ...)

我试图传入数组,但结果很糟糕(访问违规、垃圾数据等)。

//where unmanagedVariableParamsArray is an int array
funcUnmanaged(int n, unmanagedVariableParamsArray);

参考资料建议创建一个va_list并将其传递给

vFuncUnmanaged(int n, va_list vl)

但是,我如何在c++/cli域中创建va_list以接受variableParams?重构遗留的非托管代码库不是一个理想的解决方案。

如果你真的非常绝望,那么这并非不可能。变量函数只能由C代码调用,并且该调用必须由C编译器生成。让我们举一个例子:

#include <stdarg.h>
#include <stdio.h>
#pragma unmanaged
void variadic(int n, ...) {
    va_list marker;
    va_start(marker, n);
    while (n--) {
        printf("%dn", va_arg(marker, int));
    }
}

编译器将把类似variadic(3, 1, 2, 3);的示例调用转换为:

00D31045  push        3  
00D31047  push        2  
00D31049  push        1  
00D3104B  push        3  
00D3104D  call        variadic (0D31000h)  
00D31052  add         esp,10h  

注意参数是如何在堆栈上从左到右传递的。调用后堆栈将被清理。您可以通过使用内联程序集来模拟完全相同的调用模式。看起来是这样的:

void variadicAdapter(int n, int* args) {
    // store stack pointer so we can restore it
    int espsave;
    _asm mov espsave,esp;
    // push arguments
    for (int ix = n-1; ix >= 0; --ix) {
        int value = args[ix];
        _asm push value;
    }
    // make the call
    variadic(n);
    // fix stack pointer
    _asm mov esp,espsave;
}

很直接,只是一些恶作剧来恢复堆栈指针。现在您有了一个适配器函数,可以从托管代码中调用它。您需要一个pin_ptr<>将数组转换为本机指针:

#pragma managed
using namespace System;
int main(array<System::String ^> ^args)
{
    array<int>^ arr = gcnew array<int>(3) { 1, 2, 3};
    pin_ptr<int> arrp(&arr[0]);
    variadicAdapter(arr->Length, arrp);
    return 0;
}

工作良好,实际上没有那么危险,在优化的Release构建中进行了测试。注意,如果需要64位代码,您就没有希望实现这一点。

一般建议不推荐:请参阅本文

我建议使用std::vector<int>来存储参数。