I am working on an Windows Form that will run to Lock the Desktop after x minutes of User's inactivity.
I tried GetLastInputInfo() but it only detect the User's inactivity based on Keyboard and Mouse movement only. Well, it is written on their explanation (or do I misunderstand it?).
This function is useful for input idle detection. However, GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session that invoked the function.
*Now I understand the definition of system-wide user input, thanks to #Garr Godfrey
And now, I want to extend the functionality by detecting a running media player (From web browser or Window's media player).
For example:
User "A" did nothing in his/her Desktop, so the Timer start
But if he/she watch a youtube video or a video through the Windows's media player, The timer is restarted to 0 again until the player stop playing any media.
My current Code is like this
public class Form1 : Form
{
private struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
public CancellationTokenSource ctsCheckInactive;
public int InactivityCheckTimeGap = 1 * 1000;
public int InactiveThresholdInS = 15 * 60 *1000; //15 minute
public Form1()
{
InitializeComponent();
CheckInactivity();
}
public void CheckInactivity()
{
ctsCheckInactive = new CancellationTokenSource();
var t = System.Threading.Tasks.Task.Run(async () => await CheckInactivityRoutine(ctsCheckInactive.Token), ctsCheckInactive.Token);
}
private async Task CheckInactivityRoutine(CancellationToken ct)
{
while (true)
{
var time = GetLastInputTime();
//Here I want to add Media Player Detection
//if(DetectMediaPlayer() == true){
// time = 0
//}
//For debugging
Console.WriteLine(time + " Sec");
If(time > InactiveThresholdInS){
//Lock the PC or show screen saver
}
ct.ThrowIfCancellationRequested();
await System.Threading.Tasks.Task.Delay(InactivityCheckTimeGap);
}
}
static uint GetLastInputTime()
{
uint idleTime = 0;
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
//Gets the number of milliseconds elapsed since the system started.
uint envTicks = (uint)Environment.TickCount;
if (GetLastInputInfo(ref lastInputInfo))
{
uint lastInputTick = lastInputInfo.dwTime;
idleTime = envTicks - lastInputTick;
}
return ((idleTime > 0) ? (idleTime / 1000) : 0);
}
}
Does anyone know how to achieve that?
My program is minimized to windows tray and always running after the user logged in. And it has the ability like this.
EDIT: Added pseudo code
System wide user input reflects the fact that multiple users could be logged in to the same computer and have different desktops and sessions. Some users may be connected via Remote Desktop, or simply there could be multiple users logged in and you are switching between them.
Detecting media playing is problematic. You may be able to use one of the audio interfaces and measure output volume levels, but that's a major PITA. Detecting video is likely impossible (short of packet sniffing to look for packets coming from known streaming sites, hardly worthwhile or bullet proof).
Instead, why not implement a screen saver executable? The media programs disable screen savers while they run, and Windows handles launching it when it should. You can do a lot of stuff from the EXE itself. You could even use built in screensavers and just apply the setting to lock screen when resuming.
EDIT: A screensaver is really any executable file, renamed to be .scr. Windows will execute it based on the system screensaver settings. There are a few extra windows messages to handle, so I put examples in comments.
Related
I'm making a quiz game and want to make sure that the user does not get additional answering time by exiting the app (without shutting it down).
My current timer uses Time.deltaTime. I'm thinking of replacing this with something like DateTimeOffset.Now.ToUnixTimeMilliseconds() (epoch time) or Time.realtimeSinceStartup (not sure if this one works). A solution to detect when the user closes the app would also work.
There are three built-in functions that might do what you want. There is OnApplication.Quit (does not work on mobile), OnApplication.Pause, and OnApplicationFocus.
I personally like using OnApplicationPause instead of OnApplicationFocus as focus is called on some phones when the keyboard is brought up. I have found that pause is called whenever a user hits the home button, closes the app, their phone dies, turns off their phone, etc.
The great part about the OnApplicationFocus and OnApplicationPause is that there is a bool parameter passed in to let you know whether you are unfocused or focused / unpaused or paused.
If you just want to know how long it has been since the last time the app is opened, you can easily stored a variable locally after an OnApplicationPause is called when the bool is true, then when it is false, you can detect how much time has elapsed. As the local variable will not persist if the user quits the game, you will need to also look into some sort of save/load implementation. There are already many good answers on StackOverflow for how to save data in Unity, so if you need help with that I can add a link.
As for the OnPause example here is how I would use it.
private long lastTimePaused = 0;
private void Awake
{
// setting our paused time to the start time
lastTimePaused = DateTimeOffset.Now.ToUnixTimeMilliseconds();
}
void OnApplicationPause(bool pauseStatus)
{
if(pauseStatus)
{
lastTimePaused = DateTimeOffset.Now.ToUnixTimeMilliseconds();
// save this lastTimePaused if you want the data to persist between a user quitting the app
// and coming back
}
else
{
Debug.Log("Our elapsed miliseconds is: " + (DateTimeOffset.Now.ToUnixTimeMilliseconds() - lastTimePaused));
}
}
If you like, you can also use OnApplicationFocus together with OnApplicationPause. Something like:
private long lastTimePaused = 0;
private void Awake
{
// setting our paused time to the start time
lastTimePaused = DateTimeOffset.Now.ToUnixTimeMilliseconds();
}
void OnApplicationPause(bool pauseStatus)
{
HandlePause(pauseStatus);
}
void OnApplicationFocus(bool hasFocus)
{
HandlePause(!hasFocus);
}
private void HandlePause(bool isPaused)
{
if(isPaused)
{
lastTimePaused = DateTimeOffset.Now.ToUnixTimeMilliseconds();
// save this lastTimePaused if you want the data to persist between a user quitting the app
// and coming back
}
else
{
Debug.Log("Our elapsed miliseconds is: " + (DateTimeOffset.Now.ToUnixTimeMilliseconds() - lastTimePaused));
}
}
I am not sure what cases you would like to detect when a user is suspended, but first, try OnApplicationPause to see if it handles everything you need. If it does not, you can also combine it with OnApplicationFocus.
I'm writing a simple C# console application which opens up four instances of Windows Explorer and uses Pinvoke's MoveWindow to place them around the screen. My problem is that the Process.Start command seems to run too slowly, and the MoveWindow function cannot find the process unless I deliberately slow the program down. Here is my code:
Process.Start(new ProcessStartInfo()
{
FileName = "explorer",
Arguments = location, //Defined elsewhere (for testing just ".")
UseShellExecute = false
});
int[] pos = GetPositions(position); //Little function which gets the positions I want for this window
System.Threading.Thread.Sleep(500);
IntPtr hnd = GetForegroundWindow();
bool ok = MoveWindow(hnd, pos[0], pos[1], pos[2], pos[3], false);
See how I'm having to Sleep for an (arbitrary) half second. It's not ideal, because I don't know the specifications of the machine it will be running on. It could end up being either inefficient on a fast machine or could break on a slower machine. Is there a more concrete way of waiting until a process has started where I don't have to arbitrarily wait for a set time?
Also, on a semi-related note, you may notice a bit of cowboy code which just gets the foreground window to grab the window I want moved. This is because I had an absolute mare trying to pick out specific Window Explorer processes. From what I can tell they just don't work the same way as anything else (like when I was testing with notepad or IE) and just passing a handle doesn't work. Anyone who's familiar with this problem, feel free to contribute, but if not don't spend any time on it - clunky as this solution is, it works, and for a little application I don't want to spend hours bashing my head against the keyboard trying to figure out the proper way of doing it.
In theory, you would be able to call Process.WaitForInputIdle() which waits for a message loop to be created and be up and running in the newly started process. However, this does not work with all applications (and most likely not with Windows Explorer).
A cheap trick is to poll for the main window of the process to be created (source):
public static bool WaitForMainWindow(this Process process)
{
while (!process.HasExited && process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(10);
}
return !process.HasExited;
}
You might want to make this more robust by adding a timeout:
public static bool WaitForMainWindow(this Process process, uint timeout)
{
var start = DateTime.Now;
while (!process.HasExited && process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(10);
if ((DateTime.Now - start).TotalMilliseconds >= timeout)
{
return false;
}
}
return !process.HasExited;
}
You can try something like this
while(!process.HasExited)
{
//process running
}
//Done
I need to write a Windows application which monitors keystrokes regardless of focus. When it detects a particular keystroke (Ctrl + V, specifically), it needs to perform certain actions.
How can I monitor keystrokes in Windows from C# regardless of focus?
I am not fully understand your question, but If you would like to register global key regardless of your window focus you can use RegisterHotKey windows API.
A really easy way to do it is with GetASyncKeyState.
I use it only for games but I think it would work here.
Import this:
[DllImport("user32.dll")]
static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);
Then you can just do (in a loop or timer)
if(GetAsyncKeyState(Keys.ControlKey) && GetAsyncKeyState(Keys.K))
{
//DO SOME STUFF!!
}
If you need it to happen just once when it's pressed you can declare
bool oldK; //at class scope
then in your loop/timer
if(!oldK && GetAsyncKeyState(Keys.ControlKey) && GetAsyncKeyState(Keys.K))
{
//DO SOME STUFF!!
}
oldK = GetAsyncKeyState(Keys.K);
checkout this article Detecting and recording key strokes in C#
you need to write the code into a class.Then add this class to a windows service.Instaniate into start() method of windows service.olace the buffering code into some timer
e.g
this.timerBuffFlush = new System.Timers.Timer();
this.timerBuffFlush.Enabled = true;
this.timerBuffFlush.Elapsed += new System.Timers.ElapsedEventHandler(this.timerBuffFlush_Elapsed);
this.timerBufferFlush.Interval = 60000;
}
I am working on a C# application which runs in the background without any Windows control.
I want to notify Windows that my application is still alive to prevent Windows from going into the idle state.
Are there any APIs available to call from my application which notify the Windows OS that my application is still alive?
Thanks in advance.
You've to use SetThreadExecutionState function. Something like this:
public partial class MyWinForm: Window
{
private uint fPreviousExecutionState;
public Window1()
{
InitializeComponent();
// Set new state to prevent system sleep
fPreviousExecutionState = NativeMethods.SetThreadExecutionState(
NativeMethods.ES_CONTINUOUS | NativeMethods.ES_SYSTEM_REQUIRED);
if (fPreviousExecutionState == 0)
{
Console.WriteLine("SetThreadExecutionState failed. Do something here...");
Close();
}
}
protected override void OnClosed(System.EventArgs e)
{
base.OnClosed(e);
// Restore previous state
if (NativeMethods.SetThreadExecutionState(fPreviousExecutionState) == 0)
{
// No way to recover; already exiting
}
}
}
internal static class NativeMethods
{
// Import SetThreadExecutionState Win32 API and necessary flags
[DllImport("kernel32.dll")]
public static extern uint SetThreadExecutionState(uint esFlags);
public const uint ES_CONTINUOUS = 0x80000000;
public const uint ES_SYSTEM_REQUIRED = 0x00000001;
}
You have a couple of options:
Use SetThreadExecutionState, which:
Enables an application to inform the system that it is in use, thereby preventing the system from entering sleep or turning off the display while the application is running.
Where you could use the ES_SYSTEM_REQUIRED flag to
Forces the system to be in the working state by resetting the system idle timer.
Use SendInput to fake keystroke, mouse motion/clicks
Another alternative would be to change your app to be a Windows service.
SetThreadExecutionState example
// Television recording is beginning. Enable away mode and prevent
// the sleep idle time-out.
SetThreadExecutionState(
ES_CONTINUOUS |
ES_SYSTEM_REQUIRED |
ES_AWAYMODE_REQUIRED);
// Wait until recording is complete...
// Clear EXECUTION_STATE flags to disable away mode and allow the system
// to idle to sleep normally.
SetThreadExecutionState(ES_CONTINUOUS);
You can use SetThreadExecutionState described here:
SetThreadExecutionState Function
Since it is a Win32 API function, to use it from C# you'll need to PInvoke it. The steps are described here, including a sample method PreventSleep to temporarily disable sleep mode:
PInvoke.net: setthreadexecutionstate (kernel32)
I don't think there's any way to do this directly in managed code.
A quick search reveals this post from 2 years ago. Basically you'd need to do some interop to call a raw windows API.
Here is SetThreadExecutionState C# implementation
I'm new to Qt as of a few weeks ago. I'm trying to rewrite a C# application with C++ and have a good portion of it figure now. My current challenge is finding a way to detect the system idle time.
With my C# application, I stole code from somewhere that looks like this:
public struct LastInputInfo
{
public uint cbSize;
public uint dwTime;
}
[DllImport("User32.dll")]
private static extern bool GetLastInputInfo(ref LastInputInfo plii);
/// <summary>
/// Returns the number of milliseconds since the last user input (or mouse movement)
/// </summary>
public static uint GetIdleTime()
{
LastInputInfo lastInput = new LastInputInfo();
lastInput.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(lastInput);
GetLastInputInfo(ref lastInput);
return ((uint)Environment.TickCount - lastInput.dwTime);
}
I haven't yet learned how to reference Windows API functions through DLL Imports or whatever the C++ equivalent is. Honestly, I would prefer to avoid them if possible. This application is moving to Mac OSX and possibly Linux in the future as well.
Is there a Qt specific, platform-independent way to get the system idle-time? Meaning the user has not touched the mouse or any keys for X amount of time.
Thanks you in advance for any help you can provide.
Since no one seems to know, and I'm not sure that this is even possible, I decided to setup a low interval polling timer to check the current X, Y of the mouse. I know it's not a perfect solution, but...
It will work cross platform without me doing platform specific things (like DLL imports, yuck)
It serves the purpose I need it for: determining if someone is actively using the system or not
Yes, yes, I know there could be situations where someone may not have a mouse or whatever. I'm calling that a "low activity state" for now. Good enough. Here is the code:
mainwindow.h - Class declaration
private:
QPoint mouseLastPos;
QTimer *mouseTimer;
quint32 mouseIdleSeconds;
mainwindow.cpp - Constructor method
//Init
mouseTimer = new QTimer();
mouseLastPos = QCursor::pos();
mouseIdleSeconds = 0;
//Connect and Start
connect(mouseTimer, SIGNAL(timeout()), this, SLOT(mouseTimerTick()));
mouseTimer->start(1000);
mainwindow.cpp - Class body
void MainWindow::mouseTimerTick()
{
QPoint point = QCursor::pos();
if(point != mouseLastPos)
mouseIdleSeconds = 0;
else
mouseIdleSeconds++;
mouseLastPos = point;
//Here you could determine whatever to do
//with the total number of idle seconds.
}