简体   繁体   中英

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

To implement a params(variable arguments) functionality in the managed domain, we do the following in c++/cli such as:

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

I'm dumbfounded on how to pass this to the unmanaged domain that takes in variable arguments.

funcUnmanaged(int n, ...)

I tried to pass the array in, but it ended up badly (access violations, junk data, etc).

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

Resources suggest to create a va_list and pass that around,

vFuncUnmanaged(int n, va_list vl)

But how do I create the va_list in the c++/cli domain to take in the variableParams ? Refactoring the legacy unmanaged codebase is not a desirable solution.

If you are really, really desperate then this is not impossible. A variadic function can only be called by C code and the call has to be generated by the C compiler. Let's take an example:

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

The compiler will turn a sample call like variadic(3, 1, 2, 3); into:

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

Note how the arguments are passed on the stack, from left to right. The stack gets cleaned up after the call. You can emulate that exact same call pattern by using inline assembly. That looks like this:

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

Pretty straight forward, just some shenanigans to get the stack pointer restored. Now you have an adapter function that you can call from managed code. You'll need a pin_ptr<> to turn the array into a native pointer:

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

Works well and not actually that dangerous, tested in the optimized Release build. Beware that you have no hope of making this work if 64-bit code is required.

The general recommendation is that it is not recommended: See this article

I would suggest using a std::vector<int> to store the arguments.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM