Search code examples
c#dllimportrefintptr

DllImport how to get single values by reference


What's the appropiate signature/marshall attribute to be able to get output params using pointers params? So far I tried this:

// Function to calculate the norm of vector. !0 on error.
// int err_NormVec(int size, double * vector, double * norm)
[DllImport("vectors.dll")]
int err_NormVec(int size, double[] vector, ref double norm)

The previous approach doesn't pop back the value to .NET from C. I also tried to use pinned GCHandle, with the IntPtr signature.

[DllImport("vectors.dll")]
int err_NormVec(int size, double[] vector, IntPtr norm)

public void doSomething()
{
   double norm = 0;
   // ...
   GCHandle handle = GCHandle.Alloc(norm, GCHandleType.Pinned);
   int status = err_NormVec(vector.Lenght, vector, handle.AddrOfPinnedObject());
   // ... clean gchandle, check status and so on
}

In this case I got the value back but on the GCHandle.Target, not on the original norm. Which is annoying. I would like to be able to pin the intptr of the norm it self not just a copy.

What is the appropiate signature to return a value using a pointer? Is there a supported way to get a IntPtr to an int value?


Solution

  • This works for me (as it should):

    //C++ DLL (__stdcall calling convention)
    extern "C" __declspec(dllexport) void Foo(double *result) {
        *result = 1.2;
    }
    
    //C#    
    class Program
    {
        [DllImport( "Snadbox.dll", CallingConvention=CallingConvention.StdCall )]
        static extern void Foo( ref double output );
    
        static void Main( string[] args )
        {
            double d = 0;           
            Foo( ref d );
    
            Console.WriteLine( d ); // prints "1.2"         
        }
    }
    

    Passing the double using the ref keyword is sufficient. So, I am led to believe that there is something wrong (or misunderstood) in the implementation. Can you post the implementation for us?

    Also, perhaps you are building the C++ version using the default calling convention (cdecl), but .NET is using StdCall. Have you made sure that these line up? You may crash if they are mixed, but there is no guarantee. For instance, in my example, if I change the C++ side to cdecl then the out parameter is read back as 0.