在C#中赢得api。 从IntPtr获取高低字

我试图在C#中处理WM_MOUSEMOVE消息。

从lParam获取X和Y坐标的正确方法是什么?这是一种IntPtr?

尝试:
(请注意,这是初始版本,请阅读下面的最终版本)

IntPtr xy = value; int x = unchecked((short)xy); int y = unchecked((short)((uint)xy >> 16)); 

通常不需要unchecked (因为“默认”c#项目未选中)

请考虑以下是使用的宏的定义:

 #define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff)) #define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff)) #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) 

其中WORD == ushortDWORD == uint 。 我正在削减一些ushort->简短的转换。

附录:

一年半之后,经历了64位.NET的“变幻莫测”,我同意Celess(但请注意,由于兼容性原因,99%的Windows消息仍然是32位,所以我不认为这个问题现在不是很大。它更适合未来,因为如果你想做某事,你应该正确地做。)

我唯一不同的是:

 IntPtr xy = value; int x = unchecked((short)(long)xy); int y = unchecked((short)((long)xy >> 16)); 

而不是检查“是IntPtr 4或8字节长”,我采取最坏的情况(8字节长)并将xy转换为long 。 运气好的话,编译器会优化双重演绎( long ,然后short /到uint )(最后,显式转换为IntPtr int是一个红色的鲱鱼…如果你使用它你就是放你自己将来处于危险之中。你应该总是使用long转换,然后直接使用它/将其重新投射到你需要的东西,向未来的程序员展示你知道你在做什么。

一个测试示例: http : //ideone.com/a4oGW2 (遗憾的是只有32位,但如果你有64位机器,你可以测试相同的代码)

适用于32位和64位:

 Point GetPoint(IntPtr _xy) { uint xy = unchecked(IntPtr.Size == 8 ? (uint)_xy.ToInt64() : (uint)_xy.ToInt32()); int x = unchecked((short)xy); int y = unchecked((short)(xy >> 16)); return new Point(x, y); } 

– 要么 –

 int GetIntUnchecked(IntPtr value) { return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32(); } int Low16(IntPtr value) { return unchecked((short)GetIntUnchecked(value)); } int High16(IntPtr value) { return unchecked((short)(((uint)GetIntUnchecked(value)) >> 16)); } 

这些也有效:

 int Low16(IntPtr value) { return unchecked((short)(uint)value); // classic unchecked cast to uint } int High16(IntPtr value) { return unchecked((short)((uint)value >> 16)); } 

– 要么 –

 int Low16(IntPtr value) { return unchecked((short)(long)value); // presumption about internals } // is what framework lib uses int High16(IntPtr value) { return unchecked((short)((long)value >> 16)); } 

走另一条路

 public static IntPtr GetLParam(Point point) { return (IntPtr)((point.Y << 16) | (point.X & 0xffff)); } // mask ~= unchecked((int)(short)x) 

- 要么 -

 public static IntPtr MakeLParam(int low, int high) { return (IntPtr)((high << 16) | (low & 0xffff)); } // (IntPtr)x is same as 'new IntPtr(x)' 

接受的答案是C定义的良好翻译。 如果直接处理原始'void *',那么大部分都可以。 但是,在.Net 64位执行环境中使用'IntPtr'时,'unchecked'不会阻止转换溢出exception从IntPtr内部抛出。 未经检查的块不会影响IntPtr函数和运算符内发生的转换。 目前接受的答案表明,使用“未经检查”并非必要。 然而,使用'unchecked' 是绝对必要的,因为从较大类型转换为负值时总是如此。

在64位,从接受的答案:

 var xy = new IntPtr(0x0FFFFFFFFFFFFFFF); int x = unchecked((short)xy); // <-- throws int y = unchecked((short)((uint)xy >> 16)); // gets lucky, 'uint' implicit 'long' y = unchecked((short)((int)xy >> 16)); // <-- throws xy = new IntPtr(0x00000000FFFF0000); // 0, -1 x = unchecked((short)xy); // <-- throws y = unchecked((short)((uint)xy >> 16)); // still lucky y = (short)((uint)xy >> 16); // <-- throws (short), no longer lucky 

在64位上,使用外推版本的DmitryG:

 var ptr = new IntPtr(0x0FFFFFFFFFFFFFFF); var xy = IntPtr.Size == 8 ? (int)ptr.ToInt64() : ptr.ToInt32(); // <-- throws (int) int x = unchecked((short)xy); // fine, if gets this far int y = unchecked((short)((uint)xy >> 16)); // fine, if gets this far y = unchecked((short)(xy >> 16)); // also fine, if gets this far ptr = new IntPtr(0x00000000FFFF0000); // 0, -1 xy = IntPtr.Size == 8 ? (int)ptr.ToInt64() : ptr.ToInt32(); // <-- throws (int) 

关于表现

 return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32(); 

IntPtr.Size属性返回一个常量作为编译时文字,如果跨程序集内联,则该常量是有效的。 因此,JIT几乎可以对所有这些进行优化。 也可以这样做:

 return unchecked((int)value.ToInt64()); 

- 要么 -

 return unchecked((int)(long)value); 

- 要么 -

 return unchecked((uint)value); // traditional 

并且所有这三个将始终调用IntPtr.ToInt64()的等效项。 ToInt64()和'operator long'也可以内联,但不太可能。 32位版本的代码比大小常量多得多。 我认为顶部的解决方案可能在语义上更正确。 同样重要的是要注意符号扩展工件,它会在像(long)int_val这样的东西上填充所有64位,尽管我已经在这里稍微掩饰了,但是可能另外影响32位的内联。

使用率

 if (Low16(wParam) == NativeMethods.WM_CREATE)) { } var x = Low16(lParam); var point = GetPoint(lParam); 

下面显示的'安全'IntPtr模型用于未来的traverlers。

无需在32位上设置WIN32定义即可运行此操作,以获得64位IntPtr行为的可靠模拟。

 public struct IntPtrMock { #if WIN32 int m_value; #else long m_value; #endif int IntPtr_ToInt32() { #if WIN32 return (int)m_value; #else long l = m_value; return checked((int)l); #endif } public static explicit operator int(IntPtrMock value) { //(short) resolves here #if WIN32 return (int)value.m_value; #else long l = value.m_value; return checked((int)l); // throws here if any high 32 bits #endif // check forces sign stay signed } public static explicit operator long(IntPtrMock value) { //(uint) resolves here #if WIN32 return (long)(int)value.m_value; #else return (long)value.m_value; #endif } public int ToInt32() { #if WIN32 return (int)value.m_value; #else long l = m_value; return checked((int)l); // throws here if any high 32 bits #endif // check forces sign stay signed } public long ToInt64() { #if WIN32 return (long)(int)m_value; #else return (long)m_value; #endif } public IntPtrMock(long value) { #if WIN32 m_value = checked((int)value); #else m_value = value; #endif } } public static IntPtr MAKELPARAM(int low, int high) { return (IntPtr)((high << 16) | (low & 0xffff)); } public Main() { var xy = new IntPtrMock(0x0FFFFFFFFFFFFFFF); // simulate 64-bit, overflow smaller int x = unchecked((short)xy); // <-- throws int y = unchecked((short)((uint)xy >> 16)); // got lucky, 'uint' implicit 'long' y = unchecked((short)((int)xy >> 16)); // <-- throws int xy2 = IntPtr.Size == 8 ? (int)xy.ToInt64() : xy.ToInt32(); // <-- throws int xy3 = unchecked(IntPtr.Size == 8 ? (int)xy.ToInt64() : xy.ToInt32()); //ok // proper 32-bit lParam, overflow signed var xy4 = new IntPtrMock(0x00000000FFFFFFFF); // x = -1, y = -1 int x2 = unchecked((short)xy4); // <-- throws int xy5 = IntPtr.Size == 8 ? (int)xy4.ToInt64() : xy4.ToInt32(); // <-- throws var xy6 = new IntPtrMock(0x00000000FFFF0000); // x = 0, y = -1 int x3 = unchecked((short)xy6); // <-- throws int xy7 = IntPtr.Size == 8 ? (int)xy6.ToInt64() : xy6.ToInt32(); // <-- throws var xy8 = MAKELPARAM(-1, -1); // WinForms macro int x4 = unchecked((short)xy8); // <-- throws int xy9 = IntPtr.Size == 8 ? (int)xy8.ToInt64() : xy8.ToInt32(); // <-- throws } 

通常,对于低级鼠标处理,我使用了以下帮助程序(它还认为IntPtr大小取决于x86 / x64):

 //... Point point = WinAPIHelper.GetPoint(msg.LParam); //... static class WinAPIHelper { public static Point GetPoint(IntPtr lParam) { return new Point(GetInt(lParam)); } public static MouseButtons GetButtons(IntPtr wParam) { MouseButtons buttons = MouseButtons.None; int btns = GetInt(wParam); if((btns & MK_LBUTTON) != 0) buttons |= MouseButtons.Left; if((btns & MK_RBUTTON) != 0) buttons |= MouseButtons.Right; return buttons; } static int GetInt(IntPtr ptr) { return IntPtr.Size == 8 ? unchecked((int)ptr.ToInt64()) : ptr.ToInt32(); } const int MK_LBUTTON = 1; const int MK_RBUTTON = 2; }