【问题标题】:How to detect a mouseclick event while MouseHook is set?如何在设置 MouseHook 时检测 mouseclick 事件?
【发布时间】:2018-11-24 22:02:25
【问题描述】:

我正在创建一个工具,它可以抓取屏幕上任何内容的颜色,无论是在表单内部还是在表单外部。用户将使用他们的鼠标光标通过单击来抓取屏幕上的任何像素。问题在于,根据他们单击的内容,它可能会单击按钮或选择并突出显示图标。为了解决这个问题,我使用了 SetWindowsHookEx() 方法来全局禁用 mouseclick 事件。这行得通。但现在的问题是我的应用程序无法检测到点击事件已经发生。我曾尝试在不同的位置和不同的时刻解开鼠标,但它要么使鼠标处于禁用状态,要么重新启用鼠标,但仍会继续执行上面提到的不需要的点击。

在用户可以获取像素颜色期间,计时器正在运行。很明显,我别无选择,只能在“像素抓取模式”下保持鼠标悬空,以便继续抓取像素颜色。我试过设置鼠标钩,然后马上解开它,但这也不起作用。这似乎是一个问题 22。当然,我有一个 ESC 和 ENTER 键的键盘事件作为停止计时器和解开鼠标的替代方法。用于“取消”操作的 ESC 键和用于获取像素颜色并停止计时器的 ENTER 键。但我希望鼠标点击执行相同的功能而不点击/激活其他任何东西。这是我的代码。

         Private Structure MSLLHOOKSTRUCT
                    Public pt As Point
                    Public mouseData As Int32
                    Public flags As Int32
                    Public time As Int32
                    Public extra As IntPtr
                End Structure

                Private mHook As IntPtr = IntPtr.Zero
                Private Const WH_MOUSE_LL As Int32 = &HE
                Private Const WM_RBUTTONDOWN As Int32 = &H204
                Private Const WM_LBUTTONDOWN As Int32 = &H201
                <MarshalAs(UnmanagedType.FunctionPtr)> Private mProc As MouseHookDelegate
                Private Declare Function SetWindowsHookExW Lib "user32.dll" (ByVal idHook As Int32, ByVal HookProc As MouseHookDelegate, ByVal hInstance As IntPtr, ByVal wParam As Int32) As IntPtr
                Private Declare Function UnhookWindowsHookEx Lib "user32.dll" (ByVal hook As IntPtr) As Boolean
                Private Declare Function CallNextHookEx Lib "user32.dll" (ByVal idHook As Int32, ByVal nCode As Int32, ByVal wParam As IntPtr, ByRef lParam As MSLLHOOKSTRUCT) As Int32
                Private Declare Function GetModuleHandleW Lib "kernel32.dll" (ByVal fakezero As IntPtr) As IntPtr
                Private Delegate Function MouseHookDelegate(ByVal nCode As Int32, ByVal wParam As IntPtr, ByRef lParam As MSLLHOOKSTRUCT) As Int32

                Public Function SetHookMouse() As Boolean
                    If mHook = IntPtr.Zero Then
                        mProc = New MouseHookDelegate(AddressOf MouseHookProc)
                        mHook = SetWindowsHookExW(WH_MOUSE_LL, mProc, GetModuleHandleW(IntPtr.Zero), 0)
                    End If
                    Return mHook <> IntPtr.Zero
                End Function

                Public Sub UnHookMouse()
                    If mHook = IntPtr.Zero Then Return
                    UnhookWindowsHookEx(mHook)
                    mHook = IntPtr.Zero
                End Sub

                Private Function MouseHookProc(ByVal nCode As Int32, ByVal wParam As IntPtr, ByRef lParam As MSLLHOOKSTRUCT) As Int32
                    'Label1.Text = "Message=" & wParam.ToInt32.ToString & "  X=" & lParam.pt.X.ToString & "  Y=" & lParam.pt.Y.ToString
                    If wParam.ToInt32 = WM_LBUTTONDOWN Then
                        Return 1
                    End If

                    Return CallNextHookEx(WH_MOUSE_LL, nCode, wParam, lParam)
                End Function



        Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick

Using bmp2 As New Bitmap(1, 1)
                Using g As Graphics = Graphics.FromImage(bmp2)
                    g.CopyFromScreen(Cursor.Position,
                                          New Point(0, 0), New Size(1, 1))
                End Using

                firsty = bmp2.GetPixel(0, 0).ToArgb
                ToolStripButton7.BackColor = Color.FromArgb(firsty)
                Form4.PictureBox1.BackColor = Color.FromArgb(firsty)
                Form4.PictureBox5.BackColor = Color.FromArgb(firsty)
                Form4.PictureBox4.BackColor = Color.FromArgb(firsty)
                Form4.PictureBox3.BackColor = Color.FromArgb(firsty)
                Panel3.BackColor = Color.FromArgb(firsty)
                Me.Invalidate()

                Dim CodeCodeInHex As String = bmp2.GetPixel(0, 0).ToArgb().ToString("X")
                CodeCodeInHex = CodeCodeInHex.Remove(0, 2)
                BGCOLOR.Text = "#" & CodeCodeInHex
                Form4.Label1.Text = "#" & CodeCodeInHex
                Label3.Text = "#" & CodeCodeInHex
            End Using


SetHookMouse()
UnHookMouse()
    Select Case MouseButtons
                    Case MouseButtons.Left

                        Cursor = Cursors.Default
                        Form4.Close()
                        SplitContainer3.SplitterDistance = SplitContainer3.Width - SPLITC3
                        Me.TopMost = True

                        ColorDialog1.Color = Color.FromArgb(firsty)
                        ToolStripTextBox1.Text = ColorDialog1.Color.A & ", " & ColorDialog1.Color.R & ", " & ColorDialog1.Color.G & ", " & ColorDialog1.Color.B
                        Panel2.Visible = False
                        If CSSToolbarToolStripMenuItem.Checked = True Then
                        Else
                            SplitContainer3.Panel2Collapsed = True
                        End If
                        Timer2.Stop()
                        Me.TopMost = False

                    Case MouseButtons.Right

                    Case MouseButtons.Middle

                    Case MouseButtons.Left + MouseButtons.Right

                End Select
        End Sub

另外,我也考虑过用不同的方法来解决这个问题。在此像素颜色抓取模式下,鼠标光标悬停在辅助窗体上的图片框上。表单跟随鼠标光标并保持在表单的死点。它悬停的图片框具有 Lime 的背景色,这是表单的透明度键,从而使其透明,允许用户看到表单后面的屏幕并抓住鼠标光标指向的像素颜色。

因此,知道了这一点,也许我可以以某种方式让它表现得好像它点击了图片框,而不是它背后的东西。但我也没有找到解决方案。我对您可能拥有的任何方法持开放态度。谢谢你。我曾考虑让图片框半透明,使其透明但不完全,因为它会阻止任何点击,但这会使它抓取的像素颜色不准确。我曾经做过一个应用程序,其中一个无边框表单在整个屏幕上最大化,它的不透明度调整为“屏幕亮度过滤器”并允许点击它,但这与我在这里尝试做的相反.所以我想知道。

【问题讨论】:

    标签: vb.net disabled-input mouse-hook


    【解决方案1】:

    我认为你把事情复杂化了......你已经有一个鼠标钩子来检测鼠标点击,所以不要在钩子回调中只在 WM_LBUTTONDOWN 上返回 1,而是在那里执行你的“获取颜色”逻辑。

    例如,创建一个获取特定位置颜色的函数:

    Private Function GetScreenPixelColor(ByVal Position As Point) As Color
        Using bmp As New Bitmap(1, 1)
            Using g As Graphics = Graphics.FromImage(bmp)
                 g.CopyFromScreen(Position, New Point(0, 0), New Size(1, 1))
            End Using
    
            Return bmp.GetPixel(0, 0)
        End Using
    End Function
    

    那么你需要在你的钩子中做的就是调用函数并对结果颜色做一些事情:

    If wParam.ToInt32() = WM_LBUTTONDOWN Then
        Dim PickedColor As Color = GetScreenPixelColor(Cursor.Position)
    
        'Do something with PickedColor...
    
        UnHookMouse()
        Return 1
    End If
    

    如果您要在多个场景中应用此逻辑,只需创建另一种为您完成所有工作的方法:

    Private Sub PickColor()
        Dim PickedColor As Color = GetScreenPixelColor(Cursor.Position)
    
        'Do something with PickedColor...
    End Sub
    

    那么在你的钩子里你需要做的就是:

    If wParam.ToInt32() = WM_LBUTTONDOWN Then
        PickColor()
        UnHookMouse()
        Return 1
    End If
    

    【讨论】:

    • 天哪。有效。谢谢!我没有意识到 mousehook 函数正在捕获鼠标事件。非常感谢!!!!
    • @BuddyRoach :这就是钩子的用途! ;) 很高兴我能帮上忙!
    • 钩子有 mouse_hover 吗?
    • @BuddyRoach:不幸的是没有。您必须为此构建自己的解决方案。您可以使用设置为 400 毫秒(默认鼠标悬停时间)的计时器,如果鼠标钩子检测到移动停止并重新启动计时器。如果计时器Ticks,停止它并做任何你想做的事情,因为鼠标已经悬停了 400 毫秒。
    • @BuddyRoach :查看我的InputHelper library(编译下载here)以获得易于使用的鼠标挂钩。我的项目 wiki 描述了它是如何工作的:github.com/Visual-Vincent/InputHelper/wiki/Low-level-mouse-hook
    【解决方案2】:

    我想我会根据 Visual Vincent 给出的答案添加我的解决方案。

    我创建了一个执行获取像素颜色方法的函数:

    Private Sub getcolor()
            Cursor = Cursors.Default
            Form4.Close()
            SplitContainer3.SplitterDistance = SplitContainer3.Width - SPLITC3
            Me.TopMost = True
    
            ColorDialog1.Color = Color.FromArgb(firsty)
            ToolStripTextBox1.Text = ColorDialog1.Color.A & ", " & ColorDialog1.Color.R & ", " & ColorDialog1.Color.G & ", " & ColorDialog1.Color.B
            Panel2.Visible = False
            If CSSToolbarToolStripMenuItem.Checked = True Then
    
            Else
                SplitContainer3.Panel2Collapsed = True
            End If
            Timer2.Stop()
            Me.TopMost = False
        End Sub
    

    然后在mousehook函数中调用函数:

    Private Function MouseHookProc(ByVal nCode As Int32, ByVal wParam As IntPtr, ByRef lParam As MSLLHOOKSTRUCT) As Int32
            'Label1.Text = "Message=" & wParam.ToInt32.ToString & "  X=" & lParam.pt.X.ToString & "  Y=" & lParam.pt.Y.ToString
            If wParam.ToInt32 = WM_LBUTTONDOWN Then
                getcolor()
                Return 1
            End If
            Return CallNextHookEx(WH_MOUSE_LL, nCode, wParam, lParam)
        End Function
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-04
      • 2014-11-29
      • 1970-01-01
      • 1970-01-01
      • 2017-05-23
      • 2023-03-15
      相关资源
      最近更新 更多