Ani*_*Ani 5 .net c# winapi wndproc winforms
我们有一个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)
Run Code Online (Sandbox Code Playgroud)
我查看了供应商库控件的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...
Run Code Online (Sandbox Code Playgroud)
这是转换和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
Run Code Online (Sandbox Code Playgroud)
显然,由于在64位进程中将Message.LParam(IntPtr)转换为Int32,因此该例程看起来容易出现溢出问题.实际上,这个例程是错误的,因为它没有正确处理负坐标 - 它看起来像是一个不正确的Windows GET_X_LPARAM和GET_Y_PARAM宏到C#的端口.
但是,我无法看到如何为NCHITTEST/NCMOUSEHOVER构建LParam,实际上,它会溢出Int32的范围.(我认为低16位由带符号的 16位X坐标组成,其余位由符号扩展的16位Y坐标组成.如果错误,请纠正我,因为这可能是一个严重的误解).
我无法在具有许多不同显示器配置和窗口位置的开发盒上重现异常.
什么屏幕坐标实际上可能导致溢出?或者这个块有没有其他方式可能导致溢出?
我认为你问题的关键在于“多显示器”。多个显示器可能会导致负坐标
来自 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 没有符号扩展(你可以用调试器尝试一下吗?你将需要一个负 y 坐标)。因此它变成:
0x0000 0000 FFFF FFFF
Run Code Online (Sandbox Code Playgroud)
或者 4294967295 这是一个太大的数字,无法将其转换为 Int32。
一般来说,任何负 Y 坐标都将采用以下形式
000.(32 Zeros)..001 ...(other 31 digits) .. 0
Run Code Online (Sandbox Code Playgroud)
并且应该提出问题(您是否尝试过将显示器一个一个地堆叠起来?)
归档时间: |
|
查看次数: |
1928 次 |
最近记录: |