Winforms 프로그램으로도 실행할 수있는 C# 서비스를 작성하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/421516

문제

C#에 작성된 Windows 서비스가 있으며, 이는 백엔드 데이터베이스에 대한 네트워크 장치의 대리로 작용합니다. 테스트와 시뮬레이션 레이어를 추가하기 위해 백엔드를 테스트하기 위해 테스트 연산자가 시뮬레이션을 실행할 수 있도록 GUI를 갖고 싶습니다. 또한 스트라이프 다운 버전이 데모로 전송됩니다. GUI와 서비스는 동시에 실행할 필요가 없습니다. 이 결투 작업을 달성하는 가장 좋은 방법은 무엇입니까?

편집 : 여기 내 솔루션이 있습니다 이 질문 , 나는 서비스로 실행 중입니다 그리고 installUtil.exe없이 .NET Windows 서비스를 설치하십시오 사용 이 훌륭한 코드 ~에 의해 마크 그라벨

다음 줄을 사용하여 GUI를 실행하거나 서비스로 실행하는지 테스트합니다.

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

코드는 다음과 같습니다.


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

}
도움이 되었습니까?

해결책

당신은 기본적으로 두 가지 선택이 있습니다. 서비스에 API를 노출 한 다음 UI 앱에서 호출 할 수 있거나 서비스가 WinForms 앱 또는 서비스로 실행할 수 있도록합니다.

첫 번째 옵션은 매우 쉽습니다. API를 노출시키기 위해 원격 또는 WCF를 사용합니다.

두 번째 옵션은 앱의 "내장"을 별도의 클래스로 옮기고 서비스 래퍼와 "내장"클래스로 호출하는 윈 포름 래퍼를 만들어 달성 할 수 있습니다.

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

다른 팁

새 Winforms 앱을 작성하여 서비스의 어셈블리를 참조하십시오.

아래 코드를 사용하는 경우 :

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

그런 다음 EXE는 서비스 (SCM이 시작한 경우) 또는 GUI (다른 프로세스에서 시작한 경우)로 실행됩니다.

본질적으로, 내가 여기서 한 모든 일이 사용됩니다 반사기 고기가 무엇인지 알아 내기 위해 ServiceBase.Run 여기에 복제하고 복제합니다 (개인 방법을 호출하기 때문에 반사가 필요합니다). 전화하지 않은 이유 ServiceBase.Run 직접 메시지 상자를 팝업하는 것입니다. 사용자 서비스를 시작할 수 없으며 (SCM이 시작하지 않은 경우) 암호 서비스를 시작할 수 없습니다.

이것은 반사를 사용하여 개인 프레임 워크 방법을 호출하기 때문에 프레임 워크의 향후 개정에서 올바르게 작동하지 않을 수 있습니다. 경고 코더.

도 있습니다 Firedaemon. 이를 통해 Windows 응용 프로그램을 서비스로 실행할 수 있습니다.

보다 나는 서비스로 실행 중입니다 더 유용한 정보를 위해.

가장 중요한 것은 대화식으로 또는 서비스를 통해 실행 중인지 안정적으로 결정하는 방법입니다.

서비스와 통신 할 수있는 별도의 프로세스를 구현해야합니다. XP와 이전 시스템에서는 UI를 보여주는 서비스를 가질 수 있지만 더 이상 Vista 이상에서는 불가능합니다.

또 다른 가능성은 서비스를 사용하지 않고 작업 표시 줄 (roxio drag-to-disc, 그리고 바이러스 백신 소프트웨어가 그 아래에 살고있을 가능성이 높음)에있는 응용 프로그램을 사용하는 것입니다. 마우스 오른쪽 버튼을 클릭하면 메뉴를 시작하고 두 번 클릭하면 UI를 시작합니다.

서비스가 올바르게 변조 된 경우 서비스를 서비스로 실행 파일 또는 테스트를 위해 GUI로 실행 파일로 호스팅 할 수 있습니다. 우리는이 방법을 서비스와 함께 사용합니다. 독립형 서비스를 사용하여 생산 환경에서 서비스를 호스팅하지만 서비스를 호스팅하기위한 콘솔 앱도 있습니다.

코드를 다른 구성 요소로 분리하십시오. 서비스 측면을 관리하기위한 하나의 구성 요소, 실제 비즈니스 로직을 수행하기위한 구성 요소. 서비스 구성 요소에서 비즈니스 로직을 생성하고 상호 작용합니다. (비즈니스 로직) 테스트의 경우 서비스 구성 요소없이 비즈니스 로직 구성 요소를 사용하는 Winform 또는 콘솔 응용 프로그램을 만들 수 있습니다. 더 나은 방법은 대부분의 테스트를 위해 단위 테스트 프레임 워크를 사용하십시오. 서비스 구성 요소의 많은 방법은 의심 할 여지없이 단위 테스트 가능합니다.

서비스 클래스에서 비즈니스 로직을 캡슐화 한 다음 공장 패턴을 사용하여 해당 서비스를 작성하는 경우 데스크탑 응용 프로그램 (데스크탑 팩토리) 및 웹 서비스 (WCF의 호스트)에 동일한 서비스 세트를 사용할 수 있습니다.

서비스 정의 :

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

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

}

데스크탑 winforms를위한 공장 비즈니스 서비스를받을 수있는 공장 :

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

이를 WCF ServiceHost 클래스 또는 IIS에서 호스팅합니다. 둘 다 연결 문자열과 같은 초기화를 수행 할 수 있도록 서비스의 각 인스턴스를 인스턴스화하는 방법을 지정할 수 있습니다.

명령 줄 인수로 다른 실행 파일을 호출하여 양식없이 실행되도록 서비스를 작성할 수 있습니다. 그 exe가 명령 줄 인수없이 호출되면 형식을 보여주고 정상적인 역할을합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top