title: pinvoke async delegate when desktop windows collection changes?
body: <p>I have a C# program that polls for changes to EnumDesktopWindows collection. If a user closes or opens a window the polling routine detects this and sends an updated list of available windows to another .Net windows forms project. However, I do not like the polling method. I would prefer that any change to the EnumDesktopWindows triggers an event so that responding to the change is done asynchronously. </p>
  
  <p>The best I could come up with is what you see below. I tried out Scott C.'s suggestion to execute from a console window, but it did not work.</p>
  
  <p>Currently what you see below captures CreateWnd=3 when the Windows Form loads (this is a windows form application). However it does not capture globally: it only captures the window events from the currently running executable. If anyone has eagle eyes and can spot how to make this code capture globally I will award the answer.</p>
  
  <p>To try it out; first create a Windows Forms application project and add the following code to Form1.cs (you will need to add a ListBox to the Form named lstLog to compile correctly)</p>
  
  <pre><code>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.");
          }
      }
  }
  </code></pre>
  
  <p>Create a class file in the same project named GlobalWindowHook.cs and copy paste the following:</p>
  
  <pre><code>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 &amp;&amp; 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);
      }
  }
  </code></pre>
  
  <p>After performing the above steps execute the windows forms project. You should see that it detects one window being created, namely the one you just executed.</p>