ekw*_*ekw 1 .net c# c++ c++-cli visual-c++
要在托管域中实现params(可变参数)功能,我们在c ++/cli中执行以下操作,例如:
funcManaged(int n, ...array<int>^ variableParams)
Run Code Online (Sandbox Code Playgroud)
我对如何将其传递给接受可变参数的非托管域感到茫然.
funcUnmanaged(int n, ...)
Run Code Online (Sandbox Code Playgroud)
我试图传入数组,但结果很糟糕(访问冲突,垃圾数据等).
//where unmanagedVariableParamsArray is an int array
funcUnmanaged(int n, unmanagedVariableParamsArray);
Run Code Online (Sandbox Code Playgroud)
资源建议创建一个va_list并传递它,
vFuncUnmanaged(int n, va_list vl)
Run Code Online (Sandbox Code Playgroud)
但是如何在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));
}
}
Run Code Online (Sandbox Code Playgroud)
编译器将调用样本调用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
Run Code Online (Sandbox Code Playgroud)
请注意参数如何从左到右传递到堆栈.通话结束后,堆栈会被清理干净.您可以使用内联汇编来模拟完全相同的调用模式.看起来像这样:
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;
}
Run Code Online (Sandbox Code Playgroud)
非常直接,只是一些恶作剧,以恢复堆栈指针.现在您有一个可以从托管代码调用的适配器函数.您需要一个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;
}
Run Code Online (Sandbox Code Playgroud)
在优化的版本构建中经过测试,效果很好而且实际上并不危险.请注意,如果需要64位代码,则无法使其工作.