Question

I have a C# project that imports a C dll, the dll has this function:

int primary_read_serial(int handle, int *return_code, int *serial, int length);

I want to get access to the serial parameter. I've actually got it to return one character of the serial parameter, but I'm not really sure what I'm doing and would like to understand what is going, and of course get it working.

So, I'm very sure that the dll is being accessed, other functions without pointers work fine. How do I handle pointers? Do I have to marshal it? Maybe I have to have a fixed place to put the data it?

An explanation would be great.

Thanks! Richard

Was it helpful?

Solution

You will have to use an IntPtr and Marshal that IntPtr into whatever C# structure you want to put it in. In your case you will have to marshal it to an int[].

This is done in several steps:

  • Allocate an unmanaged handle
  • Call the function with the unamanged array
  • Convert the array to managed byte array
  • Convert byte array to int array
  • Release unmanaged handle

That code should give you an idea:

// The import declaration
[DllImport("Library.dll")]
public static extern int primary_read_serial(int, ref int, IntPtr, int) ;


// Allocate unmanaged buffer
IntPtr serial_ptr = Marshal.AllocHGlobal(length * sizeof(int));

try
{
    // Call unmanaged function
    int return_code;
    int result = primary_read_serial(handle, ref return_code, serial_ptr, length);

    // Safely marshal unmanaged buffer to byte[]
    byte[] bytes = new byte[length * sizeof(int)];
    Marshal.Copy(serial_ptr, bytes, 0, length);

    // Converter to int[]
    int[] ints = new int[length];
    for (int i = 0; i < length; i++)
    {
        ints[i] = BitConverter.ToInt32(bytes, i * sizeof(int));
    }

}
finally
{
    Marshal.FreeHGlobal(serial_ptr);
}

Don't forget the try-finally, or you will risk leaking the unmanaged handle.

OTHER TIPS

If I understand what you're trying to do, this should work for you.

unsafe
{
    int length = 1024; // Length of data to read.

    fixed (byte* data = new byte[length]) // Pins array to stack so a pointer can be retrieved.
    {
        int returnCode;
        int retValue = primary_read_serial(0, &returnCode, data, length);

        // At this point, `data` should contain all the read data.
    }
}

JaredPar gives you the easy way to do it however, which is just to change your external function declaration so that C# does the marshalling for you in the background.

Hopefully this gives you an idea of what's happening at a slightly lower level anyway.

When writing your P/invoke declaration of that function, try using keyword ref for the pointer parameters like this:

[DllImport("YouDll.dll", EntryPoint = "primary_read_serial")]
public static extern int primary_read_serial(int, ref int, ref int, int) ;

I'm not sure if you need to specify the parameters' name in C#. And remember, when calling that method, you will also have to use ref in the arguments you're passing by reference.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top