WM_MOUSEMOVE LParam переполнение младшего разряда

Когда я получаю сообщение WM_MOUSEMOVE в WndProc, lParam содержит координаты курсора. Я перевожу этот параметр в Point состав:

Friend Shared Function LParamToPoint(ByVal lParam As IntPtr) As Point
    Return New Point(LOWORD(lParam.ToInt32()), HIWORD(lParam.ToInt32()))
End Function

Friend Shared Function HIWORD(ByVal value As Integer) As Short
    Return CShort((value >> 16))
End Function

Friend Shared Function LOWORD(ByVal value As Integer) As Short
    Return CShort((value And UShort.MaxValue))
End Function

Моя проблема в том, что, когда координата x курсора становится отрицательной, функция LOWORD завершается с ошибкой переполнения. Согласно MSDN вы должны использовать макросы GET_X_LPARAM и GET_Y_LPARAM, а не макросы HI/LO WORD. Но в vb.net таких функций нет.

Так что делать?

1 ответ

Решение

Проверка переполнения действительно мешает здесь. Лучший способ решить эту проблему - объявить профсоюз. Объединение - это структура, которая имеет перекрывающиеся значения. Что элегантно позволяет накладывать IntPtr поверх элементов структуры, имеющих тип Short. Преобразование выполняется очень быстро и не может вызвать исключение переполнения.

Добавьте новый модуль в ваш проект, назовите его NativeMethods и сделайте так:

Imports System.Runtime.InteropServices
Imports System.Drawing

Module NativeMethods
    <StructLayout(LayoutKind.Explicit)> _
    Public Structure LParamMap
        Public Sub New(value As IntPtr)
            lparam = value
        End Sub

        Public Shared Widening Operator CType(value As LParamMap) As Point
            Return New Point(value.loword, value.hiword)
        End Operator

        <FieldOffset(0)> Public lparam As IntPtr
        <FieldOffset(0)> Public loword As Short
        <FieldOffset(2)> Public hiword As Short
    End Structure

End Module

Я добавил оператор преобразования для Point, потому что это тот, который вы действительно хотите. Некоторый тестовый код, который осуществляет это:

Imports System.Drawing
Imports System.Diagnostics

Module Module1
    Sub Main()
        Debug.Assert(BitConverter.IsLittleEndian)
        Dim test As New LParamMap(New IntPtr(-1))
        Debug.Assert(test.loword = -1)
        Debug.Assert(test.hiword = -1)
        Dim pos As Point = test
        Debug.Assert(pos = New Point(-1, -1))
    End Sub
 End Module

Теперь он становится очень простым однострочником, скажем, в переопределении метода WndProc() формы:

Protected Overrides Sub WndProc(ByRef m As Windows.Forms.Message)
    If m.Msg = &H200 Then
        Dim pos As Point = New LParamMap(m.LParam)
        '' etc...
    End If
    MyBase.WndProc(m)
End Sub
Другие вопросы по тегам