recineshto
recineshto

Reputation: 128

Misleading parameter type in SendMessage (... IntPtr wParam ...)

While working on issue described in What's wrong with this unmanaged code in C#? I have stumbled upon parameter wParam and its type IntPtr for SendMessage function.

Code for original function is something like

var length = (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var buff = new char[length + 1];
var iptr = Marshal.AllocHGlobal(buff.Length * sizeof(char));
SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)(length + 1), iptr);
Marshal.Copy(iptr, buff, 0, length + 1);
Marshal.FreeHGlobal(iptr);

I did not like part with allocation and Copy so I have tried to replace it with

var length = 1 + (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var buffer = new char[length];
var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var lengthHandle = GCHandle.Alloc(length, GCHandleType.Pinned);
SendMessage(hWnd, WindowMessage.WM_GETTEXT, lengthHandle.AddrOfPinnedObject(), bufferHandle.AddrOfPinnedObject());
bufferHandle.Free();
lengthHandle.Free();

as it turned out, this will not work because parameter wParam is not pointer (which can be concluded from its IntPtr type).

Specific description for WM_GETTEXT message can be found on MSDN (thank you SO community for pointing this) and this leads us to following code

var length = 1 + (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var buffer = new char[length];
var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)length, bufferHandle.AddrOfPinnedObject());
bufferHandle.Free();

Is there a library, or collection of signatures for SendMessage function which is specialized or narrowed for specific messages?

Do not get me wrong, PInvoke already did ton of work by making this function available to managed world and I appreciate that, however multi-purpose nature of single method makes it more difficult to use.

Upvotes: 0

Views: 1186

Answers (1)

xanatos
xanatos

Reputation: 111870

You can copy them from the PInvoke page, but note that often I have to modify the PInvoke definitions (they are often 32 bits oriented, so they often suppose that sizeof(IntPtr) == sizeof(int), something very wrong!, or they use the "obsolete" ANSI API, but nowadays the Unicode API should always be used... To give examples, in the linked page, the SendMessage with the [MarshalAs(UnmanagedType.LPStr)] string lParam is using the ANSI API, while the the one with Int32 wParam will break on 64 bits)

Your code can be changed to:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam);

var length = (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
var sb = new StringBuilder(length + 1);
SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)sb.Capacity, sb);
string str = sb.ToString();

using the fact that the pinvoke subsystem "supports" StringBuilder for marshaling strings.

Upvotes: 1

Related Questions