2

I have a large library written in C that I would like to use as a DLL in a C# program. Most of the C code will be used by the libraries own functions, but I do need one function to be able to be called from the C# project.

So there's an example C function below

__declspec(dllexport) char* test(char* a){
    char* b = "World";
    char* result = malloc(strlen(a) + strlen(b) + 1);

    strcpy(result, a);
    strcpy(result, b);

    return result;
}

Now in the C# code I have got using System.Running.InteropServices; and also [DllImport("mydll.dll")] but I'm not sure how to declared the function.

public static extern char* test(char* a); obviously doesn't work because C# doesn't support pointers like C does.

So how should I pass a string to this C function and have it return a string as well?

Conor Watson
  • 567
  • 1
  • 7
  • 28
  • do you have to use the C library why can't you write your own method to handle the same thing in C# this seems pretty straight forward in my opinion.. – MethodMan Oct 17 '16 at 15:19
  • 1
    C# does support pointers, just like C, but you don't need them here. Your function returns an `int` but you're trying to return a `char*` - that's an error. And usually you provide the function a buffer for it to fill, or you'll have to export a second function just to free the string you allocated. – Lucas Trzesniewski Oct 17 '16 at 15:22
  • Returning a string like this is a leak. See http://stackoverflow.com/questions/5298268/returning-a-string-from-pinvoke for the proper way to do it. – Michael Gunter Oct 17 '16 at 15:23
  • @MethodMan This was just an example. The real C code is a lot more in depth. – Conor Watson Oct 17 '16 at 15:25
  • 1
    The argument is simply *string*. The return value is a problem however, the pinvoke marshaller is going to try to release the string buffer with CoTaskMemFree() and that is not going work. Beware that this function is also hard to call reliably from a C program. You need to expose another helper function that calls free(). Declare as IntPtr in C# and use Marshal.PtrToStringAnsi(). A much smarter signature is int test(const char* a, char* buffer, int buflen), allow you declare the 2nd argument as StringBuilder and return an error code when buflen isn't big enough. – Hans Passant Oct 17 '16 at 15:25
  • @LucasTrzesniewski Apologies, was playing around with some tests and forgot to change it to char* – Conor Watson Oct 17 '16 at 15:26
  • @HansPassant Could you give me an example as an answer? I'm not sure where the IntPtr comes into play with Marhsal.PtrToStringAnsi()? – Conor Watson Oct 17 '16 at 15:31
  • Hmya, the code is entirely too fake for me to waste time on something you are not going to use anyway. Just do this correctly. – Hans Passant Oct 17 '16 at 15:35
  • @HansPassant Right, but I don't know what the correct way is, which is why I asked for help? – Conor Watson Oct 17 '16 at 15:40

1 Answers1

9

You're looking for a MarshalAs attribute:

   [DllImport("mydll.dll")]
   static public int test([MarshalAs(UnmanagedType.LPStr)]String a);

As for returning a dynamically allocated C string, bad idea. How will you reliably de-allocate the string? It's a recipe for a memory leak. Allocate a byte array in C#, pin it and pass it to the C code along with its size so the C code can copy the result into it. Then convert to a string using System.Text.Encoding.ASCII.GetString().

Asik
  • 21,506
  • 6
  • 72
  • 131