C# ushort[] to string conversion; is this possible?

mmr picture mmr · Nov 8, 2008 · Viewed 11.1k times · Source

I have a very painful library which, at the moment, is accepting a C# string as a way to get arrays of data; apparently, this makes marshalling for pinvokes easier.

So how do I make a ushort array into a string by bytes? I've tried:

int i;
String theOutData = "";
ushort[] theImageData = inImageData.DataArray;
 //this is as slow like molasses in January
 for (i = 0; i < theImageData.Length; i++) {
     byte[] theBytes = System.BitConverter.GetBytes(theImageData[i]);
     theOutData += String.Format("{0:d}{1:d}", theBytes[0], theBytes[1]);
 }

I can do it this way, but it doesn't finish in anything remotely close to a sane amount of time.

What should I do here? Go unsafe? Go through some kind of IntPtr intermediate?

If it were a char* in C++, this would be significantly easier...

edit: the function call is

DataElement.SetByteValue(string inArray, VL Length);

where VL is a 'Value Length', a DICOM type, and the function itself is generated as a wrapper to a C++ library by SWIG. It seems that the representation chosen is string, because that can cross managed/unmanaged boundaries relatively easily, but throughout the C++ code in the project (this is GDCM), the char* is simply used as a byte buffer. So, when you want to set your image buffer pointer, in C++ it's fairly simple, but in C#, I'm stuck with this weird problem.

This is hackeration, and I know that probably the best thing is to make the SWIG library work right. I really don't know how to do that, and would rather a quick workaround on the C# side, if such exists.

Answer

Barry Kelly picture Barry Kelly · Nov 8, 2008

P/Invoke can actually handle what you're after most of the time using StringBuilder to create writable buffers, for example see pinvoke.net on GetWindowText and related functions.

However, that aside, with data as ushort, I assume that it is encoded in UTF-16LE. If that is the case you can use Encoding.Unicode.GetString(), but that will exepect a byte array rather than a ushort array. To turn your ushorts into bytes, you can allocate a separate byte array and use Buffer.BlockCopy, something like this:

ushort[] data = new ushort[10];
for (int i = 0; i < data.Length; ++i)
    data[i] = (char) ('A' + i);

string asString;
byte[] asBytes = new byte[data.Length * sizeof(ushort)];
Buffer.BlockCopy(data, 0, asBytes, 0, asBytes.Length);
asString = Encoding.Unicode.GetString(asBytes);

However, if unsafe code is OK, you have another option. Get the start of the array as a ushort*, and hard-cast it to char*, and then pass it to the string constructor, like so:

string asString;
unsafe
{
    fixed (ushort *dataPtr = &data[0])
        asString = new string((char *) dataPtr, 0, data.Length);
}