我正在为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
功能的签名。
问题:
我已使用MarshalAs(UnmanagedType.LPArray, SizeParamIndex = <approriate_index>)
从C#传递数组。这适用于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;
}
答案 0 :(得分:3)
此回调需要一个自定义编组程序。但是没有什么意义,你可以自己简单地编组阵列:
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()以匹配并使用Project + Properties,Build选项卡,勾选“允许不安全的代码”。这往往会引起Eek的注意!响应,如果您的代码片段与实际代码相当匹配,那么就没有那么多方法来编写代码并编写超出数组边界的内容。为这些论点选择好名字以避免其他人。