1

I'm trying to call some C++ I've written from some C# (also my code) and having troubles with Interop/PInvoke getting a populated int[] back. Is there a way I could just add a compile-time reference to the C++ project from the C# project and call the functions within it as though it were just another C# class library?

I have a basic sample to illustrate the problem with the array. This is the C# signature for the C++ function I'm calling.

    [DllImport("CPlusPlusTargetLibrary.dll", CallingConvention = CallingConvention.StdCall)]
    private static extern void Accumulate
    (
        int input,
        int accumulationFactor,
        int numberOfAccumulations,
        [In, Out, MarshalAs(UnmanagedType.LPArray)]
        int[] accumulationsPointer
    );

This is how it's called from C#:

        var numberOfAccumulations = 3;
        var accumulations = new int[numberOfAccumulations];

        Accumulate(-8, 1, numberOfAccumulations, accumulations);

This is its declaration in the C++ header file:

__declspec(dllexport) const void __stdcall Accumulate
(
    const signed long input,
    const signed long accumulationFactor,
    const signed long numberOfAccumulations,
    signed long* accumulationsPointer
);

And this is its implementation in C++:

__declspec(dllexport) const void __stdcall Accumulate
(
    const signed long input,
    const signed long accumulationFactor,
    const signed long numberOfAccumulations,
    signed long* accumulationsPointer
)
{
    for (auto index = 0; index < numberOfAccumulations; index++, accumulationsPointer++)
    {
        auto accumulation = input * ((index + 1) * accumulationFactor);

        accumulationsPointer = &accumulation;            
    }
}

The accumulations array just comes back as a 3-element array of all 0 - just as it was passed-in. Instead it should have come back containing -8, -16 and -24.

I followed the documentation on MSDN for marshaling int[] and according to it, I shouldn't have needed any manual Marshaling (but even taking out the MarshalAs attribute didn't resolve the issue): https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-different-types-of-arrays

I had hoped if I could reference the C++ project directly then I wouldn't have to deal with all the type-related runtime failures.

Matt Arnold
  • 668
  • 2
  • 8
  • 21
  • 2
    Only if it's managed C++, which I'm guessing it isn't. – Matthew Watson Jan 12 '21 at 14:28
  • I'm open to making it managed C++. – Matt Arnold Jan 12 '21 at 14:29
  • 2
    Making the C++ into managed C++ is probably about the same amount of effort as making the C++ into C#. – Eljay Jan 12 '21 at 14:29
  • You'll still run into the same problems because it's still a C++ project, and the method signatures have not changed--you'll still have the Interop/PInvoke issues. You'll either need to get the method call correct, or convert the C++ code to C# – Russ Jan 12 '21 at 14:30
  • Unfortunately my C++ code calls upon C++ code for a driver so I can't get away from the interop issues by going all C#. – Matt Arnold Jan 12 '21 at 14:32
  • What is the declaration of the C++ method you're trying to call? – Matthew Watson Jan 12 '21 at 14:33
  • 1
    Post the relevant pieces of code, both the exported C++ function being called and the C# caller. – Alejandro Jan 12 '21 at 14:33
  • Your C# signature does not match the C++ signature. long is not an int. In C#, you need to use long. – Russ Jan 12 '21 at 14:55
  • @Russ `long` in C++ is `int` in C#: https://www.displayfusion.com/Discussions/View/converting-c-data-types-to-c/?ID=38db6001-45e5-41a3-ab39-8004450204b3 `long` in C# is actually `long long` in C++! – Matt Arnold Jan 12 '21 at 14:59
  • @Russ I tried changing all my `long`s to `int`s out of curiosity anyway (I didn't realize `int` was also a 32-bit `int` in C++) but the result was still the same anyway. – Matt Arnold Jan 12 '21 at 15:07
  • 1
    Well, it was a thought. I don't work with C++ so it surprised me to learn that longs in C++ were 32-bit. – Russ Jan 12 '21 at 15:12
  • 1
    isn't the c++ code `accumulationsPointer = &accumulation;` should be `*accumulationsPointer = accumulation;`? – apple apple Jan 12 '21 at 15:29
  • well not really sure what the code supposed to do, but certainly it never write to the content of `accumulationsPointer` – apple apple Jan 12 '21 at 15:31
  • (or `accumulationsPointer[index] = accumulation`, without `accumulationsPointer++` ) – apple apple Jan 12 '21 at 15:34
  • 1
    @appleapple You were right! This is because I don't usually write C++ code - I was previously trying `accumulationsPointer* = accumulation` and getting a compiler error of `expected an expression` so I assumed pointer values were read-only in C++. I'm now getting back the expected values with your suggestion. I'll happily mark it as an answer for my problem although I don't know whether I should now change the title of the question to help others in the future coming to this. – Matt Arnold Jan 12 '21 at 15:36
  • Would you not consider mapping a function pointer to a delegate in C#? see; https://stackoverflow.com/questions/39790977/how-to-pass-a-delegate-or-function-pointer-from-c-sharp-to-c-and-call-it-there – Ol1v3r Jan 12 '21 at 16:05
  • I'm not sure I follow - what would be the benefit of doing that? – Matt Arnold Jan 12 '21 at 16:17

1 Answers1

2

your code doesn't write to the content of accumulationsPointer but overwrite the pointer itself with address of accumulation


it should be something like this

for (auto index = 0; index < numberOfAccumulations; index++, accumulationsPointer++)
{
   auto accumulation = input * ((index + 1) * accumulationFactor);
   *accumulationsPointer = accumulation;
}

or like this, like in c#

for (auto index = 0; index < numberOfAccumulations; ++index)
{
   auto accumulation = input * ((index + 1) * accumulationFactor);   
   accumulationsPointer[index] = accumulation;            
}

btw, signed long* accumulationsPointer can also be written as signed long accumulationsPointer[], e.g.

void Accumulate
(
   const signed long input,
   const signed long accumulationFactor,
   const signed long numberOfAccumulations,
   signed long accumulations[]
)
{
   for (auto index = 0; index < numberOfAccumulations; ++index)
   {
      auto accumulation = input * ((index + 1) * accumulationFactor);
      accumulations[index] = accumulation;            
   }
}
apple apple
  • 10,292
  • 2
  • 16
  • 36