У нас есть приложение 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. На самом деле эта подпрограмма неправильна в том смысле, что она неправильно обрабатывает отрицательные координаты — это выглядит как неправильный перенос оконных макросов GET_X_LPARAM и GET_Y_PARAM на C#.
Однако я не понимаю, как можно построить LParam для NCHITTEST/NCMOUSEHOVER, который на практике переполнил бы диапазон Int32. (Я думаю, что младшие 16 бит состоят из со знаком 16-битной координаты X, а остальные биты состоят из 16-битной координаты Y с расширенным знаком. Пожалуйста, поправьте меня, если ошибаюсь, так как это может быть критическое недоразумение).
Я не могу воспроизвести исключение на моем dev-box с множеством различных конфигураций монитора и положения окон.
Какие экранные координаты могут здесь привести к переполнению? Или есть ли другой способ, которым этот блок может привести к переполнению?