0

我有一个显示的表单,不是通过 ShowDialog,而是通过将其可见属性设置为 true。这样它的行为就像一个下拉菜单。

该表单安装了一个鼠标钩子,使用SetWindowsHookEx(WH_MOUSE, ...).

我检测是否在下拉列表之外单击了鼠标,如果是,则在我的HookProc方法中返回 1 并关闭下拉列表。

奇怪的是,如果我在下拉列表之外单击文本框,则在我的下拉列表关闭后,文本框仍会收到鼠标单击,即使它已由我的HookProc方法处理。

它变得陌生......如果我点击一个标签或按钮,他们没有收到鼠标点击,正如预期的那样,在下拉关闭后!

知道发生了什么吗?

预计到达时间 2:

您可以忽略下面我的所有代码,因为在进一步调查中,我发现这种行为至少表现在一个实现下拉类型表单的框架控件中。

要复制,创建一个表单并添加一个属性网格、按钮、文本框和标签。将属性网格的选定对象设置为字体。

运行表单并选择字体名称。出现一个下拉列表。现在单击表单的文本框。触发文本框单击事件。但是,按钮或标签不会发生同样的情况。

这是怎么回事?

预计到达时间 1:

下面是How to set a Windows hook in Visual C# .NET中的一些基本代码,以演示发生了什么。我使用转换器将代码转换回 C#,但希望没问题。我不确定,但您可能需要替换Console.WriteLineDebug.WriteLine.

创建两个表格,Form1DropDown

(1) VB.NET

Form1中,添加一个 Button、Label 和 TextBox,以及以下代码。

Imports System.Runtime.InteropServices

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Console.WriteLine("Button1_Click")
        Dim dd As New DropDown
        dd.Visible = True
        Do While dd.Visible
            Application.DoEvents()
            MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, &HFF, 4)
        Loop
    End Sub

    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> _
    Public Shared Function MsgWaitForMultipleObjectsEx(ByVal nCount As Integer, ByVal pHandles As IntPtr, ByVal dwMilliseconds As Integer, ByVal dwWakeMask As Integer, ByVal dwFlags As Integer) As Integer
    End Function

    Private Sub Label1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Label1.Click
        Console.WriteLine("Label1_Click")
    End Sub

    Private Sub TextBox1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Click
        Console.WriteLine("TextBox1_Click")
    End Sub

End Class

在 DropDown 中,放置以下代码。

Imports System.Runtime.InteropServices

Public Class DropDown

    Public Delegate Function HookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

    Private hHook As Integer = 0
    Public Const WH_MOUSE As Integer = 7
    Private MouseHookProcedure As HookProc

    <StructLayout(LayoutKind.Sequential)> _
    Public Class POINT
        Public x As Integer
        Public y As Integer
    End Class

    <StructLayout(LayoutKind.Sequential)> _
    Public Class MouseHookStruct
        Public pt As POINT
        Public hwnd As Integer
        Public wHitTestCode As Integer
        Public dwExtraInfo As Integer
    End Class

    <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As HookProc, ByVal hInstance As IntPtr, ByVal threadId As Integer) As Integer
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
    End Function

    Protected Overrides Sub OnDeactivate(ByVal e As System.EventArgs)
        MyBase.OnDeactivate(e)
        UnhookWindowsHookEx(hHook)
        hHook = 0
    End Sub

    Public Sub New()
        InitializeComponent()
        MouseHookProcedure = New HookProc(AddressOf MouseHookProc)
        hHook = SetWindowsHookEx(WH_MOUSE, MouseHookProcedure, New IntPtr(0), AppDomain.GetCurrentThreadId())
    End Sub

    Public Function MouseHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
        Dim MyMouseHookStruct As MouseHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(MouseHookStruct)), MouseHookStruct)
        If nCode < 0 Then
            Return CallNextHookEx(hHook, nCode, wParam, lParam)
        Else
            Select Case CInt(wParam)
                Case &H21, &HA1, &HA4, &H204, &H207, &HA7, &H201
                    Me.Visible = False
                    Return 1
            End Select
            Return CallNextHookEx(hHook, nCode, wParam, lParam)
        End If
    End Function

End Class

(2) C#

在 Form1 中添加一个 Button、Label 和 TextBox,以及以下代码:

using System.Runtime.InteropServices;

public class Form1
{
    public Form1()
    {
        InitializeComponent();
        Button1.Click += Button1_Click;
        Label1.Click += Label1_Click;
        TextBox1.Click += TextBox1_Click;
    }

    private void Button1_Click(System.Object sender, System.EventArgs e)
    {
        Console.WriteLine("Button1_Click");
        DropDown dd = new DropDown();
        dd.Visible = true;
        while (dd.Visible) {
            Application.DoEvents();
            MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, 0xff, 4);
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags);

    private void Label1_Click(object sender, System.EventArgs e)
    {
        Console.WriteLine("Label1_Click");
    }

    private void TextBox1_Click(object sender, System.EventArgs e)
    {
        Console.WriteLine("TextBox1_Click");
    }
}

在 DropDown 中,放置以下代码:

using System.Runtime.InteropServices;

public class DropDown
{
    public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    private int hHook = 0;
    public const int WH_MOUSE = 7;
    private HookProc MouseHookProcedure;

    [StructLayout(LayoutKind.Sequential)]
    public class POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class MouseHookStruct
    {
        public POINT pt;
        public int hwnd;
        public int wHitTestCode;
        public int dwExtraInfo;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern bool UnhookWindowsHookEx(int idHook);

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

    protected override void OnDeactivate(System.EventArgs e)
    {
        base.OnDeactivate(e);
        UnhookWindowsHookEx(hHook);
        hHook = 0;
    }

    public DropDown()
    {
        InitializeComponent();
        MouseHookProcedure = new HookProc(MouseHookProc);
        hHook = SetWindowsHookEx(WH_MOUSE, MouseHookProcedure, new IntPtr(0), AppDomain.GetCurrentThreadId());
    }

    public int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
        if (nCode < 0) {
            return CallNextHookEx(hHook, nCode, wParam, lParam);
        }
        else {
            switch ((int)wParam) {
                case 0x21:
                case 0xa1:
                case 0xa4:
                case 0x204:
                case 0x207:
                case 0xa7:
                case 0x201:
                    this.Visible = false;
                    return 1;
            }
            return CallNextHookEx(hHook, nCode, wParam, lParam);
        }
    }
}
4

1 回答 1

1

此问题是由于您过滤了鼠标按下消息而不是鼠标按下消息而引起的。你可以像这样修复它:

  Select Case CInt(wParam)
    Case &HA1, &HA4, &HA7, &H201, &H204, &H207
      Me.Capture = True
    Case &HA2, &hA5, &HA8, &H202, &H205, &H208
      Me.Visible = False
  End Select

考虑改为实现 IMessageFilter。

于 2009-11-15T15:49:10.123 回答