0

I'm trying to register some object-level WinEvents hooks on new created processes but as the official documentation says :

For out-of-context events, the event is delivered on the same thread that called SetWinEventHook.

My problem is that I'm calling SetWinEventHook from a thread other than the main one which will result in not receiving any callback in the main thread. I have been looking for a way to trigger (from another thread) SetWinEventHook method call in the main thread. Knowing that it's a Console Application, Invoke and BeginInvoke solution did not work. I also tried Events and EventHandler.

I hope my problem won't be misunderstood. Here's the code and the output.

// storing the callback as a field to make sure the garbage collector do not move it
// while being used in managed code, this is easier than using GCHandle 
static WinEventDelegate procDelegate = new Native.WinEventDelegate(WinEventProc);

static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, 
    int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        Console.WriteLine("callback, tid = " + Thread.CurrentThread.ManagedThreadId);
    }

ManagementEventWatcher processStartEvent = new ManagementEventWatcher("SELECT * FROM Win32_ProcessStartTrace");

void processStartEvent_EventArrived(object sender, EventArrivedEventArgs e)
{
    string processName = e.NewEvent.Properties["ProcessName"].Value.ToString();
    int processID = Convert.ToInt32(e.NewEvent.Properties["ProcessID"].Value);
    Process process = Process.GetProcessById(processID);
    IntPtr hook = SetWinEventHook(eventId, eventId, IntPtr.Zero, procDelegate, (uint)process.Id, 
            GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero),
            Native.WINEVENT_OUTOFCONTEXT | Native.WINEVENT_SKIPOWNPROCESS | Native.WINEVENT_SKIPOWNTHREAD); 
    if (hook == IntPtr.Zero)  Console.WriteLine("Hooking failed");
    Console.WriteLine("event, tid = " + Thread.CurrentThread.ManagedThreadId);
}

void watchProcess()
{
    processStartEvent.EventArrived += new EventArrivedEventHandler(processStartEvent_EventArrived);
    processStartEvent.Start();
}

static void Main(string[] args)
{
        Console.WriteLine("main, tid = " + Thread.CurrentThread.ManagedThreadId);
        watchProcess(); 
        MessageBox.Show("Message loop");          
}

Output :

main, tid = 1
event, tid = 4 // repeated as many times as new processes created

JohnTube
  • 1,782
  • 27
  • 46
  • Would it help: http://stackoverflow.com/a/10943848? – AlexD Feb 27 '14 at 22:34
  • No, this isn't ever going to work. You really do need a way to get from the thread on which the event handler runs to another thread that makes the SetWinEventHook *and* pumps a message loop so that the callback can be made. That requires Application.Run(), not MessageBox.Show(). You trivially get that from a Winforms or WPF app. Or you can use [this class](http://stackoverflow.com/a/21684059/17034) if a console mode app is a hard requirement. – Hans Passant Feb 27 '14 at 23:04
  • @HansPassant is there another way to detect new process/window (I need the window so I was testing if the process.MAIN_WINDOW_HANDLE != IntPtr.Zero) creation that does not create/run in a separate thread. I know EVENT_OBJECT_CREATE is not an option. – JohnTube Feb 28 '14 at 08:48
  • @HansPassant what do you recommend if this will be used in a background process ? – JohnTube Feb 28 '14 at 09:38
  • @John There certainly is another way. But asking in comments is the wrong way to go. You need to ask a question. – David Heffernan Mar 01 '14 at 11:51

1 Answers1

0

Have had the same problem. Use Invoke method from any form:

            this.Invoke(new MethodInvoker(() => StartEventListener()));

Regards

Martin.Martinsson
  • 1,894
  • 21
  • 25