Reputation: 18086
I have the following code:
using System;
using System.Runtime.InteropServices;
public class WindowsFunctions
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
public static int TicksSinceLastInput()
{
var info = new LASTINPUTINFO();
GetLastInputInfo(ref info);
var lastInputTickCount = info.dwTime;
return (int)lastInputTickCount;
}
}
[StructLayout(LayoutKind.Sequential)]
struct LASTINPUTINFO
{
public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
[MarshalAs(UnmanagedType.U4)]
public UInt32 cbSize;
[MarshalAs(UnmanagedType.U4)]
public UInt32 dwTime;
}
However, on running, info.dwTime
is zero.
Running in VS2019 IDE
Update:
I tried making TicksSinceLastInput
not static it fails either way.
My failing unit test is now:
[TestMethod]
public void TestTicksSinceLastInput()
{
var funcs = new WindowsFunctions();
var ticks = funcs.TicksSinceLastInput();
Assert.IsTrue( ticks > 0);
}
Update:
My code is now:
public class WindowsFunctions
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
public int TicksSinceLastInput()
{
var info = new LASTINPUTINFO();
var result = GetLastInputInfo(ref info);
var lastInputTickCount = info.dwTime;
return (int)lastInputTickCount;
}
}
result is being set to false.
Upvotes: 2
Views: 1766
Reputation: 32223
The declaration of LASTINPUTINFO
, as it appears, comes from PInvoke.net.
The struct contains a static int:
public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
It may look like it's defining the size of the struct, but it really has no practical purpose.
In your original code, the struct's cbSize
member is not initialized and it has to be: it specifies the size of the structure itself; assigning it is mandatory.
The GetLastInputInfo function declaration is correct:
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
Its return Type is BOOL
(defined as typedef int BOOL
), not BOOLEAN
(defined as typedef BYTE BOOLEAN
).
BOOL
is managed, it doesn't require:
[return: MarshalAs(UnmanagedType.Bool)]
The declaration and initialization of the LASTINPUTINFO structure can be simplified, to include a non-empty Constructor that initializes its members:
[StructLayout(LayoutKind.Sequential)]
struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
public LASTINPUTINFO(uint init) {
cbSize = (uint)Marshal.SizeOf<LASTINPUTINFO>();
dwTime = init;
}
}
Its members' Types are:
UINT cbSize
: UINT
is defined as typedef unsigned int UINT
, unsigned integer.
It's a managed Type, it doesn't require (here) [MarshalAs(UnmanagedType.U4)]
DWORD dwTime
: DWORD
is defined as typedef unsigned long DWORD
, which is a 32-bit unsigned integer. Still managed, it doesn't require (here) [MarshalAs(UnmanagedType.U4)]
.Of course, specifying the marshalling Type doesn't hurt (when it's correct, that is).
Sample usage, using a Timer to show the elapsed milliseconds since the last Input:
// Better resolution than System.Windows.Forms.Timer
System.Timers.Timer sysIdleTimer = null;
// [...]
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
sysIdleTimer = new System.Timers.Timer() {
Interval = 100,
SynchronizingObject = this // Marshal events to the UI Thread
};
sysIdleTimer.Elapsed += OnTimerElapsed;
sysIdleTimer.Start();
}
static uint GetLastInputTimeMilliseconds()
{
var info = new LASTINPUTINFO(0);
if (GetLastInputInfo(ref info)) {
// Valid within 24.9 days of machine activity
uint idleTime = (uint)Environment.TickCount - info.dwTime;
return idleTime;
}
return 0;
}
protected void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// Shows the Input Idle time in a Label
lblInputIdle.Text = $"Idle Time: {GetLastInputTimeMilliseconds()} milliseconds";
}
Upvotes: 3