Необъяснимое приведение OverflowException IntPtr, представляющее координату экрана, к Int32

У нас есть приложение 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 с множеством различных конфигураций монитора и положения окон.

Какие экранные координаты могут здесь привести к переполнению? Или есть ли другой способ, которым этот блок может привести к переполнению?


person Ani    schedule 28.02.2013    source источник
comment
Даже если вы не узнаете, почему происходит переполнение параметров, слепое преобразование IntPtr в Int32 явно является ошибкой в ​​библиотеке. Microsoft гарантирует только то, что задокументировано, и ничего не гарантирует в отношении недокументированных частей. Вы должны связаться с поставщиком для исправления ошибки   -  person Simon Mourier    schedule 28.02.2013
comment
@Simon: Это правда. Однако в прошлом этот поставщик не исправлял ошибки; поэтому я ожидаю, что это займет время. Между тем, я хочу определить, что точно вызывает исключение в рабочей среде, чтобы мы могли проинструктировать пользователей, как это обойти. В среднесрочной перспективе мы, вероятно, добавим некоторые обходные пути в код нашего приложения. Во-первых, мне нужна реплика!   -  person Ani    schedule 28.02.2013
comment
Хм... может быть, это как-то связано с размером виртуального рабочего стола? Единственное, что я могу придумать, это правильное переполнение...   -  person JerKimball    schedule 28.02.2013
comment
Просто идея: не могли бы вы установить глобальный фильтр IMessageFilter, чтобы избежать входа в эту функцию, или заменить его? Немного похоже на то, что описано здесь: stackoverflow.com/questions/12898680/   -  person Simon Mourier    schedule 28.02.2013
comment
@Simon: Спасибо за это. Я все еще на том этапе, когда хочу иметь возможность воспроизводить; но ваше предложение кажется достойным возможным обходным путем.   -  person Ani    schedule 28.02.2013


Ответы (1)


Я думаю, что ключ ваших проблем заключается в «нескольких мониторах». Несколько мониторов могут привести к отрицательным координатам

Из MSDN:

Важно! Не используйте макросы LOWORD или HIWORD для извлечения координат x и y положения курсора, поскольку эти макросы возвращают неверные результаты в системах с несколькими мониторами. Системы с несколькими мониторами могут иметь отрицательные координаты x и y, а LOWORD и HIWORD обрабатывают координаты как величины без знака.

Так как в CLR числа со знаком представлены с использованием нотации с двумя дополнениями, отрицательные числа представлены как «большие» числа без знака (те, старший бит которых равен «1»); например, -1 равно 1111....1. Следовательно, переполнение при приведении к (со знаком) 32-битному целому числу.

РЕДАКТИРОВАТЬ: (отказ от ответственности: у меня нет нескольких мониторов, поэтому применяются некоторые догадки) Короче говоря: я предполагаю, что вам нужно сгенерировать отрицательную координату y.

Предположим, что координаты (x: -1, y: -1)

В виде коротких чисел: x: 0xFFFF, y: 0xFFFF

Упаковано в 32-битное число: 0xFFFF FFFF

Вот здесь и приходится гадать: для IntPtr НЕТ расширения знака (вы можете попробовать его с отладчиком? Вам понадобится отрицательная координата). Поэтому становится:

0x0000 0000 FFFF FFFF

Или 4294967295. Слишком большое число, чтобы привести его к Int32.

В общем случае любая отрицательная координата Y будет иметь вид

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

и должен поднять проблему (вы пробовали ставить мониторы один над другим?)

person Lorenzo Dematté    schedule 28.02.2013
comment
Я знаю об ошибке с отрицательными координатами в процедуре поставщика (как я уже упоминал). Однако ни одно из моих тестов с отрицательными координатами (как x, так и y) не вызывает каких-либо исключений переполнения - они вызывают неправильную интерпретацию координат. Не могли бы вы дать мне какие-нибудь образцы отрицательных координат, которые могут вызвать ошибки? - person Ani; 28.02.2013
comment
Я думаю, что ошибка в библиотеке конвертируется в int перед применением маски/сдвига. Позвольте мне подумать, может ли быть какой-то крайний случай, когда координаты могут привести к чрезвычайно большим числам ... или если можно сделать вывод, что никакие значения не могут вызвать переполнение, и вам нужно искать в другом месте... - person Lorenzo Dematté; 28.02.2013
comment
Я добавил свое (образованное) предположение и привел пример - person Lorenzo Dematté; 28.02.2013
comment
Я попытался смоделировать координаты y (включая сообщение Windows, что конфигурация моего монитора включает вертикальное стекирование), и именно тогда я заметил, что происходит расширение знака. В любом случае, спасибо за ваши усилия до сих пор. - person Ani; 28.02.2013
comment
..и поэтому мое предположение было неверным :) Основываясь на вашей проверке (число расширено знаком), я бы сказал, что переполнение невозможно: Win32/BCL/Winforms правильно расширяют знак, затем библиотека поставщика обрезает его. Таким образом, не может появиться число вне 32-битного диапазона со знаком. Вы уверены, что это единственная точка, которая может вызвать переполнение? - person Lorenzo Dematté; 28.02.2013