2014-07-08 2 views
1

프로덕션 환경에서 사용중인 KeyboardHook이 있습니다. 훌륭하게 작동합니다. 어제 저는 HOOKPROC 델리게이트와 일치하는 가상 메서드를 가진 추상 클래스를 만들 때 가진 아이디어를 테스트하기 위해 Unit 테스트를 만들고 싶었습니다. 이런 식으로.Nunit이 SetWindowsHookEx 이벤트를받지 못함

public abstract class HookRuleBase : IDisposable 
{ 
    private readonly IntPtr hookId; 
    private static KeyboardProc thisDelegate; 
    public HookRuleBase() 
    { 
     thisDelegate = new KeyboardProc(ProcessKey); 
     IntPtr user = Kernel32.LoadLibrary("User32.dll"); 
     hookId = User32.SetWindowsHookEx(WH_KEYBOARD_LL, thisDelegate, user, 0); 
     Console.WriteLine(hookId.ToInt32().ToString("X8")); 
    } 

    public void Dispose() 
    { 
     var disposed = User32.UnhookWindowsHookEx(hookId); 
    } 

    /// <summary> 
    /// If your rule does not need to handle the key or the key's current state then call this base 
    /// method. 
    /// </summary> 
    public virtual int ProcessKey(int code, System.UIntPtr wParam, ref KeyboardHookStruct lParam) 
    { 
     return User32.CallNextHookEx(hookId, code, wParam, ref lParam); 
    } 

    private const int WH_KEYBOARD_LL = 13; 
} 

내가 그 일을 할 때 내가 KeyboardHook이라는 클래스에있는 정적 메서드 중 일부를 대체 할 계획이 있습니다. 나는 그것이 전략 패턴을 따른다고 생각한다. 나는 생각한다. .. Anywho. 그래서 NUnit 프로젝트를 만들었습니다. 내 HookRuleBase를 확장하는 매우 간단한 클래스를 만들었습니다.

private class HookRuleTest : HookRuleBase 
    { 
     public override int ProcessKey(int code, UIntPtr wParam, ref KeyboardHookStruct lParam) 
     { 
      Console.WriteLine("ProcessKey:code:{0}, wParam:{1:X8}, {2}", code, wParam.ToUInt32(), lParam.ToString()); 
      return base.ProcessKey(code, wParam, ref lParam); 
     } 
    } 

테스트에서 몇 가지 시도를했습니다. 먼저 콘솔 삐 (오디오 퀴즈)를 3 초 동안했는데 아무 것도 나타나지 않습니다. 그래서 나는 내 가상 키보드 클래스를 테스트하기 위해 몇 가지 코드를 훔쳤다. 그것의 아무것도 다른 스레드에서 시작되는 Winforms 양식 이상. 그래서 내가로드 이벤트에 넣어 후크 규칙을 말하고, 폐회 내가 그것의 처분. Ran은 내 테스트에서 hello world를 입력하고 콘솔에는 아무것도 입력하지 않았습니다./최후의 수단으로 새로운 winforms 프로젝트를 만들었습니다. 로드 중 및 닫는 이벤트에 대한 코드를 복사하고 프로젝트를 콘솔 프로젝트로 변경 한 다음 코드를 실행했습니다. 타이핑을 시작했고 콘솔 창에 출력하기 시작했습니다. 그래서 이것은 나를 미치게합니다. 나는 그것이 왜 그렇게하고 있는지 전혀 모른다. 이 코드는 작동하지만 Nunit에는 없습니다. 누군가가 왜 제대로 작동하지 않는지에 관해 올바른 방향으로 안내 할 수 있습니까? 후크가 어떤 이벤트를받는 것처럼 동작하지만 SetWindowsHookEx는 매번 유효한 포인터를 반환합니다. 그래서 나는 몰라. 도와주세요.

답변

3

흔히 낮은 수준의 후크로 만든 실수를 저지르고 있습니다. Windows는 임의로 ProcessKey() 메서드에 대한 콜백을 할 수 없습니다. 스레드가 잘 정의 된 상태 여야 Windows는 유휴 상태임을 확신하고 통화가 안전 할 수 있으며 재입국 문제를 일으키지 않습니다.

이 조건은 "메시지 루프 펌프"로 잘 알려져 있습니다. 메시지 루프는 .NET 프로그램의 Application.Run()에 의해 시작됩니다. 그리고 그것을 시작하는 스레드는 다른 방법으로는 사용할 수 없으며 Windows가 다음 메시지를 전달하기를 기다리면서 루프를 다시 입력해야합니다. 다음으로 콜백을 할 수 있습니다.

NUnit에서 메시지 루프를 얻지 못하면 테스트를 실행하여 스레드를 차지합니다. "다른 스레드에서 시작된 Winforms 양식"에서 가져 오지는 않습니다. 잘못된 스레드입니다. 일반 Winforms 프로젝트에서 가져온 UI 스레드는 항상 메시지 루프를 보내고 스레드는 메시지를 기다리는 시간의 99.9 %를 소비합니다.

그래서 본 것은 완전히 설계된 것입니다. 펌프가있는 스레드에 대한 샘플 코드는 this answer에 있습니다. Initialize() 메서드에 대한 재정의로 HookRuleTest 개체를 만듭니다.

+0

언제나 귀하의 답변은 환상적입니다. 키보드 훅에 대한 다른 게시물을 발견하고 내 기본 호출에서 정의한 변수 유형을 일부 변경했습니다. 고맙습니다. –