监视桌面应用程序的最佳方法是什么?
我需要一些方法来监视桌面应用程序并在它死亡时重新启动它。
最初我假设最好的方法是从Windows服务监视/重启进程,直到我发现Vista Windows服务不应该与桌面交互
我已经看到了几个处理这个问题的问题,但是我看到的每个答案都涉及到某种被微软劝阻的黑客攻击,可能会在未来的操作系统更新中停止工作。
因此,Windows服务可能不再是一个选项。 我可能只是创建一个不同的桌面/控制台应用程序来做到这一点,但这种方式失败了。
在您看来,哪种方式最优雅?
编辑: 这既不是恶意软件也不是病毒。 需要监控的应用程序是一个可以在嵌入式系统上运行的媒体播放器,即使我试图涵盖所有可能的崩溃情况,我也不会冒着因意外错误而崩溃的风险(不会发生) 。 这个看门狗只是一个安全措施,万一其他一切都出错了。 此外,由于播放器将显示第三方闪存内容,所以额外的加号将是例如监视资源使用情况,并重新启动播放器,如果说,一些蹩脚的flash电影开始泄漏内存。
编辑2:我忘了提到,我想监视/重启的应用程序绝对不需要在LocalSystem帐户上运行,也不需要任何管理权限。 实际上,我更喜欢它使用当前记录的用户凭据运行。
最初我假设最好的方法是从Windows服务监视/重启进程…
你当然可以! 我前几次做过。 你可以开始学习如何看这个:
http://msdn.microsoft.com/en-us/windows7trainingcourse_win7session0isolation_topic2#_Toc243675529
还有这个:
http://www.codeproject.com/Articles/18367/Launch-your-application-in-Vista-under-the-local-s
实质上,您必须以SYSTEM身份运行程序,但使用当前用户的SessionID。
如果你感到懒惰,我想可能有一些很好的servlets可以让你找到你想要的东西。 尝试在www.codeproject.com上搜索。
我终于实现了@A_nto2建议的解决方案,它实现了我正在寻找的东西:我现在有一个监视进程列表的Windows服务,每当它们关闭时,它们会使用活动用户的凭据和会话自动重新启动,因此GUI是可见的。
但是,由于他发布的链接显示了VC ++代码,我正在为处理同一问题的任何人分享我的C#实现:
public static class ProcessExtensions { public enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } [StructLayout(LayoutKind.Sequential)] public class SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } public enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } [Flags] public enum CREATE_PROCESS_FLAGS : uint { NONE = 0x00000000, DEBUG_PROCESS = 0x00000001, DEBUG_ONLY_THIS_PROCESS = 0x00000002, CREATE_SUSPENDED = 0x00000004, DETACHED_PROCESS = 0x00000008, CREATE_NEW_CONSOLE = 0x00000010, NORMAL_PRIORITY_CLASS = 0x00000020, IDLE_PRIORITY_CLASS = 0x00000040, HIGH_PRIORITY_CLASS = 0x00000080, REALTIME_PRIORITY_CLASS = 0x00000100, CREATE_NEW_PROCESS_GROUP = 0x00000200, CREATE_UNICODE_ENVIRONMENT = 0x00000400, CREATE_SEPARATE_WOW_VDM = 0x00000800, CREATE_SHARED_WOW_VDM = 0x00001000, CREATE_FORCEDOS = 0x00002000, BELOW_NORMAL_PRIORITY_CLASS = 0x00004000, ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000, INHERIT_PARENT_AFFINITY = 0x00010000, INHERIT_CALLER_PRIORITY = 0x00020000, CREATE_PROTECTED_PROCESS = 0x00040000, EXTENDED_STARTUPINFO_PRESENT = 0x00080000, PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000, PROCESS_MODE_BACKGROUND_END = 0x00200000, CREATE_BREAKAWAY_FROM_JOB = 0x01000000, CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, CREATE_DEFAULT_ERROR_MODE = 0x04000000, CREATE_NO_WINDOW = 0x08000000, PROFILE_USER = 0x10000000, PROFILE_KERNEL = 0x20000000, PROFILE_SERVER = 0x40000000, CREATE_IGNORE_SYSTEM_DEFAULT = 0x80000000, } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } public class Kernel32 { [DllImport("kernel32.dll", EntryPoint = "WTSGetActiveConsoleSessionId")] public static extern uint WTSGetActiveConsoleSessionId(); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hObject); } public class WtsApi32 { [DllImport("Wtsapi32.dll", EntryPoint = "WTSQueryUserToken")] public static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr phToken); } public class AdvApi32 { public const uint MAXIMUM_ALLOWED = 0x2000000; [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public extern static bool DuplicateTokenEx ( IntPtr hExistingToken, uint dwDesiredAccess, SECURITY_ATTRIBUTES lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, out IntPtr phNewToken ); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool CreateProcessAsUser ( IntPtr hToken, string lpApplicationName, string lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, CREATE_PROCESS_FLAGS dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation ); } public class UserEnv { [DllImport("userenv.dll", SetLastError = true)] public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit); [DllImport("userenv.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); } public static void StartAsActiveUser(this Process process) { // Sanity check. if (process.StartInfo == null) { throw new InvalidOperationException("The StartInfo property must be defined"); } if (string.IsNullOrEmpty(process.StartInfo.FileName)) { throw new InvalidOperationException("The StartInfo.FileName property must be defined"); } // Retrieve the active session ID and its related user token. var sessionId = Kernel32.WTSGetActiveConsoleSessionId(); var userTokenPtr = new IntPtr(); if (!WtsApi32.WTSQueryUserToken(sessionId, out userTokenPtr)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } // Duplicate the user token so that it can be used to create a process. var duplicateUserTokenPtr = new IntPtr(); if (!AdvApi32.DuplicateTokenEx(userTokenPtr, AdvApi32.MAXIMUM_ALLOWED, null, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out duplicateUserTokenPtr)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } // Create an environment block for the interactive process. var environmentPtr = new IntPtr(); if (!UserEnv.CreateEnvironmentBlock(out environmentPtr, duplicateUserTokenPtr, false)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } // Create the process under the target user's context. var processFlags = CREATE_PROCESS_FLAGS.NORMAL_PRIORITY_CLASS | CREATE_PROCESS_FLAGS.CREATE_NEW_CONSOLE | CREATE_PROCESS_FLAGS.CREATE_UNICODE_ENVIRONMENT; var processInfo = new PROCESS_INFORMATION(); var startupInfo = new STARTUPINFO(); startupInfo.cb = Marshal.SizeOf(startupInfo); if (!AdvApi32.CreateProcessAsUser ( duplicateUserTokenPtr, process.StartInfo.FileName, process.StartInfo.Arguments, null, null, false, processFlags, environmentPtr, null, ref startupInfo, out processInfo )) { throw new Win32Exception(Marshal.GetLastWin32Error()); } // Free used resources. Kernel32.CloseHandle(processInfo.hProcess); Kernel32.CloseHandle(processInfo.hThread); if (userTokenPtr != null) { Kernel32.CloseHandle(userTokenPtr); } if (duplicateUserTokenPtr != null) { Kernel32.CloseHandle(duplicateUserTokenPtr); } if (environmentPtr != null) { UserEnv.DestroyEnvironmentBlock(environmentPtr); } } }
以下是调用代码的方式:
var process = new Process(); process.StartInfo = new ProcessStartInfo { FileName = @"C:\path-to\target.exe", Arguments = "-arg1 -arg2" }; process.StartAsActiveUser();
希望能帮助到你!
监视程序进程可以使用System.Diagnostics.Process
来启动应用程序,使用WaitForExitMethod()
并检查ExitCode
属性。
在回答有关该问题的投诉时,我在使用遗留呼叫中心应用程序时不得不使用这种方法,而我没有源控制访问权限。
编辑:
对于宿主应用程序,您可以使用输出类型为“Windows Application”的.NET应用程序,根本就没有表单。 例如:
namespace WindowsFormsApplication1 { static class Program { /// /// The main entry point for the application. /// [STAThread] static void Main() { var info = new ProcessStartInfo(@"calc.exe"); var process = Process.Start(info); process.WaitForExit(); MessageBox.Show("Hello World!"); } } }