4

常にオンになっていないWindowsサービスを使用するC#アプリケーションがあり、サービスの開始時とシャットダウン時に電子メール通知を送信できるようにしたいと考えています。メールスクリプトを書いていますが、サービスステータスの変化を検出する方法がわからないようです。

クラスを読んでいて、メソッドが必要なのかもしれないとServiceController思いますが、まだ開始されていないサービスで使用されている例を見つけることができませんでした。WaitForStatus()編集:WaitForStatus()メソッドがbusy-waitsであり、サービスの開始/停止をリッスンしながらサービスによって実行されるプログラムの残りの部分を実行する必要があるという事実のため、これは私にとってのメソッドではないと思います、誰かがマルチスレッドと組み合わせてこの方法を使用し、クリーンで効率的なソリューションを持っていない限り。

 

もっと:

  • サービスはアプリケーションによって開始されません。アプリケーションユーザーは、管理ツールの[サービス]ウィンドウから直接サービスを開始します。
  • 使用されるサービスはデフォルトのWindowsサービスではありません-このアプリケーションで使用するために設計されたカスタムサービスです

 

ご協力いただきありがとうございます!

 

PS私はC#にかなり慣れておらず、ここに行くにつれて学習していることに注意してください。

 

アップデート:

サービスが開始するたびにアラートメールを送信することができました。自分が持っているコード(残念ながらここに投稿できません)を読み続けると、サービスの作成に使用したクラスが拡張されていることに気付きました。クラスと、誰かがデフォルトのメソッドをオーバーライドServiceBaseするカスタムメソッドを作成したこと。OnStart()新しいメソッドに必要なメソッド呼び出しを追加するOnStart()と、通知が正常に送信されました。

このメソッドでも同じことを試みましたが、OnStop()うまくいきませんでした。続行する前に、Javaでプログラミングを数年間行っており、Javaデザインに精通していることを付け加えておきます。パターン。

私がやろうとしたのは、Javaで機能するはずだったのですが、ServiceBaseクラスのメソッドを、電子メール通知を呼び出すメソッドでオーバーライドし、型OnStop()にキャストしてから、クラスのメソッドを再度呼び出すことでした(注:保護されたメソッドであるため、できませんでした)直接呼び出される-メソッドが呼び出してから、サービスを停止するために必要なコードを続行します)。タイプにキャストすると、カスタムメソッドではなく、デフォルトのメソッドが強制的に呼び出されると思いました。MyServiceServiceBaseServiceBaseStop()OnStop()Stop()OnStop()ServiceBaseOnStop()

ご想像のとおり、コンピュータを強制的にシャットダウンする前に、受信トレイに10,000通弱の電子メールが正常に送信されました。

今必要なのは、オーバーライドされたOnStop()メソッドを使用してからデフォルトのメソッドを呼び出す方法、またはこの問題の別の解決策です。ありとあらゆる助けをいただければ幸いです。本当にありがとう。

 

マルチスレッドソリューションをお持ちの方へ:

protected override void OnStart(string[] args) {
     string subject = "Notice: Service Started";
     string body = "This message is to notify you that the service " +
         "has been started. This message was generated automatically.";
     EmailNotification em = new EmailNotification(subject, body);
     em.SendNotification();

     ...INITIALIZE LISTENER FOR SERVICE STOPPING HERE...

     ...custom stuff to be run on start...
 }

Serviceまた、このメソッドが呼び出されるクラスを、それを呼び出して、クラスを拡張することを忘れないでServiceBaseください。

 

更新2:

私が使用する提案に関して、NotifyServerStatusChangeさまざまな理由により、ソリューションがシステム機能を使用することは許可されていないことを知りました。明確にするために、純粋にC#と.NETの範囲内にあるソリューションのみが実行可能です。もう一度、あなたの助けに感謝します!

4

6 に答える 6

4

解決策と、以前にそれを見つけることができなかった理由は次のとおりです。前に述べたように、私のクラスはServiceBaseクラスを拡張しました。私の最初のアップデートでは、Javaで解決したのと同じ方法でこれを解決しようとしたことを投稿しました。キャストを通じてです。ただし、C#のキャストでは、派生クラスでベースメソッドをオーバーライドした場合、ベースメソッドを呼び出せないようです。この質問とこの更新を最初に投稿したときに私が知らなかったことの1つ(そして明らかに誰も考えていなかったことの1つ)は、C#にbase基本クラスのメソッドをから呼び出すために使用できるコンストラクターが含まれていることでした派生クラス。コンストラクターはC#のどのクラスにも使用できるため、 ServiceBaseクラスのドキュメントbaseには記載されていません。

これを学んだ後、元のソリューションを使用して、基本クラスを使用するように変更することができました。

    protected override void OnStop() {
        string subject = "Notice: Service Stopped";
        string body = "This message is to notify you that the service has " +
            "been stopped. This message was generated automatically.";
        EmailNotification em = new EmailNotification(subject, body);
        em.SendNotification();
        base.OnStop();
    }

Visual Studioでコードをbase試していて、IntelliSenseで気付いたときに、これを理解しました。クリックしてその定義に移動すると、ServiceBaseに送信されました(明らかに定義されていませんでした)。ベースが私のコードで定義されておらず、それがServiceBaseクラスのインスタンスであることに気付いた後、それはある種のコンストラクターであるに違いないことに気づきました。グーグルで簡単に検索したところ、探していたものが見つかりました。IntelliSenseへの道!

みなさん、ありがとうございました!

于 2012-07-31T16:15:18.827 に答える
3

win32 apiのない.NETソリューションが必要な場合は、以下の私のソリューションを確認してください。私はServiceControllerクラスから継承し、タスク内でWaitForStatus()を使用して非ブロッキングにし、ステータスが変更されたときにイベントを発生させています。おそらくそれはより多くのテストが必要ですが、私にとってはうまくいきます:

クラス定義

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.ServiceProcess; // not referenced by default

public class ExtendedServiceController: ServiceController
{
    public event EventHandler<ServiceStatusEventArgs> StatusChanged;
    private Dictionary<ServiceControllerStatus, Task> _tasks = new Dictionary<ServiceControllerStatus, Task>();

    new public ServiceControllerStatus Status
    {
        get
        {
            base.Refresh();
            return base.Status;
        }
    }

    public ExtendedServiceController(string ServiceName): base(ServiceName)
    {
        foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
        {
            _tasks.Add(status, null);
        }
        StartListening();
    }

    private void StartListening()
    {
        foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
        {
            if (this.Status != status && (_tasks[status] == null || _tasks[status].IsCompleted))
            {
                _tasks[status] = Task.Run(() =>
                {
                    try
                    {
                        base.WaitForStatus(status);
                        OnStatusChanged(new ServiceStatusEventArgs(status));
                        StartListening();
                    }
                    catch
                    {
                        // You can either raise another event here with the exception or ignore it since it most likely means the service was uninstalled/lost communication
                    }
                });
            }
        }
    }

    protected virtual void OnStatusChanged(ServiceStatusEventArgs e)
    {
        EventHandler<ServiceStatusEventArgs> handler = StatusChanged;
        handler?.Invoke(this, e);
    }
}

public class ServiceStatusEventArgs : EventArgs
{
    public ServiceControllerStatus Status { get; private set; }
    public ServiceStatusEventArgs(ServiceControllerStatus Status)
    {
        this.Status = Status;
    }
}

利用方法

static void Main(string[] args)
{
    ExtendedServiceController xServiceController = new ExtendedServiceController("myService");
    xServiceController.StatusChanged += xServiceController_StatusChanged;
    Console.Read();

    // Added bonus since the class inherits from ServiceController, you can use it to control the service as well.
}

// This event handler will catch service status changes externally as well
private static void xServiceController_StatusChanged(object sender, ServiceStatusEventArgs e)
{
    Console.WriteLine("Status Changed: " + e.Status);
}
于 2021-06-28T09:11:15.860 に答える
1

ServiceControllerクラスにはメソッドWaitForStatusがあります。ただし、内部的にはポーリングを行います。

于 2012-07-30T21:16:05.613 に答える
1

wmiを使用して、イベントを監視できます:technet.microsoft.com/en-us/library/ff730927.aspx

于 2012-07-31T13:35:11.850 に答える
1

NotifyServiceStatusChangeをPInvokeできない場合は、サービスをポーリングする必要があります。例えば:

ServiceController sc = new ServiceController("Some Service");
Console.WriteLine("Status = " + sc.Status);
于 2012-07-31T14:29:00.077 に答える
0

NotifyServiceStatusChange()は、Windows Vista / Windows 2008(およびそれ以降)でのみサポートされているため、使用には十分注意してください。以下のプラットフォームをターゲットにしている場合、そのAPIを使用することはできません。(XP / Windows 2000-2003システムはまだたくさんあります。)

さらに悪いことに、非常に高速なシステム(SSDドライブまたは仮想マシンの事前バッファリングされたI / O)でサービスをポーリングしている場合、サービスが再起動する可能性があるため、サービスの再起動の場合にポーリングが常に信頼できるとは限りません。 2つの投票の間。

于 2013-05-30T09:37:51.363 に答える