Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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://docs.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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
1.1k views
Welcome To Ask or Share your Answers For Others

1 Answer

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...