反向 Pinvoke:传递数组长度,而不为大小提供显式函数参数
本文关键字:参数 函数 Pinvoke 数组 反向 | 更新日期: 2023-09-27 18:35:22
我正在向C++库编写一个 C# API,该库采用具有此签名的函数指针:
typedef int MyCallback(
int n,
int m,
const double * x,
const double * l,
double * c);
数组 x
的大小为 n
,数组c
的大小为 m
,数组l
的大小为 m + n
。
我无法更改C++
MyCallback
函数的签名。
问题:
我已经使用 C# 传递了 MarshalAs(UnmanagedType.LPArray, SizeParamIndex = <approriate_index>)
的数组。 这对x
和c
很好。当我没有明确包含其大小的参数时,如何封送l
并跟踪其大小?
我知道的唯一有效的解决方案是从 C# 传递IntPtr
并使用 unsafe
和 ToPointer()
.有没有其他解决方案?
我正在尝试做的一个例子如下:
C# 代码:
using System.Runtime.InteropServices;
namespace PInvokeTest
{
public class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate double MyCallback(
int n,
int m,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] double[] x,
[In, MarshalAs(UnmanagedType.LPArray)] double[] l,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] double[] c);
private static double CallbackFunction(
int n,
int m,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] double[] x,
[In, MarshalAs(UnmanagedType.LPArray)] double[] l,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] double[] c)
{
// THIS IS WILL NOT WORK, l HAS SIZE 1.
// In this example, only l[0] and l[1] are used, but in general
// the size of l is n + m.
c[0] = x[0] + x[1] + x[2] + l[0]*l[1];
c[1] = x[0] * x[1] * x[2];
return c[0] + c[1];
}
private static MyCallback _myCallback;
[DllImport("NativeLib", CallingConvention = CallingConvention.StdCall)]
private static extern int f(MyCallback cf);
private static void Main()
{
_myCallback = CallbackFunction;
f(_myCallback);
}
}
}
头文件:
#ifndef _NATIVELIB_H_
#define _NATIVELIB_H_
#ifndef MYAPI
#define MYAPI
#endif
#ifdef __cplusplus
extern "C"
{
#endif
typedef int MyCallback(
int n,
int m,
const double * x,
const double * l,
double * c);
MYAPI int f(MyCallback * fnPtr);
#ifdef __cplusplus
}
#endif
#endif // _NATIVELIB_H_
C++来源:
#include "NativeLib.h"
#include <stdio.h>
#include <malloc.h>
MYAPI int f(MyCallback * fnPtr)
{
int n = 3;
int m = 2;
double x[] = { 1.0, 2.0, 3.0 };
double l[] = { 1.0, 1.0, 1.0, 1.0, 1.0 };
double c[] = { 0.0, 0.0};
printf("%e'n", fnPtr(n, m, x, l, c));
printf("the value of c after the function call:'n");
printf("%e %e'n", c[0], c[1]);
return 0;
}
您需要为此回调自定义编组器。 但是没有什么意义,您可以自己简单地封送数组:
private static double CallbackFunction(..., IntPtr lptr, ...) {
var l = new double[n + m];
Marshal.Copy(lptr, l, 0, l.Length);
// etc...
}
更新委托签名以匹配。
请注意,所有数组都被复制了。 这是非常昂贵的,您可能更喜欢使用指针,因此不需要复制:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private unsafe delegate double MyCallback(int n, int m, double* x, double* y, double* c);
更新 CallbackFunction() 以匹配和使用项目 + 属性,构建选项卡,勾选"允许不安全的代码"。 这往往会调用 Eek!响应,如果您的代码片段与真实代码匹配良好,那么没有那么多方法可以摸索代码并超出数组边界进行编写。 为这些参数选择好的名字以避免其他 eek。