Reputation: 1620
Consider the WM_SETTEXT
message that you can use to set the text of another window via old school Win32 API. There are probably a multitude of ways of doing this in .NET, here's two that I know:
[DllImport("USER32", EntryPoint = "SendMessage", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);
SendMessage(handle, WM_SETTEXT, IntPtr.Zero, "Magic String");
[DllImport("USER32", EntryPoint = "SendMessage", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
IntPtr textPointer = Marshal.StringToCoTaskMemUni("Magic String");
SendMessage(handle, WM_SETTEXT, IntPtr.Zero, textPointer);
Marshal.FreeCoTaskMem(textPointer);
The first one uses a string in the declaration of the dll import and lets .NET handle it. This second uses an IntPtr
and explicitly creates a pointer using Marshal.StringToCoTaskMemUni
and then freed using Marshal.FreeCoTaskMem
. Both of these approaches work happily as far as I can tell.
Note I normally do the former (which in fact I used to do many years ago as a VB6 programmer), but I came across the latter on a comment on another StackOverflow post and was curious if the former was another bad habit that needed squashing.
I have also seen a variant of the first example using a char[]
array in the declaration and passing in a StringBuilder
My question is simple - which (if any) is the correct way of doing it? For example, the SendMessage
Win32 call only accepts pointers anyway, so "something" must be creating pointers behind the scenes for the first version, and if so does it do the cleaning up, or is it better to be verbose and use the explicit allocation and deallocation of pointers.
Upvotes: 0
Views: 1032
Reputation: 613311
When you declare a parameter of string
, the p/invoke marshaller will, behind the scenes, marshal that to a pointer to a null-terminated array of characters. The marshaller will handle all memory allocation and deallocation. You can do it yourself with an IntPtr
parameter, but it comes to the same in the end.
Which you choose comes down to whether or not you plan to use the SendMessage
declaration for any other messages that you send.
SendMessage
for WM_SETTEXT
, then you declare it to receive a string
parameter.SendMessage
to send other messages, then you can declare the parameter to be IntPtr
and perform the marshalling manually.SendMessage
as convenient as possible for the programmer.Now, let's pick up that last option. Suppose you needed to send WM_GETTEXT
as well as WM_SETTEXT
. For WM_SETTEXT
you need to pass string data from managed to native, so you'd prefer string
. For WM_GETTEXT
, you need to supply a buffer so that the native code can fill out the window text. And that means you want to use StringBuilder
. So, declare two overloads, one receiving string
and one receiving StringBuilder
. Use the former for WM_SETTEXT
and the latter for WM_GETTEXT
.
Upvotes: 2