简体   繁体   English

Pinvoke-来自C ++的回调,函数之间传递的数组具有意外的大小

[英]Pinvoke - callback from C++, arrays passed between functions have unexpected size

EDIT: I've updated the code given the suggestions in @Hans Passant's comment and @David Heffernan's answer. 编辑:我已经更新了代码,给出了@Hans Passant的注释和@David Heffernan的答案中的建议。

The argument c is no longer null, but both x and c still have length one when they are passed back to CallbackFunction . 参数c不再为null,但是xc传递回CallbackFunction时,它们的长度仍为1。

I am trying to write C# code that passes a function pointer (using a delegate) to a C++ function, which calls the function pointer. 我正在尝试编写将函数指针(使用委托)传递给C ++函数的C#代码,C ++函数调用函数指针。

Code is below. 代码如下。

The problem I'm having is that when the C++ function f calls fnPtr(x,c) , in the C# function CallbackFunction , x has one element (with the correct value of 1.0), and c is null. 我遇到的问题是,当C ++函数f调用fnPtr(x,c) ,在C#函数CallbackFunctionx具有一个元素(正确值为1.0),而c为null。 I have no idea what the problem is. 我不知道是什么问题。

I can't change the signature of MyCallback. 我无法更改MyCallback的签名。

C# code: C#代码:

using System.Runtime.InteropServices;

namespace PInvokeTest
{
    class Program
    {
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate double MyCallback(
            [In] double[] x,
            [Out] double[] c);

        private static double CallbackFunction(
            [In] double[] x,
            [Out] double[] c)
        {
            c[0] = x[0] + x[1] + x[2];
            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 = new MyCallback(CallbackFunction);
            f(_myCallback);
        }
    }
}

NativeLib.h: NativeLib.h:

#ifndef _NATIVELIB_H_
#define _NATIVELIB_H_

#ifndef MYAPI
  #define MYAPI 
#endif

#ifdef __cplusplus
extern "C"
{
#endif

#ifdef _WIN32
  #ifdef MAKE_MY_DLL
    #define MYAPI __declspec(dllexport) __stdcall
  #else
    #define MYAPI __stdcall
  #endif
#else
  #if __GNUC__ >= 4
    #define MYAPI __attribute__ ((visibility ("default")))
  #else
    #define MYAPI
  #endif
#endif

  typedef int MyCallback(const double * x,
    double * c);

  MYAPI int f(MyCallback * fnPtr);

#ifdef __cplusplus
}
#endif

#endif // _NATIVELIB_H_

NativeLib.cpp: NativeLib.cpp:

#include "NativeLib.h"
#include <stdio.h>
#include <malloc.h>

MYAPI int f(MyCallback * fnPtr)
{
  double x[] = { 1.0, 2.0, 3.0 };
  double c[] = { 0.0, 0.0, 0.0 };

  printf("%e\n", fnPtr(x, c));


  return 0;
}

Using ref on your array parameter is wrong. 在数组参数上使用ref是错误的。 That's a spurious extra level of indirection. 那是一个虚假的间接附加级别。 You also need to pass the array lengths as parameters and let the marshaller know these lengths. 您还需要将数组长度作为参数传递,并让编组知道这些长度。

The delegate should be: 代表应为:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate double MyCallback(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
    [In]  double[] x,
    [In]  int lenx,
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=3)]
    [Out] double[] c,
    [In]  int lenc
);

Change CallbackFunction to match. 更改CallbackFunction以匹配。

The "duh" assumption I was making was that C would somehow pass some information on the size of x and c arrays, when it's just passing pointers. 我所做的“ duh”假设是C仅在传递指针时就以某种方式传递有关xc数组大小的信息。 The solution that I found was to mimic the code found in a related SO question . 我找到的解决方案是模仿在一个相关的SO问题中找到的代码。 (I don't know if it's a good way to solve my problem, but it works). (我不知道这是否是解决我的问题的好方法,但是它可以工作)。

I changed the callback function (and the delegate definition to match) to use an IntPtr . 我将回调函数(和匹配的委托定义)更改为使用IntPtr In general, I obviously need to pass information on the sizes of c and x. 通常,我显然需要传递有关c和x大小的信息。

unsafe private static double CallbackFunction(
  [In] IntPtr xp,
  [Out] IntPtr cp)
  {
    Double* c = (Double*) cp.ToPointer();
    Double* x = (Double*) xp.ToPointer();
    c[0] = x[0] + x[1] + x[2];
    c[1] = x[0] * x[1] * x[2];
    return c[0] + c[1];
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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