【问题标题】:VB.net Integer and Short conversion, GetMessagePos()VB.net Integer 和 Short 转换,GetMessagePos()
【发布时间】:2016-12-02 20:09:50
【问题描述】:

我正在尝试在 VB.net 中使用 DWORD WINAPI GetMessagePos(void) 函数。

该函数返回一个可以存储在 VB.net 整数变量中的 DWORD(32 位)。引用 MSDN 文档:

x坐标在返回值的低位;这 y坐标是高阶短(都代表有符号值 因为它们可以在具有多个 监视器)

如何使用 vb.net 检索 x 和 y 坐标?

我正在尝试

<System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling:=True)>
Private Shared Function GetMessagePos() As Integer
End Function
Sub test()
    Dim pos As Integer = GetMessagePos()

    Try
        Dim x As Short = CShort(pos And &HFFFF)
        Dim y As Short = CShort(pos >> 16)

        MessageBox.Show(x & ", " & y)
    Catch ex As Exception
        MessageBox.Show(ex.Message)
    End Try
End Sub

但我不确定这是否是正确的方法。我正在尝试做一些测试,例如

    Try
        Dim x As Short = -1
        Dim y As Short = 1

        Dim i As Int32 = (y << 16) Or x

        Dim x2 As Short = CShort(i And &HFFFF)
        Dim y2 As Short = CShort(i >> 16)

        MessageBox.Show(x & ", " & y)
    Catch ex As Exception
        MessageBox.Show(ex.Message)
    End Try

基本上我在 Short (Int16) 变量中编码 x 和 y 坐标,将它们放在 Int32 中,然后尝试解码。
但它似乎不起作用,因为它会导致溢出。

关于如何从GetMessagePos() WINAPI 解码 x-y 坐标的任何想法?

【问题讨论】:

    标签: vb.net winapi c#-to-vb.net twain


    【解决方案1】:

    提取这些值时必须小心,以确保它们在多显示器系统上正常工作。就您通常与GetMessagePos 函数一起使用的方式而言,执行此操作的“标准”方式是在Windows SDK 标头中定义的GET_X_LPARAMGET_Y_LPARAM 宏。在GetMessagePos documentation 中特别提到了这些,并且在所有文档中都应该有类似的引用来返回打包坐标的函数。还有一个警告不要使用经典的 LOWORDHIWORD 宏,因为它们将值视为无符号数量,正如您在问题中提到的那样。

    因此,任务本质上是将GET_X_LPARAMGET_Y_LPARAM 宏从C 转换为VB.NET。将它们翻译成 C# 相对简单,因为您可以利用 unchecked 关键字:

    int GetXValue(UInt32 lParam)
    {
        return unchecked((short)(long)lParam);
    }
    
    int GetYValue(UInt32 lParam)
    {
        return unchecked((short)((long)lParam >> 16));
    }
    

    但是 VB.NET 没有 C# 的 unchecked 关键字的等效项,因此您必须找到其他方法来避免溢出。就我个人而言,我用 C# 编写了这种互操作代码并将其保存在一个库中,这样我就可以做我认为最易读的事情。

    如果您更喜欢使用 VB.NET,有几种方法可以做到。最简单的概念是将值作为UInt32 操作以避免溢出。对于低位的 x 坐标,您需要显式屏蔽高位以避免溢出。对于 y 坐标,您需要平移和遮罩。然后你可以转换回Short:

    Public Function GetXValue(lParam As UInt32) As Short
        Return CShort(lParam And &HFFFF)
    End Function
    
    Public Function GetYValue(lParam As UInt32) As Short
        Return CShort((lParam >> 16) And &HFFFF)
    End Function
    

    另一种选择更聪明,也许聪明,但可能更有效。它涉及声明 C 样式联合的等价物,在 VB.NET 术语中,它只是一个字段重叠的结构:

    <StructLayout(LayoutKind.Explicit)> _
    Public Structure CoordUnion
    
        <FieldOffset(0)> Public LParam As UInt32
        <FieldOffset(0)> Public XCoord As Short
        <FieldOffset(2)> Public YCoord As Short
    
        Public Sub New(lParam As UInt32)
            LParam = lParam
        End Sub
    
    End Structure
    

    那么你可以这样使用它:

    Dim temp As CoordUnion = New CoordUnion(GetMessagePos())
    Dim pt As Point = New Point(temp.XCoord, temp.YCoord)
    ' ...
    

    另外请注意,我已经隐式更改了 GetMessagePos 的 P/Invoke 签名,以便它返回 UInt32

    <System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling:=True)>
    Private Shared Function GetMessagePos() As UInt32
    End Function
    

    在所有这些帮助程序/转换函数中,您可以像使用 UInt32 类型一样轻松地使用 IntPtr 类型。实际上,这就是您通常的编写方式,因为您通常会将这些指针坐标打包到一个 lParam 值中,作为窗口消息的一部分(例如,当覆盖 a 的 WndProc 方法时控制)。

    【讨论】:

    • 我刚刚使用在线 VB.NET 编译器对此进行了测试,它确实有效。从 65537 作为输入开始,我得到 (-1, 1) 的坐标,如预期的那样。您在构建i 的问题中的测试代码仍会产生算术溢出,所以我只是在脑海中构建了它。 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多