2012-06-21 7 views
20

Ich brauche eine Möglichkeit, eine Desktop-Anwendung zu überwachen und neu zu starten, wenn sie abbricht.Was ist der beste Weg, um eine Desktop-Anwendung zu überwachen?

Anfangs nahm ich der beste Weg, um den Prozess von einem Windows-Dienst zu überwachen/neu starten würde, bis ich da Vista herausgefunden, dass Windows services should not interact with the desktop

Ich habe mit diesem Thema einige Fragen gesehen tun haben, aber jede Antwort, die ich habe eine Art von Hack gesehen, der von Microsoft abgeraten wird und wahrscheinlich in zukünftigen OS-Updates nicht mehr funktionieren wird.

Also, ein Windows-Dienst ist wahrscheinlich keine Option mehr. Ich könnte wahrscheinlich nur eine andere Desktop/Konsole-Anwendung erstellen, um dies zu tun, aber diese Art von besiegt seinen Zweck.

Was wäre Ihrer Meinung nach der eleganteste Weg, dies zu erreichen?

EDIT: Dies ist weder Malware noch Virus. Die App, die überwacht werden muss, ist ein Mediaplayer, der auf einem eingebetteten System läuft, und obwohl ich versuche, alle möglichen Unfallszenarien abzudecken, kann ich nicht riskieren, dass sie bei einem unerwarteten Fehler abstürzt (das passiert nicht)). Dieser Wachhund wäre nur ein Schutz, falls alles andere schief geht. Da der Player Flash-Inhalte von Drittanbietern zeigen würde, wäre ein zusätzliches Plus, zum Beispiel, um die Ressourcennutzung zu überwachen und den Player neu zu starten, wenn zum Beispiel ein beschissener Flash-Film Speicherlecks verursacht.

EDIT 2: Ich vergaß zu erwähnen, die Anwendung Ich mag würde überwachen/Neustart hat absolut keine Notwendigkeit entweder auf dem lokalen Systemkonto ausgeführt wird, noch mit irgendwelchen administrativen Rechten an allen. Eigentlich würde ich bevorzugen es mit den derzeit angemeldeten Benutzer-Anmeldeinformationen ausführen.

+9

Warum klingt das wie ** Malware ** oder ein ** Virus **? – SliverNinja

+1

Monitor lokal oder remote? –

+0

Kann nicht an etwas anderes als einen Prozess denken, der überwacht .. – nawfal

Antwort

6

Anfangs nahm ich der beste Weg, um den Prozess von einem Windows-Dienst zu überwachen/neu starten würde ...

Sicher Sie können! Ich habe es schon einige Male gemacht. Sie können anfangen zu lernen, wie diese hier:

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

und diese:

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

Im Wesentlichen müssen Sie Programme wie SYSTEM ausgeführt, aber mit der SessionID des aktuellen Benutzers.

Wenn Sie faul sind, nehme ich an, dass es ein paar gute kleine Dienste geben könnte, die das Ding machen, nach dem Sie suchen. Versuchen Sie, auf www.codeproject.com zu suchen.

+0

Entschuldigung, ich habe mit C++ - Codes verknüpft ... aber Sie könnten die C# -Aromen leicht finden. –

+0

Cool, ich lese gerade diese Links. Vielen Dank! –

+0

Es funktioniert! Vielen Dank für Ihre Antwort! = D –

4

Der Watchdog-Prozess könnte System.Diagnostics.Process verwenden, um die Anwendung zu starten, verwenden Sie die WaitForExitMethod() und überprüfen Sie die ExitCode Eigenschaft.

Als Antwort auf die Beschwerden über die Frage musste ich eine solche Methode verwenden, wenn ich mit einer alten Call-Center-Anwendung arbeitete, auf die ich keinen Zugriff auf die Quellcodeverwaltung hatte.

EDIT:

Für die Host-Anwendung Sie sich auch alle .NET-Anwendung von Ausgabetyp "Windows Application" und nicht einfach ein Formular verwenden könnte. Zum Beispiel:

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

Das ist mehr oder weniger genau das, was ich ausprobiert habe, aber die zu überwachende Anwendung hat eine GUI, so dass diese Methode ab Vista nicht funktioniert. –

+0

@AxelMagagnini - Warum wird es nicht funktionieren. Er schlug nicht vor, dies in einem Windows-Dienst zu tun. –

+0

@ChrisDunaway Ich nahm an, dass er das meinte, tut mir leid, wenn ich es falsch verstanden habe. Und wenn ja, von wo würdest du es dann tun? –

13

Endlich habe ich die von @ A_nto2 vorgeschlagene Lösung implementiert und genau das erreicht, wonach ich gesucht habe: Ich habe jetzt einen Windows-Dienst, der eine Liste von Prozessen überwacht und sie automatisch mit der aktiven wieder startet Anmeldeinformationen und Sitzung des Benutzers, sodass die GUI sichtbar ist.

Da jedoch die Links er VC gezeigt posted ++ Code, ich meine C# -Implementierung für jedermann zu teilen mit dem gleichen Problem zu tun:

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

Und hier ist, wie der Code aufgerufen wird:

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

Hoffe es hilft!

+2

Für jeden, der diesen Code in der Zukunft verwendet: Es funktioniert sehr gut, aber der Dienst muss als LocalSystem ausgeführt werden. –

+0

Welche Windows-Version wird unterstützt? –

+0

Es sollte unter Vista und 7 gut funktionieren. Habe keine anderen getestet. Wenn Sie ein älteres Betriebssystem (z. B. XP) verwenden, sollten Sie diesen Code nicht benötigen, da Windows Services damals eine Benutzeroberfläche anzeigen konnten. –

Verwandte Themen