2012-06-21 71 views
20

Tôi cần một số cách để giám sát một ứng dụng máy tính để bàn và khởi động lại nó nếu nó chết.Cách tốt nhất để giám sát một ứng dụng máy tính để bàn là gì?

Ban đầu tôi cho rằng cách tốt nhất là để giám sát/khởi động lại quá trình từ một dịch vụ Windows, cho đến khi tôi phát hiện ra rằng kể từ khi Vista Windows services should not interact with the desktop

Tôi đã nhìn thấy một số câu hỏi đối phó với vấn đề này, nhưng mỗi câu trả lời tôi đã thấy một số loại hack bị Microsoft nản lòng và có thể sẽ ngừng hoạt động trong các bản cập nhật hệ điều hành trong tương lai.

Vì vậy, dịch vụ Windows có thể không còn là tùy chọn nữa. Tôi có lẽ có thể chỉ cần tạo một ứng dụng desktop/console khác để thực hiện điều này, nhưng loại đó đánh bại mục đích của nó.

Cách nào là cách thanh lịch nhất để đạt được điều này, theo ý kiến ​​của bạn?

EDIT: Đây không phải là phần mềm độc hại cũng như vi-rút. Ứng dụng cần giám sát là trình phát đa phương tiện sẽ chạy trên hệ thống được nhúng và mặc dù tôi đang cố gắng bao quát tất cả các trường hợp có thể xảy ra, tôi không thể gặp phải sự cố xảy ra do lỗi không mong muốn (s ** t xảy ra). Cơ quan giám sát này sẽ chỉ là một biện pháp bảo vệ trong trường hợp mọi thứ khác đi sai. Ngoài ra, vì người chơi sẽ hiển thị nội dung flash của bên thứ 3, một cộng thêm sẽ là ví dụ để theo dõi việc sử dụng tài nguyên và khởi động lại trình phát nếu nói, một số bộ phim flash bắt đầu rò rỉ bộ nhớ.

EDIT 2: Tôi quên đề cập đến, ứng dụng tôi muốn theo dõi/khởi động lại có hoàn toàn không cần để chạy trên tài khoản LocalSystem hoặc bất kỳ đặc quyền quản trị nào. Trên thực tế, tôi muốn thích để chạy bằng thông tin đăng nhập người dùng hiện đã đăng nhập.

+9

Tại sao âm thanh này như ** phần mềm độc hại ** hoặc ** vi-rút **? – SliverNinja

+1

Giám sát cục bộ hoặc từ xa? –

+0

Không thể nghĩ ra bất kỳ điều gì khác ngoài quy trình giám sát .. – nawfal

Trả lời

6

Ban đầu tôi cho rằng cách tốt nhất là để giám sát/khởi động lại quá trình từ một dịch vụ Windows ...

Chắc chắn bạn có thể! Tôi đã làm điều đó một vài lần trước đây. Bạn có thể bắt đầu học cách xem này:

http://msdn.microsoft.com/en-us/windows7trainingcourse_win7session0isolation_topic2#_Toc243675529

và điều này:

http://www.codeproject.com/Articles/18367/Launch-your-application-in-Vista-under-the-local-s

Trong chất, bạn phải chạy các chương trình như hệ thống, nhưng với sự SessionID của người sử dụng hiện nay.

Nếu bạn cảm thấy lười biếng, tôi cho rằng có thể có một số Dịch vụ nhỏ tốt làm cho thứ bạn đang tìm kiếm. Thử tìm kiếm trên www.codeproject.com.

+0

Xin lỗi, tôi đã liên kết với mã C++ ... nhưng bạn có thể dễ dàng tìm thấy các hương vị C#. –

+0

Tuyệt, tôi đang đọc các liên kết đó ngay bây giờ. Cảm ơn! –

+0

Nó hoạt động! Cảm ơn bạn rất nhiều cho câu trả lời của bạn! = D –

4

Quy trình giám sát có thể sử dụng System.Diagnostics.Process để khởi chạy ứng dụng, sử dụng WaitForExitMethod() và kiểm tra thuộc tính ExitCode.

Để trả lời các khiếu nại về câu hỏi, tôi đã phải sử dụng phương pháp như vậy khi làm việc với ứng dụng trung tâm cuộc gọi kế thừa mà tôi không có quyền truy cập kiểm soát nguồn.

EDIT:

Đối với các ứng dụng máy chủ mà bạn có thể sử dụng một ứng dụng .NET kiểu đầu ra "Windows Application" và đơn giản là không có một hình thức nào cả. Ví dụ:

namespace WindowsFormsApplication1 
{ 
    static class Program 
    { 
     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     [STAThread] 
     static void Main() 
     { 
      var info = new ProcessStartInfo(@"calc.exe"); 
      var process = Process.Start(info); 
      process.WaitForExit(); 
      MessageBox.Show("Hello World!"); 
     } 
    } 
} 
+0

Đó là nhiều hơn hoặc ít hơn chính xác những gì tôi đã cố gắng, nhưng các ứng dụng đang được theo dõi có một GUI, do đó, phương pháp này sẽ không làm việc từ Vista và lên. –

+0

@AxelMagagnini - Tại sao nó không hoạt động. Anh ta không đề nghị làm điều này trong một dịch vụ Windows. –

+0

@ChrisDunaway Tôi cho rằng anh ta có ý đó, xin lỗi nếu tôi hiểu lầm. Và nếu vậy, từ đó bạn sẽ làm gì sau đó? –

13

Cuối cùng tôi đã triển khai giải pháp được đề xuất bởi @ A_nto2 và nó đã đạt được chính xác những gì tôi đang tìm kiếm: Tôi hiện có Dịch vụ Windows theo dõi danh sách quy trình và bất cứ khi nào họ ngừng hoạt động. thông tin đăng nhập và phiên của người dùng, vì vậy GUI có thể hiển thị.

Tuy nhiên, kể từ khi các liên kết Ông đăng thấy VC++ mã, tôi chia sẻ C# thực hiện của tôi cho bất cứ ai đối phó với vấn đề tương tự:

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); 
     } 
    } 
} 

Và dưới đây là cách mã được gọi:

var process = new Process(); 
process.StartInfo = new ProcessStartInfo { FileName = @"C:\path-to\target.exe", Arguments = "-arg1 -arg2" }; 
process.StartAsActiveUser(); 

Hy vọng điều đó sẽ hữu ích!

+2

Đối với bất kỳ ai sử dụng mã này trong tương lai: nó hoạt động rất tốt, nhưng dịch vụ PHẢI chạy như LocalSystem. –

+0

Phiên bản cửa sổ nào được hỗ trợ? –

+0

Nó sẽ hoạt động tốt dưới Vista và 7. Chưa thử nghiệm bất kỳ thứ gì khác. Nếu bạn đang chạy một hệ điều hành cũ hơn (ví dụ XP), bạn không cần mã này, bởi vì Windows Services đã có thể hiển thị một GUI trở lại sau đó. –

Các vấn đề liên quan