15

一部のCOMコンポーネントを呼び出すWindowsサービスを作成したので、[STAThread]をMain関数にタグ付けしました。ただし、タイマーが起動すると、MTAが報告され、COM呼び出しは失敗します。どうすればこれを修正できますか?

using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Timers;



namespace MyMonitorService
{
    public class MyMonitor : ServiceBase
    {
        #region Members
        private System.Timers.Timer timer = new System.Timers.Timer();
        #endregion

        #region Construction
        public MyMonitor ()
        {
            this.timer.Interval = 10000; // set for 10 seconds
            this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
        }
        #endregion

        private void timer_Elapsed (object sender, ElapsedEventArgs e)
        {
            EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);
        }

        #region Service Start/Stop
        [STAThread]
        public static void Main ()
        {
            ServiceBase.Run(new MyMonitor());
        }

        protected override void OnStart (string[] args)
        {
            EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
            this.timer.Enabled = true;
        }

        protected override void OnStop ()
        {
            EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
            this.timer.Enabled = false;
        }
        #endregion
    }
}
4

5 に答える 5

28

サービスは、MTAスレッドを使用して実行されるWindowsサービスホスティングシステムによって実行されます。これを制御することはできません。新しいスレッドを作成し、そのApartmentStateをSTAに設定して、このスレッドで作業を行う必要があります。

これを行うServiceBaseを拡張するクラスは次のとおりです。

public partial class Service1 : ServiceBase
{
    private System.Timers.Timer timer;

    public Service1()
    {
        InitializeComponent();
        timer = new System.Timers.Timer();
        this.timer.Interval = 10000; // set for 10 seconds
        this.timer.Elapsed += new System.Timers.ElapsedEventHandler(Tick);
    }

    protected override void OnStart(string[] args)
    {
        timer.Start();
    }

    private void Tick(object sender, ElapsedEventArgs e)
    {
        // create a thread, give it the worker, let it go
        // is collected when done (not IDisposable)
        var thread = new Thread(WorkerMethod);
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        OnStop(); // kill the timer
    }

    private void WorkerMethod(object state)
    {
        // do your work here in an STA thread
    }

    protected override void OnStop()
    {
        timer.Stop();
        timer.Dispose();
    }
}

このコードは実際にはサービスを停止するのではなく、タイマーを停止することに注意してください。複数のスレッドでまだ多くの作業が行われている可能性があります。たとえば、作業が大規模なデータベースから複数のクエリを実行することで構成されている場合、同時に実行しているスレッドが多すぎるためにクラッシュする可能性があります。

このような状況では、作業項目のスレッドセーフキューを監視する一定数のSTAスレッド(おそらく最初にコアの数の2倍)を作成します。タイマーティックイベントは、実行する必要のある作業をキューにロードする役割を果たします。

それはすべて、実際に10秒ごとに何をしているか、次にタイマーが作動したときに完了する必要があるかどうか、この状況で何をすべきかなどによって異なります。

于 2010-01-04T19:56:05.523 に答える
5

これはサービスでは機能しません。Main()メソッドを呼び出すスレッドは、サービスマネージャーによって既に開始されています。Thread.SetApartmentState()で初期化され、メッセージループをポンピングする別のスレッドを作成する必要があります。

于 2010-01-04T19:48:34.883 に答える
5

STAThread属性の設定は、サービスでは機能しません。アプリケーションと同じように処理されていないため、これは無視されます。

私の推奨事項は、サービス用に別のスレッドを手動で作成し、そのアパートメントの状態を設定して、すべてをそこに移動することです。このようにして、スレッドをSTAに正しく設定できます。

ただし、ここには別の問題があります。サービスの動作方法を作り直す必要があります。System.Threading.Timerインスタンスをタイミングに使用することはできません。これは、STAではない別のスレッドで実行されます。経過したイベントが発生すると、別の非STAスレッドで作業することになります。

タイマーイベントで作業を行う代わりに、明示的に作成したスレッドで主な作業を行うことをお勧めします。そのスレッドでリセットイベントをブロックして、タイマーに「設定」させて、ロジックをSTAスレッドで実行できるようにすることができます。

于 2010-01-04T19:49:29.887 に答える
0

同様の例を見てください:http ://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/

あなたのメインが...

    [STAThread]
    public static void Main ()
    {
        MyMonitor m = new MyMonitor();
        m.Start();
    }

タイマーの開始/停止をイベントから移動します...

 public void Start() { this.timer.Enabled = true;}
 public void Stop() { this.timer.Enabled = false;}

  protected override void OnStart (string[] args)
    {
        EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
    }

    protected override void OnStop ()
    {
        EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
    }
于 2010-01-04T19:49:11.293 に答える
0

これは、STAを使用していることを報告します。これは、ウィルの提案とhttp://en.csharp-online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1:_Use_a_Separate_Threadに基づいています。

using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;



namespace MyMonitorService
{
    internal class MyMonitorThreaded : ServiceBase
    {
        private Boolean bServiceStarted = false;
        private Thread threadWorker;

        private void WorkLoop ()
        {
            while (this.bServiceStarted)
            {
                EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);

                if (this.bServiceStarted)
                    Thread.Sleep(new TimeSpan(0, 0, 10));
            }

            Thread.CurrentThread.Abort();
        }

        #region Service Start/Stop
        protected override void OnStart (String[] args)
        {
            this.threadWorker = new Thread(WorkLoop);
            this.threadWorker.SetApartmentState(ApartmentState.STA);
            this.bServiceStarted = true;
            this.threadWorker.Start();
        }

        protected override void OnStop ()
        {
            this.bServiceStarted = false;
            this.threadWorker.Join(new TimeSpan(0, 2, 0));
        }
        #endregion
    }
}
于 2010-01-04T21:01:01.427 に答える