让用户在C#中空闲时间?

我找到了关于如何获得用户空闲时间的空闲时间的教程。

问题是它只有在应用程序在用户上运行时才有效。

我的应用程序在SYSTEM上运行。

我怎样才能获得空闲时间? 或者如果PC空闲?

据我所知,你可以使用GetLastInputInfo函数的结果。 因此,我建议如下。

现在,假设您有在本地系统帐户下运行的可执行文件A. 创建可执行文件B,它将收集相关的系统信息(在GetLastInputInfo的帮助下)。 接下来,使用此类使用CreateProcessAsUser函数和已记录的用户令牌从可执行文件A运行可执行文件B(遗憾的是我无法找到发布它的stackoverflow问题):

using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace Helpers { [StructLayout(LayoutKind.Sequential)] internal struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } [StructLayout(LayoutKind.Sequential)] internal struct SECURITY_ATTRIBUTES { public uint nLength; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } [StructLayout(LayoutKind.Sequential)] public struct STARTUPINFO { public uint cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } internal enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } internal enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } public class ImpersonateProcessAsLoggedUser { private const short SW_SHOW = 5; private const uint TOKEN_QUERY = 0x0008; private const uint TOKEN_DUPLICATE = 0x0002; private const uint TOKEN_ASSIGN_PRIMARY = 0x0001; private const int GENERIC_ALL_ACCESS = 0x10000000; private const int STARTF_USESHOWWINDOW = 0x00000001; private const int STARTF_FORCEONFEEDBACK = 0x00000040; private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400; [DllImport("advapi32.dll", SetLastError = true)] private static extern bool CreateProcessAsUser( IntPtr hToken, string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)] private static extern bool DuplicateTokenEx( IntPtr hExistingToken, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, Int32 ImpersonationLevel, Int32 dwTokenType, ref IntPtr phNewToken); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool OpenProcessToken( IntPtr ProcessHandle, UInt32 DesiredAccess, ref IntPtr TokenHandle); [DllImport("userenv.dll", SetLastError = true)] private static extern bool CreateEnvironmentBlock( ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit); [DllImport("userenv.dll", SetLastError = true)] private static extern bool DestroyEnvironmentBlock( IntPtr lpEnvironment); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle( IntPtr hObject); private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock) { bool result = false; var pi = new PROCESS_INFORMATION(); var saProcess = new SECURITY_ATTRIBUTES(); var saThread = new SECURITY_ATTRIBUTES(); saProcess.nLength = (uint) Marshal.SizeOf(saProcess); saThread.nLength = (uint) Marshal.SizeOf(saThread); var si = new STARTUPINFO(); si.cb = (uint) Marshal.SizeOf(si); //if this member is NULL, the new process inherits the desktop //and window station of its parent process. If this member is //an empty string, the process does not inherit the desktop and //window station of its parent process; instead, the system //determines if a new desktop and window station need to be created. //If the impersonated user already has a desktop, the system uses the //existing desktop. si.lpDesktop = @"WinSta0\Default"; //Modify as needed si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK; si.wShowWindow = SW_SHOW; //Set other si properties as required. result = CreateProcessAsUser( token, null, cmdLine, ref saProcess, ref saThread, false, CREATE_UNICODE_ENVIRONMENT, envBlock, null, ref si, out pi); if (result == false) { int error = Marshal.GetLastWin32Error(); string message = String.Format("CreateProcessAsUser Error: {0}", error); Debug.WriteLine(message); } return result; } private static IntPtr GetPrimaryToken(int processId) { IntPtr token = IntPtr.Zero; IntPtr primaryToken = IntPtr.Zero; bool retVal = false; Process p = null; try { p = Process.GetProcessById(processId); } catch (ArgumentException) { string details = String.Format("ProcessID {0} Not Available", processId); Debug.WriteLine(details); throw; } //Gets impersonation token retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, ref token); if (retVal) { var sa = new SECURITY_ATTRIBUTES(); sa.nLength = (uint) Marshal.SizeOf(sa); //Convert the impersonation token into Primary token retVal = DuplicateTokenEx( token, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY, ref sa, (int) SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int) TOKEN_TYPE.TokenPrimary, ref primaryToken); //Close the Token that was previously opened. CloseHandle(token); if (retVal == false) { string message = String.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error()); Debug.WriteLine(message); } } else { string message = String.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error()); Debug.WriteLine(message); } //We'll Close this token after it is used. return primaryToken; } private static IntPtr GetEnvironmentBlock(IntPtr token) { IntPtr envBlock = IntPtr.Zero; bool retVal = CreateEnvironmentBlock(ref envBlock, token, false); if (retVal == false) { //Environment Block, things like common paths to My Documents etc. //Will not be created if "false" //It should not adversley affect CreateProcessAsUser. string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error()); Debug.WriteLine(message); } return envBlock; } public static bool Launch(string appCmdLine /*,int processId*/) { bool ret = false; //Either specify the processID explicitly //Or try to get it from a process owned by the user. //In this case assuming there is only one explorer.exe Process[] ps = Process.GetProcessesByName("explorer"); int processId = -1; //=processId if (ps.Length > 0) { processId = ps[0].Id; } if (processId > 1) { IntPtr token = GetPrimaryToken(processId); if (token != IntPtr.Zero) { IntPtr envBlock = GetEnvironmentBlock(token); ret = LaunchProcessAsUser(appCmdLine, token, envBlock); if (envBlock != IntPtr.Zero) DestroyEnvironmentBlock(envBlock); CloseHandle(token); } } return ret; } } } 

接下来,您需要找到一种方法将收集的信息从可执行文件B发送到可执行文件A.有很多方法可以执行此操作。 其中之一是.Net Remoting。 但是,您可以创建中间XML甚至文本文件。

也许这不是解决问题的最佳方法,但如果您需要更多本地系统< - >记录用户交互,您可以遵循一种模式。

我知道这个答案已被接受,但我只想补充一下,以防人们想在终端服务环境中做同样的事情。

Cassia是一个开源库,它将.NET包装器放在Windows终端服务API上。 我已将它用于我的服务器管理,它运行得非常好。 您可以通过调用ITerminalServicesSession.LastInputTime获取任何会话的空闲时间

执行此类操作的推荐方法是运行单独的应用程序,将用户会话数据传递给服务。 您可以在用户会话开始时通过将应用程序添加到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run下的注册表来配置应用程序

您可以在以下知识库文章中阅读有关此方法的更多详细信息

http://support.microsoft.com/kb/308403