我有一个显示的表单,不是通过 ShowDialog,而是通过将其可见属性设置为 true。这样它的行为就像一个下拉菜单。
该表单安装了一个鼠标钩子,使用SetWindowsHookEx(WH_MOUSE, ...)
.
我检测是否在下拉列表之外单击了鼠标,如果是,则在我的HookProc
方法中返回 1 并关闭下拉列表。
奇怪的是,如果我在下拉列表之外单击文本框,则在我的下拉列表关闭后,文本框仍会收到鼠标单击,即使它已由我的HookProc
方法处理。
它变得陌生......如果我点击一个标签或按钮,他们没有收到鼠标点击,正如预期的那样,在下拉关闭后!
知道发生了什么吗?
预计到达时间 2:
您可以忽略下面我的所有代码,因为在进一步调查中,我发现这种行为至少表现在一个实现下拉类型表单的框架控件中。
要复制,创建一个表单并添加一个属性网格、按钮、文本框和标签。将属性网格的选定对象设置为字体。
运行表单并选择字体名称。出现一个下拉列表。现在单击表单的文本框。触发文本框单击事件。但是,按钮或标签不会发生同样的情况。
这是怎么回事?
预计到达时间 1:
下面是How to set a Windows hook in Visual C# .NET中的一些基本代码,以演示发生了什么。我使用转换器将代码转换回 C#,但希望没问题。我不确定,但您可能需要替换Console.WriteLine
为Debug.WriteLine
.
创建两个表格,Form1
和DropDown
。
(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);
}
}
}