web-dev-qa-db-de.com

Wie kann ich ein EXE-Programm von einem Windows-Dienst mit C # ausführen?

Wie kann ich ein EXE-Programm von einem Windows-Dienst mit C # ausführen?

Das ist mein Code:

System.Diagnostics.Process.Start(@"E:\PROJECT XL\INI SQLLOADER\ConsoleApplication2\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");

Wenn ich diesen Dienst ausführen, wird die Anwendung nicht gestartet.
.__ Was ist mit meinem Code falsch?

38
xoops

Dies wird niemals funktionieren, zumindest nicht unter Windows Vista oder höher. Das Hauptproblem ist, dass Sie versuchen, dies in einem Windows-Dienst und nicht in einer Windows-Standardanwendung auszuführen. Der von Ihnen gezeigte Code funktioniert in einer Windows Forms-, WPF- oder Konsolenanwendung einwandfrei, in einem Windows-Dienst jedoch überhaupt nicht.

Windows-Dienste können keine zusätzlichen Anwendungen starten, da sie nicht im Kontext eines bestimmten Benutzers ausgeführt werden. Im Gegensatz zu normalen Windows-Anwendungen werden Dienste werden jetzt in einer isolierten Sitzung ausgeführt und dürfen nicht mit einem Benutzer oder dem Desktop interagieren. Dies lässt keinen Platz für die Ausführung der Anwendung.

Weitere Informationen finden Sie in den Antworten auf diese verwandten Fragen:

Die wahrscheinlich beste Lösung für Ihr Problem besteht darin, anstelle eines Dienstes eine Standard-Windows-Anwendung zu erstellen. Diese werden von einem bestimmten Benutzer ausgeführt und sind dem Desktop dieses Benutzers zugeordnet. Auf diese Weise können Sie jederzeit zusätzliche Anwendungen ausführen, indem Sie den bereits angezeigten Code verwenden.

Eine weitere mögliche Lösung, vorausgesetzt, dass Ihre Konsolenanwendung keine Schnittstelle oder Ausgabe jeglicher Art erfordert, besteht darin, den Prozess anzuweisen, kein Fenster zu erstellen. Dadurch wird verhindert, dass Windows die Erstellung Ihres Prozesses blockiert, da nicht mehr die Erstellung eines Konsolenfensters angefordert wird. Den entsprechenden Code finden Sie in diese Antwort zu einer verwandten Frage.

61
Cody Gray

ich habe diesen Artikel Code Project ausprobiert, es funktioniert gut für mich ..__ Ich habe den Code auch verwendet. Artikel ist in Erklärung mit Screenshot ausgezeichnet.

Ich füge diesem Szenario die notwendige Erklärung hinzu

Sie haben gerade Ihren Computer hochgefahren und melden sich gerade an. Wenn Sie sich anmelden, weist Ihnen das System eine eindeutige Sitzungs-ID zu. In Windows Vista erhält der erste Benutzer, der sich am Computer anmeldet, eine Sitzungs-ID von 1 vom Betriebssystem. Dem nächsten Benutzer, der sich anmeldet, wird die Sitzungs-ID 2 zugewiesen. Und so weiter und so fort. Sie können die jedem angemeldeten Benutzer zugewiesene Sitzungs-ID auf der Registerkarte Benutzer im Task-Manager anzeigen. enter image description here

Ihr Windows-Dienst wird jedoch mit der Sitzungs-ID 0 abgelegt. Diese Sitzung ist von anderen Sitzungen isoliert. Dies verhindert letztendlich, dass der Windows-Dienst die Anwendung aufruft, die unter Benutzersitzungen wie 1 oder 2 ausgeführt wird.

Um die Anwendung über den Windows-Dienst aufzurufen, müssen Sie das Steuerelement aus der Datei winlogon.exe kopieren, die als derzeit angemeldeter Benutzer fungiert (siehe Screenshot unten). __ enter image description here

Wichtige Codes

// obtain the process id of the winlogon process that 
// is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
    if ((uint)p.SessionId == dwSessionId)
    {
        winlogonPid = (uint)p.Id;
    }
}

// obtain a handle to the winlogon process
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
    CloseHandle(hProcess);
    return false;
}

// Security attibute structure used in DuplicateTokenEx and   CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just 
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and   therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);

// copy the access token of the winlogon process; 
// the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, 
    (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
    (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
    {
      CloseHandle(hProcess);
      CloseHandle(hPToken);
      return false;
    }

 STARTUPINFO si = new STARTUPINFO();
 si.cb = (int)Marshal.SizeOf(si);

// interactive window station parameter; basically this indicates 
// that the process created can display a GUI on the desktop
si.lpDesktop = @"winsta0\default";

// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

// create a new process in the current User's logon session
 bool result = CreateProcessAsUser(hUserTokenDup,  // client's access token
                            null,             // file to execute
                            applicationName,  // command line
                            ref sa,           // pointer to process    SECURITY_ATTRIBUTES
                            ref sa,           // pointer to thread SECURITY_ATTRIBUTES
                            false,            // handles are not inheritable
                            dwCreationFlags,  // creation flags
                            IntPtr.Zero,      // pointer to new environment block 
                            null,             // name of current directory 
                            ref si,           // pointer to STARTUPINFO structure
                            out procInfo      // receives information about new process
                            );
7

Sie können eine EXE-Datei sehr gut in Windows XP ausführen. Ich habe es in der Vergangenheit selbst gemacht.

Sie müssen sicherstellen, dass Sie in den Eigenschaften des Windows-Dienstes die Option "Interaktion mit dem Desktop zulassen" aktiviert haben. Wenn dies nicht der Fall ist, wird es nicht ausgeführt.

Ich muss Windows 7 oder Vista einchecken, da für diese Versionen zusätzliche Sicherheitsrechte erforderlich sind, sodass möglicherweise ein Fehler ausgegeben wird. Ich bin jedoch ziemlich sicher, dass dies entweder direkt oder indirekt erreicht werden kann. Für XP bin ich mir sicher, wie ich es selbst getan hatte.

2
Depinder Bharti

Zunächst erstellen wir einen Windows-Dienst, der unter .__ ausgeführt wird. Systemkonto Dieser Dienst ist für das Laichen eines .__ verantwortlich. interaktiven Prozess innerhalb der gerade aktiven Benutzersitzung. Diese Der neu erstellte Prozess zeigt eine Benutzeroberfläche an und wird mit dem vollständigen Administrator ausgeführt Rechte. Wenn sich der erste Benutzer am Computer anmeldet, wird dieser Dienst gestartet werden und in Session0 ausgeführt werden; jedoch der Prozess, dass Dieser Dienst wird auf dem Desktop des aktuell .__ ausgeführt. angemeldeter Benutzer Wir bezeichnen diesen Dienst als LoaderService.

Als Nächstes ist der Prozess winlogon.exe für die Verwaltung der Benutzeranmeldung .__ verantwortlich. und Logout-Verfahren. Wir wissen, dass jeder Benutzer, der sich an der .__ anmeldet. Der Computer verfügt über eine eindeutige Sitzungs-ID und ein entsprechendes winlogon.exe Prozess mit ihrer Sitzung verbunden. Nun haben wir erwähnt. Der LoaderService wird oben unter dem Systemkonto ausgeführt. Wir ... auch Es wurde bestätigt, dass jeder winlogon.exe-Prozess auf dem Computer unter .__ ausgeführt wird. das Systemkonto. Weil das Systemkonto der Besitzer beider .__ ist. Der LoaderService und der winlogon.exe-Prozess, unser LoaderService kann das Zugriffstoken (und die Sitzungs-ID) des Prozesses winlogon.exe kopieren Rufen Sie dann die Win32-API-Funktion CreateProcessAsUser auf, um eine .__ zu starten. in die derzeit aktive Sitzung des angemeldeten Benutzers ein. Schon seit Die Sitzungs-ID befindet sich im Zugriffstoken des kopierten winlogon.exe Prozess ist größer als 0, wir können ein interaktives .__ starten. Prozess mit diesem Token.

Versuchen Sie es mit diesem . Subversion von Vista UAC in 32- und 64-Bit-Architekturen

2
Elshan

Die beste Antwort mit den meisten positiven Stimmen ist nicht falsch, aber immer noch das Gegenteil von dem, was ich posten würde. Ich sage , es wird vollkommen funktionieren , um eine exe-Datei zu starten, und Sie können dies im Kontext eines beliebigen Benutzers tun . Logischerweise kann man einfach keine Benutzeroberfläche haben oder nach Benutzereingaben fragen ...

Hier ist mein Rat:

  1. Erstellen Sie eine einfache Konsolenanwendung, die genau das tut, was Ihr Dienst beim Start tun soll, ohne dass der Benutzer eingreift. Ich empfehle wirklich, den Windows-Dienst-Projekttyp nicht zu verwenden, insbesondere, weil Sie .NET Core (derzeit) nicht verwenden können.
  2. Fügen Sie Code hinzu, um Ihre Exe zu starten, die Sie vom Dienst anrufen möchten

Beispiel zum Starten z.B. plink.exe. Sie könnten sogar die Ausgabe anhören:

var psi = new ProcessStartInfo()
{
    FileName = "./Client/plink.exe", //path to your *.exe
    Arguments = "-telnet -P 23 127.0.0.1 -l myUsername -raw", //arguments
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    UseShellExecute = false,
    CreateNoWindow = true //no window, you can't show it anyway
};

var p = Process.Start(psi);
  1. Verwenden Sie NSSM (Non-Sucking Service Manager), um diese Konsolenanwendung als Dienst zu registrieren. NSSM kann über die Befehlszeile gesteuert werden und zeigt eine Benutzeroberfläche zum Konfigurieren des Dienstes an, oder Sie konfigurieren ihn über die Befehlszeile. Sie können den Dienst im Kontext eines beliebigen Benutzers ausführen, wenn Sie die Anmeldedaten dieses Benutzers kennen.

Ich habe das LocalSystem-Konto verwendet, das Standard ist und mehr als Local Service. Es funktionierte einwandfrei, ohne dass Anmeldeinformationen eines bestimmten Benutzers eingegeben werden mussten. Ich habe nicht einmal das Kontrollkästchen "Dienst darf mit dem Desktop interagieren" aktiviert, das Sie verwenden könnten, wenn Sie höhere Berechtigungen benötigen.

Option to allow service to interact with desktop

Zum Schluss möchte ich nur sagen, wie lustig es ist, dass die Top-Antwort das Gegenteil von meiner Antwort ist und wir beide trotzdem Recht haben, es ist nur, wie Sie die Frage interpretieren :-D. Wenn Sie jetzt sagen, aber Sie können nicht mit dem Windows-Dienstprojekttyp - Sie KÖNNEN, aber ich hatte dies vor und die Installation war skizzenhaft und es war vielleicht eine Art unbeabsichtigter Hack, bis ich NSSM fand.

0
CodingYourLife

Ich denke, Sie kopieren die .exe-Datei an einen anderen Speicherort. Das könnte das Problem sein, denke ich. Wenn Sie das Exe kopieren, kopieren Sie nicht dessen Abhängigkeiten.

Sie können also alle abhängigen DLLs in GAC einfügen, damit alle .net-Dateien darauf zugreifen können

Andernfalls kopieren Sie das Exe nicht an einen neuen Ort. Erstellen Sie einfach eine Umgebungsvariable und rufen Sie das Exe in Ihrem c # auf. Da der Pfad in Umgebungsvariablen definiert ist, kann auf das Exe-Programm von Ihrem c # -Programm zugegriffen werden.

Aktualisieren:

zuvor hatte ich in meinem c # .net 3.5 - Projekt ein ähnliches Problem, in dem ich versuchte, eine .exe - Datei mit c # .net - Code auszuführen, und dieses Exe war nichts anderes als das andere Projektexe (in dem ich ein paar unterstützende DLLs für mein Funktionalität) und die DLL-Methoden, die ich in meiner Exe-Anwendung verwendete. Zum Schluss habe ich dies gelöst, indem ich diese Anwendung als separates Projekt für dieselbe Lösung erstellt habe und diese Projektausgabe meinem Bereitstellungsprojekt hinzugefügt habe. Nach diesem Szenario antwortete ich: Wenn es nicht das ist, was er will, tut es mir sehr leid.

0
SharpUrBrain

sie können den Taskplaner von Windows für diesen Zweck verwenden. Es gibt viele Bibliotheken wie TaskScheduler , die Ihnen helfen.

nehmen wir beispielsweise an, wir möchten eine Aufgabe planen, die fünf Sekunden später ausgeführt wird:

using (var ts = new TaskService())
        {

            var t = ts.Execute("notepad.exe")
                .Once()
                .Starting(DateTime.Now.AddSeconds(5))
                .AsTask("myTask");

        }

notepad.exe wird fünf Sekunden später ausgeführt.

details und weitere Informationen finden Sie unter Wiki

wenn Sie wissen, welche Klasse und Methode in dieser Assembly benötigt werden, können Sie sie wie folgt selbst aufrufen:

        Assembly assembly = Assembly.LoadFrom("yourApp.exe");
        Type[] types = Assembly.GetTypes();
        foreach (Type t in types)
        {
            if (t.Name == "YourClass")
            {
                MethodInfo method = t.GetMethod("YourMethod",
                BindingFlags.Public | BindingFlags.Instance);
                if (method != null)
                {
                    ParameterInfo[] parameters = method.GetParameters();
                    object classInstance = Activator.CreateInstance(t, null);

                    var result = method.Invoke(classInstance, parameters.Length == 0 ? null : parameters);

                    break;
                }
            }

        }
0
Rahmat Anjirabi