Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PInvoke char* in C DLL handled as String in C#. Issue with null characters

The function in C DLL looks like this:

int my_Funct(char* input, char* output);

I must call this from C# app. I do this in the following way:

...DllImport stuff...
public static extern int my_Funct(string input, string output);

The input string is perfectly transmitted to the DLL (I have visible proof of that). The output that the function fills out although is wrong. I have hexa data in it, like:

3F-D9-00-01

But unfortunately everything that is after the two zeros is cut, and only the first two bytes come to my C# app. It happens, because (I guess) it treats as null character and takes it as the end of the string.

Any idea how could I get rid of it? I tried to specifiy it as out IntPtr instead of a string, but I don't know what to do with it afterwards. I tried to do after:

 byte[] b1 = new byte[2];
 Marshal.Copy(output,b1,0,2);

2 should be normally the length of the byte array. But I get all kind of errors: like "Requested range extends past the end of the array." or "Attempted to read or write protected memory..."

I appreciate any help.

like image 897
user2124150 Avatar asked Mar 07 '13 14:03

user2124150


1 Answers

Your marshalling of the output string is incorrect. Using string in the p/invoke declaration is appropriate when passing data from managed to native. But you cannot use that when the data flows in the other direction. Instead you need to use StringBuilder. Like this:

[DllImport(...)]
public static extern int my_Funct(string input, StringBuilder output);

Then allocate the memory for output:

StringBuilder output = new StringBuilder(256);
//256 is the capacity in characters - only you know how large a buffer is needed

And then you can call the function.

int retval = my_Funct(inputStr, output);
string outputStr = output.ToString();

On the other hand, if these parameters have null characters in them then you cannot marshal as string. That's because the marshaller won't marshal anything past the null. Instead you need to marshal it as a byte array.

public static extern int my_Funct(
    [In] byte[] input, 
    [Out] byte[] output
);

That matches your C declaration.

Then assuming the ANSI encoding you convert the input string to a byte array like this:

byte[] input = Encoding.Default.GetBytes(inputString);

If you want to use a different encoding, it's obvious how to do so.

And for the output you do need to allocate the array. Assuming it's the same length as the input you would do this:

byte[] output = new byte[input.Length];

And somehow your C function has got to know the length of the arrays. I'll leave that bit to you!

Then you can call the function

int retval = my_Funct(input, output);

And then to convert the output array back to a C# string you use the Encoding class again.

string outputString = Encoding.Default.GetString(output);
like image 150
David Heffernan Avatar answered Nov 02 '22 10:11

David Heffernan