Invoking Win32 SendMessge

Z

Zytan

Most code on the net is wrong. This is SendMessage():
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
lParam);

All of those parameters are 32/64-bit in size depending on the
compiler, except for UINT which is a C int, which is always 32-bit,
even on a 64-bit machine, AFAIK. Most code on the net uses C# int (32-
bit) for all (or some) of these, when it should use IntPtr, which is
32/64-bit.

Now, I've seen code use HandleRef for the hWnd, instead of IntPtr, but
I cannot pass Control.Handle into a HandleRef, since it returns
IntPtr. So, should I stick with IntPtr? Or is HandleRef better?

Also, I want to do this:

const IntPtr SB_BOTTOM = 7;

But, C# doesn't allow me to make IntPtr constant, like I could do with
int! AND I cannot just store 7 into an IntPtr, since it's a type
conversion. Dammit. Is there another 32/64 integer data type that I
could use in place of IntPtr that IS allowed to be constant, which
acts like an int?

I am surprised at how annoying all of this is. No wonder people just
use int (and wait for their app to blow up on 64-bit).

Zytan
 
Z

Zytan

Also, I see code like so (look at the DllImport part):

[DllImport("Kernel32.dll")]
public static extern void GetSystemTime(...);

But, NET Reflector shows me VB DLLs like so:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(...);

Is the extra ", CharSet = CharSet.Auto, SetLastError = true"
required? MSDN fails to explain this extra stuff.

Zytan
 
M

Mattias Sjögren

Most code on the net uses C# int (32-
bit) for all (or some) of these, when it should use IntPtr, which is
32/64-bit.

Well some people don't care about 64-bit, and as long as you stay on
Win32 ints will work. Other people simply don't know how to write
proper P/Invoke declarations and use trial and error until they get
something that seemingly works. Or they start from old VB6 Declare
statements that often lack proper type info for the parameters.

Now, I've seen code use HandleRef for the hWnd, instead of IntPtr, but
I cannot pass Control.Handle into a HandleRef, since it returns
IntPtr.

You can't use it directly, but you can create a new HandleRef when you
need it.

SendMessage(new HandleRef(yourControl, yourControl.Handle), ...

So, should I stick with IntPtr? Or is HandleRef better?

You can use IntPtr if you know what you're doing. Nowadays, HandleRef
has sort of been superseded by SafeHandles which is an even better
wrapper type.

Also, I want to do this:

const IntPtr SB_BOTTOM = 7;

Use int for the const, then cast it to IntPtr when you use it.

const int SB_BOTTOM = 7;

SendMessage(..., (IntPtr)SB_BOTTOM, ...



Mattias
 
M

Mattias Sjögren

Also, I see code like so (look at the DllImport part):

[DllImport("Kernel32.dll")]
public static extern void GetSystemTime(...);

But, NET Reflector shows me VB DLLs like so:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(...);
Is the extra ", CharSet = CharSet.Auto, SetLastError = true"
required? MSDN fails to explain this extra stuff.

It's not required, but recommended. It ensures that SendMessageW is
called instead of SendMessageA on NT-based systems. And since .NET and
most APIs use Unicode internally that's usually good for performance
reasons.

SetLastError=true only makes sense if you also use
Marshal.GetLastWin32Error to tell the reason for failure when a
function returns an error code.

Since GetSystemTime doesn't have a return value and doesn't deal with
text data, it's fine to leave out CharSet and SetLastError.


Mattias
 
Z

Zytan

Also, I want to do this:
const IntPtr SB_BOTTOM = 7;

But, C# doesn't allow me to make IntPtr constant, like I could do with
int!

Is the reason for this beacuse IntPtr is a struct? I didn't realize
it was a struct until just now.

Zytan
 
Z

Zytan

Is the extra ", CharSet = CharSet.Auto, SetLastError = true"
It's not required, but recommended. It ensures that SendMessageW is
called instead of SendMessageA on NT-based systems. And since .NET and
most APIs use Unicode internally that's usually good for performance
reasons.

It explains it here had I looked a bit more before posting:
http://msdn2.microsoft.com/en-us/library/7b93s42f.aspx
"CharSet.Auto: Platform invoke chooses between ANSI and Unicode
formats at run time, based on the target platform."

So, this is precisely what I want. I don't know how it knows how to
choose the right format, but what this, I can't see any reason to use
the other possibilities. This must be slower due to the extra checks
at run-time. CharSet.Ansi is default, so all the SendMessage imports
I've seen on the internet were calling SendMessageA!

MSDN shows a great example:

[DllImport("user32.dll")]
public static extern int MessageBoxA(int hWnd, String text,
String caption, uint type);
[DllImport("user32.dll", CharSet=CharSet.Unicode)]
public static extern int MessageBoxW(int hWnd, String text,
String caption, uint type);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int MessageBox(int hWnd, String text,
String caption, uint type);
SetLastError=true only makes sense if you also use
Marshal.GetLastWin32Error to tell the reason for failure when a
function returns an error code.

Oh cool, thanks. It just passed the Win32 error into
Marshal.GetLastWin32Error? So, no harm done if I have it there and
don't use it. So, I might as well leave it there, in case in the
future I want to use it.
Since GetSystemTime doesn't have a return value and doesn't deal with
text data, it's fine to leave out CharSet and SetLastError.

Right.

Thanks, Mattias!

Oh, something that wasn't clear: I think you need this:
using System.Runtime.InteropServices;
to make all this work.

Zytan
 
Z

Zytan

Well some people don't care about 64-bit, and as long as you stay on
Win32 ints will work. Other people simply don't know how to write
proper P/Invoke declarations and use trial and error until they get
something that seemingly works. Or they start from old VB6 Declare
statements that often lack proper type info for the parameters.
Right.

You can use IntPtr if you know what you're doing. Nowadays, HandleRef
has sort of been superseded by SafeHandles which is an even better
wrapper type.

Well, yourControl.Handle returns IntPtr, and I know I just want to
pass this directly to SendMessage, so I think I'll stay with IntPtr
for now.
Use int for the const, then cast it to IntPtr when you use it.

const int SB_BOTTOM = 7;

SendMessage(..., (IntPtr)SB_BOTTOM, ...

I think I will do this. Either way is ugly. I wish a real non-struct
int_ptr / int3264 type existed, instead.

Thanks, Mattias

Zytan
 
W

Willy Denoyette [MVP]

Zytan said:
Most code on the net is wrong. This is SendMessage():
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
lParam);

All of those parameters are 32/64-bit in size depending on the
compiler, except for UINT which is a C int, which is always 32-bit,
even on a 64-bit machine, AFAIK. Most code on the net uses C# int (32-
bit) for all (or some) of these, when it should use IntPtr, which is
32/64-bit.

Now, I've seen code use HandleRef for the hWnd, instead of IntPtr, but
I cannot pass Control.Handle into a HandleRef, since it returns
IntPtr. So, should I stick with IntPtr? Or is HandleRef better?

Also, I want to do this:

const IntPtr SB_BOTTOM = 7;

Why do you want this to be an IntPtr (machine dependent integer)?
I suppose you want to use this as a Msg parameter value, but Msg is Int32 on both 32 and 64
bit, so an IntPtr is wrong and will blow on 64 bit.

Willy.
 
Z

Zytan

Also, I want to do this:
Why do you want this to be an IntPtr (machine dependent integer)?
I suppose you want to use this as a Msg parameter value, but Msg is Int32 on both 32 and 64
bit, so an IntPtr is wrong and will blow on 64 bit.

Actually, WM_VSCROLL is the message, which is only 32-bit. I caught
this myself, but never thought to repost here with the correction.
SB_BOTTOM is WPARAM which is 32/64-bit.

But, you made me think of something else:

I want to use IntPtr for 4 of the 5 parameters/return value, since 4
of the 5 should be 32/64-bit depending on the compilation (32/64-
bit). But, that's for a single source that can be compiled both in 32
and 64-bit. Doesn't both compilations need to refer to two
*different* SendMessage? Thus, using IntPtr is wrong if compiling on
64-bit but still linking to the 32-bit SendMessage. I think maybe I
am confusing myself.

Zytan
 
W

Willy Denoyette [MVP]

Zytan said:
Actually, WM_VSCROLL is the message, which is only 32-bit. I caught
this myself, but never thought to repost here with the correction.
SB_BOTTOM is WPARAM which is 32/64-bit.
Ok.
const int WM_VSCROLL = 0x115;
const int sb_bottom = 7;
IntPtr SB_BOTTOM = new IntPtr(sb_bottom);
....
SendMessage(wHandle, WM_VSCROLL, SB_BOTTOM, ...);
But, you made me think of something else:

I want to use IntPtr for 4 of the 5 parameters/return value, since 4
of the 5 should be 32/64-bit depending on the compilation (32/64-
bit). But, that's for a single source that can be compiled both in 32
and 64-bit. Doesn't both compilations need to refer to two
*different* SendMessage? Thus, using IntPtr is wrong if compiling on
64-bit but still linking to the 32-bit SendMessage. I think maybe I
am confusing myself.

Zytan


Managed code compiled as anycpu(platform:anycpu - is default) will automatically run in a 32
bit process on a 32 bit OS, and as a 64 bit process on 64 bit.
Managed code compiled as 32 bit (platform:x86), will always run as 32bit irrespective the OS
version.
Managed code compiled as 64 bit (platform:x64 or itanium), will run as 64, and this only on
X64 or IA64.
A 32 bit process will automatically get the 32 bit version of user32.dll loaded by the OS
loader, while a 64 bit version will load the 64 bit version, a 32 and 64 bit code mix isn't
possible in the same process, this means that you need to declare all machine dependent
int's as IntPtr to be portable between 32 and 64 bit, the rest is taken care of by the OS.

Willy.
 
Z

Zytan

Managed code compiled as anycpu(platform:anycpu - is default) will automatically run in a 32
bit process on a 32 bit OS, and as a 64 bit process on 64 bit.
Managed code compiled as 32 bit (platform:x86), will always run as 32bit irrespective the OS
version.
Managed code compiled as 64 bit (platform:x64 or itanium), will run as 64, and this only on
X64 or IA64.
A 32 bit process will automatically get the 32 bit version of user32.dll loaded by the OS
loader, while a 64 bit version will load the 64 bit version, a 32 and 64 bit code mix isn't
possible in the same process, this means that you need to declare all machine dependent
int's as IntPtr to be portable between 32 and 64 bit, the rest is taken care of by the OS.

Thanks, Willy. Just to be clear, the default compilation in VC# 2005
(Express) is platform:anycpu, which means it will chage IntPtr to the
proper size at runtime? Also, IntPtr is always the proper thing to
use, just as INT_PTR is in VC++, so that it will work if compiled on
32-bit, or compiled on 64-bit, or compiled as 32/64-bit
(platform:anycpu)?

Zytan
 
W

Willy Denoyette [MVP]

Zytan said:
Thanks, Willy. Just to be clear, the default compilation in VC# 2005
(Express) is platform:anycpu, which means it will chage IntPtr to the
proper size at runtime? Also, IntPtr is always the proper thing to
use, just as INT_PTR is in VC++, so that it will work if compiled on
32-bit, or compiled on 64-bit, or compiled as 32/64-bit
(platform:anycpu)?

Zytan


Yep, it's always the proper thing to use where machine dependent integers are expected.
These include pointer types like HANDLE and HWND but also opaque types like WPARAM, LPARAM,
LRESULT etc..

Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top