如何在设置MouseHook时检测鼠标点击事件?

时间:2018-06-15 04:31:26

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

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

在用户可以抓取像素颜色的时间内,计时器正在运行。很显然,我会别无选择,只能在处于"像素抓取模式的情况下保持鼠标脱钩状态。为了贯彻抓住像素颜色。我已经尝试过设置mousehook然后在之后解除它,但这也不起作用。这似乎是一个问题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,它是表单的透明度键,从而使其透明,允许用户看到表单后面的屏幕并抓住鼠标光标指向的像素颜色。

因此,知道这一点,或许我可以以某种方式使其表现得好像它点击了图片框而不是它背后的东西。但我还没有找到解决方案。我对你可能拥有的任何方法持开放态度。谢谢。我已经考虑过将图片盒设置为半透明,它是透明但不完全的地方,因为它可以防止任何点击,但这会使得它抓取的像素颜色不准确。我曾经做过一个应用程序,在整个屏幕上最大化无边框形式,并调整其不透明度以制作屏幕亮度滤镜&#34;并允许它被点击,但这与我在这里尝试做的相反。所以我想知道。

2 个答案:

答案 0 :(得分:1)

我认为你过于复杂了......你已经有了一个鼠标钩子来检测鼠标点击,所以不要只在钩子回调中的WM_LBUTTONDOWN上返回1,而是做你的&#34;得到颜色&#34 ; -logic在那里。

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

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

答案 1 :(得分:0)

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

我创建了一个执行get pixel color方法的函数:

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