2010-06-27 7 views
14

Ich beginne mit einem Beispiel: Apache Webserver (unter Windows) hat eine nette Funktion: Er kann sowohl als eigenständige Anwendung (mit aktuellen Benutzerrechten) ausgeführt werden, als auch als Windows-Dienst installiert und ausgeführt werden direkt (als lokales Systemkonto), mit derselben ausführbaren Datei.Wie Windows-Dienst-Anwendung zu machen, so dass es als eigenständiges Programm als auch laufen kann?

Damit die Anwendung als eigenständige App ausgeführt werden kann, muss sie nur statisch public Main() in einer öffentlichen Klasse haben.

Damit die Anwendung installierbar und als Dienst ausführbar ist, muss sie ServiceBase- und Installer-Klassen in bestimmter Weise implementieren. Aber wenn eine Anwendung wie diese als Standalone-App ausgeführt wird, wird das Meldungsfeld angezeigt.

Wie kann diese Apache-ähnliche Arbeitsweise erreicht werden? Ich glaube, die Lösung ist einfach, aber ich habe keine Ahnung, wo ich anfangen soll.

Der folgende Code wird zum Aufrufen des Dienstes verwendet. Kann es geändert werden, um eine eigenständige Nutzung zu ermöglichen?

static class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    static void Main() 
    { 
     ServiceBase[] ServicesToRun; 
     ServicesToRun = new ServiceBase[] 
     { 
      new Service() // defined elsewhere as Service : ServiceBase 
     }; 
     ServiceBase.Run(ServicesToRun); 
    } 
} 

Meine Sprache der Wahl ist C#.

Bearbeiten: Derzeit habe ich gemeinsamen Code in separate Assembly abstrahiert (nennen wir es Library.dll), und ich habe zwei ausführbare Dateien: Console.exe und Service.exe, die eigenständige und Windows-Dienstanwendungen sind , und beide sind nur Mittel zum Aufrufen von Library.dll.

Mein Ziel ist es, diese beiden ausführbaren Dateien zu einer zusammenführen, die immer noch zu Library.dll aufrufen wird.

Antwort

7

Nach einigem Graben habe ich endlich nachgesehen.NET-Haube (System.ServiceProcess.ServiceBase.Run-Methode), nur um zu finden, dass es überprüft Environment.UserInteractive bool, um sicherzustellen, dass die ausführbare Datei nicht interaktiv ausgeführt wird.

Stark vereinfacht Lösung, die für mich funktioniert:

class Program 
{ 
    static void Main(string[] args) 
    { 
     if (!Environment.UserInteractive) 
     { 
      ServiceBase[] ServicesToRun; 
      ServicesToRun = new ServiceBase[] 
      { 
       // Service.OnStart() creates instance of MainLib() 
       // and then calls its MainLib.Start() method 
       new Service() 
      }; 
      ServiceBase.Run(ServicesToRun); 
      return; 
     } 

     // Run in a console window 
     MainLib lib = new MainLib(); 
     lib.Start(); 
     // ... 
    } 
} 
+3

Vorsicht: Environment.UserInteractive wird nur verwendet, um zu überprüfen, ob eine grafische Desktop-Sitzung vorhanden ist, und das Ergebnis wird nur verwendet, um zu bestimmen, ob eine Dialog- oder Konsolenmeldung angezeigt werden soll. UserInteractive gibt auch true zurück, wenn es als Dienst ausgeführt wird, der mit dem Desktop interagieren darf. Die echte Überprüfung erfolgt innerhalb nativer Methoden. – Ishmaeel

+0

@Ishmaeel: danke, guter Punkt. Wusste nicht, dass diese beiden Modi verwandt waren. Haben Sie ein Beispiel für native Methoden, die dies überprüfen können? –

+1

Offenbar ruft ServiceBase nur die Funktion StartServiceCtrlDispatcher (http://msdn.microsoft.com/en-us/library/ms686324(VS.85).aspx) auf, und ein Rückgabewert ungleich Null (wahrscheinlich) bedeutet, dass Sie nicht als Dienst gestartet werden . Natürlich ist es nicht als Check nützlich, weil es genau die Operation ist, die Sie entscheiden möchten, ob sie ausgeführt werden soll oder nicht. Ich konnte noch keine richtige Check-Funktion finden. – Ishmaeel

0

In dem Beispiel Ihrer Website bin ich ziemlich zuversichtlich, dass die Apache-App in C oder C++ geschrieben ist. Dazu benötigen Sie eine ServiceMain-Funktion. Wenn Sie es wie ein normales Programm ausführen, wird main aufgerufen. Wenn Sie den Dienststeuerungs-Manager darauf verweisen, wird ServiceMain stattdessen aufgerufen.

In Bezug auf C#, kann ich nicht sagen, dass ich darüber weiß. Wenn ich einen Dienst in C# schreiben müsste, würde ich hier anfangen - http://msdn.microsoft.com/en-us/library/bb483064.aspx

3

Sie sollten wirklich alle Ihre Funktionalität in einer Bibliothek abstrahiert haben. Die Tatsache, dass es zufällig von einem Windows-Dienst ausgeführt wird, sollte keine Rolle spielen. In der Tat, wenn Sie eine Klasse namens ServiceFrontEnd mit Start() und Stop() hätten - die Windows-Dienstanwendung könnte das aufrufen, ebenso eine Befehlszeilen-App, eine Windows-App oder was auch immer.

Was Sie hier beschreiben, braucht nur mehr Abstraktion. Die Funktionalität von "der Dienst" muss nicht eng an die Funktionsweise eines Windows-Dienstes gekoppelt sein. hoffe das hilft

+0

Was ich jetzt haben, ist eine Bibliothek, die alle Logik enthält. Ich rufe dieselbe Bibliothek von der eigenständigen Konsolenanwendung und von der Dienstanwendung auf. Was ich suche, ist eine Möglichkeit, zwei ausführbare Dateien zu einem zusammenzuführen. Also, Console.exe und Service.exe rufen beide Library.dll auf, und ich möchte einzelne ConsoleAndService.exe haben, die Library.dll aufrufen :) –

+0

Der Einstiegspunkt für eine Konsolenanwendung ist eine statische Main() - Methode. Der Einstiegspunkt für den Service ist eine Klasse, die von ServiceBase erbt - und OnStart() und OnStop() werden aufgerufen. Es sollte keinen Konflikt geben. Aber empirisch, wenn Sie das finden, denke ich, dass es nicht unvernünftig ist, separate ausführbare Dateien zu haben. Schließlich werden sie auf ganz unterschiedliche Weise laufen. Hoffe, dass hilft –

+0

Natürlich ist es vollkommen akzeptabel, separate ausführbare Dateien zu haben. Ich war nur auf der Suche nach einer Möglichkeit, sie wenn möglich zusammenzuführen. Wenn nicht, dann wird es weiter so funktionieren, wie es bereits ist. –

10

In C# ist eine einfache Möglichkeit, es zu tun, ein Befehlszeilenargument erforderlich, um es als Dienst auszuführen. Wenn das Argument nicht vorhanden ist, führen Sie Ihre Formular-/Konsolenanwendung aus. Dann müssen nur Ihren Installateur das Argument in den ausführbaren Pfad enthalten, wenn die Installation des Service so sieht es aus wie so:

C:\MyApp\MyApp.exe -service 

Es ist etwas würde wie folgt aussehen:

static void Main(string[] args) 
{ 
    foreach (string arg in args) 
    { 
     //Run as a service if our argument is there 
     if (arg.ToLower() == "-service") 
     { 
      ServiceBase[] servicesToRun = new ServiceBase[] { new Service1() }; 
      ServiceBase.Run(servicesToRun); 
      return; 
     } 
    } 

    //Run the main form if the argument isn't present, like when a user opens the app from Explorer. 
    Application.Run(new Form1()); 
} 

Dies ist nur ein Beispiel zu geben, Sie eine Idee, es gibt wahrscheinlich sauberere Möglichkeiten, diesen Code zu schreiben.

+0

Das war, was ich vorher untersucht habe, aber ich konnte keine Möglichkeit finden, ein Argument an installutil zu übergeben, damit der Dienst mit einem Argument beginnen würde. Eine andere Möglichkeit wäre ein Argument, das interaktiv ausgeführt wird, aber das ist nicht so praktisch. Sehen Sie meine eigene Antwort, um zu sehen, wie ich es am Ende gemacht habe. Sehr einfach. –

+0

Wie auch immer, kannst du mir sagen, wie man Argumente an installutil weitergibt, damit ich es in Zukunft weiß? –

Verwandte Themen