I'm trying to write what I hope is a simple application tracker. That is, whenever a new application starts, or a running application becomes current, I want to note the event and start timing it's time "on top".
I have code that lists all the current applications that are running, and some code that tells me the top window (always my test console app, naturally).
What I think I'm missing is the Windows event stream to monitor or something.
I'm using .NET (C# preferred).
Any hints, tips or cheats available?
Thanks - Jonathan
-
I'm not sure if there's a way to hook a Windows event, but simply polling System.Diagnostics.Process.GetProcesses() at regular intervals (say, 100ms) and looking for new/removed processes (comparing by process ID) should do the job. Also, Process.StartTime will give you the time at which the process began.
Caveat: This method may be require a higher amount of processing compare an event-based method (none of which I am aware). Processes that start and end between each poll will not be observed, but this ought to be quite rare indeed for a reasonably high polling frequency (and perhaps you do not even care about these processes anyway). Saying this, these are minor detractions, and I would recommend you at least test this solution as it is fairly simple.
JMD : Although, if that poll happens to catch a particular app, say MSN Messenger, at two consecutive polls it would look like the user was (assuming this is for tracking employees) spending their time in MSN, when it may be that they simply happened to get polled twice at the wrong time.Noldorin : I think you misunderstand. The polling would occur at a relatively high frequency (> 1Hz). I wouldn't imagine that this would be too processor intensive. Admittedly, it may not be ideal, but it's a valid solution unless there are certain other requirements the OP has not mentioned.JMD : Yes, I did misunderstand your intention. Although, I think I did that because I instinctively ruled out polling at a frequency high enough to generate those pseudo-immediate results as not playing "nice".Noldorin : Yeah, I suppose it was slightly ambiguous. The answer has been edited to clarify that. -
I think the best way to do this would be using windows "hooks" (i.e. SetWindowsHookEx). These allow you to hook in to windows core functionality, specifically there is one called
WH_CALLWNDPROC
which calls a user function any time any window in the system receives a message.You could use this to globally listen for messages that bring a window to the foreground, and/or messages for user interaction (mouse, keyboard).
However, this is a raw Windows API function primarily meant for use in a C/C++ windows DLL. You can implement it in C#, but that is a can of worms you may not want to open. But opening it would probably be the best way of doing what you're asking.
-
This is increasingly a SO problem, the down-voted answer is the correct one. SetWindowsHookEx() is indeed required to be able to catch the WM_ACTIVATE message that the activated window gets. But that requires a WH_CALLWNDPROC or WH_SHELL hook, hooks that cannot be implemented in C#. Catching those requires injecting a DLL in every process, a managed assembly cannot be injected into another process. The CLR cannot be initialized.
+1 for Noldorin to get him back to 0, that's all I can do. The OP needs to write his code in unmanaged C/C++, creating a DLL and use a standard IPC mechanism like pipes or sockets to notify the host app. Or poll, much easier.
Noldorin : Yeah, DLL injection would be required to create an notification/event system (as far as I know). Best to avoid DLL injection where you can (it's rather nasty stuff), but if you really want to go that way check out the Console project on Sourceforge - http://sourceforge.net/projects/console/).Noldorin : (contd.) The Console project uses shared memory to communicate between the host process and the child console processes, which is efficient but not too nice to deal with either. @nobugz, thanks for the support btw.SoapBox : You can actually do as I suggested with managed code and the "EasyHook" library: http://www.codeplex.com/easyhook -
I once wrote a small app that did this to keep track of my own work habits. What I did was call
GetForegroundWindow()
periodically (every 5 seconds or something) and noted the application that is running. You can get a lot of information from the window handle, not just the title but all the way down to the actual process that created it. -
This is what I did using Java's JNA:
final HWND child = User32Ext.INSTANCE.GetForegroundWindow(); final int length = User32.INSTANCE.GetWindowTextLength(child) * 2; final byte[] buffer = new byte[length]; User32.INSTANCE.GetWindowText(child, buffer, length); title = new String(buffer, Charset.forName("UTF-16LE"));
where User32Ext is an extension I did because User32 (in JNA's distribution) doesn't have the interface for:
LRESULT callback(int nCode, WPARAM wParam, LPARAM lParam);
I'm periodically polling the active window, since I couldn't use the HCBT_SETFOCUS hook as mentioned in http://msdn.microsoft.com/en-us/library/ms997537.aspx, and I'll be very interested if someone comes up with the solution.
-
Hulk's idea sounds great.
Hulk, please post a link to the documentation or name the specific function. Googling for "win32 creation notification" gives unrelated links (about windows non-genuine notification).
Thank you.
0 comments:
Post a Comment