Pergunta

Eu tenho um serviço do Windows escrito em C # que age como um proxy para um grupo de dispositivos de rede para o banco de dados back-end. Para testar e também para adicionar uma camada de simulação para testar o back-end Eu gostaria de ter uma interface gráfica para o operador de teste para ser executado capaz da simulação. Também para uma versão listrada para baixo para enviar como uma demo. A GUI e serviço não tem que correr ao mesmo tempo. Qual é a melhor maneira de alcançar esta operação duelo?

Edit: Aqui está a minha solução pentear coisas de esta questão , Am I executado como um serviço e Instalar um serviço do Windows .NET sem InstallUtil.exe usando este excelente código por Marc Gravell

Ele usa a seguinte linha para testar se para executar o gui ou executar como serviço.

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

Aqui está o código.


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

}
Foi útil?

Solução

Você tem basicamente duas opções. Quer expor uma API sobre o serviço que você pode então chamar a partir do aplicativo de interface do usuário ou ativar o serviço para ser executado tanto como um aplicativo winforms ou um serviço.

A primeira opção é muito fácil -. Uso de comunicação remota ou WCF para expor a API

A segunda opção pode ser alcançado movendo a "coragem" do seu aplicativo em uma classe separada, em seguida, criar um wrapper de serviço e uma vitória-formas de invólucro que ambos põem em sua classe "tripas".

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

Outras dicas

Criar uma nova winforms app as referências a montagem de seu serviço.

Se você usar o código abaixo:

[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
}

Em seguida, o EXE será executado como um serviço (se lançado pelo SCM) ou como uma GUI (se lançou por qualquer outro processo).

Essencialmente, tudo que eu fiz aqui é usado refletor para descobrir o que a carne de ServiceBase.Run faz, e duplicá-lo aqui (reflexão é necessário, porque ele chama métodos privados). A razão para não chamar ServiceBase.Run diretamente é que aparece uma caixa de mensagem para dizer ao user que o serviço não pode ser iniciado (se não for lançada pelo SCM) e não retorna nada para dizer ao < em> código que o serviço não pode ser iniciado.

Uma vez que este usa a reflexão para chamar métodos de enquadramento privadas, pode não funcionar corretamente em futuras revisões do quadro. Caveat codor.

Há também FireDaemon . Isso permite que você execute qualquer aplicação de janelas como um serviço.

Am I funcionando como um serviço para alguns mais útil informações.

A coisa mais importante coberto é como determinar de forma confiável se estamos executando interativamente ou através de um serviço.

você tem que implementar um processo separado que pode se comunicar com o seu serviço. Embora seja possível no XP e sistemas anteriores para ter um serviço mostrando uma interface de usuário, que já não é possível no Vista e mais tarde.

Outra possibilidade é não usar um serviço, mas para usar uma aplicação que reside na barra de tarefas (pense Roxio Drag-to-Disc, e mais prováveis ??suas vidas software anti-vírus para baixo lá) que tem um ícone para baixo pela relógio, que lança um menu, quando é clicado com o botão direito, e uma interface do usuário quando clicado duas vezes.

Se o seu serviço é modulada corretamente, você pode hospedar o serviço ou em um executável como um serviço, ou com um executável com gui para o teste. Nós usamos este método com o nosso serviço também, os serviços de executável autônomo hospeda o serviço no ambiente produtivo, mas temos um console app para hospedar o serviço, também.

Separar o seu código em diferentes componentes: um componente para gerenciar os aspectos de serviços e um para executar a lógica de negócio real. Criar e interagir com a lógica de negócios do componente de serviço. Para testar (de sua lógica de negócio), você pode criar um WinForm ou aplicativo de console que usa o componente de lógica de negócios sem o componente de serviço. Melhor ainda, use um framework de testes unitários para a maior parte do seu teste. Muitos dos métodos no componente de serviço será, sem dúvida unidade testável também.

Se você encapsular sua lógica de negócio em classes de serviço e, em seguida, usar um padrão de fábrica para criar esses serviços, você pode usar o mesmo conjunto de serviços para um aplicativo de desktop (fábrica área de trabalho) e, como serviços web (host em WCF).

definição de serviço:

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

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

}

Fábrica para WinForms de desktop para obter os serviços para fazer negócios:

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

Você acolher este ou com a classe WCF ServiceHost, ou no IIS. Ambos permitem-lhe a capacidade para especificar como instanciar cada instância do serviço para que você pode fazer a inicialização como seqüências de conexão, etc.

Você pode criar o serviço para chamar outro executável com um argumento de linha de comando para que seja executado sem o formulário. Quando isso exe é chamado sem o argumento de linha de comando que mostra a forma e agir como normal.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top