2014-09-04 4 views
1

EnumDesktopWindows 컬렉션에 대한 변경 사항을 폴링하는 C# 프로그램이 있습니다. 사용자가 창을 닫거나 열면 폴링 루틴이이를 감지하고 업데이트 된 사용 가능한 창 목록을 다른 .Net 창 양식 프로젝트로 보냅니다. 그러나 폴링 방식이 마음에 들지 않습니다. 변경에 대한 응답이 비동기 적으로 완료되도록 EnumDesktopWindows를 변경하면 이벤트가 트리거되는 것을 선호합니다.데스크톱 윈도우 컬렉션이 변경되면 비동기 델파이로 위장됩니까?

제가 생각할 수있는 가장 좋은 점은 아래에서 보는 것입니다. 나는 Scott C의 제안을 콘솔 창에서 실행하려고 시도했지만 작동하지 않았다.

현재 Windows Form이로드 될 때 CreateWnd = 3이 캡처됩니다 (이것은 Windows 양식 응용 프로그램입니다). 그러나 전역 적으로 캡처하지는 않습니다. 현재 실행중인 실행 파일의 창 이벤트 만 캡처합니다. 독수리 눈을 가진 사람이라면이 코드를 전 세계적으로 캡처하는 방법을 알아낼 수 있습니다.

사용해보기; 먼저 윈도우 폼 응용 프로그램 프로젝트를 만들고 Form1.cs에 다음 코드를 추가

using System; 
using System.Windows.Forms; 

namespace Utilities 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      var gwh = new GlobalWindowHook(); 
      gwh.WindowCreated += onWindowCreated; 
     } 

     private void onWindowCreated() 
     { 
      lstLog.Items.Add("window creation event detected."); 
     } 
    } 
} 

이 GlobalWindowHook 이름이 같은 프로젝트에서 클래스 파일을 만듭니다 (당신은 제대로 컴파일 lstLog라는 이름의 양식에 목록 상자를 추가해야합니다) .cs 및 복사 붙여 넣기 다음 :

using System; 
using System.Runtime.InteropServices; 

namespace Utilities 
{ 
    internal class GlobalWindowHook 
    { 
     private delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam); 

     [DllImport("user32.dll")] 
     private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 

     public enum HookType 
     { 
      WH_JOURNALRECORD = 0, 
      WH_JOURNALPLAYBACK = 1, 
      WH_KEYBOARD = 2, 
      WH_GETMESSAGE = 3, 
      WH_CALLWNDPROC = 4, 
      WH_CBT = 5, 
      WH_SYSMSGFILTER = 6, 
      WH_MOUSE = 7, 
      WH_HARDWARE = 8, 
      WH_DEBUG = 9, 
      WH_SHELL = 10, 
      WH_FOREGROUNDIDLE = 11, 
      WH_CALLWNDPROCRET = 12, 
      WH_KEYBOARD_LL = 13, 
      WH_MOUSE_LL = 14 
     } 

     public enum HCBT 
     { 
      MoveSize = 0, 
      MinMax = 1, 
      QueueSync = 2, 
      CreateWnd = 3, 
      DestroyWnd = 4, 
      Activate = 5, 
      ClickSkipped = 6, 
      KeySkipped = 7, 
      SysCommand = 8, 
      SetFocus = 9 
     } 

     private IntPtr hhook = IntPtr.Zero; 

     public GlobalWindowHook() 
     { 
      hook(); 
     } 


     ~GlobalWindowHook() 
     { 
      unhook(); 
     } 


     public void hook() 
     { 
      IntPtr hInstance = LoadLibrary("User32"); 

      hhook = SetWindowsHookEx(HookType.WH_CBT, hookProc, hInstance, 0); 
     } 

     public void unhook() 
     { 
      UnhookWindowsHookEx(hhook); 
     } 

     public IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam) 
     { 
      if (code != (int) HCBT.CreateWnd && code != (int) HCBT.DestroyWnd) 
       return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); 

      //Do whatever with the created or destroyed window. 

      return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); 
     } 


     [DllImport("user32.dll")] 
     private static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadId); 

     [DllImport("user32.dll")] 
     private static extern bool UnhookWindowsHookEx(IntPtr hInstance); 

     [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] 
     private static extern IntPtr LoadLibrary(string fileName); 
    } 
} 

위의 단계를 수행 한 후 Windows 양식 프로젝트를 실행하십시오. 생성 된 하나의 창, 즉 방금 실행 한 창이 발견됩니다.

+0

에 정의 된 유형을 사용

내가 사용이 작업을 수행 할 수 있다고 생각'SetWindowsHookEx()'은'WH_CBT' 후크 타입. – cdhowie

+0

http://msdn.microsoft.com/en-us/library/windows/desktop/dd373640%28v=vs.85%29.aspx –

+0

방금 ​​질문을 수정하여 지금까지 최선의 시도로 업데이트했습니다 (감사합니다. 당신 스콧). 나는 아직도 대답을 추구하고있다. – sapbucket

답변

0

알림을 당기지 않고 밀어 넣기를 원한다면 너무 어렵지 않습니다. 당신이해야 할 일은 P/Invoke SetWindowsHookEx이고 WH_CBT 이벤트에 등록하십시오. 해당 메시지를 받으면 HCBT_CREATEWND or HCBT_DESTROYWND 코드로 메시지를 청취 할 수 있습니다. 그러면 새 창을 만들거나 파괴 할 때 알려줍니다. pinvoke.net

class HookingClass 
{ 
    private delegate IntPtr CBTProc(HCBT nCode, IntPtr wParam, IntPtr lParam); 

    private readonly CBTProc _callbackDelegate; 

    public HookingClass() 
    { 
     _callbackDelegate = CallbackFunction; 
    } 

    private IntPtr _hook; 

    private void CreateHook() 
    { 
     using (Process process = Process.GetCurrentProcess()) 
     using (ProcessModule module = process.MainModule) 
     { 
      IntPtr hModule = GetModuleHandle(module.ModuleName); 

      _hook = SetWindowsHookEx(HookType.WH_CBT, _callbackDelegate, hModule, 0); 
      if(_hook == IntPtr.Zero) 
       throw new Win32Exception(); //The default constructor automatically calls Marshal.GetLastError() 
     } 

    } 

    private void Unhook() 
    { 
     var success = UnhookWindowsHookEx(_hook); 
     if(!success) 
      throw new Win32Exception(); 
    } 

    private IntPtr CallbackFunction(HCBT code, IntPtr wParam, IntPtr lParam) 
    { 
     if (code != HCBT.CreateWnd && code != HCBT.DestroyWnd) 
     {   
      return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); 
     } 

     //Do whatever with the created or destroyed window. 

     return CallNextHookEx(IntPtr.Zero, (int)code, wParam, lParam); 
    } 
} 
+0

@sapbucket 문제는'hModule'이'_callbackDelegate' 함수를 포함하는 어셈블리를 가리켜 야한다는 것입니다. 'user32.dll'는 함수를 포함하고 있지 않기 때문에 그것이 작동하지 않습니다. 당신이 단위 테스트 내부에서 그것을 실행하려고 시도하기 전에 테스트 콘솔 응용 프로그램에서 그것을 시도하십시오. 또한 이벤트가 발생하기 전에 유닛 테스트 프로그램이 종료되고 종료됩니다. –

+0

Hans Passat은 hModule이 저수준 라이브러리에서 사용되지 않는다고 설명하고 유효한 모든 것을 전달하는 것이 트릭을 수행해야한다고 설명합니다. 그래서 내가 LoadLibrary와 붙어있다. 한스가 잘못 됐나요? 어느 쪽이든, 나는 성공하지 못한다. – sapbucket

+1

낮은 수준의 후크에만 해당됩니다. CBT 훅에 대한 하위 레벨은 없지만 모듈 핸들은 매우 중요합니다. –