Search code examples
c#c++project-referencecompile-time-type-checking

Is it possible to reference a C++ project from a C# project?


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.


Solution

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