Consider these two definitions for GetWindowText
. One uses a string
for the buffer, the other uses a StringBuilder
instead:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, string lpString, int nMaxCount);
Here's how you call them:
var windowTextLength = GetWindowTextLength(hWnd);
// You can use either of these as they both work
var buffer = new string('\0', windowTextLength);
//var buffer = new StringBuilder(windowTextLength);
// Add 1 to windowTextLength for the trailing null character
var readSize = GetWindowText(hWnd, buffer, windowTextLength + 1);
Console.WriteLine($"The title is '{buffer}'");
They both seem to work correctly whether I pass in a string
, or a StringBuilder
. However, all the examples I've seen use the StringBuilder
variant. Even PInvoke.net lists that one.
My guess is the thinking goes 'In C# strings are immutable, therefore use StringBuilder
', but since we're poking down to the Win32 API and messing with the memory locations directly, and that memory buffer is for all intents and purposes (pre)allocated (i.e. reserved for, and being currently used by the string) by the nature of it being assigned a value at its definition, that restriction doesn't actually apply, hence string
works just fine. But I'm wondering if that assumption is wrong.
I don't think so because if you test this by increasing the buffer by say 10, and change the character you're initializing it with to say 'A', then pass in that larger buffer size to GetWindowText, the string you get back is the actual title, right-padded with the ten extra 'A's that weren't overwritten, showing it did update that memory location of the earlier characters.
So provided you pre-initialize the strings, can't you do this? Could those strings ever 'move out from under you' while using them because the CLR is assuming they're immutable? That's what I'm trying to figure out.
The for statement lets you repeat a statement or compound statement a specified number of times. The body of a for statement is executed zero or more times until an optional condition becomes false.
Explanation: for(;;) loop need not contain any initialization, condition and incre/decrement sections. All are optional. BREAK breaks the FOR Loop.
The do/while loop is a variant of the while loop. This loop will execute the code block once, before checking if the condition is true, then it will repeat the loop as long as the condition is true.
First off, pre-allocated is a misleading word in current context.The string is nothing different than just another .Net immutable string, and is as immutable as Hugh Jackman in real life. I believe OP knows this already.
In fact:
// You can use either of these as they both work
var buffer = new string('\0', windowTextLength);
is exactly same as:
// assuming windowTextLength is 5
var buffer = "\0\0\0\0\0";
Why shouldn't we use String/string
and instead use StringBuilder
for passing callee modifiable arguments to Interop/Unmanaged code? Are there specific scenarios where it will fail?
Honestly, I found this an interesting question and tested a few scenarios, by writing a custom Native DLL that takes a string and StringBuilder
, while I force garbage collection, by forcing GC
in different thread and so on. My intention was to force-relocate the object while its address was passed to an external library though PInvoke. In all cases, the object's address remained same even when other objects relocated. On research I found this by Jeffrey himself: The Managed Heap and Garbage Collection in the CLR
When you use the CLRâs P/Invoke mechanism to call a method, the CLR pins the arguments for you automatically and unpins them when the native method returns.
So takeaway is we can use it because it seems to work. But should we? I believe No:
string
works for now, may not work in future releases.StringBuilder
is library-provided mutable type, and logically it makes more sense to allow a mutable type to be modified vs an immutable type (string
).StringBuilder
, we're pre-allocating capacity, and not string. What this does is, we get rid of additional steps to trim/sanitize the string, and also not bother about terminating null character.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With