I am trying to process a WM_MOUSEMOVE message in C#.
What is the proper way to get an X and Y coordinate from lParam which is a type of IntPtr?
I am trying to process a WM_MOUSEMOVE message in C#.
What is the proper way to get an X and Y coordinate from lParam which is a type of IntPtr?
Usualy, for low-level mouse processing I have used the following helper (it also considers that IntPtr size depends on x86/x64):
Try:
(note that this was the initial version, read below for the final version)
The
unchecked
normally isn't necessary (because the "default" c# projects are unchecked)Consider that these are the definitions of the used macros:
Where
WORD == ushort
,DWORD == uint
. I'm cutting some ushort->short conversions.Addendum:
one and half year later, and having experienced the "vagaries" of 64 bits .NET, I concur with Celess (but note that 99% of the Windows messages are still 32 bits for reasons of compatibility, so I don't think the problem isn't really big now. It's more for the future and because if you want to do something, you should do it correctly.)
The only thing I would make different is this:
instead of doing the check "is the
IntPtr
4 or 8 bytes long", I take the worst case (8 bytes long) and castxy
to along
. With a little luck the double cast (tolong
and then toshort
/touint
) will be optimized by the compiler (in the end, the explicit conversion toint
ofIntPtr
is a red herring... If you use it you are putting yourself at risk in the future. You should always use thelong
conversion and then use it directly/re-cast it to what you need, showing to the future programmers that you knew what you were doing.A test example: http://ideone.com/a4oGW2 (sadly only 32 bits, but if you have a 64 bits machine you can test the same code)
Correct for both 32 and 64-bit:
- or -
These also work:
- or -
Going the other way
- or -
The accepted answer is good translation of the C definition. If were dealing with just the raw 'void*' directly, then would be mostly ok. However when using 'IntPtr' in a .Net 64-bit execution environment, 'unchecked' will not stop conversion overflow exceptions from being thrown from inside IntPtr. The unchecked block does not affect conversions that happen inside IntPtr funcitons and operators. Currently the accepted answer states that use of 'unchecked' is not necesary. However the use of 'unchecked' is absolutely necessary, as would always be the case in casting to negative values from a larger type.
On 64-bit, from the accepted answer:
On 64-bit, using extrapolated version of DmitryG's:
On performance
The IntPtr.Size property returns a constant as compile time literal that is capable if being inlined across assemblies. Thus is possible for the JIT to have nearly all of this optimized out. Could also do:
- or -
- or -
and all 3 of these will always call the equivalient of IntPtr.ToInt64(). ToInt64(), and 'operator long', are also capable of being inlined, but less likely to be. Is much more code in 32-bit version than the Size constant. I would submit that the solution at the top is maybe more symantically correct. Its also important to be aware of sign-extension artifacts, which would fill all 64-bits reguardless on something like (long)int_val, though i've pretty much glossed over that here, however may additionally affect inlining on 32-bit.
Useage
A 'safe' IntPtr mockup shown below for future traverlers.
Run this without setting the WIN32 define on 32-bit to get a solid simulation of the 64-bit IntPtr behavour.