5

我们有一个 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...

这是转换和位旋转的实际 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 坐标组成。如果我错了,请纠正我,因为这可能是一个严重的误解)。

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

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

4

1 回答 1

0

我认为您的问题的关键在于“多显​​示器”。多个监视器可能导致负坐标

来自 MSDN:

重要 不要使用 LOWORD 或 HIWORD 宏来提取光标位置的 x 和 y 坐标,因为这些宏在具有多个监视器的系统上返回不正确的结果。具有多个监视器的系统可以具有负的 x 和 y 坐标,并且 LOWORD 和 HIWORD 将坐标视为无符号量。

由于在 CLR 上,有符号数使用2 补码表示法表示,负数表示为“大”无符号数(最高有效位为 '1' 的数);例如,-1 是 1111....1。因此,转换为(有符号的)32 位整数时会发生溢出。

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

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

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

打包成 32 位数字:0xFFFF FFFF

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

0x0000 0000 FFFF FFFF

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

通常,任何负 Y 坐标都将采用以下形式

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

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

于 2013-02-28T08:16:12.913 回答