3

Just to clarify something first. I am not trying to convert a byte array to a single string. I am trying to convert a byte-array to a string-array.

I am fetching some data from the clipboard using the GetClipboardData API, and then I'm copying the data from the memory as a byte array. When you're copying multiple files (hence a CF_HDROP clipboard format), I want to convert this byte array into a string array of the files copied.

Here's my code so far.

//Get pointer to clipboard data in the selected format
var clipboardDataPointer = GetClipboardData(format);

//Do a bunch of crap necessary to copy the data from the memory
//the above pointer points at to a place we can access it.
var length = GlobalSize(clipboardDataPointer);
var @lock = GlobalLock(clipboardDataPointer);

//Init a buffer which will contain the clipboard data
var buffer = new byte[(int)length];

//Copy clipboard data to buffer
Marshal.Copy(@lock, buffer, 0, (int)length);

GlobalUnlock(clipboardDataPointer);

snapshot.InsertData(format, buffer);

Now, here's my code for reading the buffer data afterwards.

var formatter = new BinaryFormatter();
using (var serializedData = new MemoryStream(buffer))
{
    paths = (string[]) formatter.Deserialize(serializedData);
}

This won't work, and it'll crash with an exception saying that the stream doesn't contain a binary header. I suppose this is because it doesn't know which type to deserialize into.

I've tried looking the Marshal class through. Nothing seems of any relevance.

2
  • Are you thinking about Clipboard.GetFileDropList()? Commented Jul 25, 2012 at 19:11
  • Yes, but I want to use the Windows API alone, since the general Clipboard functionality in WPF is buggy, as described many places on the web. Commented Jul 25, 2012 at 19:12

2 Answers 2

2

If the data came through the Win32 API then a string array will just be a sequence of null-terminated strings with a double-null-terminator at the end. (Note that the strings will be UTF-16, so two bytes per character). You'll basically need to pull the strings out one at a time into an array.

The method you're looking for here is Marshal.PtrToStringUni, which you should use instead of Marshal.Copy since it works on an IntPtr. It will extract a string, up to the first null character, from your IntPtr and copy it to a string.

The idea would be to continually extract a single string, then advance the IntPtr past the null byte to the start of the next string, until you run out of buffer. I have not tested this, and it could probably be improved (in particular I think there's a smarter way to detect the end of the buffer) but the basic idea would be:

var myptr = GetClipboardData(format);
var length = GlobalSize(myptr);

var result = new List<string>();

var pos = 0;
while ( pos < length )
{
    var str = Marshal.PtrToStringUni(myptr);
    var count = Encoding.Unicode.GetByteCount(str);

    myptr = IntPtr.Add(myptr, count + 1);
    pos += count + 1;

    result.Add(str);
}

return result.ToArray();

(By the way: the reason your deserialization doesn't work is because serializing a string[] doesn't just write out the characters as bytes; it writes out the structure of a string array, including additional internal bits that .NET uses like the lengths, and a binary header with type information. What you're getting back from the clipboard has none of that present, so it cannot be deserialized.)

Sign up to request clarification or add additional context in comments.

Comments

1

How about this:

var strings = Encoding.Unicode
    .GetString(buffer)
    .Split(new[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.