跳到主要内容

github/tai

公开

Tai · Windows 软件与网站使用时长统计。上游 Planshit/Tai 的源码镜像,非灵栈原创;MIT 许可。来源:https://github.com/Planshit/Tai 。本次为手动同步,暂不提供安装包。

提交记录
新增Core/Enums/AppType.cs+27 −0看文件
@@ -0,0 +1,27 @@
1新增:using System;
2新增:using System.Collections.Generic;
3新增:using System.Linq;
4新增:using System.Text;
5新增:using System.Threading.Tasks;
6新增:
7新增:namespace Core.Enums
8新增:{
9新增: /// <summary>
10新增: /// 应用类型
11新增: /// </summary>
12新增: public enum AppType
13新增: {
14新增: /// <summary>
15新增: /// 传统应用
16新增: /// </summary>
17新增: Win32,
18新增: /// <summary>
19新增: /// UWP
20新增: /// </summary>
21新增: UWP,
22新增: /// <summary>
23新增: /// 系统组件,如任务栏、开始菜单、状态栏、系统登录界面等
24新增: /// </summary>
25新增: SystemComponent
26新增: }
27新增:}
新增Core/Event/AppActiveChangedEventArgs.cs+34 −0看文件
@@ -0,0 +1,34 @@
1新增:using Core.Models.AppObserver;
2新增:using System;
3新增:using System.Collections.Generic;
4新增:using System.Linq;
5新增:using System.Text;
6新增:using System.Threading.Tasks;
7新增:
8新增:namespace Core.Event
9新增:{
10新增: /// <summary>
11新增: /// 应用活跃状态变更事件参数
12新增: /// </summary>
13新增: public class AppActiveChangedEventArgs
14新增: {
15新增: /// <summary>
16新增: /// 激活时间
17新增: /// </summary>
18新增: public DateTime ActiveTime { get; }
19新增: /// <summary>
20新增: /// 应用信息
21新增: /// </summary>
22新增: public AppInfo App { get; }
23新增: /// <summary>
24新增: /// 窗口信息
25新增: /// </summary>
26新增: public WindowInfo Window { get; }
27新增: public AppActiveChangedEventArgs(AppInfo app_, WindowInfo window_, DateTime activeTime_)
28新增: {
29新增: App = app_;
30新增: Window = window_;
31新增: ActiveTime = activeTime_;
32新增: }
33新增: }
34新增:}
新增Core/Event/AppDurationUpdatedEventArgs.cs+47 −0看文件
@@ -0,0 +1,47 @@
1新增:using Core.Models.AppObserver;
2新增:using System;
3新增:using System.Collections.Generic;
4新增:using System.Linq;
5新增:using System.Text;
6新增:using System.Threading.Tasks;
7新增:
8新增:namespace Core.Event
9新增:{
10新增: public class AppDurationUpdatedEventArgs
11新增: {
12新增: /// <summary>
13新增: /// 使用时长
14新增: /// </summary>
15新增: public int Duration { get; }
16新增: /// <summary>
17新增: /// 激活时间
18新增: /// </summary>
19新增: public DateTime ActiveTime { get; }
20新增: /// <summary>
21新增: /// 结束时间
22新增: /// </summary>
23新增: public DateTime EndTime { get; }
24新增: /// <summary>
25新增: /// 应用信息
26新增: /// </summary>
27新增: public AppInfo App { get; }
28新增: /// <summary>
29新增: /// 窗口信息
30新增: /// </summary>
31新增: public WindowInfo Window { get; }
32新增: public AppDurationUpdatedEventArgs(int duration_, AppInfo app_, WindowInfo window_, DateTime activeTime_, DateTime endTime_)
33新增: {
34新增: Duration = duration_;
35新增: App = app_;
36新增: Window = window_;
37新增: ActiveTime = activeTime_;
38新增: EndTime = endTime_;
39新增: }
40新增:
41新增: public override string ToString()
42新增: {
43新增: return $"Duration:{Duration},App:{App},Window:{Window},ActiveTime:{ActiveTime},EndTime:{EndTime}";
44新增:
45新增: }
46新增: }
47新增:}
新增Core/Event/AppObserverEventHandler.cs+8 −0看文件
@@ -0,0 +1,8 @@
1新增:using Core.Event;
2新增:using System;
3新增:using System.Collections.Generic;
4新增:using System.Linq;
5新增:using System.Text;
6新增:using System.Threading.Tasks;
7新增:
8新增:public delegate void AppObserverEventHandler(object sender, AppActiveChangedEventArgs e);
新增Core/Event/AppTimerEventHandler.cs+8 −0看文件
@@ -0,0 +1,8 @@
1新增:using Core.Event;
2新增:using System;
3新增:using System.Collections.Generic;
4新增:using System.Linq;
5新增:using System.Text;
6新增:using System.Threading.Tasks;
7新增:
8新增:public delegate void AppTimerEventHandler(object sender, AppDurationUpdatedEventArgs e);
修改Core/Librarys/Win32API.cs+34 −1看文件
@@ -38,7 +38,8 @@ namespace Core.Librarys
3838
3939 [DllImport("kernel32.dll", SetLastError = true)]
4040 public static extern bool CloseHandle(UIntPtr hObject);
41删除:
41新增: [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
42新增: public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
4243
4344
4445 internal struct WINDOWINFO
@@ -318,12 +319,44 @@ namespace Core.Librarys
318319 }
319320
320321 }
322新增: public static RECT GetWindowRect(IntPtr handle_)
323新增: {
324新增: try
325新增: {
326新增: GetWindowRect(handle_, out RECT rect);
327新增: return rect;
321328
329新增: }
330新增: catch (Exception e)
331新增: {
332新增: return new RECT()
333新增: {
334新增: Left = 0,
335新增: Bottom = 0,
336新增: Right = 0,
337新增: Top = 0
338新增: };
339新增: }
340新增: }
322341 #endregion
323342
324343 [DllImport("user32.dll", CharSet = CharSet.Auto)]
325344 public static extern int GetWindowTextLength(IntPtr hWnd);
326345 [DllImport("user32.dll", CharSet = CharSet.Auto)]
327346 public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
347新增: public static string GetWindowClassName(IntPtr handle_)
348新增: {
349新增: try
350新增: {
351新增: StringBuilder stringBuilder = new StringBuilder(256);
352新增: GetClassName(handle_, stringBuilder, stringBuilder.Capacity);
353新增:
354新增: return stringBuilder.ToString();
355新增: }
356新增: catch (Exception e)
357新增: {
358新增: return string.Empty;
359新增: }
360新增: }
328361 }
329362}
新增Core/Models/AppObserver/AppInfo.cs+57 −0看文件
@@ -0,0 +1,57 @@
1新增:using Core.Enums;
2新增:using System;
3新增:using System.Collections.Generic;
4新增:using System.Linq;
5新增:using System.Security.AccessControl;
6新增:using System.Text;
7新增:using System.Threading.Tasks;
8新增:
9新增:namespace Core.Models.AppObserver
10新增:{
11新增: /// <summary>
12新增: /// 应用信息
13新增: /// </summary>
14新增: public class AppInfo
15新增: {
16新增: /// <summary>
17新增: /// 进程ID
18新增: /// </summary>
19新增: public int PID { get; }
20新增: /// <summary>
21新增: /// 应用类型
22新增: /// </summary>
23新增: public AppType Type { get; }
24新增: /// <summary>
25新增: /// 进程名称
26新增: /// </summary>
27新增: public string Process { get; }
28新增: /// <summary>
29新增: /// 应用描述
30新增: /// </summary>
31新增: public string Description { get; }
32新增: /// <summary>
33新增: /// 可执行文件路径
34新增: /// </summary>
35新增: public string ExecutablePath { get; }
36新增: /// <summary>
37新增: /// 窗口句柄
38新增: /// </summary>
39新增: public IntPtr WindowHandle { get; }
40新增: public AppInfo(IntPtr windowHandle_, int pid_, string process_, string description_, string executablePath_, AppType type_ = AppType.Win32)
41新增: {
42新增: WindowHandle = windowHandle_;
43新增: PID = pid_;
44新增: Process = process_;
45新增: Description = description_;
46新增: ExecutablePath = executablePath_;
47新增: Type = type_;
48新增: }
49新增:
50新增: public override string ToString()
51新增: {
52新增: return $"WindowHandle:{WindowHandle},PID:{PID},ProcessName:{Process},Description:{Description},ExecutablePath:{ExecutablePath},Type:{Type}";
53新增: }
54新增:
55新增: public static AppInfo Empty = new AppInfo(IntPtr.Zero, 0, string.Empty, string.Empty, string.Empty);
56新增: }
57新增:}
新增Core/Models/AppObserver/WindowInfo.cs+54 −0看文件
@@ -0,0 +1,54 @@
1新增:using System;
2新增:using System.Collections.Generic;
3新增:using System.Linq;
4新增:using System.Text;
5新增:using System.Threading.Tasks;
6新增:
7新增:namespace Core.Models.AppObserver
8新增:{
9新增: /// <summary>
10新增: /// 窗口信息
11新增: /// </summary>
12新增: public class WindowInfo
13新增: {
14新增: /// <summary>
15新增: /// 窗口类名
16新增: /// </summary>
17新增: public string ClassName { get; }
18新增: /// <summary>
19新增: /// 窗口标题
20新增: /// </summary>
21新增: public string Title { get; }
22新增: /// <summary>
23新增: /// 窗口句柄
24新增: /// </summary>
25新增: public IntPtr Handle { get; }
26新增: /// <summary>
27新增: /// 窗口宽度
28新增: /// </summary>
29新增: public int Width { get; }
30新增: /// <summary>
31新增: /// 窗口高度
32新增: /// </summary>
33新增: public int Height { get; }
34新增: public int X { get; }
35新增: public int Y { get; }
36新增:
37新增: public WindowInfo(string className_, string title_, IntPtr handle_, int width_, int height_, int x_, int y_)
38新增: {
39新增: ClassName = className_;
40新增: Title = title_;
41新增: Handle = handle_;
42新增: Width = width_;
43新增: Height = height_;
44新增: X = x_;
45新增: Y = y_;
46新增: }
47新增:
48新增: public override string ToString()
49新增: {
50新增: return $"ClassName:{ClassName},Title:{Title},Handle:{Handle},Width:{Width},Height:{Height},X:{X},Y:{Y}";
51新增: }
52新增: public static WindowInfo Empty = new WindowInfo(string.Empty, string.Empty, IntPtr.Zero, 0, 0, 0, 0);
53新增: }
54新增:}
新增Core/Servicers/Instances/AppManager.cs+354 −0看文件
@@ -0,0 +1,354 @@
1新增:using Core.Enums;
2新增:using Core.Librarys;
3新增:using Core.Models.AppObserver;
4新增:using Core.Servicers.Interfaces;
5新增:using System;
6新增:using System.Collections.Generic;
7新增:using System.Diagnostics;
8新增:using System.IO;
9新增:using System.Linq;
10新增:using System.Management;
11新增:using System.Runtime.InteropServices;
12新增:using System.Text;
13新增:using System.Text.RegularExpressions;
14新增:using System.Threading;
15新增:using System.Threading.Tasks;
16新增:
17新增:namespace Core.Servicers.Instances
18新增:{
19新增: public class AppManager : IAppManager
20新增: {
21新增: #region win32api
22新增: [DllImport("user32.dll", CharSet = CharSet.Auto)]
23新增: public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
24新增: [DllImport("kernel32.dll", SetLastError = true)]
25新增: public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
26新增: [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
27新增: public static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] int dwFlags, [Out] StringBuilder lpExeName, ref int lpdwSize);
28新增: public const int PROCESS_QUERY_INFORMATION = 0x0400;
29新增: public const int PROCESS_VM_READ = 0x0010;
30新增:
31新增: [DllImport("psapi.dll", SetLastError = true)]
32新增: public static extern uint GetModuleBaseName(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, ref uint nSize);
33新增:
34新增: [DllImport("kernel32.dll", SetLastError = true)]
35新增: public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
36新增:
37新增: [DllImport("kernel32.dll", SetLastError = true)]
38新增: [return: MarshalAs(UnmanagedType.Bool)]
39新增: public static extern bool CloseHandle(IntPtr hObject);
40新增:
41新增: [StructLayout(LayoutKind.Sequential)]
42新增: public struct GUITHREADINFO
43新增: {
44新增: public uint cbSize;
45新增: public uint flags;
46新增: public IntPtr hwndActive;
47新增: public IntPtr hwndFocus;
48新增: public IntPtr hwndCapture;
49新增: public IntPtr hwndMenuOwner;
50新增: public IntPtr hwndMoveSize;
51新增: public IntPtr hwndCaret;
52新增: public RECT rcCaret;
53新增: }
54新增:
55新增: [StructLayout(LayoutKind.Sequential)]
56新增: public struct RECT
57新增: {
58新增: public int Left;
59新增: public int Top;
60新增: public int Right;
61新增: public int Bottom;
62新增: }
63新增:
64新增: [DllImport("user32.dll")]
65新增: [return: MarshalAs(UnmanagedType.Bool)]
66新增: public static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);
67新增: #endregion
68新增:
69新增: private Dictionary<string, AppInfo> _apps;
70新增: private int _outTime = 5000;
71新增: //private string _windowsVersionName;
72新增: public AppManager()
73新增: {
74新增: _apps = new Dictionary<string, AppInfo>();
75新增: //_windowsVersionName = SystemCommon.GetWindowsVersionName();
76新增: }
77新增:
78新增: public AppInfo GetAppInfo(IntPtr hwnd_)
79新增: {
80新增: try
81新增: {
82新增: AppInfo app;
83新增: var sw = Stopwatch.StartNew();
84新增: GetWindowThreadProcessId(hwnd_, out int processId);
85新增: sw = Stopwatch.StartNew();
86新增: string processName = GetAppProcessName(processId);
87新增: string executablePath = string.Empty;
88新增: string description = string.Empty;
89新增: AppType appType = AppType.Win32;
90新增:
91新增: sw = Stopwatch.StartNew();
92新增: if (string.IsNullOrEmpty(processName))
93新增: {
94新增: return AppInfo.Empty;
95新增: }
96新增: else if (_apps.ContainsKey(processName))
97新增: {
98新增: return _apps[processName];
99新增: }
100新增: else if (processName == "ApplicationFrameHost")
101新增: {
102新增: // uwp应用
103新增: // 尝试直接获取可执行文件路径,如果为空表示刚启动或者全屏状态
104新增: executablePath = GetUWPAPPExecutablePath(hwnd_, (uint)processId);
105新增: appType = AppType.UWP;
106新增:
107新增: if (string.IsNullOrEmpty(executablePath))
108新增: {
109新增: // 刚启动的程序需要延迟获取
110新增: int duration = 0;
111新增: while (processName == "ApplicationFrameHost")
112新增: {
113新增: Thread.Sleep(100);
114新增: duration += 100;
115新增: if (duration >= _outTime)
116新增: {
117新增: break;
118新增: }
119新增: executablePath = GetUWPAPPExecutablePath(hwnd_, (uint)processId);
120新增:
121新增: if (string.IsNullOrEmpty(executablePath))
122新增: {
123新增: // 可能是全屏状态
124新增: int pid = GetFullScreenUWPPID();
125新增: string name = GetAppProcessName(pid);
126新增:
127新增: processName = string.IsNullOrEmpty(name) ? processName : name;
128新增: executablePath = GetAppExecutablePath(pid);
129新增: processId = pid;
130新增: }
131新增: else
132新增: {
133新增: processName = Path.GetFileNameWithoutExtension(executablePath);
134新增: }
135新增: }
136新增: }
137新增: else
138新增: {
139新增: processName = Path.GetFileNameWithoutExtension(executablePath);
140新增: }
141新增: }
142新增: //else if (_windowsVersionName.Contains("Windows 7") && processName == "dllhost")
143新增: //{
144新增: // // Windows7的COM应用
145新增:
146新增: //}
147新增: else
148新增: {
149新增: executablePath = GetAppExecutablePath(processId);
150新增: }
151新增:
152新增: if (!string.IsNullOrEmpty(executablePath))
153新增: {
154新增: FileVersionInfo info = FileVersionInfo.GetVersionInfo(executablePath);
155新增: description = info.FileDescription;
156新增: }
157新增:
158新增: appType = IsSystemComponent(processName, hwnd_) ? AppType.SystemComponent : AppType.Win32;
159新增: app = new AppInfo(hwnd_, processId, processName, description, executablePath, appType);
160新增: if (processName != "explorer" && !_apps.ContainsKey(processName))
161新增: {
162新增: _apps.Add(processName, app);
163新增: }
164新增: return app;
165新增: }
166新增: catch (Exception ex)
167新增: {
168新增: Logger.Error(ex.ToString());
169新增: return AppInfo.Empty;
170新增: }
171新增: }
172新增:
173新增: private string GetAppProcessName(int processId_)
174新增: {
175新增: Process process = Process.GetProcessById(processId_);
176新增: return process.ProcessName;
177新增: }
178新增:
179新增: private string GetAppExecutablePath(int processId_)
180新增: {
181新增: IntPtr processHandle = IntPtr.Zero;
182新增: processHandle = OpenProcess(0x001F0FFF, false, processId_);
183新增: string executablePath = string.Empty;
184新增: if (processHandle != IntPtr.Zero)
185新增: {
186新增: int MaxPathLength = 4096;
187新增: var buffer = new StringBuilder(MaxPathLength);
188新增: QueryFullProcessImageName(processHandle, 0, buffer, ref MaxPathLength);
189新增: executablePath = buffer.ToString();
190新增: }
191新增: return executablePath;
192新增: }
193新增:
194新增: internal struct WINDOWINFO
195新增: {
196新增: public uint ownerpid;
197新增: public uint childpid;
198新增: }
199新增:
200新增: private string GetUWPAPPExecutablePath(IntPtr hWnd_, uint pID_)
201新增: {
202新增: WINDOWINFO windowinfo = new WINDOWINFO();
203新增: windowinfo.ownerpid = pID_;
204新增: windowinfo.childpid = windowinfo.ownerpid;
205新增:
206新增: IntPtr pWindowinfo = Marshal.AllocHGlobal(Marshal.SizeOf(windowinfo));
207新增:
208新增: Marshal.StructureToPtr(windowinfo, pWindowinfo, false);
209新增:
210新增: Win32API.EnumWindowProc lpEnumFunc = new Win32API.EnumWindowProc(EnumChildWindowsCallback);
211新增: Win32API.EnumChildWindows(hWnd_, lpEnumFunc, pWindowinfo);
212新增:
213新增: windowinfo = (WINDOWINFO)Marshal.PtrToStructure(pWindowinfo, typeof(WINDOWINFO));
214新增: if (windowinfo.childpid == windowinfo.ownerpid)
215新增: {
216新增: return null;
217新增: }
218新增: IntPtr proc;
219新增: if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, (int)windowinfo.childpid)) == IntPtr.Zero) return null;
220新增:
221新增: int capacity = 2000;
222新增: StringBuilder sb = new StringBuilder(capacity);
223新增: QueryFullProcessImageName(proc, 0, sb, ref capacity);
224新增:
225新增: Marshal.FreeHGlobal(pWindowinfo);
226新增:
227新增: return sb.ToString(0, capacity);
228新增: }
229新增: private bool EnumChildWindowsCallback(IntPtr hWnd_, IntPtr lParam_)
230新增: {
231新增: WINDOWINFO info = (WINDOWINFO)Marshal.PtrToStructure(lParam_, typeof(WINDOWINFO));
232新增:
233新增: int pID;
234新增: GetWindowThreadProcessId(hWnd_, out pID);
235新增:
236新增: if (pID != info.ownerpid) info.childpid = (uint)pID;
237新增:
238新增: Marshal.StructureToPtr(info, lParam_, true);
239新增:
240新增: return true;
241新增: }
242新增:
243新增: private IntPtr getThreadWindowHandle(uint dwThreadId_)
244新增: {
245新增: IntPtr hWnd;
246新增:
247新增: // Get Window Handle and title from Thread
248新增: var guiThreadInfo = new GUITHREADINFO();
249新增: guiThreadInfo.cbSize = (uint)Marshal.SizeOf(guiThreadInfo);
250新增:
251新增: GetGUIThreadInfo(dwThreadId_, ref guiThreadInfo);
252新增:
253新增: hWnd = guiThreadInfo.hwndFocus;
254新增: //some times while changing the focus between different windows, it returns Zero so we would return the Active window in that case
255新增: if (hWnd == IntPtr.Zero)
256新增: {
257新增: hWnd = guiThreadInfo.hwndActive;
258新增: }
259新增: return hWnd;
260新增: }
261新增:
262新增: #region 获取全屏UWP应用PID
263新增: /// <summary>
264新增: /// 获取全屏UWP应用PID
265新增: /// </summary>
266新增: /// <returns></returns>
267新增: private int GetFullScreenUWPPID()
268新增: {
269新增: var current = getThreadWindowHandle(0);
270新增: int processId = 0;
271新增: GetWindowThreadProcessId(current, out processId);
272新增: return processId;
273新增: }
274新增: #endregion
275新增:
276新增: #region 判断应用是否是系统组件
277新增: private bool IsSystemComponent(string processName_, IntPtr windowHandle_)
278新增: {
279新增: // 系统组件类名
280新增: string[] sysClassNameArr = {
281新增: "Progman",
282新增: "WorkerW",
283新增: "Shell_TrayWnd",
284新增: "XamlExplorerHostIslandWindow",
285新增: "TopLevelWindowForOverflowXamlIsland",
286新增: "Shell_InputSwitchTopLevelWindow",
287新增: "LockScreenControllerProxyWindow",
288新增: // win7
289新增: "DV2ControlHost",
290新增: "Button"
291新增: };
292新增: // 系统进程名称
293新增: string[] sysProcessArr = {
294新增: "ShellExperienceHost",
295新增: "StartMenuExperienceHost",
296新增: "SearchHost",
297新增: "LockApp"
298新增: };
299新增:
300新增: // 在windows7下,dllhost进程可能是Windows照片查看器,classname为:Photo_Lightweight_Viewer
301新增: // 需要单独为windows7写一个判定器,然后加一个转换器,dllhost进程->windows照片查看器
302新增: // explorer,class:CabinetWClass在win7下可能是文件夹、控制面板、我的电脑属性
303新增: bool isSys;
304新增: if (processName_ == "explorer")
305新增: {
306新增: // 先通过类名判定
307新增: string className = Win32API.GetWindowClassName(windowHandle_);
308新增: Logger.Info($"IsSystemComponent,process:{processName_},className:{className}");
309新增: if (!string.IsNullOrEmpty(className))
310新增: {
311新增: isSys = sysClassNameArr.Contains(className);
312新增: }
313新增: else
314新增: {
315新增: int titleLength = Win32API.GetWindowTextLength(windowHandle_);
316新增: isSys = titleLength <= 0;
317新增: }
318新增: }
319新增: else
320新增: {
321新增: // 通过进程名称判定
322新增: isSys = sysProcessArr.Contains(processName_);
323新增: }
324新增: return isSys;
325新增: }
326新增: #endregion
327新增:
328新增: #region 获取COM应用
329新增: private AppInfo GetCOMAPP(int processId_)
330新增: {
331新增: try
332新增: {
333新增: string wmiQuery = string.Format("select ProcessId,CommandLine from Win32_Process where ProcessId='{0}'", processId_);
334新增: ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiQuery);
335新增: var enu = searcher.Get().GetEnumerator();
336新增: if (enu.MoveNext())
337新增: {
338新增: var commandLine = enu.Current["CommandLine"];
339新增: string appId = Regex.Match(commandLine.ToString(), @"\{(.*?)\}").Value;
340新增: if (!string.IsNullOrEmpty(appId))
341新增: {
342新增:
343新增: }
344新增: }
345新增: }
346新增: catch (Exception ex)
347新增: {
348新增: Logger.Error(ex.ToString());
349新增: }
350新增: return null;
351新增: }
352新增: #endregion
353新增: }
354新增:}
修改Core/Servicers/Instances/AppObserver.cs+70 −139看文件
@@ -11,176 +11,107 @@ using System.Runtime.InteropServices;
1111using System.Text;
1212using System.Threading;
1313using System.Threading.Tasks;
14新增:using System.Timers;
1415
1516namespace Core.Servicers.Instances
1617{
1718 public class AppObserver : IAppObserver
1819 {
19删除: private IntPtr hook;
20删除: Win32API.WinEventDelegate winEventDelegate;
21删除:
22删除: public event ObserverEventHandler OnAppActive;
23删除:
24删除: public AppObserver()
20新增: public event AppObserverEventHandler OnAppActiveChanged;
21新增:
22新增: #region win32 api
23新增: public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType,
24新增: IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
25新增: [DllImport("user32.dll")]
26新增: public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr
27新增: hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess,
28新增: uint idThread, uint dwFlags);
29新增: [DllImport("user32.dll")]
30新增: public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
31新增: #endregion
32新增:
33新增: // 获得焦点事件
34新增: private WinEventDelegate _foregroundEventDelegate;
35新增: private readonly IAppManager _appManager;
36新增: private readonly IWindowManager _windowManager;
37新增: private IntPtr _hook;
38新增: private bool _isStart = false;
39新增: private System.Timers.Timer _delayDetectTimer;
40新增: public AppObserver(IAppManager appManager_, IWindowManager windowManager)
2541 {
26删除: winEventDelegate = new Win32API.WinEventDelegate(WinEventProc);
42新增: _appManager = appManager_;
43新增: _windowManager = windowManager;
44新增: _foregroundEventDelegate = new WinEventDelegate(ForegroundEventCallback);
45新增: _delayDetectTimer = new System.Timers.Timer();
46新增: _delayDetectTimer.Interval = 1000;
47新增: _delayDetectTimer.Elapsed += DelayDetectTimer_Elapsed;
2748 }
28删除: public void Start()
29删除: {
30删除: hook = Win32API.SetWinEventHook(0x0003, 0x0003, IntPtr.Zero,
31删除: winEventDelegate, 0, 0, 0);
3249
33删除: var window = Win32API.GetForegroundWindow();
34删除: Handle(window);
35删除: }
3650
37删除: private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
38删除: {
39删除: Handle(hwnd);
40删除: }
4151
42删除: private async void Handle(IntPtr hwnd)
52新增: private void ForegroundEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
4353 {
44删除: string processName = String.Empty, processFileName = String.Empty, processDescription = String.Empty;
45删除:
46删除: var processResult = await Task.Run(() =>
54新增: _delayDetectTimer?.Stop();
55新增: DateTime activeTime = DateTime.Now;
56新增: var stopwatch = Stopwatch.StartNew();
57新增: var args = GetAppInfoEventArgs(hwnd, activeTime);
58新增: Debug.WriteLine(activeTime);
59新增: Debug.WriteLine(args.App.ToString());
60新增: // 响应事件
61新增: OnAppActiveChanged?.Invoke(this, args);
62新增: if (args.App.Type == Enums.AppType.SystemComponent)
4763 {
48删除: try
49删除: {
50删除: string[] processInfo = GetProcessInfoByWindowHandle(hwnd);
51删除: int processID = int.Parse(processInfo[2]);
52删除: processName = processInfo[0];
53删除: processFileName = processInfo[1];
54删除:
55删除:
56删除: // handle uwp app
57删除: if (processFileName.IndexOf("ApplicationFrameHost.exe") != -1)
58删除: {
59删除: processFileName = Win32API.UWP_AppName(hwnd, (uint)processID);
60删除: if (processFileName == null)
61删除: {
62删除: Thread.Sleep(1500);
63删除:
64删除: processFileName = Win32API.UWP_AppName(hwnd, (uint)processID);
65删除: processName = GetUWPAPPName(processFileName);
66删除:
67删除: IntPtr foregeroundHwnd = Win32API.GetForegroundWindow();
68删除:
69删除: if (foregeroundHwnd != hwnd)
70删除: {
71删除: // 获取后焦点变了
72删除: processInfo = GetProcessInfoByWindowHandle(foregeroundHwnd);
73删除: processID = int.Parse(processInfo[2]);
74删除: processName = processInfo[0];
75删除: processFileName = processInfo[1];
76删除: }
77删除:
78删除: }
79删除: else
80删除: {
81删除: processName = GetUWPAPPName(processFileName);
82删除: }
83删除:
84删除: }
85删除: FileVersionInfo info = FileVersionInfo.GetVersionInfo(processFileName);
86删除: processDescription = info.FileDescription;
87删除:
88删除:
89删除: int titleCapacity = Win32API.GetWindowTextLength(hwnd) * 2;
90删除: StringBuilder stringBuilder = new StringBuilder(titleCapacity);
91删除: Win32API.GetWindowText(hwnd, stringBuilder, stringBuilder.Capacity);
92删除:
93删除:
94删除: if (IsSystemProcess(processName, stringBuilder.ToString()))
95删除: {
96删除: // 过滤掉一些系统进程
97删除: processName = string.Empty;
98删除: }
99删除: }
100删除: catch (Exception e)
101删除: {
102删除: Logger.Warn(e.Message + " | Process Name:" + processName + " | Process File:" + processFileName + " | Process Description:" + processDescription);
103删除: }
104删除: return processName;
105删除: });
64新增: _delayDetectTimer?.Start();
65新增: }
10666
107删除: EventInvoke(processName, processDescription, processFileName, hwnd);
10867 }
10968
110删除: /// <summary>
111删除: /// 指示指定进程是否属于系统进程
112删除: /// </summary>
113删除: /// <param name="processName">进程名称</param>
114删除: /// <param name="title">窗口标题</param>
115删除: /// <returns></returns>
116删除: private bool IsSystemProcess(string processName, string title)
69新增: private void DelayDetectTimer_Elapsed(object sender, ElapsedEventArgs e)
11770 {
118删除: processName = processName.ToLower();
119删除: int length = title.Length;
120删除: string[] titleArr = { "UnlockingWindow", "Program Manager" };
121删除:
122删除: if (processName == "explorer")
123删除: {
124删除: if (length == 0)
125删除: {
126删除: // 可能是任务栏
127删除: return true;
128删除: }
129删除: return titleArr.Contains(title);
130删除:
131删除: }
132删除: return false;
71新增: _delayDetectTimer?.Stop();
72新增: DelayDetect();
13373 }
13474
135删除: private string GetUWPAPPName(string processFileName)
75新增: private void DelayDetect()
13676 {
137删除: string processName = string.Empty;
138删除: if (!string.IsNullOrEmpty(processFileName) && processFileName.IndexOf("\\") != -1)
77新增: DateTime activeTime = DateTime.Now;
78新增: IntPtr w = Win32API.GetForegroundWindow();
79新增: var args = GetAppInfoEventArgs(w, activeTime);
80新增: if (args.App.Type != Enums.AppType.SystemComponent)
13981 {
140删除: processName = processFileName.Split('\\').Last();
141删除: processName = processName.Replace(".exe", "");
82新增: // 响应事件
83新增: OnAppActiveChanged?.Invoke(this, args);
14284 }
143删除: return processName;
14485 }
145删除: /// <summary>
146删除: /// 通过窗口句柄获取进程信息
147删除: /// </summary>
148删除: /// <param name="hwnd"></param>
149删除: /// <returns></returns>
150删除: private string[] GetProcessInfoByWindowHandle(IntPtr hwnd)
151删除: {
152删除: string processName, processFileName = string.Empty;
15386
154删除: int processID = 0;
155删除: Win32API.GetWindowThreadProcessId(hwnd, out processID);
156删除: Process process = Process.GetProcessById(processID);
157删除: processName = process.ProcessName;
87新增: private AppActiveChangedEventArgs GetAppInfoEventArgs(IntPtr handle_, DateTime activeTime_)
88新增: {
89新增: var app = _appManager.GetAppInfo(handle_);
90新增: var window = _windowManager.GetWindowInfo(handle_);
91新增: return new AppActiveChangedEventArgs(app, window, activeTime_);
92新增: }
15893
159删除: IntPtr processHandle = IntPtr.Zero;
160删除: processHandle = Win32API.OpenProcess(0x001F0FFF, false, processID);
94新增: private void HandleForegroundWindow()
95新增: {
96新增: IntPtr w = Win32API.GetForegroundWindow();
97新增: ForegroundEventCallback(IntPtr.Zero, 0, w, 0, 0, 0, 0);
98新增: }
16199
162删除: if (processHandle != IntPtr.Zero)
163删除: {
164删除: int MaxPathLength = 4096;
165删除: var buffer = new StringBuilder(MaxPathLength);
166删除: Win32API.QueryFullProcessImageName(processHandle, 0, buffer, ref MaxPathLength);
167删除: processFileName = buffer.ToString();
168删除: }
169删除: else
100新增: public void Start()
101新增: {
102新增: if (_isStart)
170103 {
171删除: Logger.Warn($"无法获取进程:{processName}的句柄,请尝试以管理员身份运行Tai。");
104新增: return;
172105 }
173删除: return new string[] { processName, processFileName, processID.ToString() };
174删除: }
175删除: private void EventInvoke(string processName, string description, string filename, IntPtr handle)
176删除: {
177删除: var args = new AppObserverEventArgs(processName, description, filename, handle);
178删除: OnAppActive?.Invoke(args);
106新增: _isStart = true;
107新增: _hook = SetWinEventHook(0x0003, 0x0003, IntPtr.Zero, _foregroundEventDelegate, 0, 0, 0);
108新增: HandleForegroundWindow();
179109 }
180110
181111 public void Stop()
182112 {
183删除: Win32API.UnhookWinEvent(hook);
113新增: _isStart = false;
114新增: UnhookWinEvent(_hook);
184115 }
185116 }
186117}
新增Core/Servicers/Instances/AppTimerServicer.cs+170 −0看文件
@@ -0,0 +1,170 @@
1新增:using Core.Enums;
2新增:using Core.Event;
3新增:using Core.Models.AppObserver;
4新增:using Core.Servicers.Interfaces;
5新增:using System;
6新增:using System.Collections.Generic;
7新增:using System.Diagnostics;
8新增:using System.Linq;
9新增:using System.Text;
10新增:using System.Threading.Tasks;
11新增:using System.Timers;
12新增:
13新增:namespace Core.Servicers.Instances
14新增:{
15新增: public class AppTimerServicer : IAppTimerServicer
16新增: {
17新增: public event AppTimerEventHandler OnAppDurationUpdated;
18新增:
19新增: private readonly IAppObserver _appObserver;
20新增:
21新增: private bool _isStart = false;
22新增: private int _appDuration = 0;
23新增: private DateTime _startTime = DateTime.MinValue;
24新增: private DateTime _endTime = DateTime.MinValue;
25新增: private string _activeProcess;
26新增:
27新增: private Dictionary<string, AppData> _appData;
28新增: private System.Timers.Timer _timer;
29新增: private AppDurationUpdatedEventArgs _lastInvokeEventArgs;
30新增:
31新增: public struct AppData
32新增: {
33新增: public AppInfo App { get; set; }
34新增: public WindowInfo Window { get; set; }
35新增:
36新增: }
37新增: public AppTimerServicer(IAppObserver appObserver_)
38新增: {
39新增: _appObserver = appObserver_;
40新增:
41新增: }
42新增:
43新增: private void Init()
44新增: {
45新增: _appData = new Dictionary<string, AppData>();
46新增: _appDuration = 0;
47新增: _activeProcess = string.Empty;
48新增:
49新增: _timer = new System.Timers.Timer();
50新增: _timer.Interval = 1000;
51新增: _timer.Elapsed += Timer_Elapsed;
52新增: }
53新增:
54新增:
55新增:
56新增: public void Start()
57新增: {
58新增: if (_isStart) { return; }
59新增:
60新增: Init();
61新增:
62新增: _isStart = true;
63新增: _appObserver.OnAppActiveChanged += AppObserver_OnAppActiveChanged;
64新增: }
65新增:
66新增:
67新增: public void Stop()
68新增: {
69新增: _isStart = false;
70新增: StopTimer();
71新增: _appObserver.OnAppActiveChanged -= AppObserver_OnAppActiveChanged;
72新增: }
73新增:
74新增: private void AppObserver_OnAppActiveChanged(object sender, AppActiveChangedEventArgs e)
75新增: {
76新增: string processName = e.App.Process;
77新增: bool isStatistical = IsStatistical(e.App);
78新增:
79新增: Debug.WriteLine(processName+ " -> " + _activeProcess);
80新增: if (processName != _activeProcess)
81新增: {
82新增: StopTimer();
83新增: InvokeEvent();
84新增:
85新增: if (isStatistical)
86新增: {
87新增: StartTimer();
88新增: }
89新增:
90新增: _activeProcess = e.App.Type == AppType.SystemComponent ? string.Empty : processName;
91新增: }
92新增:
93新增: if (isStatistical)
94新增: {
95新增: var data = new AppData()
96新增: {
97新增: App = e.App,
98新增: Window = e.Window,
99新增: };
100新增: if (!_appData.ContainsKey(processName))
101新增: {
102新增: // 缓存应用信息
103新增: _appData.Add(processName, data);
104新增: }
105新增: else
106新增: {
107新增: // 更新缓存
108新增: _appData[processName] = data;
109新增: }
110新增: }
111新增: }
112新增:
113新增: private void Timer_Elapsed(object sender, ElapsedEventArgs e)
114新增: {
115新增: _appDuration++;
116新增: }
117新增:
118新增: private void StartTimer()
119新增: {
120新增: _timer.Start();
121新增: _startTime = DateTime.Now;
122新增: _appDuration = 0;
123新增: }
124新增:
125新增: private void StopTimer()
126新增: {
127新增: _timer.Stop();
128新增: _endTime = DateTime.Now;
129新增: }
130新增:
131新增: private void InvokeEvent()
132新增: {
133新增: var info = GetAppDuration();
134新增: if (info != null)
135新增: {
136新增: if (_lastInvokeEventArgs?.ActiveTime.ToString() != _startTime.ToString() || _lastInvokeEventArgs?.EndTime.ToString() != _endTime.ToString())
137新增: {
138新增: Debug.WriteLine("【计时更新】" + info);
139新增: OnAppDurationUpdated?.Invoke(this, info);
140新增: }
141新增: else
142新增: {
143新增: Debug.WriteLine("【重复!!】" + _lastInvokeEventArgs + ",【now】" + info);
144新增: }
145新增: _lastInvokeEventArgs = info;
146新增: }
147新增: }
148新增:
149新增: /// <summary>
150新增: /// 判断应用是否需要被统计
151新增: /// </summary>
152新增: /// <param name="app_"></param>
153新增: /// <returns>需要统计时返回 true </returns>
154新增: private bool IsStatistical(AppInfo app_)
155新增: {
156新增: return !string.IsNullOrEmpty(app_.Process) && app_.Type != AppType.SystemComponent && !string.IsNullOrEmpty(app_.ExecutablePath);
157新增: }
158新增:
159新增: public AppDurationUpdatedEventArgs GetAppDuration()
160新增: {
161新增: if (_appDuration > 0 && !string.IsNullOrEmpty(_activeProcess) && _appData.ContainsKey(_activeProcess))
162新增: {
163新增: var data = _appData[_activeProcess];
164新增: var args = new AppDurationUpdatedEventArgs(_appDuration, data.App, data.Window, _startTime, _endTime);
165新增: return args;
166新增: }
167新增: return null;
168新增: }
169新增: }
170新增:}
修改Core/Servicers/Instances/Data.cs+114 −64看文件
@@ -14,6 +14,7 @@ using System.IO;
1414using CsvHelper;
1515using System.Globalization;
1616using System.Threading.Tasks;
17新增:using System.Windows;
1718
1819namespace Core.Servicers.Instances
1920{
@@ -28,100 +29,149 @@ namespace Core.Servicers.Instances
2829 _database = database_;
2930 }
3031
31删除: public void SaveAppDuration(string processName_, int duration_, DateTime startDateTime_)
32新增: public void UpdateAppDuration(string process_, int duration_, DateTime startTime_)
3233 {
33删除: // 过滤无效数据
34删除: if (string.IsNullOrEmpty(processName_) || duration_ <= 0) return;
34新增: // 过滤无效值
35新增: if (string.IsNullOrEmpty(process_) || duration_ <= 0 || startTime_ == DateTime.MinValue) return;
36新增:
37新增: Logger.Info($"UpdateAppDuration,process:{process_},duration:{duration_},start:{startTime_}");
38新增: // 开始时间剩余最大统计时长
39新增: int startTimeMaxHoursDuration = (59 - startTime_.Minute) * 60 + (60 - startTime_.Second);
40新增: // 开始时间使用时长
41新增: int startTimeHoursDuration = duration_ > startTimeMaxHoursDuration ? startTimeMaxHoursDuration : duration_;
42新增: // 剩余时长
43新增: int outHoursDuration = duration_ - startTimeHoursDuration;
44新增: // 结束时间
45新增: DateTime endTime = new DateTime(startTime_.Year, startTime_.Month, startTime_.Day, startTime_.Hour, 0, 0);
46新增: // 时段使用数据
47新增: Dictionary<DateTime, int> durationHoursData = new Dictionary<DateTime, int>
48新增: {
49新增: { endTime, startTimeHoursDuration }
50新增: };
51新增: // 计算时段数据
52新增: if (outHoursDuration > 0)
53新增: {
54新增: int outHours = outHoursDuration / 3600;
55新增:
56新增: DateTime outStartTime = new DateTime(startTime_.Year, startTime_.Month, startTime_.Day, startTime_.Hour, 0, 0);
57新增: for (int i = 0; i < outHours; i++)
58新增: {
59新增: outStartTime = outStartTime.AddHours(1);
60新增: int duration = 3600;
61新增: durationHoursData.Add(outStartTime, duration);
62新增: }
63新增: if (outHoursDuration % 3600 > 0)
64新增: {
65新增: outStartTime = outStartTime.AddHours(1);
66新增: int duration = outHoursDuration - outHours * 3600;
67新增: durationHoursData.Add(outStartTime, duration);
68新增: }
69新增: endTime = outStartTime;
70新增: }
3571
36删除: Task.Run(() =>
72新增: // 计算每日统计数据
73新增: DateTime nextDayTime = new DateTime(startTime_.Year, startTime_.Month, startTime_.Day, 0, 0, 0).AddDays(1);
74新增: int startTimeMaxDayDuration = (int)(nextDayTime - startTime_).TotalSeconds;
75新增: // 开始时间使用时长
76新增: int startTimeDayDuration = duration_ > startTimeMaxDayDuration ? startTimeMaxDayDuration : duration_;
77新增: // 剩余时长
78新增: int outDayDuration = duration_ - startTimeDayDuration;
79新增: // 结束时间
80新增: DateTime endDayTime = startTime_.Date;
81新增: // 每日使用数据
82新增: Dictionary<DateTime, int> durationDayData = new Dictionary<DateTime, int>
3783 {
38删除: try
84新增: { startTime_.Date, startTimeDayDuration }
85新增: };
86新增: if (outDayDuration > 0)
87新增: {
88新增: int outDays = outDayDuration / 86400;
89新增:
90新增: DateTime outStartTime = new DateTime(startTime_.Year, startTime_.Month, startTime_.Day, 0, 0, 0);
91新增: for (int i = 0; i < outDays; i++)
3992 {
40删除: if (startDateTime_.Minute == 59 && startDateTime_.Second >= 58)
41删除: {
42删除: // 当记录开始时间接近59分59秒时划到下一个小时时段
43删除: startDateTime_ = startDateTime_.AddSeconds(2);
44删除: startDateTime_ = new DateTime(startDateTime_.Year, startDateTime_.Month, startDateTime_.Day, startDateTime_.Hour, 0, 0);
45删除: }
93新增: outStartTime = outStartTime.AddDays(1);
94新增: int duration = 86400;
95新增: durationDayData.Add(outStartTime, duration);
96新增: }
97新增: if (outDayDuration % 86400 > 0)
98新增: {
99新增: outStartTime = outStartTime.AddDays(1);
100新增: int duration = outDayDuration - outDays * 86400;
101新增: durationDayData.Add(outStartTime, duration);
102新增: }
103新增: endDayTime = outStartTime.Date;
104新增: }
46105
47删除: using (var db = _database.GetWriterContext())
106新增: // 开始写入数据
107新增: try
108新增: {
109新增: using (var db = _database.GetWriterContext())
110新增: {
111新增: var app = db.App.Where(m => m.Name == process_).FirstOrDefault();
112新增: if (app == null)
48113 {
49删除: var app = db.App.Where(m => m.Name == processName_).FirstOrDefault();
50删除: if (app == null)
51删除: {
52删除: _database.CloseWriter();
53删除: return;
54删除: }
55删除:
56删除: // 当前时段最大可记录时长
57删除: int nowHoursMaxDuration = (60 - startDateTime_.Minute) * 60;
58删除: duration_ = duration_ > nowHoursMaxDuration ? nowHoursMaxDuration : duration_;
114新增: _database.CloseWriter();
115新增: return;
116新增: }
59117
60删除: // 更新app累计时长
61删除: app.TotalTime += duration_;
118新增: // 更新app累计总时长
119新增: app.TotalTime += duration_;
62120
63删除: // 更新每日数据
64删除: var dailyLog = db.DailyLog.SingleOrDefault(m => m.Date == startDateTime_.Date && m.AppModelID == app.ID);
65删除: if (dailyLog == null)
121新增: // 更新每日数据
122新增: var dailyLogs = db.DailyLog.Where(m => m.Date >= startTime_.Date && m.Date <= endDayTime && m.AppModelID == app.ID).ToList();
123新增: foreach (var item in durationDayData)
124新增: {
125新增: var log = dailyLogs.Where(m => m.Date == item.Key).FirstOrDefault();
126新增: if (log == null)
66127 {
67128 //数据库中没有时则创建
68129 db.DailyLog.Add(new DailyLogModel()
69130 {
70删除: Date = startDateTime_.Date,
131新增: Date = item.Key,
71132 AppModelID = app.ID,
72删除: Time = duration_ > 86400 ? 86400 : duration_,
133新增: Time = item.Value,
73134 });
74135 }
75136 else
76137 {
77删除: if (dailyLog.Time + duration_ > 86400)
78删除: {
79删除: dailyLog.Time = 86400;
80删除: }
81删除: else
82删除: {
83删除: dailyLog.Time += duration_;
84删除: }
138新增: int time = log.Time + item.Value;
139新增: log.Time = time > 86400 ? 86400 : time;
85140 }
86删除:
87删除: // 更新时段数据
88删除: var time = new DateTime(startDateTime_.Year, startDateTime_.Month, startDateTime_.Day, startDateTime_.Hour, 0, 0);
89删除: var hoursLog = db.HoursLog.SingleOrDefault(m => m.DataTime == time && m.AppModelID == app.ID);
90删除: if (hoursLog == null)
141新增: }
142新增: // 更新时段数据
143新增: var startDataTime = new DateTime(startTime_.Year, startTime_.Month, startTime_.Day, startTime_.Hour, 0, 0);
144新增: var hoursLogs = db.HoursLog.Where(m => m.DataTime >= startDataTime && startDataTime <= endTime && m.AppModelID == app.ID).ToList();
145新增: foreach (var item in durationHoursData)
146新增: {
147新增: var log = hoursLogs.Where(m => startDataTime == item.Key).FirstOrDefault();
148新增: if (log == null)
91149 {
92150 // 没有记录时创建
93151 db.HoursLog.Add(new HoursLogModel()
94152 {
95删除: DataTime = time,
153新增: DataTime = item.Key,
96154 AppModelID = app.ID,
97删除: Time = duration_
155新增: Time = item.Value
98156 });
99157 }
100158 else
101159 {
102删除: if (hoursLog.Time + duration_ > 3600)
103删除: {
104删除: hoursLog.Time = 3600;
105删除: }
106删除: else
107删除: {
108删除: hoursLog.Time += duration_;
109删除: }
160新增: int time = log.Time + item.Value;
161新增: log.Time = time > 3600 ? 3600 : time;
110162 }
111删除:
112删除: db.SaveChanges();
113删除: Logger.Info($"Save app time done.Process:{processName_},Duration:{duration_},StartDateTime:{startDateTime_},AppID:{app.ID}");
114163 }
164新增: db.SaveChanges();
115165 }
116删除: catch (Exception e)
117删除: {
118删除: Logger.Error($"Save app time error!Process:{processName_},Duration:{duration_},StartDateTime:{startDateTime_}.\r\nError:\r\n{e.Message}");
119删除: }
120删除: finally
121删除: {
122删除: _database.CloseWriter();
123删除: }
124删除: });
166新增: }
167新增: catch (Exception e)
168新增: {
169新增: Logger.Error($"UpdateAppDuration error!Process:{process_},Duration:{duration_},StartDateTime:{startTime_}.\r\nError:\r\n{e.Message}");
170新增: }
171新增: finally
172新增: {
173新增: _database.CloseWriter();
174新增: }
125175 }
126176
127177 public List<DailyLogModel> GetTodaylogList()
修改Core/Servicers/Instances/Main.cs+43 −124看文件
@@ -1,4 +1,5 @@
11using Core.Enums;
2新增:using Core.Event;
23using Core.Librarys;
34using Core.Librarys.SQLite;
45using Core.Models;
@@ -22,14 +23,14 @@ namespace Core.Servicers.Instances
2223 public class Main : IMain
2324 {
2425 private readonly IAppObserver appObserver;
25删除: private readonly IBrowserObserver browserObserver;
2626 private readonly IData data;
2727 private readonly ISleepdiscover sleepdiscover;
2828 private readonly IAppConfig appConfig;
29删除: private readonly IDateTimeObserver dateTimeObserver;
3029 private readonly IAppData appData;
3130 private readonly ICategorys categories;
3231 private readonly IWebFilter _webFilter;
32新增: private readonly IAppTimerServicer _appTimer;
33新增: private readonly IWebServer _webServer;
3334 // 忽略的进程
3435 private readonly string[] DefaultIgnoreProcess = new string[] {
3536 "Tai",
@@ -45,16 +46,6 @@ namespace Core.Servicers.Instances
4546 "SystemSettingsAdminFlows"
4647 };
4748
48删除: /// <summary>
49删除: /// 当前聚焦进程
50删除: /// </summary>
51删除: private string activeProcess = null;
52删除:
53删除: /// <summary>
54删除: /// 焦点开始时间
55删除: /// </summary>
56删除: private DateTime activeStartTime = DateTime.Now;
57删除:
5849 /// <summary>
5950 /// 睡眠状态
6051 /// </summary>
@@ -92,42 +83,27 @@ namespace Core.Servicers.Instances
9283 IAppConfig appConfig,
9384 IDateTimeObserver dateTimeObserver,
9485 IAppData appData, ICategorys categories,
95删除: IBrowserObserver browserObserver,
96删除: IWebFilter webFilter_)
86新增: IWebFilter webFilter_,
87新增: IAppTimerServicer appTimer_,
88新增: IWebServer webServer_)
9789 {
9890 this.appObserver = appObserver;
9991 this.data = data;
10092 this.sleepdiscover = sleepdiscover;
10193 this.appConfig = appConfig;
102删除: this.dateTimeObserver = dateTimeObserver;
10394 this.appData = appData;
10495 this.categories = categories;
105删除: this.browserObserver = browserObserver;
10696 _webFilter = webFilter_;
97新增: _appTimer = appTimer_;
98新增: _webServer = webServer_;
10799
108100 IgnoreProcessCacheList = new List<string>();
109101 ConfigIgnoreProcessRegxList = new List<string>();
110102 ConfigIgnoreProcessList = new List<string>();
111103
112删除: appObserver.OnAppActive += AppObserver_OnAppActive;
113104 sleepdiscover.SleepStatusChanged += Sleepdiscover_SleepStatusChanged;
114105 appConfig.ConfigChanged += AppConfig_ConfigChanged;
115删除: dateTimeObserver.OnDateTimeChanging += DateTimeObserver_OnDateTimeChanging;
116删除: }
117删除:
118删除:
119删除:
120删除: private void DateTimeObserver_OnDateTimeChanging(object sender, DateTime time)
121删除: {
122删除: Logger.Info("[time changed] status:" + sleepStatus + ",process:" + activeProcess + ",start:" + activeStartTime.ToString() + ",end:" + DateTime.Now.ToString() + ",time:" + time.ToString());
123删除: if (sleepStatus == SleepStatus.Wake)
124删除: {
125删除: UpdateTime();
126删除: }
127删除: else
128删除: {
129删除: activeStartTime = DateTime.Now;
130删除: }
106新增: _appTimer.OnAppDurationUpdated += _appTimer_OnAppDurationUpdated;
131107 }
132108
133109 private void AppConfig_ConfigChanged(ConfigModel oldConfig, ConfigModel newConfig)
@@ -188,21 +164,16 @@ namespace Core.Servicers.Instances
188164 }
189165 public void Start()
190166 {
191删除: activeStartTime = DateTime.Now;
192删除: activeProcess = null;
193删除:
194删除: dateTimeObserver.Start();
167新增: // appTimer必须比Observer先启动*
168新增: _appTimer.Start();
195169 appObserver.Start();
196删除:
197删除: if (config.General.IsWebEnabled)
198删除: {
199删除: browserObserver.Start();
200删除: }
170新增: _webServer.Start();
201171 }
202172 public void Stop()
203173 {
204删除: dateTimeObserver.Stop();
205174 appObserver.Stop();
175新增: _appTimer.Stop();
176新增: _webServer.Stop();
206177 }
207178 public void Exit()
208179 {
@@ -241,7 +212,7 @@ namespace Core.Servicers.Instances
241212 {
242213 this.sleepStatus = sleepStatus;
243214
244删除: Logger.Info($"[{sleepStatus}] process:{activeProcess},start:{activeStartTime}");
215新增: Logger.Info($"[{sleepStatus}]");
245216 if (sleepStatus == SleepStatus.Sleep)
246217 {
247218 // 进入睡眠状态
@@ -251,7 +222,7 @@ namespace Core.Servicers.Instances
251222 Stop();
252223
253224 // 更新时间
254删除: UpdateTime();
225新增: UpdateAppDuration();
255226 }
256227 else
257228 {
@@ -337,39 +308,6 @@ namespace Core.Servicers.Instances
337308 return true;
338309 }
339310
340删除: private void AppObserver_OnAppActive(Models.AppObserver.AppObserverEventArgs args)
341删除: {
342删除: if (sleepStatus == SleepStatus.Sleep)
343删除: {
344删除: return;
345删除: }
346删除:
347删除: string lastActiveProcess = activeProcess != null ? activeProcess.ToString() : "";
348删除:
349删除: bool isCheck = IsCheckApp(args.ProcessName, args.Description, args.File);
350删除:
351删除: Logger.Info($"Active[{isCheck}]:" + args.ProcessName + ",Last:" + lastActiveProcess + ",Time:" + activeStartTime.ToString());
352删除:
353删除: if (activeProcess != args.ProcessName)
354删除: {
355删除: UpdateTime();
356删除: }
357删除:
358删除: if (isCheck)
359删除: {
360删除: if (activeProcess == null)
361删除: {
362删除: activeStartTime = DateTime.Now;
363删除: }
364删除: activeProcess = args.ProcessName;
365删除: }
366删除: else
367删除: {
368删除: activeProcess = null;
369删除: }
370删除:
371删除: }
372删除:
373311 private void HandleLinks(string processName, int seconds, DateTime time)
374312 {
375313 Task.Run(() =>
@@ -391,7 +329,7 @@ namespace Core.Servicers.Instances
391329 if (IsProcessRuning(linkProcess))
392330 {
393331 // 同步更新
394删除: data.SaveAppDuration(linkProcess, seconds, time);
332新增: data.UpdateAppDuration(linkProcess, seconds, time);
395333 }
396334
397335 }
@@ -421,44 +359,6 @@ namespace Core.Servicers.Instances
421359 }
422360 #endregion
423361
424删除: private void UpdateTime()
425删除: {
426删除: string app = activeProcess != null ? activeProcess.ToString() : "";
427删除:
428删除: if (!string.IsNullOrEmpty(app))
429删除: {
430删除: var time = new DateTime(activeStartTime.Ticks);
431删除:
432删除: // 更新计时
433删除: TimeSpan timeSpan = DateTime.Now - time;
434删除: int seconds = (int)timeSpan.TotalSeconds;
435删除:
436删除: if (seconds > 3600)
437删除: {
438删除: Logger.Warn($"计时异常。start:{time},process:{app},duration:{seconds},status:{sleepStatus}");
439删除: activeStartTime = DateTime.Now;
440删除: return;
441删除: }
442删除:
443删除: // 如果是休眠状态要减去5分钟
444删除: if (sleepStatus == SleepStatus.Sleep)
445删除: {
446删除: seconds -= 300;
447删除: }
448删除:
449删除: if (seconds > 0)
450删除: {
451删除: data.SaveAppDuration(app, seconds, time);
452删除:
453删除: // 关联进程更新
454删除: HandleLinks(app, seconds, time);
455删除: }
456删除:
457删除: activeStartTime = DateTime.Now;
458删除: OnUpdateTime?.Invoke(this, null);
459删除: }
460删除: }
461删除:
462362 #region 处理网站数据记录配置项开关
463363 /// <summary>
464364 /// 处理网站数据记录配置项开关
@@ -469,17 +369,36 @@ namespace Core.Servicers.Instances
469369 {
470370 return;
471371 }
372新增: }
373新增: #endregion
472374
473删除: if (config.General.IsWebEnabled)
474删除: {
475删除: browserObserver.Start();
476删除: }
477删除: else
375新增: private void _appTimer_OnAppDurationUpdated(object sender, Event.AppDurationUpdatedEventArgs e)
376新增: {
377新增: UpdateAppDuration(e);
378新增: }
379新增:
380新增: private void UpdateAppDuration()
381新增: {
382新增: UpdateAppDuration(_appTimer.GetAppDuration());
383新增: }
384新增: private void UpdateAppDuration(AppDurationUpdatedEventArgs e)
385新增: {
386新增: if (e == null) return;
387新增:
388新增: var app = e.App;
389新增: int duration = e.Duration;
390新增: DateTime startTime = e.ActiveTime;
391新增:
392新增: bool isCheck = IsCheckApp(app.Process, app.Description, app.ExecutablePath);
393新增: if (isCheck)
478394 {
479删除: browserObserver.Stop();
395新增: // 更新统计时长
396新增: data.UpdateAppDuration(app.Process, duration, startTime);
397新增: // 关联进程更新
398新增: HandleLinks(app.Process, duration, startTime);
399新增: OnUpdateTime?.Invoke(this, null);
480400 }
481401 }
482删除: #endregion
483402
484403 }
485404}
新增Core/Servicers/Instances/WindowManager.cs+57 −0看文件
@@ -0,0 +1,57 @@
1新增:using Core.Librarys;
2新增:using Core.Models.AppObserver;
3新增:using Core.Servicers.Interfaces;
4新增:using System;
5新增:using System.Collections.Generic;
6新增:using System.Linq;
7新增:using System.Text;
8新增:using System.Threading.Tasks;
9新增:
10新增:namespace Core.Servicers.Instances
11新增:{
12新增: public class WindowManager : IWindowManager
13新增: {
14新增:
15新增: public WindowInfo GetWindowInfo(IntPtr handle_)
16新增: {
17新增: try
18新增: {
19新增: string title = GetWindowTitle(handle_);
20新增: var rect = Win32API.GetWindowRect(handle_);
21新增: string className = Win32API.GetWindowClassName(handle_);
22新增: int width = rect.Width;
23新增: int height = rect.Height;
24新增: int x = rect.Left;
25新增: int y = rect.Top;
26新增:
27新增: return new WindowInfo(className, title, handle_, width, height, x, y);
28新增: }
29新增: catch (Exception e)
30新增: {
31新增: return WindowInfo.Empty;
32新增: }
33新增:
34新增: }
35新增:
36新增: /// <summary>
37新增: /// 获取窗口标题
38新增: /// </summary>
39新增: /// <param name="handle_"></param>
40新增: /// <returns></returns>
41新增: private string GetWindowTitle(IntPtr handle_)
42新增: {
43新增: try
44新增: {
45新增: int titleCapacity = Win32API.GetWindowTextLength(handle_) * 2;
46新增: StringBuilder stringBuilder = new StringBuilder(titleCapacity);
47新增: Win32API.GetWindowText(handle_, stringBuilder, stringBuilder.Capacity);
48新增: string title = stringBuilder.ToString();
49新增: return stringBuilder.ToString();
50新增: }
51新增: catch (Exception e)
52新增: {
53新增: return string.Empty;
54新增: }
55新增: }
56新增: }
57新增:}
新增Core/Servicers/Interfaces/IAppManager.cs+20 −0看文件
@@ -0,0 +1,20 @@
1新增:using Core.Models.AppObserver;
2新增:using NPOI.XSSF.Model;
3新增:using System;
4新增:using System.Collections.Generic;
5新增:using System.Linq;
6新增:using System.Text;
7新增:using System.Threading.Tasks;
8新增:
9新增:namespace Core.Servicers.Interfaces
10新增:{
11新增: public interface IAppManager
12新增: {
13新增: /// <summary>
14新增: /// 通过窗口句柄获取应用信息
15新增: /// </summary>
16新增: /// <param name="hwnd_">窗口句柄</param>
17新增: /// <returns></returns>
18新增: AppInfo GetAppInfo(IntPtr hwnd_);
19新增: }
20新增:}
修改Core/Servicers/Interfaces/IAppObserver.cs+1 −1看文件
@@ -24,6 +24,6 @@ namespace Core.Servicers.Interfaces
2424 /// <summary>
2525 /// 切换活跃APP时发生
2626 /// </summary>
27删除: event ObserverEventHandler OnAppActive;
27新增: event AppObserverEventHandler OnAppActiveChanged;
2828 }
2929}
新增Core/Servicers/Interfaces/IAppTimerServicer.cs+31 −0看文件
@@ -0,0 +1,31 @@
1新增:using Core.Event;
2新增:using System;
3新增:using System.Collections.Generic;
4新增:using System.Linq;
5新增:using System.Text;
6新增:using System.Threading.Tasks;
7新增:
8新增:namespace Core.Servicers.Interfaces
9新增:{
10新增: public interface IAppTimerServicer
11新增: {
12新增: /// <summary>
13新增: /// 当有应用的计时更新时发生
14新增: /// </summary>
15新增: event AppTimerEventHandler OnAppDurationUpdated;
16新增:
17新增: /// <summary>
18新增: /// 启动计时服务
19新增: /// </summary>
20新增: void Start();
21新增: /// <summary>
22新增: /// 停止计时服务
23新增: /// </summary>
24新增: void Stop();
25新增: /// <summary>
26新增: /// 获取当前应用的计时信息
27新增: /// </summary>
28新增: /// <returns></returns>
29新增: AppDurationUpdatedEventArgs GetAppDuration();
30新增: }
31新增:}
修改Core/Servicers/Interfaces/IData.cs+1 −1看文件
@@ -17,7 +17,7 @@ namespace Core.Servicers.Interfaces
1717 /// <param name="processName_">进程名称</param>
1818 /// <param name="duration_">时长(秒)</param>
1919 /// <param name="startDateTime_">记录开始时间</param>
20删除: void SaveAppDuration(string processName_, int duration_, DateTime startDateTime_);
20新增: void UpdateAppDuration(string processName_, int duration_, DateTime startDateTime_);
2121
2222 /// <summary>
2323 /// 获取今天的数据
新增Core/Servicers/Interfaces/IWindowManager.cs+20 −0看文件
@@ -0,0 +1,20 @@
1新增:using Core.Models.AppObserver;
2新增:using System;
3新增:using System.Collections.Generic;
4新增:using System.Linq;
5新增:using System.Text;
6新增:using System.Threading.Tasks;
7新增:using static Core.Librarys.Win32API;
8新增:
9新增:namespace Core.Servicers.Interfaces
10新增:{
11新增: public interface IWindowManager
12新增: {
13新增: /// <summary>
14新增: /// 获取窗口信息
15新增: /// </summary>
16新增: /// <param name="handle_">窗口句柄</param>
17新增: /// <returns></returns>
18新增: WindowInfo GetWindowInfo(IntPtr handle_);
19新增: }
20新增:}
修改UI/App.xaml.cs+5 −2看文件
@@ -92,8 +92,11 @@ namespace UI
9292 {
9393 // 核心服务
9494 services.AddSingleton<IDatabase, Database>();
95新增: services.AddSingleton<IAppManager, AppManager>();
96新增: services.AddSingleton<IWindowManager, WindowManager>();
97新增: services.AddSingleton<IAppTimerServicer, AppTimerServicer>();
9598 services.AddSingleton<IAppObserver, AppObserver>();
96删除: services.AddSingleton<IBrowserObserver, BrowserObserver>();
99新增: services.AddSingleton<IWebServer, WebServer>();
97100 services.AddSingleton<IMain, Main>();
98101 services.AddSingleton<IData, Data>();
99102 services.AddSingleton<IWebData, WebData>();
@@ -167,7 +170,7 @@ namespace UI
167170 isSelfStart = true;
168171 }
169172 }
170删除: main.Start(isSelfStart);
173新增: main.Start(isSelfStart);
171174
172175 // 创建保活窗口
173176 keepaliveWindow = new HideWindow();
修改UI/Servicers/StatusBarIconServicer.cs+3 −2看文件
@@ -67,11 +67,12 @@ namespace UI.Servicers
6767
6868 WatchStateAsync();
6969
70删除: _appObserver.OnAppActive += _appObserver_OnAppActive;
70新增: _appObserver.OnAppActiveChanged += _appObserver_OnAppActiveChanged; ;
7171 _themeServicer.OnThemeChanged += _themeServicer_OnThemeChanged;
7272 Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
7373 }
7474
75新增:
7576 private void Current_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
7677 {
7778 _statusBarIcon.Visible = false;
@@ -83,7 +84,7 @@ namespace UI.Servicers
8384 _contextMenu.UpdateDefaultStyle();
8485 }
8586
86删除: private void _appObserver_OnAppActive(Core.Models.AppObserver.AppObserverEventArgs args)
87新增: private void _appObserver_OnAppActiveChanged(object sender, Core.Event.AppActiveChangedEventArgs e)
8788 {
8889 Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
8990 {