2014-09-04 2 views
1

У меня есть программа на C#, которая опросит об изменениях в коллекции EnumDesktopWindows. Если пользователь закрывает или открывает окно, процедура опроса обнаруживает это и отправляет обновленный список доступных окон в другой проект .net windows. Однако мне не нравится метод опроса. Я бы предпочел, чтобы любое изменение в EnumDesktopWindows запускало событие, так что ответ на изменение выполняется асинхронно.делегат pinvoke async при изменении коллекций окон на рабочем столе?

Лучшее, что я мог придумать, - это то, что вы видите ниже. Я попробовал предложение Скотта C. выполнить из окна консоли, но это не сработало.

В настоящее время то, что вы видите ниже, захватывает CreateWnd = 3 при загрузке Windows Form (это приложение для форм Windows). Однако он не захватывает глобально: он только захватывает события окна из текущего исполняемого исполняемого файла. Если у кого-то есть глаза орла и вы можете определить, как сделать этот захват кода глобально, я дам вам ответ.

Чтобы проверить это; сначала создать проект с Windows Forms приложений и добавьте следующий код в Form1.cs (вам нужно будет добавить ListBox в форму с именем lstLog правильно скомпилировать)

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

После выполнения вышеуказанных шагов выполните проект форм окон. Вы должны увидеть, что он определяет одно созданное окно, а именно тот, который вы только что выполнили.

+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, ручка модуля * очень важна. –

Смежные вопросы