2010-11-16 2 views
35

मुझे सी # में लिखी गई एक विंडोज सेवा विरासत में मिली है। दुर्लभ परिस्थितियों में यह बुरी तरह विफल रहता है। हालांकि, यह बिल्कुल स्पष्ट नहीं है कि कैसे ठीक से विफल होना है। रॉस बेनेट bytes.com पर सुंदरता से समस्या बताते हैं। सादगी के लिए मैं बस उसे यहां उद्धृत करूंगा।विंडोज सेवा विफल होने का उचित तरीका क्या है?

Ahoy, Folks!

I've been looking all over for this, but I just can't seem to shake any documentation out of the MSDN or from Google. I've reviewed every .NET article on developing Windows Services in the MSDN I've located.

I'm developing a Windows Service application. This service reads its configuration data from the system registry (HKLM) where it was deposited by another "manager" application. No problems there.

The service uses a worker thread to do its work. The thread is created in the OnStart() and signaled/joined/disposed in the OnStop(). Again, no problems.

Everything works beautifully when:

  1. The system administrator has set up everything properly, and
  2. the foreign network resources are all reachable.

But of course, we as developers simply can't rely on:

  1. The system administrator having set up everything properly, or
  2. the foreign network resources being reachable.

Really, what we need is for the service application to have some way of dying on its own. If a network resource goes down, we need the service to stop. But more to the point, we need the SCM to know it has stopped on its own accord. SCM needs to know that the service has "failed"...and hasn't just been shut down by someone.

Calling "return" or throwing an exception in the "OnStart()" method isn't even helpful for services still in the start-up process.. The SCM goes merrily on and the process keeps running in the Task Manager--though it's not actually doing anything since the worker thread was never created and started.

Using a ServiceController instance doesn't do it, either. That appears to the SCM as a normal shutdown--not a service failure. So none of the recovery actions or restarts happen. (Also, there is MSDNful documentation warning about the perils of a ServiceBase descendant using a ServiceController to make things happen with itself.)

I've read articles where people were messing about with PInvoking calls to the native code just to set the "Stopped" status flag in the SCM. But that doesn't shut down the process the service is running within.

I'd really like to know the Intended Way of:

  1. Shutting down a service from within the service, where
  2. The SCM is appropriatedly notified that the service has "Stopped", and
  3. The process disappears from the Task Manager.

Solutions involving ServiceControllers don't seem to be appropriate, if only because 2 is not satisfied. (That the Framework documentation specifically contraindicates doing that carries a good deal of weight, incidentally.)

I'd appreciate any recommendations, pointers to documentation, or even well-reasoned conjecture. :-) Oh! And I'm perfectly happy to entertain that I've missed the point.

Most cordially,

Ross Bennett

उत्तर

27

देशी कोड में सर्वोत्तम अभ्यास SetServiceStatus को इंगित करने के लिए गैर-शून्य निकास कोड के साथ कॉल करना है 1) यह बंद हो गया है और 2) कुछ गलत हो गया है।

प्रबंधित कोड में, आप the ServiceBase.ServiceHandle Property और पी/Inv32-Win32 एपीआई के माध्यम से एससीएम हैंडल प्राप्त करके एक ही प्रभाव प्राप्त कर सकते हैं।

मुझे नहीं पता कि क्यों एससीएम ServiceBase.ExitCode संपत्ति गैर-शून्य सेट करने और फिर ServiceBase.Stop पर कॉल करने से अलग तरीके से इसका इलाज करेगा। यदि सेवा आतंक मोड में है तो पी/Invoke शायद थोड़ा अधिक प्रत्यक्ष है।

+16

जो उत्तर मैं ढूंढ रहा था वह इस उत्तर में निहित है। संक्षिप्त उत्तर यह है: ServiceBase.ExitCode प्रॉपर्टी को गैर-शून्य सेट करें और फिर ServiceBase.Stop पर कॉल करें। –

+0

स्पष्ट करने के लिए धन्यवाद कि सबसे अच्छा क्या काम करता है। –

+4

ने इसे (प्रबंधित एपीआई के माध्यम से, और SetServiceStatus के PInvoke के माध्यम से) की कोशिश की, और इस दृष्टिकोण ने सेवा को स्वचालित रूप से पुनरारंभ करने की अनुमति नहीं दी अगर यह ऐसा करने के लिए कॉन्फ़िगर किया गया है। हालांकि यह पूरी तरह से सेवा बंद कर देता है। 'पर्यावरण। एक्सिट() 'को कॉल करना या अपवाद फेंकना और इसे पकड़ना नहीं, ऑटो-रीस्टार्ट-ऑन-विफलता के लिए ओएस लगता है कि" असामान्य "समाप्ति का कारण बनता है। – jglouie

5

मुझे पता चला है कि पर्यावरण। एक्सिट (1) मेरे लिए ठीक काम करता है। मैं आम तौर पर इसे एक ऐसी विधि में रखता हूं जो अनचाहे अपवादों को पकड़ता है और इसे रोकने से पहले समस्या को लॉग करता है। यह पूरी तरह से सेवा को नष्ट कर देता है, लेकिन एससीएम यह भी जानता है कि यह बंद हो गया है। जब आप x बार बार जाते हैं तो आप स्वचालित रूप से अपनी सेवा को पुनरारंभ करने के लिए एससीएम सेट कर सकते हैं। मुझे लगता है कि यह आपके अपने पुनरारंभ/शट डाउन कोड लिखने से कहीं अधिक उपयोगी है।

+0

पर्यावरण। एक्सिट() शायद अधिकतर समय काम करता है, लेकिन आप अपनी प्रक्रिया के तहत कुर्सी को प्रभावी ढंग से लात मार रहे हैं। उदाहरण के लिए, यदि आप एक फ़ाइल लिख रहे थे, तो यदि आप प्रक्रिया को केवल हार्ड-स्टॉप करते हैं तो बफर डिस्क पर नहीं पहुंचेगा। – ntcolonel

2

अगर वहाँ एक (गैर पी/आह्वान) इस बात के लिए बराबर है, लेकिन जिस तरह से WinAPI SERVICE_STOPPED के एक मूल्य के साथ SetServiceStatus कॉल करने के लिए और फिर आप को बंद करने के एससीएम के लिए इंतजार हो रहा है मैं नहीं जानता। सकारात्मक दुष्प्रभाव के रूप में, यह ईवेंट लॉग में आपकी सेवा की विफलता को लॉग करता है।

यहाँ the relevant part of the documentation से कुछ उद्धरण हैं:

If a service calls SetServiceStatus with the dwCurrentState member set to SERVICE_STOPPED and the dwWin32ExitCode member set to a nonzero value, the following entry is written into the System event log:

[...] <ServiceName> terminated with the following error: <ExitCode> [...]

The following are best practices when calling this function:

[...]

  • If the status is SERVICE_STOPPED, perform all necessary cleanup and call SetServiceStatus one time only. This function makes an LRPC call to the SCM. The first call to the function in the SERVICE_STOPPED state closes the RPC context handle and any subsequent calls can cause the process to crash.
  • Do not attempt to perform any additional work after calling SetServiceStatus with SERVICE_STOPPED, because the service process can be terminated at any time.

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

+1

+1 कि पीएस सिर्फ वही था जो मैं ढूंढ रहा था। मान लीजिए कि उपलब्धता की जांच के लिए कुछ उचित अंतराल ढूंढना है, तो – LuqJensen

1

आप here वर्णित उचित ExitCode प्राप्त कर सकते हैं। तो विंडोज सेवा प्रबंधक सही त्रुटि पाठ देगा।

ServiceBase में मेरे onStart इस तरह दिखता है:

protected override void OnStart(string[] args) 
{ 
    try 
    { 
     DoStart(); 
    } 
    catch (Exception exp) 
    { 
     Win32Exception w32ex = exp as Win32Exception; 
     if (w32ex == null) 
     { 
      w32ex = exp.InnerException as Win32Exception; 
     } 
     if (w32ex != null) 
     { 
      ExitCode = w32ex.ErrorCode; 
     } 
     Stop(); 
    } 
} 
+1

प्रश्न जो आप संदर्भित करते हैं [सी # में अपवाद त्रुटि कोड कैसे प्राप्त करें] (http://stackoverflow.com/questions/6893165/how-to-get-exception-error-code-in-c-sharp) बताता है कि कैसे प्राप्त करें PInvoke के माध्यम से WMI रूटीन को कॉल करते समय अपवाद कोड। यह इस सवाल का कोई सामान्य जवाब नहीं है। –

0

कुछ परीक्षण करने के बाद मैंने पाया मामलों में निम्नलिखित काम करता है जहाँ आप बंद करो बुला अन्य मुद्दों के कारण हो सकता:

 ExitCode = 1; 
     Environment.Exit(1); 

बस बुला Environment.Exit एससीएम को गलती से निपटने में कोई दिक्कत नहीं होती है, लेकिन पहले सर्विसबेस एक्ज़िटकोड सेट करता है।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^