views:

1044

answers:

3

Hi,

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

+3  A: 

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.

Coincoin
Specifically, allocate an unmanaged array and a managed int[] and Marshal.Copy your IntPtr from the unmanaged array to the managed one.
Ron Warholic
try-catch or try-finally?
MedicineMan
try-finally, thanks for pointing it out. Fixed.
Coincoin
A: 

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.

Anzurio
If it's supposed to be an array he needs to make sure to pass as an IntPtr and Marshal.Copy to a managed array however.
Ron Warholic
A: 

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.

Noldorin