Unexplained OverflowException将表示屏幕坐标的IntPtr转换为Int32

时间:2013-02-28 07:14:59

标签: c# .net winforms winapi wndproc

我们有一个WinForms AnyCPU应用程序,其中供应商库控件偶尔会在运行多个监视器的64位用户盒子上抛出以下异常:

System.OverflowException: Arithmetic operation resulted in an overflow.
   at VendorLibraryName.VendorControl.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

我查看了供应商库控件的WndProc处理程序,看起来它可能产生溢出的唯一代码就是这个(评论我的 - 这是反编译的):

switch (msg)
{
    case 132: // NCHITTEST 
    case 672: // NCMOUSEHOVER 

    // Technically dangerous: convert IntPtr to Int32 in a 64-bit process.
    // However, note that for these message codes, 
    // LParam represents a "packed" x and y screen-coordinate. 
    // Given my understanding of how this packing occurs, I can't think
    // of how to construct an LParam such that it would overflow an Int32.
    SomeMethod(x: (int)m.LParam & 65535, y: (int)m.LParam >> 16);

    // More code...

这是转换和bit-twiddling的实际IL:

IL_0092: ldarg.1
IL_0093: call instance native int [System.Windows.Forms]System.Windows.Forms.Message::get_LParam()

// As far as I can tell, this is the only instruction on which overflow could occur
IL_0098: call int32 [mscorlib]System.IntPtr::op_Explicit(native int)

IL_009d: ldc.i4 65535
IL_00a2: and
IL_00a3: ldarg.1

// Same thing here...
IL_00a4: call instance native int [System.Windows.Forms]System.Windows.Forms.Message::get_LParam()
IL_00a9: call int32 [mscorlib]System.IntPtr::op_Explicit(native int)

IL_00ae: ldc.i4.s 16
IL_00b0: shr

显然,由于在64位进程中将Message.LParam(IntPtr)转换为Int32,因此该例程看起来容易出现溢出问题。实际上,这个例程错误因为它没有正确处理负坐标 - 它看起来像是一个不正确的Windows GET_X_LPARAM和GET_Y_PARAM宏到C#的端口。

然而,我无法看到如何为NCHITTEST / NCMOUSEHOVER构建一个LParam,实际上,它会溢出Int32的范围。 (我认为低16位由带符号 16位X坐标组成,其余位由符号扩展的16位Y坐标组成。请纠正我我错了,因为这可能是一个严重的误解)。

我无法在具有许多不同显示器配置和窗口位置的开发盒上重现异常。

哪些屏幕坐标实际上会导致溢出?或者这个块有没有其他方式可能导致溢出?

1 个答案:

答案 0 :(得分:0)

我认为你的问题的关键在于多个监视器"。 Multiple monitors can lead to negative coordinates

来自MSDN:

  

重要事项请勿使用LOWORD或HIWORD宏提取x-   和光标位置的y坐标,因为这些宏返回   多个监视器的系统上的结果不正确。系统与   多个监视器可以有负x和y坐标,以及LOWORD   和HIWORD将坐标视为无符号数量。

因为在CLR上签名的数字是使用2-complements notation表示的,所以负数表示为"大"无符号数(最重要的位是' 1');例如,-1是1111 .... 1。因此,当转换为(带符号的)32位整数时溢出。

编辑: (免责声明:我没有多台显示器,所以有些猜测适用) 简而言之:我的猜测是你必须生成负y坐标。

假设坐标为(x:-1,y:-1)

作为短数:x:0xFFFF,y:0xFFFF

以32位数字打包:0xFFFF FFFF

现在,这就是涉及猜测的地方:IntPtr没有符号扩展(你能用调试器试试吗?你需要一个负坐标)。因此它变成了:

0x0000 0000 FFFF FFFF

Or 4294967295这是一个太大而无法将其强制转换为Int32的数字。

通常,任何负Y坐标都将采用

形式
000.(32 Zeros)..001 ...(other 31 digits) .. 0 

并且应该提出问题(您是否尝试将显示器堆叠在另一个之上?)