结构示意图
graph TD;
DLL调用,DLLInvoke.cs-->窗口监控,WindowWatcher.cs;
DLL调用,DLLInvoke.cs-->输入监控,InputWatcher.cs;
窗口监控,WindowWatcher.cs-->窗口管理器,IWindowManager.cs;
窗口管理器,IWindowManager.cs-->过滤器,ApplicationFilter.cs;
过滤器,ApplicationFilter.cs-->窗口渲染;
窗口监控:SetwinEventHook()
窗口监控可以监听活动窗口的切换.
核心是通过对系统活动窗口切换事件SetWinEventHook()(文档入口)的监听实现的。
/// <summary>
/// 对windows事件添加钩子函数
/// </summary>
/// <param name="eventMin"></param>
/// <param name="eventMax"></param>
/// <param name="hmodeWinEventProc"></param>
/// <param name="pfnWinEventProc"></param>
/// <param name="idProcess"></param>
/// <param name="idThread"></param>
/// <param name="dwFlags"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr SetWinEventHook(Int32 eventMin, Int32 eventMax, IntPtr hmodeWinEventProc, WinEventDelegate pfnWinEventProc, Int32 idProcess, Int32 idThread, Int32 dwFlags);
public delegate void WinEventDelegate(IntPtr hWinEventHook, Int32 eventType, IntPtr hwnd, long idObject, long idChlid, Int32 dwEventThread, Int32 dwmsEventTime);
SetWinEventHook()要求传入要监听的事件常量范围eventMin到eventMax,idProcess要监听的进程id,idThread线程id.
idProcess为0监听当前桌面上的所有的进程.idThread为0监听当前桌面上的所有的线程.
还有对监听事件的回调函数,用C#的托管函数实现.SetWinEventHook()已经定义了自己的回调函数类型,就是上面的WinEventDelegate()(在文档里也有对该函数的定义).把他传到pfnWinEventProc里.
所有事件常量
活动窗口切换的事件常量是0x0003,只要对eventMin,eventMax都传入该值,就行了.
在文档中有说明0x0003事件在切换同一线程中的不同的窗口也会触发此事件.
所以要对切换前后的线程或进程进行判断.
pfnWinEventProc()
pfnWinEventProc()中的几个参数:
hWinEventHook: 注册的挂钩函数的句柄eventType: 事件常量hwnd: 生成事件的窗口(切换到前台的窗口)的句柄
输入监控:GetLastInputInfo()
输入监控用来判断系统为繁忙/空闲状态,以决定是否累计使用时间.
输入监控通过GetLastInputInfo()获取最后一次输入的时间来判断系统是否空闲.
/// <summary>
/// 返回上次输入的时间
/// 收到最后一个输入事件时的TickCount(TickCount: 系统运行时间)
/// 实际返回的是最后一个输入事件时的系统运行时间
/// </summary>
/// <param name="lastInputInfo"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern int GetLastInputInfo(ref LastInputInfo lastInputInfo);
GetLastInputInfo要求传入一个指针,指针结构如下:
public struct LastInputInfo {
public int cbSize;
public uint dwTime;
}
其中cbSize字段被要求设置为结构体的大小(sizeof(LastInputInfo)).
GetLastInputInfo文档入口
C#的使用例子:
public DLLInvoke.LastInputInfo LastInputInfo;
LastInputInfo = new DLLInvoke.LastInputInfo();
// Marshal.SizeOf()类似c的sizeof()
LastInputInfo.cbSize = Marshal.SizeOf(LastInputInfo);
DLLInvoke.GetLastInputInfo(ref lastInputInfo)
窗口切换
End
程序的核心主要就是上面两个dll的调用,剩下的就是代码逻辑性稍微有点强,因为窗口切换和系统繁忙的判定对时间顺序的要求比较高. Github地址