c# - 如何使用 C++/CLI Wrapper 将变量参数从托管传递到非托管?

标签 c# c++ .net visual-c++ c++-cli

要在托管域中实现参数(可变参数)功能,我们在 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("%d\n", 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<> 将数组转换为 native 指针:

#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;
}

工作良好,实际上并没有那么危险,在优化的发布版本中进行了测试。请注意,如果需要 64 位代码,您将无法完成这项工作。

关于c# - 如何使用 C++/CLI Wrapper 将变量参数从托管传递到非托管?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17821794/

相关文章:

c# - 从共享某些部分的 2 个接口(interface)实现一个类

c# - 大量身份验证请求,然后尝试访问 OneNote 页面时出现 0x8000FFFF 错误?

java - 使用 Kerberos 进行委派时,GSS API 和 SSPI API 有什么区别?

c++ - typedef 枚举的重新定义

c# - 无法发布 .Net Blazor 应用程序 : The application was terminated by a signal: SIGHUP

c# - 警报不起作用,这有什么问题

c++ - 为什么我们不能将 char 分配给字符串?

C++ UTF-8 实际字符串长度

c# - Mono 上的垃圾收集最坏情况性能

.net - .NET 结构中的成员相等性测试使用的算法是什么?