0

I am writing a C# API to a C++ library that takes a function pointer with this signature:

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

Array x has size n, array c has size m, and array l has size m + n.

I cannot change the signature of the C++ MyCallback function.

The question:

I've passed the arrays from C# using MarshalAs(UnmanagedType.LPArray, SizeParamIndex = <approriate_index>). this is fine for x and c. How do I marshal l and keep track of its size, when I don't explicitly have a parameter with its size?

The only solution I know of that works is to pass IntPtr from C# and use unsafe and ToPointer(). Is there a another solution?

An example of what I'm trying to do is below:

C# code:

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

Header file:

#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++ source:

#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;
}
5
  • I may well have misunderstood something, but I'm wondering why you say "I've passed the arrays from C# using ..." and "... is to pass IntPtr from C# and ...". Don't you mean to C#? Commented Dec 28, 2014 at 4:12
  • Anyway, would it be feasible for you to write a very thin C++ wrapper for the C++ library, where you define a callback function that conforms to the MyCallback signature, and then it invokes a slightly more marshalling-friendly callback function with an extra parameter that contains the length of the l array? Commented Dec 28, 2014 at 4:16
  • Yes you're right, I guess it should be "to". They're coming from C++ as pointers and to C# as whatever I'm marshalling them as, but I wrote 'from' because I was defining how I was passing them to C# in terms of C#. Anyway... Commented Dec 28, 2014 at 4:20
  • About the second comment. The People In Charge want the C# interface to consist of "only .cs files". It would be easier to create a C++ wrapper, but I want avoid using one before trying to convince them that I need one. Commented Dec 28, 2014 at 4:23
  • Not that I'd recommend cheating, (oh, no, not me), but if it comes to that then try doing a Google search for "c# dll as embedded resource". For example: stackoverflow.com/questions/72264/… Commented Dec 28, 2014 at 4:49

1 Answer 1

3

You'd need a custom marshaller for this callback. But there is little point, you can simply marshal the array yourself:

private static double CallbackFunction(..., IntPtr lptr, ...) {
    var l = new double[n + m];
    Marshal.Copy(lptr, l, 0, l.Length);
    // etc...
}

Update the delegate signature to match.

Do note that all the arrays are getting copied. That is pretty expensive, you may well favor using pointers so no copying is required:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private unsafe delegate double MyCallback(int n, int m, double* x, double* y, double* c);

Update CallbackFunction() to match and use Project + Properties, Build tab, tick "Allow unsafe code". This tends to invoke the Eek! response, there just aren't that many ways to fumble the code and write beyond the array boundaries if your snippet is a decent match for the real code. Pick good names for these arguments to avoid the other eek.

Sign up to request clarification or add additional context in comments.

2 Comments

To be clear, when you say that all the arrays are getting copied in the first solution you presented, you mean arrays x, c, AND l, and not just l?
All of them, the underlying native arrays are unmanaged so a copy must be made. And the reason the pinvoke marshaller needs to know the size. A copy can only be avoided if native code consumes a managed array, the non-reverse pinvoke way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.