【问题标题】:Reverse Pinvoke: passing array length without an explicit function parameter for size反向Pinvoke:传递数组长度而没有明确的函数参数大小
【发布时间】:2015-02-24 16:59:06
【问题描述】:

我正在向一个 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# 传递了数组。这适用于xc。当我没有明确的参数大小时,如何编组 l 并跟踪其大小?

我知道的唯一可行的解​​决方案是从 C# 传递 IntPtr 并使用 unsafeToPointer()。有没有其他解决方案?

我正在尝试做的一个例子如下:

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

【问题讨论】:

  • 我很可能误解了一些东西,但我想知道为什么你说“我已经通过数组 from C# using ...”和“... is传递 IntPtr from C# 和 ..."。你不是说to C#吗?
  • 无论如何,您是否可以为 C++ 库编写一个非常薄的 C++ 包装器,在其中定义一个符合 MyCallback 签名的回调函数,然后它会调用更多的编组 -带有一个包含 l 数组长度的额外参数的友好回调函数?
  • 是的,你是对的,我想应该是“to”。它们作为指针来自 C++,而作为我编组它们的任何内容来自 C#,但我写了“from”,因为我定义了如何根据 C# 将它们传递给 C#。无论如何...
  • 关于第二条评论。负责人希望 C# 界面由“仅 .cs 文件”组成。创建 C++ 包装器会更容易,但我想在试图说服他们我需要一个之前避免使用它。
  • 我不建议作弊,(哦,不,不是我),但如果涉及到,请尝试在 Google 上搜索“c# dll 作为嵌入式资源”。例如:stackoverflow.com/questions/72264/…

标签: c# pinvoke marshalling


【解决方案1】:

您需要一个自定义编组器来处理此回调。但是没有什么意义,您可以简单地自己编组数组:

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 选项卡,勾选“Allow unsafe code”。这往往会调用 Eek!响应,如果您的 sn-p 与真实代码相匹配,则没有太多方法可以摸索代码并超出数组边界进行写入。为这些论点选择好名字以避免其他 eek。

【讨论】:

  • 要清楚,当您说在您提出的第一个解决方案中所有数组都被复制时,您的意思是数组 x、c 和 l,而不仅仅是 l?
  • 所有这些,底层的原生数组都是非托管的,因此必须进行复制。 pinvoke marshaller 需要知道大小的原因。仅当本机代码使用托管数组(非反向 pinvoke 方式)时,才能避免复制。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-16
  • 1970-01-01
  • 2013-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-05
相关资源
最近更新 更多