How to detect inactive user - windows

How to detect inactive (idle) user in Windows application? I'd like to shutdown application when there hasn't been any input (keyboard, mouse) from user for certain period of time.

To track a user's idle time you could hook keyboard and mouse activity. Note, however, that installing a system-wide message hook is a very invasive thing to do and should be avoided if possible, since it will require your hook DLL to be loaded into all processes.
Another solution is to use the GetLastInputInfo API function (if your application is running on Win2000 (and up) machines).
GetLastInputInfo retrieves the time (in milliseconds) of the last input event (when the last detected user activity has been received, be it from keyboard or mouse).
Here's a simple example. The SecondsIdle function returns a number of second with no user activity (called in an OnTimer event of a TTimer component).
~~~~~~~~~~~~~~~~~~~~~~~~~
function SecondsIdle: DWord;
var
liInfo: TLastInputInfo;
begin
liInfo.cbSize := SizeOf(TLastInputInfo) ;
GetLastInputInfo(liInfo) ;
Result := (GetTickCount - liInfo.dwTime) DIV 1000;
end;
procedure TForm1.Timer1Timer(Sender: TObject) ;
begin
Caption := Format('System IDLE last %d seconds', [SecondsIdle]) ;
end;
http://delphi.about.com/od/adptips2004/a/bltip1104_4.htm

You might want to see the answer to this question: How to tell when Windows is inactive [1] it is basically same question the solution suggested is to use the GetLastInputInfo [2] API call.
This post explains some aspects as well: (The Code Project) How to check for user inactivity with and without platform invokes in C# [3]
[1] How to tell when Windows is inactive
[2] http://msdn.microsoft.com/en-us/library/ms646302%28VS.85%29.aspx
[3] http://www.codeproject.com/KB/cs/uim.aspx

Your application will get a WM_SYSCOMMAND message with SC_SCREENSAVE as a command id when the Screen Saver is about to kick in. Would that do? there's also the SC_MONITORPOWER command id when the monitor is about to blank (also a WM_SYSCOMMAND message).
Edit: looking at the comments, it appears that you don't care about whether the user is inative, but rather whether your application is inactive.
This is easy. If your app is minimized, then the user isn't interacting with it. If your app is not the foreground application, that's a good inicator as well.
You could also pay attention to messages in your pump to notice if there have been any user input messages to your app, In C++ adding code to the pump is trivial, in delphi you can use a WH_GETMESSAGE hook to monitor the pump hook into the message loop that TApplication implements. Or GetLastInputInfo

This SecondsIdle doens't work at all.
The way is to use a TTimer combined with a second variable that resets every time user inputs mouse or keyboard.

Related

win32 PostMessage WM_APPCOMMAND sends multiple messages instead of one

I'm writing a small accessibility app which simulates certain keyboard gestures, such as volume up\down.
The goal is to send a single command.
In practice, the volume goes all the way up to 100%, as if user pressed a button for couple seconds or as if the message was dispatched multiple times.
This behavior is the same with both PostMessage and SendMessage, in both C and C# (using PInvoke)
C:
PostMessage(0xffff, 0x0319, 0, 0xa0000)
C#:
PostMessage(new IntPtr(0xffff), WindowMessage.WM_APPCOMMAND, (void*)0, (void*)0xa0000);
The meaning of parameters: send to all windows, message, no source, volume up
Question: How do I issue a command which would result in Windows adjusting volume by the smallest increment?
Additionally, I attempted using WP_KEYUP and WP_KEYDOWN, without success
// dispatch to all apps, message, wparam: virtual key, lparam: repeat count = 1
User32.PostMessage(new IntPtr(0xffff), User32.WindowMessage.WM_KEYDOWN, new IntPtr(0xaf000), new IntPtr(1));
User32.PostMessage(new IntPtr(0xffff), User32.WindowMessage.WM_KEYUP, new IntPtr(0xaf000), new IntPtr(1));
The reason why the command is sent multiple times is, as pointed by Hans in the comment, I broadcasted it to all windows using 0xffff as first parameter. Every window handled it by increasing volume by a notch.
The solution to sending multiple messages is to send the message to either
The shell handle GetShellWindow()
The foreground window handle GetForegroundWindow()
Both handles adjusted the volume by one notch. GetDesktopWindow() did not work, though.

OSX Cocoa input source detect change

Does anyone know how to detect when the user changes the current input source in OSX?
I can call TISCopyCurrentKeyboardInputSource() to find out which input source ID is being used like this:
TISInputSourceRef isource = TISCopyCurrentKeyboardInputSource();
if ( isource == NULL )
{
cerr << "Couldn't get the current input source\n.";
return -1;
}
CFStringRef id = (CFStringRef)TISGetInputSourceProperty(
isource,
kTISPropertyInputSourceID);
CFRelease(isource);
If my input source is "German", then id ends up being "com.apple.keylayout.German", which is mostly what I want. Except:
The results of TISCopyCurrentKeyboardInputSource() doesn't change once my process starts? In particular, I can call TISCopyCurrentKeyboardInputSource() in a loop and switch my input source, but TISCopyCurrentKeyboardInputSource() keeps returning the input source that my process started with.
I'd really like to be notified when the input source changes. Is there any way of doing this? To get a notification or an event of some kind telling me that the input source has been changed?
You can observe the NSTextInputContextKeyboardSelectionDidChangeNotification notification posted by NSTextInputContext to the default Cocoa notification center. Alternatively, you can observe the kTISNotifySelectedKeyboardInputSourceChanged notification delivered via the Core Foundation distributed notification center.
However, any such change starts in a system process external to your app. The system then notifies the frameworks in each app process. The frameworks can only receive such notifications when it is allowed to run its event loop. Likewise, if you're observing the distributed notification yourself, that can only happen when the event loop (or at least the main thread's run loop) is allowed to run.
So, that explains why running a loop which repeatedly checks the result of TISCopyCurrentKeyboardInputSource() doesn't work. You're not allowing the frameworks to monitor the channel over which it would be informed of the change. If, rather than a loop, you were to use a repeating timer with a low enough frequency that other stuff has a chance to run, and you returned control to the app's event loop, you would see the result of TISCopyCurrentKeyboardInputSource() changing.

Win32: Get message notification of other application's close/exit

My application needs to monitor all other running applications on the system. Is there some way I could get notified on exit of every application exe?
The methods I could find:
1) Use PSAPI functions to get the list of running exes at frequent intervals. At each poll compare with the previous list to find which application/process has exited.
Disadvantage: Requires constant polling, will take CPU time.
2) Set a global hook for WM_CLOSE message: Using this I would be able to get a notification when any application gets closed through the close button on the title bar
Disadvantage:
(-)Not all the applications are generating a WM_CLOSE message(Ex: Total Video Player Exe)
(-)If the application was closed through the "Exit" menu or button (e.g. File->Exit) , I can't trap that message
Is there any other better way that I missed? Please advise.
Get a list of PIDs using PSAPI.
Then get a handle on each process using OpenProcess().
Use WaitForMultipleObjects() to be signalled when one of the processes exits.
You could try the RegisterShellHookWindow() API and filter for HSHELL_WINDOWCREATED and HSHELL_WINDOWDESTROYED messages.
Of course, that will only get you notified about applications that have a window.
I recently ran into this problem and found a solution so wanted to share with you all. It all correct the way we should obtain handle to the process. Instead of WaitForSingleOBject though, I would recommend to use RegisterWaitForSingle object function. With this function you are giving a callback function and whenever the process exits, your callback function will be called. This is better than calling WaitForSingleObject in a thread. Calling WaitForSingleObject in your code by itself will cause your code to wait until the process exits. Here is an example of how to call it:
RegisterWaitForSingleObject(&waitHandle, processHandle, ProcessTerminatedCallback, param, INFINITE, WT_EXECUTEONLYONCE);
Where:
[out]waitHandle - new handle created for you. Please note that you cannot use this handle to call CloseHandle, but you can wait on it, if you want to.
[in] processHandle - handle to the process that you are supposed to obtain yourself
[in] ProcessTerminatedCallback - the callback function that will be called when the process exits
[in] param - LPVOID parameter that will be passed to the callback
[in] INFINITE - either wait infinitely or for a specified time, look up MSDN for more info
[in] WM_EXECUTEONLYONCE - will call the callback function only once. look up MSDN for more info
> Is there any other better way that I missed?
Yes, plenty. See on Win32 group (system notifications, without any hook)

Modal operation using IMessageFilter and DoEvents

This is a Windows Forms application. I have a function which captures some mouse events modally till a condition is met. For example, I would like to wait for the user to select a point in the window's client area (or optionally cancel the operation using the Escape key) before the function returns. I am using the following structure:
Application::AddMessageFilter(someFilter);
while(someFilter->HasUserSelectedAPoint_Or_HitEscapeKey()){
Application::DoEvents();
}
Application::RemoveMessageFilter(someFilter);
This works quite nicely except for taking up nearly 100% CPU usage when control enters the while loop. I am looking for an alternative similar to what is shown below:
Application::AddMessageFilter(someFilter);
while(someFilter->HasUserSelectedAPoint_Or_HitEscapeKey()){
// Assuming that ManagedGetMessage() below is a blocking
// call which yields control to the OS
if(ManagedGetMessage())
Application::DoEvents();
}
Application::RemoveMessageFilter(someFilter);
What is the right way to use IMessageFilter and DoEvents? How do I surrender control to the OS till a message is received? Any GetMessage equivalent in the managed world?
You could sleep the thread for 500ms or so between DoEvents() calls. Experiment with different values to see what feels right.

How Can I Monitor Which Window Currently Has Keyboard Focus

Is there a way to track which window currently has keyboard focus. I could handle WM_SETFOCUS for every window but I'm wondering if there's an alternative, simpler method (i.e. a single message handler somewhere).
I could use OnIdle() in MFC and call GetFocus() but that seems a little hacky.
So from the way you worded the question I'm inferring that you want to have an event handler which is invoked whenever focus switches between windows. You want to be notified, rather than having to poll.
I actually don't think calling GetFocus from OnIdle is that much of a hack - sure it's polling, but it's low-overhead polling without side effects - but if you really want to track this, Windows Hooks are probably your best choice. Specifically you can install a CBT hook (WH_CBT) and listen for the HCBT_SETFOCUS notification.
Windows calls the WH_CBT hook with this hook code when Windows is about to set the focus to any window. In the case of thread-specific hooks, the window must belong to the thread. If the filter function returns TRUE, the focus does not change.
You could also do with with a WH_CALLWNDPROC hook and listen for the WM_SETFOCUS message.
Depending on whether you make it a global hook, or app-local, you can track focus across all windows on the system, or only the windows owned by your process.
There is an easy way using .Net Framework 3.5 : the library UI Automation provides an event focus changed that fires every time the focus change to a new control.
Page on MSDN
Sample:
public void SubscribeToFocusChange()
{
AutomationFocusChangedEventHandler focusHandler
= new AutomationFocusChangedEventHandler(OnFocusChanged);
Automation.AddAutomationFocusChangedEventHandler(focusHandler);
}
private void OnFocusChanged(object sender, AutomationFocusChangedEventArgs e)
{
AutomationElement focusedElement = sender as AutomationElement;
//...
}
This api in fact use windows hook behind the scenes to do that. However you have to use the .Net Framework...
How about the Win32 GetForegroundWindow?
If you're programming in .net 3.5, the Automation package olorin mentions is by far the easiest, but beware of using it in a program that itself has a UI, at least if the UI is done in WPF -- the focus tracking hooks get confused by events in its own app, and quickly lock up the UI. I sent MS a bug report on it. I have not observed the same problem using a traditional Windows Forms UI. You could, of course, put the tracking code in a separate console app and use some kind of ipc to transmit the info you need.
The tempting alternative of using Interop to access the WH_CBT Windows Hook from C# won't work -- the only global hooks you can get at from C# are the mouse and keyboard.
Well, this may not be very graceful... but you can retrieve the current focused control pretty easily. So you might consider setting up a timer that asks every 1/2 second or so "Where is the current focus?"... Then you can observe changes. Example Delphi code is below; it should be pretty easy to adapt, since the real work is in the Windows API calls.
<snip>
function TForm1.GetCurrentHandle: integer;
var
activeWinHandle: HWND;
focusedThreadID : DWORD;
begin
//return the Windows handle of the currently focused control
Result := 0;
activeWinHandle := GetForegroundWindow;
focusedThreadID := GetWindowThreadProcessID(activeWinHandle,nil);
if AttachThreadInput(GetCurrentThreadID,focusedThreadID,true) then begin
try
Result := GetFocus;
finally
AttachThreadInput(GetCurrentThreadID, focusedThreadID, false);
end;
end; //if attached
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
//give notification if the handle changed
//(this code gets fired by a timer)
CurrentHandle := GetCurrentHandle;
if CurrentHandle <> PreviousHandle then begin
Label1.Caption := 'Last focus change occurred # ' + DateTimeToStr(Now);
end;
PreviousHandle := CurrentHandle;
end;
<snip>
http://msdn.microsoft.com/en-us/library/ms771428.aspx
Has a window focus tracker sample.
You could monitor messages for the WM_ACTIVATE event.
ref

Resources