Question

When i receive a WM_MOUSEMOVE message in WndProc the lParam contains the cursor coordinates. I translate this parameter into a Point structure:

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

My problem is that when the x-coordinate of the cursor becomes negative the LOWORD function fails with an overflow exception. According to MSDN you should use the GET_X_LPARAM and GET_Y_LPARAM macros not the HI/LO WORD macros. But no such functions are available in vb.net.

So what to do?

Was it helpful?

Solution

Overflow checking really gets in the way here. The best way to tackle this problem is by declaring a union. A union is a structure that has overlapping values. Which elegantly allows you to overlay an IntPtr on top of structure members that have type Short. The conversion is extremely fast and cannot throw an overflow exception.

Add a new Module to your project, name it NativeMethods and make it look like this:

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

I threw in a conversion operator for Point since that's the one you really want. Some test code that exercises this:

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

It now becomes a very simple one-liner in, say, an override of a form's WndProc() method:

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top