2010-01-04 15 views
14

का उपयोग करने की आवश्यकता है मैंने एक विंडोज सेवा बनाई है जो कुछ COM घटकों को कॉल कर देगी, इसलिए मैंने मुख्य कार्य में [STAThread] को टैग किया। हालांकि, जब टाइमर आग लगती है, तो यह एमटीए की रिपोर्ट करता है और COM कॉल विफल हो जाती है। मैं इसे कैसे ठीक करूं?.NET विंडोज सेवा को STAThread

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

उत्तर

24

सेवाएं विंडोज सेवा होस्टिंग सिस्टम द्वारा संचालित की जाती हैं, जो एमटीए धागे का उपयोग करती है। आप इसे नियंत्रित नहीं कर सकते। आपको एक नया Thread और set its ApartmentState to 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(); 
    } 
} 

नोट इस कोड को वास्तव में सेवा बंद नहीं है, यह घड़ी बंद हो जाता है। कई धागे पर अभी भी बहुत सारे काम किए जा सकते हैं। उदाहरण के लिए, यदि आपके काम में बड़े डेटाबेस से कई प्रश्नों को चलाने का समावेश होता है तो आप क्रैशिंग को समाप्त कर सकते हैं क्योंकि आपके पास एक ही समय में बहुत से थ्रेड चल रहे हैं।

इस तरह की स्थिति में, मैं एसटीए धागे की एक निश्चित संख्या (शायद 2x कोर की संख्या शुरू करने के लिए) तैयार करता हूं जो काम वस्तुओं के लिए थ्रेड-सुरक्षित कतार की निगरानी करता है। टाइमर टिक घटना उस कतार को काम करने की आवश्यकता के साथ लोड करने के लिए ज़िम्मेदार होगी।

यह सब क्या आप वास्तव में प्रति दस सेकंड में कर रहे हैं पर निर्भर करता है, यह, अगली बार जब टाइमर टिक्स, क्या आप इस स्थिति में क्या करना चाहिए पूरा किया जाना चाहिए या नहीं, आदि आदि

+1

+1; हालांकि, आप कैसे समझाते हैं कि कंसोल अनुप्रयोगों के लिए एक ही समस्या मौजूद है? मेरी समझ से आपकी व्याख्या हालांकि सही नहीं लगती है। सेवा नियंत्रण प्रबंधक (और थ्रेड के रूप में नहीं) की एक बाल प्रक्रिया के रूप में एक सेवा शुरू की जाती है और फिर सेवा 'StartServiceCtrlDispatcher' (जिसे मैं प्रतिबिंब में देख सकता हूं) से कॉल करके नियंत्रक से जुड़ती है, इसलिए एसटीए के रूप में सेवा शुरू करना चाहिए मुमकिन। –

+1

यह कंसोल ऐप्स के लिए मौजूद नहीं है। कंसोल ऐप्स STAThreadAttribute का सम्मान करते हैं। सेवाएं नहीं एससीएम एक एसटीए धागे में नहीं चला है। एससीएम का सम्मान नहीं करता है और न ही यह उनकी वांछित अपार्टमेंट स्थिति को संवाद करने के लिए सेवाओं के लिए कोई तरीका प्रदान करता है। ओपी का कोड उलझन में है, क्योंकि इसमें मुख्य विधि शामिल है। सभी मुख्य विधि कहती हैं, "अरे, एससीएम, यहां एक डीएलएल है जिसमें एक सेवा प्रकार है जिसमें मैं चाहता हूं कि आप दौड़ें, kthxbai" और चले जाओ। हाँ, जो एक एसटीए धागे में चलता है, लेकिन यह ब्लॉक नहीं करता है और तुरंत लौटता है। एससीएम तब निर्धारित करता है कि सेवा को अपने धागे का उपयोग करके कब चलाया जाए। – Will

+0

विल, वेब और कुछ प्रतिक्रियाओं पर कुछ और देखने के बाद, मेरे पास एक नई संरचना है (इसे टिप्पणी के रूप में छोड़ने के लिए पर्याप्त वर्ण नहीं थे) –

4

यह एक सेवा, धागा है कि अपने मुख्य() विधि कॉल में काम नहीं कर सकता पहले से ही सेवा प्रबंधक द्वारा शुरू किया गया था। आपको थ्रेड.SetApartmentState() के साथ प्रारंभ किया गया एक अलग थ्रेड बनाना होगा और एक संदेश लूप पंप करना होगा।

+0

मुझे लगता है कि समस्या टाइमर धागे के सूत्रण फ्लैट के भीतर निहित है, क्योंकि मैं (मुख्य धागा एसटीए है, लेकिन एक सांत्वना आवेदन में एक ही समस्या मिल टाइमर घटना सूत्रण अपार्टमेंट में एमटीए है)। –

+0

हाँ - टाइमर एक एमटीए धागा होने जा रहा है। मैंने इसे विशेष रूप से मेरे उत्तर में संबोधित किया। –

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

STAThread विशेषता सेट करना किसी सेवा पर काम नहीं करेगा। इसे एप्लिकेशन के समान तरीके से संभाला नहीं जा रहा है, इसलिए इसे अनदेखा कर दिया जाएगा।

मेरी सिफारिश मैन्युअल रूप से आपकी सेवा के लिए एक अलग थ्रेड बनाने, अपनी अपार्टमेंट स्थिति सेट करने और इसमें सब कुछ ले जाने के लिए होगी। इस तरह, आप थ्रेड को एसटीए में सही ढंग से सेट कर सकते हैं।

हालांकि, यहां एक और मुद्दा होगा - आपको अपनी सेवा के तरीके को फिर से काम करना होगा। आप समय के लिए System.Threading.Timer उदाहरण का उपयोग नहीं कर सकते - यह एक अलग थ्रेड पर चलता है, जो एसटीए नहीं होगा। जब इसकी विलुप्त घटना आग लगती है, तो आप एक अलग, गैर-एसटीए थ्रेड पर काम करेंगे।

टाइमर ईवेंट में अपना काम करने के बजाय, आप शायद अपने मुख्य काम को उस धागे में करना चाहेंगे जो आप स्पष्ट रूप से बनाते हैं। आपके पास उस थ्रेड में रीसेट इवेंट हो सकता है जो ब्लॉक करता है, और अपना टाइमर एसटीए थ्रेड में आपके तर्क को चलाने की अनुमति देने के लिए "सेट" करता है।

+1

सेवा के मुख्य भाग पर STAThread का उपयोग कर वेब पर बहुत से उदाहरण हैं। क्या वे गलत हैं? –

+0

उनमें से अधिकतर काम नहीं करते हैं, हालांकि। यह सेवा सेवा प्रबंधक द्वारा एक अलग थ्रेड में शुरू की जाती है, और मुख्य दिनचर्या का उपयोग उसी तरह नहीं करता है। –

+0

ओपी 'System.Timers.Timer' का उपयोग करता है (जो अभी भी एमटीए पर चल रहा है)। हालांकि, मुझे अभी भी समझ में नहीं आता है, क्यों STAThread सेवा में काम नहीं करना चाहिए? –

0

यह रिपोर्ट है कि यह एसटीए का उपयोग कर रहा है।यह विल के सुझाव के आधार पर किया जाता है और 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 
    } 
}