2009-11-15 3 views
0

ShowDialog가 아니라 표시 특성을 true로 설정하여 표시되는 양식이 있습니다. 이것은 드롭 다운처럼 동작합니다..NET 및 Windows Forms에서 마우스 후크로 이상한 동작이 발생했습니다.

이 양식은 SetWindowsHookEx(WH_MOUSE, ...)을 사용하여 마우스 훅을 설치합니다.

마우스를 드롭 다운 바깥에서 클릭하면 내 HookProc 메서드에서 1을 반환하고 드롭 다운을 닫습니다.

이상한 점은 내 드롭 다운을 텍스트 상자로 클릭하면 내 HookProc 메서드로 처리되었지만 드롭 다운이 닫힌 후에도 텍스트 상자에 마우스 클릭이 계속 수신된다는 것입니다.

낯선 사람이 ... 레이블이나 단추를 클릭하면 드롭 다운이 끝난 후 예상대로 마우스 클릭을받지 못합니다!

어떤 일이 벌어지고 있는지 알 수 있습니까?

ETA 2 : 추가 조사에, 나는이 행동 유형 양식 드롭 다운을 구현하는 적어도 하나의 프레임 워크 제어에 전시되어 있음을 발견했습니다 때문에

당신은 내 모든 코드 아래 무시할 수 있습니다.

복제하려면 양식을 만들고 속성 표, 단추, 텍스트 상자 및 레이블을 추가하십시오. 속성 격자의 선택된 객체를 글꼴로 설정하십시오.

폼을 실행하고 글꼴 이름을 선택하십시오. 드롭 다운 목록이 나타납니다. 이제 양식의 텍스트 상자를 클릭하십시오. 텍스트 상자 클릭 이벤트가 발생합니다. 그러나 단추 또는 레이블도 이와 동일하지 않습니다.

무슨 일 이니?

ETA 1 : 여기

는 무슨 일이 일어나고 있는지 설명하기 How to set a Windows hook in Visual C# .NET 일부 빈약 한 코드입니다. 나는 변환기를 사용하여 코드를 다시 C#으로 변환했지만 잘하면 좋다. 확실하지 않지만 Console.WriteLineDebug.WriteLine으로 바꿔야 할 수 있습니다.

Form1DropDown의 두 가지 양식을 만듭니다.

(1) Form1에서 VB.NET

하는 버튼, 레이블 및 텍스트 상자, 다음과 같은 코드를 추가합니다.

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에서 다음 코드를 삽입하십시오. 배치, 다음 코드 드롭 다운에

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"); 
    } 
} 

:

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); 
     } 
    } 
} 
을에 Button, 레이블 및 텍스트 상자, 다음과 같은 코드를 추가를 Form1에

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#을

답변

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를 구현하는 것이 좋습니다.

+0

안녕하세요. 나는 IMessageFilter에 대해 들어 본 적이 없었는데, 그것은 나를위한 후킹을 돌보는 것처럼 보입니다. 기존 코드를 IMessageFilter로 대체하려고했지만 문제가 발생했습니다. 내 드롭 다운과 관련이없는 이벤트를 무시하기 위해 이벤트를 활성화 및 비활성화하는 드롭 다운에서 후크 앤 언 후크를 사용했습니다. 그러나 PreFilterMessage 전에 Deactivate가 발생합니다. PreFilterMessage를 사용하여 클릭 한 컨트롤과 해당 컨트롤이 속한 폼을 결정할 수 있습니다. 드롭 다운 후 해당 양식이 작성되었는지 어떻게 확인할 수 있습니까? – Jules

+0

나는 그것을 할 방법이 있을지도 모른다. 먼저 드롭 다운을 표시하기 직전에 열려있는 모든 폼의 목록을 가져옵니다. PreFiltureMessage에서 클릭 된 컨트롤이 저장된 컬렉션의 폼에 속한 경우 클릭을 처리하고 드롭 다운을 닫습니다. 그렇지 않으면 무시합니다. – Jules

+0

위의 내 drivel 무시합니다. IMessageFilter와 반대로 내 후크를 고수하기로 결정했습니다. 마우스 업 이벤트 필터를 추가하면 내 문제가 해결되었습니다. 다시 한번 감사드립니다. – Jules

관련 문제