Frage

Ich habe einen Windows-Dienst in C # geschrieben, das für eine Reihe von Netzwerk-Geräten an die Back-End-Datenbank als Proxy fungiert. Für die Prüfung und auch eine Simulation Schicht zu testen, das hintere Ende hinzuzufügen, würde Ich mag eine GUI haben für den Test Bediener in der Lage Lauf die Simulation zu sein. Auch für eine gestreifte abgespeckte Version als Demo auszusenden. Die GUI und Service müssen nicht zur gleichen Zeit ausgeführt werden. Was ist der beste Weg, um dieses Duell Betrieb zu erreichen?

Edit: Hier ist meine Lösung Sachen von diese Frage Kämmen, Bin ich als Dienst ausgeführt und Installieren eines .NET-Windows-Dienst ohne InstallUtil.exe mit Dieser ausgezeichnete Code von Marc GRA

Es verwendet die folgende Zeile zu testen, ob die GUI oder laufen als Dienst ausgeführt werden.

 if (arg_gui || Environment.UserInteractive || Debugger.IsAttached)

Hier ist der Code.


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.ComponentModel;
using System.ServiceProcess;
using System.Configuration.Install;
using System.Diagnostics;

namespace Form_Service
{
   static class Program
   {
      /// 
      /// The main entry point for the application.
      /// 
      [STAThread]
      static int Main(string[] args)
      {
         bool arg_install =  false;
         bool arg_uninstall = false;
         bool arg_gui = false;
         bool rethrow = false;
         try
         {
            foreach (string arg in args)
            {
               switch (arg)
               {
                  case "-i":
                  case "-install":
                     arg_install = true; break;
                  case "-u":
                  case "-uninstall":
                     arg_uninstall = true; break;
                  case "-g":
                  case "-gui":
                     arg_gui = true; break;
                  default:
                     Console.Error.WriteLine("Argument not expected: " + arg);
                     break;
               }
            }
            if (arg_uninstall)
            {
               Install(true, args);
            }
            if (arg_install)
            {
               Install(false, args);
            }
            if (!(arg_install || arg_uninstall))
            {
               if (arg_gui || Environment.UserInteractive || Debugger.IsAttached)
               {
                  Application.EnableVisualStyles();
                  Application.SetCompatibleTextRenderingDefault(false);
                  Application.Run(new Form1());
               }
               else
               {
                  rethrow = true; // so that windows sees error... 
                  ServiceBase[] services = { new Service1() };
                  ServiceBase.Run(services);
                  rethrow = false;
               }
            }
            return 0;
         }
         catch (Exception ex)
         {
            if (rethrow) throw;
            Console.Error.WriteLine(ex.Message);
            return -1;
         }
      }

      static void Install(bool undo, string[] args)
      {
         try
         {
            Console.WriteLine(undo ? "uninstalling" : "installing");
            using (AssemblyInstaller inst = new AssemblyInstaller(typeof(Program).Assembly, args))
            {
               IDictionary state = new Hashtable();
               inst.UseNewContext = true;
               try
               {
                  if (undo)
                  {
                     inst.Uninstall(state);
                  }
                  else
                  {
                     inst.Install(state);
                     inst.Commit(state);
                  }
               }
               catch
               {
                  try
                  {
                     inst.Rollback(state);
                  }
                  catch { }
                  throw;
               }
            }
         }
         catch (Exception ex)
         {
            Console.Error.WriteLine(ex.Message);
         }
      }
   }

   [RunInstaller(true)]
   public sealed class MyServiceInstallerProcess : ServiceProcessInstaller
   {
      public MyServiceInstallerProcess()
      {
         this.Account = ServiceAccount.NetworkService;
      }
   }

   [RunInstaller(true)]
   public sealed class MyServiceInstaller : ServiceInstaller
   {
      public MyServiceInstaller()
      {
         this.Description = "My Service";
         this.DisplayName = "My Service";
         this.ServiceName = "My Service";
         this.StartType = System.ServiceProcess.ServiceStartMode.Manual;
      }
   }

}
War es hilfreich?

Lösung

Sie haben grundsätzlich zwei Möglichkeiten. Entweder aussetzen eine API auf den Dienst, den Sie dann aus dem UI-App aufrufen oder aktivieren den Dienst auszuführen, entweder als WinForms-Anwendung oder einen Dienst.

Die erste Option ist recht einfach -. Verwenden Remote oder WCF die API zu belichten

Die zweite Option kann durch Verschieben der „Innereien“ des App in eine separate Klasse erreicht wird dann einen Service-Wrapper und ein Win-Formulare erstellen Wrapper, dass beide Anruf in Ihre „Innereien“ -Klasse.

static void Main(string[] args)
{
    Guts guts = new Guts();

    if (runWinForms)
    {
        System.Windows.Forms.Application.EnableVisualStyles();
        System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);

        FormWrapper fw = new FormWrapper(guts);

        System.Windows.Forms.Application.Run(fw);
    }
    else
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new ServiceWrapper(guts) };
        ServiceBase.Run(ServicesToRun);
    }
}

Andere Tipps

Erstellen Sie eine neue WinForms-Anwendung die Referenzen mit dem Aufbau Ihres Service.

Wenn Sie den Code unten:

[DllImport("advapi32.dll", CharSet=CharSet.Unicode)]
static extern bool StartServiceCtrlDispatcher(IntPtr services);
[DllImport("ntdll.dll", EntryPoint="RtlZeroMemory")]
static extern void ZeroMemory(IntPtr destination, int length);

static bool StartService(){
    MySvc svc = new MySvc(); // replace "MySvc" with your service name, of course
    typeof(ServiceBase).InvokeMember("Initialize", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
        null, svc, new object[]{false});
    object entry = typeof(ServiceBase).InvokeMember("GetEntry",
        BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, svc, null);
    int len = Marshal.SizeOf(entry) * 2;
    IntPtr memory = Marshal.AllocHGlobal(len);
    ZeroMemory(memory, len);
    Marshal.StructureToPtr(entry, memory, false);
    return StartServiceCtrlDispatcher(memory);
}

[STAThread]
static void Main(){
    if(StartService())
        return;

    Application.Run(new MainWnd()); // replace "MainWnd" with whatever your main window is called
}

Dann führen Sie Ihre EXE entweder als Dienst (wenn vom SCM gestartet) oder als GUI (wenn von einem anderen Prozess gestartet).

Im Grunde alles, was ich habe hier getan verwendet wird Reflector , um herauszufinden, was das Fleisch von ServiceBase.Run der Fall ist, und es duplizieren hier (Reflexion erforderlich, weil es nennt private Methoden). Der Grund für die nicht ServiceBase.Run direkten Aufruf ist, dass es ein Meldungsfeld erscheint den Benutzer zu sagen, dass der Dienst kann nicht gestartet werden (wenn nicht vom SCM ins Leben gerufen) und nicht wieder etwas das zu sagen < em> Code , dass der Dienst nicht gestartet werden kann.

Da diese Reflexion nutzt private Rahmen Methoden aufrufen, es kann in zukünftigen Revisionen des Rahmens nicht richtig funktionieren. Caveat CODOR.

Es gibt auch FireDaemon . Auf diese Weise können Sie jede Windows-Anwendung als Dienst ausgeführt werden.

Siehe ich als Service leite einige weitere nützliche Informationen.

Das Wichtigste abgedeckt ist, wie zuverlässig zu bestimmen, ob wir interaktiv oder über einen Dienst ausgeführt werden.

Sie müssen einen separaten Prozess implementieren, die mit Ihrem Service kommunizieren kann. Während es auf XP und frühere Systeme möglich ist, einen Dienst zu haben, eine UI zeigt, das ist nicht mehr möglich, auf Vista und höher.

Eine andere Möglichkeit besteht darin, einen Dienst nicht zu verwenden, sondern eine Anwendung zu verwenden, die in der Task-Leiste befindet (man denke Roxio Drag-to-Disc, und höchstwahrscheinlich Ihre Anti-Virus-Software lebt dort unten), die ein Symbol hat nach unten durch die Uhr, die ein Menü startet, wenn sie mit der rechten geklickt haben, und eine UI, wenn doppelgeklickt.

ist

Wenn Ihr Dienst richtig moduliert wird, können Sie die Service-Host entweder in einer ausführbaren Datei als Dienst oder mit einer ausführbaren Datei mit gui für den Test. Wir verwenden diese Methode mit unserem Service auch die Standalone-Service-ausführbaren Hosts des Dienst in produktiver Umgebung, aber wir haben eine Konsole-App für den Hosting-Service auch.

Trennen Sie den Code in verschiedene Komponenten: eine Komponente der Service-Aspekte zu verwalten und eine die eigentliche Business-Logik auszuführen. Erstellen und Interaktion mit der Geschäftslogik von der Servicekomponente. Für die Prüfung (Ihre Business-Logik) können Sie eine WinForm oder Konsole-Anwendung erstellen, die die Business-Logik-Komponente, ohne die Service-Komponente verwendet. Noch besser wäre es, eine Einheit Test-Framework für den Großteil der Tests verwenden. Viele der Methoden in der Servicekomponente werden zweifellos sein Gerät prüfbar als auch.

Wenn Sie Ihre Geschäftslogik in Service-Klassen kapseln und dann eine Fabrik Muster verwenden, um diese Dienste zu erstellen, geben Sie den gleichen Satz von Dienstleistungen für eine Desktop-Anwendung (Desktop-Fabrik) und als Web Service (Host in WCF) verwenden können.

Service-Definition:

[ServiceContract]
public interface IYourBusinessService
{
    [OperationContract]
    void DoWork();
}

public class YourBusinessService : IYourBusinessService
{
    public void DoWork()
    {
        //do some business logic here
    }

}

Fabrik für Desktop-WinForms an Dienstleistungen zu erhalten, Geschäfte zu machen:

public class ServiceFactory
{
    public static IYourBusinessService GetService()
    {
        //you can set any addition info here
        //like connection string for db, etc.
        return new YourBusinessService();
    }
}

Sie hosten diese entweder mit der WCF Servicehost-Klasse oder in IIS. Beide ermöglichen es Ihnen die Möglichkeit, festzulegen, wie jede Instanz des Dienstes zu instanziiert, so dass Sie die Initialisierung wie Verbindungszeichenfolgen tun, usw.

Sie können den Dienst erstellen Sie eine andere ausführbare Datei mit einer Befehlszeilenargument zu nennen, so dass es ohne das Formular ausgeführt wird. Wenn die exe ohne das Befehlszeilenargument genannt werden zeigt es die Form und wirkt als normal.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top