8

バックエンドデータベースへの一連のネットワークデバイスのプロキシとして機能する C# で記述された Windows サービスがあります。テストと、バックエンドをテストするためのシミュレーション レイヤーを追加するために、テスト オペレーターがシミュレーションを実行できる GUI が必要です。また、ストライプ ダウン バージョンをデモとして送信することもできます。GUI とサービスは同時に実行する必要はありません。この決闘作戦を達成するための最良の方法は何ですか?

編集: これは、この質問からのものを組み合わせた私のソリューションです。Am I Running as a Serviceで、 InstallUtil.exe なしで .NET Windows サービスをインストールします。Marc Gravell によるこの優れたコードを使用します。

次の行を使用して、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;
      }
   }

}
4

11 に答える 11

17

基本的に 2 つの選択肢があります。UIアプリから呼び出すことができるサービスでAPIを公開するか、サービスをwinformsアプリまたはサービスとして実行できるようにします。

最初のオプションは非常に簡単です。リモート処理または WCF を使用して API を公開します。

2 番目のオプションは、アプリの "guts" を別のクラスに移動してから、"guts" クラスを呼び出すサービス ラッパーと win-forms ラッパーを作成することで実現できます。

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);
    }
}
于 2009-01-07T19:02:49.680 に答える
2

以下のコードを使用する場合:

[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、それをここに複製することだけです (プライベート メソッドを呼び出すため、リフレクションが必要です)。直接呼び出さない理由は、(SCM によって起動されていない場合) サービスを開始できないことをユーザーServiceBase.Runに通知するメッセージ ボックスをポップアップ表示し、サービスを開始できないことをコードに通知するものを何も返さないためです。

これはリフレクションを使用してプライベート フレームワーク メソッドを呼び出すため、フレームワークの将来のリビジョンでは正しく機能しない可能性があります。 警告コード。

于 2009-01-07T19:15:46.347 に答える
2

サービスのアセンブリを参照する新しい winforms アプリを作成します。

于 2009-01-07T18:46:49.667 に答える
1

FireDaemonもあります。これにより、任意の Windows アプリケーションをサービスとして実行できます。

于 2009-01-07T18:48:05.513 に答える
1

さらに役立つ情報については、「サービスとして実行していますか」を参照してください。

最も重要なことは、インタラクティブに実行しているか、サービスを介して実行しているかを確実に判断する方法です。

于 2009-01-08T18:28:16.723 に答える
0

コードをさまざまなコンポーネントに分割します。1 つのコンポーネントはサービスの側面を管理し、もう 1 つのコンポーネントは実際のビジネス ロジックを実行します。サービス コンポーネントからビジネス ロジックを作成して操作します。(ビジネス ロジックの) テストでは、サービス コンポーネントなしでビジネス ロジック コンポーネントを使用する WinForm またはコンソール アプリケーションを作成できます。さらに良いのは、テストの大部分に単体テスト フレームワークを使用することです。サービス コンポーネントのメソッドの多くは、間違いなく単体テストも可能です。

于 2009-01-07T18:56:08.977 に答える
0

ビジネス ロジックをサービス クラスにカプセル化し、ファクトリ パターンを使用してそれらのサービスを作成する場合、同じサービス セットをデスクトップ アプリケーション (デスクトップ ファクトリ) および Web サービス (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 でホストします。どちらも、サービスの各インスタンスをインスタンス化する方法を指定できるため、接続文字列などの初期化を行うことができます。

于 2009-01-07T21:52:37.623 に答える
0

サービスと通信できる別のプロセスを実装する必要があります。XP 以前のシステムではサービスに UI を表示できますが、Vista 以降ではできなくなりました。

于 2009-01-07T18:48:17.807 に答える
0

もう 1 つの可能性は、サービスを使用するのではなく、タスクバー (Roxio Drag-to-Disc を考えてみてください。おそらくアンチウイルス ソフトウェアはそこにあると考えてください) に常駐し、時計の横にアイコンがあるアプリケーションを使用することです。右クリックするとメニューが起動し、ダブルクリックすると UI が起動します。

于 2009-01-07T18:50:08.940 に答える
0

コマンドライン引数を使用して別の実行可能ファイルを呼び出すサービスを作成して、フォームなしで実行することができます。そのexeがコマンドライン引数なしで呼び出されると、フォームが表示され、通常どおりに動作します。

于 2009-01-10T18:25:30.450 に答える
0

サービスが適切に調整されている場合は、サービスとしての実行可能ファイル、またはテスト用の gui を使用した実行可能ファイルでサービスをホストできます。私たちのサービスでもこの方法を使用します。スタンドアロンのサービス実行可能ファイルが本稼働環境でサービスをホストしますが、サービスをホストするためのコンソール アプリもあります。

于 2009-01-07T18:51:46.020 に答える