Attention: open in a new window. PDFPrint

C# and the char* mess

In this article, I will shortly describe, how you could exchange char-array data between a DLL and C# in different ways. It will contain methods of ANSI and Unicode conversion for Windows CE devices and a way to exchange binary data instead of null terminated strings only.

If you ever tried using C# to access native libraries or non-.NET libraries you might have heard something about P/Invoke and marshalling. Marshalling is used by the .NET runtime to create objects that are passable to .NET. Assume that you have a DLL (dynamic linked library) containing a method called foo that looks like this:

void foo(char* bar) {
// do write some information into char* bar
}

Ans let's assume, that the method parameter bar of the method foo is a pointer on a char array and the method foo writes some information to the char array. The maximum length of the information written to the array is limited to 256 bytes. So calling the method in C would look somewhat like this:

char result[256];
memset(result, 0, sizeof(result));
foo(result);

If the method writes a null terminated string to the char array, calling the method on a standard windows system is very straightforward by using a StringBuilder having a predefined size of 256 characters. You first define the method foo as external by using DllImport and set the parameter type for result as StringBuffer:

[DllImport("foobar.dll")]
private static external void foo(StringBuilder bar);

public String ReadFoo() {
StringBuilder result = new StringBuilder(256);
foo(result);
return result.ToString();
}

You can perform some format definition to marshall ANSI or Unicode data by using a definition for the CharSet during DllImport

[DllImport("foobar.dll", CharSet = CharSet.Ansi)]

or use the MarshalAs directive to perform some unmanaged pointer definition. This does not work on Windows CE devices as they only support Unicode strings but there is a workaround on those systems. Just read the ANSI-string into a Unicode buffer and convert it into a byte array using Unicode encoding and back into a String using ASCII encoding:

StringBuilder strBuilder = new StringBuilder(256);
foo(strBuilder);
Byte[] buf = Encoding.Unicode.GetBytes(strBuilder.ToString());
String result = Encoding.ASCII.GetString(buf);

This works great, as long as your result ends with at least two 0x00 bytes or the length fits the length of the buffer of the StringBuilder. But it will fail, if the method foo writes some junk data into the buffer and terminates the end of the string with a single null byte.

Now, you might think that this way would work in any case to read information from a C library, but it has a single flaw and would not work with binary data. Just think about a getpicture(char* buffer) reading a raw image from a device camera on a Windows CE device. The StringBuilder would stop converting the information into a String on a black pixel form RGB data (0x000000). So, how about this?

On a Windows CE device, I just used a (let's say) very odd solution to solve this problem. What most of the C# developers probably do not know is, that C# allows using pointers the way we know them from C or C++. The thing that is odd about them is, that pointers cannot be managed by the garbage collector and thus you have to have to cope with so called unsafe-code. So, the unsafe code allows you to create a pointer on a byte array within the memory of your application and pass it to the C method to fill the memory area with the requested information.

[DllImport("foobar.dll")]
private unsafe static extern void getpicture(byte* imageBuffer);

private byte[] GetImage() {
// size of the picture is 1024 * 1024 at RGB color, 8 bit each color
Byte[] rc = new Byte[1024 * 1024 * 3];

// this block contains unsafe code!!!
unsafe {
// create the pointer by disabling garbage collection and
// memory reallocation
fixed (byte* rcPrt = rc) {
this.getpicture(rcPtr);
}
// devalidate pointer and reenable memory reallocation and
// garbage collection
}
// and get safe again

return rc;
}

Some words to note:

  1. Never ever stay more time than necessary within your unsafe or fixed block as this could cause very odd memory leaks.
  2. Do not use unsafe or fixed objects where not really really really necessary.
  3. Put your unsafe code into external assembly as you have to compile it with /unsafe as compiler option (can be enabled in VisualStudio by selecting "Allow unsafe code" within the project properties)

Using unsafe code is the last solution, if every other way of reading the information from the C method failes. Make sure that a StringBuilder is not suitable for your application before you use this way of reading the information. You might create a C/C++ wrapper around your method to avoid the use of unsafe code on simple modules.