दिलचस्प पोस्ट
प्रवेश 2007 वीबीए में "ओपन फाइल" संवाद कैसे दिखाया जाए? जावास्क्रिप्ट के साथ क्वेरी स्ट्रिंग कैसे बनाएं कॉलबैक के रूप में उपयोग करने के लिए किसी ब्लॉक को लेते हुए एक विधि लागू करना मैं std namespace में क्या और क्या नहीं कर सकता हूँ? निर्देशिका में प्रत्येक फ़ाइल के लिए लूप कोड स्विफ्ट में UITextField और UITextView की कर्सर स्थिति प्राप्त करना और सेट करना इको ने पीएस 1 का विस्तार किया PHP दिनांक () प्रारूप जब MySQL में datetime में डालने गतिशील रूप से आवंटित सरणी के लिए आदर्श विकास दर क्या है? एक्जीप्शन से चलने वाले सभी जेयूनेट परीक्षणों के लिए कस्टम लॉग 4 जे.प्रॉपर्टीज़ फ़ाइल निर्दिष्ट करना पायथन में फ़ाइल से संख्याओं को कैसे पढ़ें? SSRS 2005 सर्वर पर रिपोर्ट की बजाय सिंपल पृष्ठ हेडर सेट करें? स्प्रिंग कैसे काम करता है? चींटी बिल्ड फ़ाइलों को स्वचालित रूप से चलाने के लिए एक ग्रहण जावा प्रोजेक्ट चाहिए स्थानीय भंडारण कभी सुरक्षित माना जा सकता है?

मैं एक्सेल इंटरॉप ऑब्जेक्ट को ठीक से कैसे साफ़ कर सकता हूं?

मैं सी # ( ApplicationClass क्लास) में एक्सेल इंटरॉप का उपयोग कर रहा हूं और मेरे अंतिम खंड में निम्न कोड रखा है:

 while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { } excelSheet = null; GC.Collect(); GC.WaitForPendingFinalizers(); 

यद्यपि इस तरह का काम करता है, Excel के पास होने के बाद भी Excel.exe प्रक्रिया अभी भी पृष्ठभूमि में है I यह मेरा आवेदन मैन्युअल रूप से बंद होने के बाद ही जारी किया जाता है।

मैं क्या कर रहा हूँ गलत है, या वहाँ एक विकल्प है सुनिश्चित करने के लिए interop वस्तुओं का ठीक से निपटारा कर रहे हैं?

वेब के समाधान से एकत्रित समाधान "मैं एक्सेल इंटरॉप ऑब्जेक्ट को ठीक से कैसे साफ़ कर सकता हूं?"

एक्सेल बंद नहीं करता क्योंकि आपके अनुप्रयोग में अभी भी COM ऑब्जेक्ट के संदर्भ हैं।

मुझे लगता है कि आप किसी COM ऑब्जेक्ट के कम से कम एक सदस्य को एक चर में निर्दिष्ट किए बिना प्रयोग कर रहे हैं।

मेरे लिए यह excelApp.Worksheets वस्तु है जो मैं सीधे इसे एक चर के लिए निर्दिष्ट बिना इस्तेमाल किया गया था:

 Worksheet sheet = excelApp.Worksheets.Open(...); ... Marshal.ReleaseComObject(sheet); 

मुझे नहीं पता था कि आंतरिक रूप से सी # वर्कशीट COM ऑब्जेक्ट के लिए एक आवरण बनाया है जो मेरे कोड द्वारा जारी नहीं हुआ (क्योंकि मुझे इसकी जानकारी नहीं थी) और यही वजह थी कि एक्सेल अनलोड नहीं था।

मुझे इस पृष्ठ पर मेरी समस्या का समाधान मिला है, जिसमें सी # ऑब्जेक्ट्स के उपयोग के लिए अच्छा नियम है C #:

COM ऑब्जेक्ट के साथ दो बिंदुओं का उपयोग कभी नहीं करें


इसलिए इस ज्ञान से उपर्युक्त करने का सही तरीका है:

 Worksheets sheets = excelApp.Worksheets; // <-- The important part Worksheet sheet = sheets.Open(...); ... Marshal.ReleaseComObject(sheets); Marshal.ReleaseComObject(sheet); 

आप वास्तव में अपने एक्सेल एप्लिकेशन ऑब्जेक्ट को ठीक से रिलीज कर सकते हैं, लेकिन आपको ध्यान रखना है।

पूरी तरह से प्रत्येक COM ऑब्जेक्ट के लिए एक नामित संदर्भ बनाए रखने की सलाह आपको Marshal.FinalReleaseComObject() माध्यम से जारी करते हैं और फिर स्पष्ट रूप से इसे Marshal.FinalReleaseComObject() माध्यम से रिलीज करते हैं। Marshal.FinalReleaseComObject() सिद्धांत में सही है, लेकिन दुर्भाग्य से, व्यवहार में प्रबंधन करना बहुत मुश्किल है। यदि कोई भी कहीं भी फिसल जाता है और "दो बिंदु" का उपयोग करता है, या for each लूप के लिए, या किसी अन्य समान प्रकार की कमांड के for each कोशिकाओं को पुनरावृत्त करता है, तो आपके पास COM ऑरेजेस नहीं होंगे और एक लटका होगा। इस मामले में, कोड में कारण खोजने का कोई तरीका नहीं होगा; आपको आंखों से अपने सभी कोड की समीक्षा करनी होगी और आशा है कि कारण, एक बड़ी परियोजना के लिए लगभग असंभव हो सकता है

अच्छी खबर यह है कि आपको वास्तव में प्रत्येक COM ऑब्जेक्ट के लिए नामित वैरिएबल संदर्भ को बनाए रखने का उपयोग नहीं करना है इसके बजाय, GC.WaitForPendingFinalizers() GC.Collect() और फिर GC.WaitForPendingFinalizers() को सभी (आमतौर पर छोटे) ऑब्जेक्ट्स को रिलीज़ करने के लिए कॉल करें, जिन पर आप कोई संदर्भ नहीं रखते हैं, और फिर उन ऑब्जेक्ट को स्पष्ट रूप से रिलीज़ करते हैं जिसमें आप नामित चर संदर्भ को रखते हैं।

आपको अपने नाम के संदर्भ को महत्व के विपरीत क्रम में भी जारी करना चाहिए: रेंज ऑब्जेक्ट्स पहले, वर्कशीट्स, वर्कबुक, और फिर अंत में आपका एक्सेल एप्लीकेशन ऑब्जेक्ट।

उदाहरण के लिए, यह मानते हुए कि आपके पास xlRng नामक एक रेंज ऑब्जेक्ट वेरिएबल, xlRng नामित वर्कशीट वेरिएबल, xlSheet नामक एक वर्कबुक चर और xlBook नामक एक एक्सेल एप्लीकेशन वैरिएबल था, तो आपका क्लीनअप कोड कुछ जैसा दिख सकता है:

 // Cleanup GC.Collect(); GC.WaitForPendingFinalizers(); Marshal.FinalReleaseComObject(xlRng); Marshal.FinalReleaseComObject(xlSheet); xlBook.Close(Type.Missing, Type.Missing, Type.Missing); Marshal.FinalReleaseComObject(xlBook); xlApp.Quit(); Marshal.FinalReleaseComObject(xlApp); 

अधिकांश कोड उदाहरणों में आप .NET से COM ऑब्जेक्ट को सफाई के लिए देखेंगे, GC.WaitForPendingFinalizers() और GC.WaitForPendingFinalizers() कॉल दो बार के रूप में GC.WaitForPendingFinalizers() बनाये जाते हैं:

 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); 

यह आवश्यक नहीं होना चाहिए, हालांकि, जब तक आप कार्यालय (वीएसटीओ) के लिए विजुअल स्टूडियो टूल्स का उपयोग नहीं कर रहे हों, जो अंतिम रूप में इस्तेमाल करता है, जो अंतिम रूप में कतार में पदोन्नति करने के लिए ऑब्जेक्ट का एक पूरा ग्राफ़ बनाता है ऐसी वस्तुओं को अगले कचरा संग्रह तक जारी नहीं किया जाएगा। हालांकि, अगर आप GC.WaitForPendingFinalizers() प्रयोग नहीं कर रहे हैं, तो आपको GC.WaitForPendingFinalizers()GC.WaitForPendingFinalizers() और GC.WaitForPendingFinalizers() सिर्फ एक बार फोन करने में सक्षम होना चाहिए।

मुझे पता है कि GC.Collect() स्पष्ट रूप से बुला रहा है। GC.Collect() कोई नहीं है (और निश्चित रूप से इसे दो बार बहुत दर्दनाक लगता है), लेकिन इसके चारों ओर कोई रास्ता नहीं है, ईमानदारी से। सामान्य ऑपरेशन के माध्यम से आप छिपी वस्तुओं को उत्पन्न करेंगे जिसमें आप कोई संदर्भ नहीं रखेंगे कि आप, इसलिए, GC.Collect() कॉल करने के अलावा किसी भी अन्य माध्यम से जारी नहीं कर सकते हैं। GC.Collect()

यह एक जटिल विषय है, लेकिन यह वास्तव में इसके लिए है। एक बार जब आप इस टेम्पलेट को अपनी क्लीनअप प्रक्रिया के लिए स्थापित करते हैं, तो आप सामान्य रूप से कोड कर सकते हैं, रैपर, आदि की आवश्यकता के बिना 🙂

मेरे पास इसके बारे में एक ट्यूटोरियल है:

VB.Net / COM इंटरओप के साथ ऑफ़िस प्रोग्राम्स को स्वचालित करना

इसे VB.NET के लिए लिखा गया है, लेकिन इसे बंद नहीं किया जा सकता, सिद्धांत बिल्कुल वैसा ही होते हैं जब सी # का उपयोग करते हैं

प्रस्तावना: मेरे उत्तर में दो समाधान होते हैं, इसलिए सावधान रहें जब पढ़ना और कुछ भी याद न रखें।

एक्सेल इंस्टॉलेशन कैसे अनलोड करने के तरीकों और सुझाव हैं, जैसे:

  • प्रत्येक कॉम को स्पष्ट रूप से मार्शल के साथ प्रदर्शित किया जा रहा है। फ़ाइनलरेलेकॉमऑब्जेक्ट () (अस्पष्ट रूप से निर्मित कॉम-ऑब्जेक्ट्स के बारे में नहीं भूलना) प्रत्येक निर्मित कॉम ऑब्जेक्ट को रिलीज करने के लिए, आप यहां वर्णित 2 बिंदुओं के नियम का उपयोग कर सकते हैं:
    मैं एक्सेल इंटरॉप ऑब्जेक्ट को ठीक से कैसे साफ़ कर सकता हूं?

  • सीसीआर रिलीज अप्रयुक्त कॉम-ऑब्जेक्ट्स * बनाने के लिए जीसी। कल्लक () और जीसी वैटफॉरपैंटिंग फैनलाइजर्स () को कॉल करना (वास्तव में, यह काम करता है, विवरण के लिए मेरा दूसरा समाधान देखें)

  • जांच कर रहा है कि क्या कॉम-सर्वर-एप्लिकेशन शायद उपयोगकर्ता को जवाब देने के लिए एक संदेश बॉक्स का इंतजार कर रहा है (हालांकि मुझे यकीन नहीं है कि यह एक्सेल को बंद करने से रोक सकता है, लेकिन मैंने इसके बारे में कई बार सुना)

  • मुख्य एक्सेल विंडो में संदेश भेजना WM_CLOSE

  • फ़ंक्शन निष्पादित करना जो एक अलग ऐपडोमेन में Excel के साथ काम करता है। कुछ लोगों का मानना ​​है कि एक्सेल उदाहरण बंद हो जाएगा, जब AppDomain उतार दिया है।

  • हमारे एक्सेल-इंटरॉपिंग कोड के आरंभ होने के बाद इंस्टेंट किए गए सभी उत्कृष्ट उदाहरणों को मारना

परंतु! कभी-कभी ये सभी विकल्प सिर्फ मदद नहीं करते हैं या उपयुक्त नहीं हो सकते!

उदाहरण के लिए, कल मुझे पता चला कि फ़ंक्शन समाप्त होने के बाद मेरे एक फ़ंक्शन में (जो Excel के साथ काम करता है) एक्सेल रहता है मैंने सबकुछ कोशिश की! मैंने 10 बार पूरे फ़ंक्शन को अच्छी तरह से जाँच कर लिया और मार्शल को जोड़ा। फ़ाइनलरेबल कॉम ऑब्जेक्ट () सब कुछ के लिए! मेरे पास जीसी भी था। संकलक () और जीसी वैटफ़ॉरफिंगफैनिआइज़र () मैंने छिपी हुई संदेश बक्से के लिए जाँच की मैंने मुख्य एक्सेल विंडो में WM_CLOSE संदेश भेजने का प्रयास किया। मैंने अपने फ़ंक्शन को एक अलग ऐपडोमेन में निष्पादित किया और उस डोमेन को उतार दिया। कुछ भी मदद नहीं की! सभी उत्कृष्ट उदाहरणों को बंद करने का विकल्प अनुचित है, क्योंकि यदि उपयोगकर्ता एक अन्य एक्सेल इंस्टेंस मैन्युअल रूप से शुरू करता है, तो मेरे फ़ंक्शन के निष्पादन के दौरान जो एक्सेल के साथ भी काम करता है, तब भी यह समारोह मेरे फ़ंक्शन द्वारा बंद हो जाएगा। मुझे यकीन है कि उपयोगकर्ता खुश नहीं होगा! तो, ईमानदारी से, यह एक लंगड़ा विकल्प है (कोई अपराध नहीं है) तो मैंने एक अच्छा (मेरी विनम्र राय) समाधान से पहले कुछ घंटों तक बिताया: इसके मुख्य विंडो के एचडब्ल्यूडब्ल्यू द्वारा एक्सेल प्रोसेस को मार डालो (यह पहला समाधान है)।

यहां सरल कोड है:

 [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); /// <summary> Tries to find and kill process by hWnd to the main window of the process.</summary> /// <param name="hWnd">Handle to the main window of the process.</param> /// <returns>True if process was found and killed. False if process was not found by hWnd or if it could not be killed.</returns> public static bool TryKillProcessByMainWindowHwnd(int hWnd) { uint processID; GetWindowThreadProcessId((IntPtr)hWnd, out processID); if(processID == 0) return false; try { Process.GetProcessById((int)processID).Kill(); } catch (ArgumentException) { return false; } catch (Win32Exception) { return false; } catch (NotSupportedException) { return false; } catch (InvalidOperationException) { return false; } return true; } /// <summary> Finds and kills process by hWnd to the main window of the process.</summary> /// <param name="hWnd">Handle to the main window of the process.</param> /// <exception cref="ArgumentException"> /// Thrown when process is not found by the hWnd parameter (the process is not running). /// The identifier of the process might be expired. /// </exception> /// <exception cref="Win32Exception">See Process.Kill() exceptions documentation.</exception> /// <exception cref="NotSupportedException">See Process.Kill() exceptions documentation.</exception> /// <exception cref="InvalidOperationException">See Process.Kill() exceptions documentation.</exception> public static void KillProcessByMainWindowHwnd(int hWnd) { uint processID; GetWindowThreadProcessId((IntPtr)hWnd, out processID); if (processID == 0) throw new ArgumentException("Process has not been found by the given main window handle.", "hWnd"); Process.GetProcessById((int)processID).Kill(); } 

जैसा कि आप देख सकते हैं कि मैंने कोशिश की-पार्स पैटर्न के अनुसार दो तरीकों को प्रदान किया है (मुझे लगता है कि यह उचित है): यदि प्रक्रिया को मार डाला नहीं जा सकता है तो एक विधि अपवाद नहीं डालती (उदाहरण के लिए प्रक्रिया अब मौजूद नहीं है), और प्रक्रिया को मार डाला नहीं गया था, तो एक और विधि अपवाद फेंकता इस कोड में केवल कमजोर स्थान सुरक्षा अनुमति है। सैद्धांतिक रूप से, उपयोगकर्ता को प्रक्रिया को मारने की अनुमति नहीं हो सकती है, लेकिन 99.99% सभी मामलों में उपयोगकर्ताओं की ऐसी अनुमतियां हैं। मैंने यह भी एक अतिथि खाते के साथ परीक्षण किया – यह पूरी तरह से काम करता है

तो, आपका कोड, Excel के साथ काम कर रहा है, इस तरह दिख सकता है:

 int hWnd = xl.Application.Hwnd; // ... // here we try to close Excel as usual, with xl.Quit(), // Marshal.FinalReleaseComObject(xl) and so on // ... TryKillProcessByMainWindowHwnd(hWnd); 

देखा! Excel समाप्त हो गया है! 🙂

ठीक है, हम दूसरे समाधान पर वापस जाते हैं, जैसा कि मैंने पोस्ट की शुरुआत में वादा किया था। दूसरा समाधान जीसी को कॉल करना है। संकलक () और जीसी वैटफ़ॉरेंडिंग फ़ाइनलाइजर्स () हां, वे वास्तव में काम करते हैं, लेकिन आपको सावधान रहना चाहिए!
बहुत से लोग कहते हैं (और मैंने कहा है) कि जीसी को बुलाओ। संकलक () मदद नहीं करता है लेकिन इसका कारण यह नहीं है कि अगर कॉम ऑब्जेक्ट्स के संदर्भ अभी भी हैं! जीसी के लिए सबसे लोकप्रिय कारणों में से एक। संकलक () मददगार नहीं है, डीबग-मोड में परियोजना चल रहा है। डीबग-मोड ऑब्जेक्ट्स में जो अब वास्तव में संदर्भित नहीं हैं, विधि के अंत तक एकत्र कचरा नहीं होगा।
इसलिए, यदि आपने GC.Collect () और जीसी.WaitForPendingFinalizers () की कोशिश की और इसे मदद नहीं की, तो निम्न करने की कोशिश करें:

1) अपने प्रोजेक्ट को रिलीज़ मोड में चलाने का प्रयास करें और जांचें कि Excel को सही ढंग से बंद कर दिया गया है या नहीं

2) एक अलग विधि में एक्सेल के साथ काम करने की विधि लपेटें तो, इसके बजाय कुछ ऐसा करने के बजाय:

 void GenerateWorkbook(...) { ApplicationClass xl; Workbook xlWB; try { xl = ... xlWB = xl.Workbooks.Add(...); ... } finally { ... Marshal.ReleaseComObject(xlWB) ... GC.Collect(); GC.WaitForPendingFinalizers(); } } 

तुम लिखो:

 void GenerateWorkbook(...) { try { GenerateWorkbookInternal(...); } finally { GC.Collect(); GC.WaitForPendingFinalizers(); } } private void GenerateWorkbookInternal(...) { ApplicationClass xl; Workbook xlWB; try { xl = ... xlWB = xl.Workbooks.Add(...); ... } finally { ... Marshal.ReleaseComObject(xlWB) ... } } 

अब, एक्सेल बंद हो जाएगा =)

अद्यतन : सी # कोड जोड़ा गया है, और विंडोज नौकरियों के लिए लिंक

मैं इस समस्या का पता लगाने के लिए कुछ समय बिताया था, और इस समय XtremeVBTalk सबसे सक्रिय और उत्तरदायी था। यहां मेरे मूल पोस्ट का एक लिंक है, एक एक्सेल इंटरओप प्रक्रिया को साफ रूप से बंद करना, भले ही आपका एप्लिकेशन क्रैश हो । नीचे पोस्ट का सारांश दिया गया है, और इस पोस्ट पर कॉपी किए गए कोड।

  • Application.Quit() और Process.Kill() साथ इंटरऑप प्रक्रिया को बंद करना, अधिकांश भाग के लिए काम करता है, लेकिन यदि अनुप्रयोग क्रेटफ़ोफीलिक रूप से क्रैश करता है तो विफल रहता है। Ie यदि ऐप क्रैश हो जाता है, तो Excel प्रक्रिया अभी भी ढीली चल रही होगी।
  • इसका समाधान है कि OS को Win32 कॉल का उपयोग कर Windows Job ऑब्जेक्ट्स के माध्यम से आपकी प्रक्रियाओं की सफाई को संभालने दें। जब आपका मुख्य एप्लिकेशन मर जाता है, तो संबंधित प्रक्रियाओं (यानी एक्सेल) को भी समाप्त कर दिया जाएगा।

मुझे यह एक साफ समाधान माना गया क्योंकि ओएस साफ करने का वास्तविक काम कर रहा है। आपको बस इतना करना है कि Excel प्रक्रिया को पंजीकृत करें।

विंडोज़ नौकरी कोड

इंटरऑप प्रक्रियाओं को पंजीकृत करने के लिए Win32 API कॉल को लपेटता है

 public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, BasicUIRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, GroupInformation = 11 } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } [StructLayout(LayoutKind.Sequential)] struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public Int16 LimitFlags; public UInt32 MinimumWorkingSetSize; public UInt32 MaximumWorkingSetSize; public Int16 ActiveProcessLimit; public Int64 Affinity; public Int16 PriorityClass; public Int16 SchedulingClass; } [StructLayout(LayoutKind.Sequential)] struct IO_COUNTERS { public UInt64 ReadOperationCount; public UInt64 WriteOperationCount; public UInt64 OtherOperationCount; public UInt64 ReadTransferCount; public UInt64 WriteTransferCount; public UInt64 OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public IO_COUNTERS IoInfo; public UInt32 ProcessMemoryLimit; public UInt32 JobMemoryLimit; public UInt32 PeakProcessMemoryUsed; public UInt32 PeakJobMemoryUsed; } public class Job : IDisposable { [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject(object a, string lpName); [DllImport("kernel32.dll")] static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); private IntPtr m_handle; private bool m_disposed = false; public Job() { m_handle = CreateJobObject(null, null); JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); info.LimitFlags = 0x2000; JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); extendedInfo.BasicLimitInformation = info; int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(m_handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) throw new Exception(string.Format("Unable to set information. Error: {0}", Marshal.GetLastWin32Error())); } #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion private void Dispose(bool disposing) { if (m_disposed) return; if (disposing) {} Close(); m_disposed = true; } public void Close() { Win32.CloseHandle(m_handle); m_handle = IntPtr.Zero; } public bool AddProcess(IntPtr handle) { return AssignProcessToJobObject(m_handle, handle); } } 

कन्स्ट्रक्टर कोड के बारे में नोट

  • कंस्ट्रक्टर में, info.LimitFlags = 0x2000; कहा जाता है। 0x2000 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE enum मान है, और यह मान MSDN द्वारा इस रूप में परिभाषित किया गया है:

नौकरी से अंतिम संभाल बंद होने पर नौकरी से जुड़े सभी प्रक्रियाएं समाप्त होती हैं।

प्रक्रिया आईडी (पीआईडी) प्राप्त करने के लिए अतिरिक्त Win32 API कॉल

  [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 

कोड का उपयोग करना

  Excel.Application app = new Excel.ApplicationClass(); Job job = new Job(); uint pid = 0; Win32.GetWindowThreadProcessId(new IntPtr(app.Hwnd), out pid); job.AddProcess(Process.GetProcessById((int)pid).Handle); 

यह मैंने जिस परियोजना पर काम कर रहा था, उसके लिए काम किया है:

 excelApp.Quit(); Marshal.ReleaseComObject (excelWB); Marshal.ReleaseComObject (excelApp); excelApp = null; 

हमने सीखा है कि Excel के प्रत्येक ऑब्जेक्ट को रिक्त करने के लिए हर संदर्भ सेट करना महत्वपूर्ण है जब आप इसके साथ किया गया था। इसमें सेल, शीट, और सब कुछ शामिल थे

एक्सेल नामस्थान में है जो कुछ भी जारी किया जाना चाहिए। अवधि

आप ऐसा नहीं कर सकते:

 Worksheet ws = excel.WorkBooks[1].WorkSheets[1]; 

आपको करना होगा

 Workbooks books = excel.WorkBooks; Workbook book = books[1]; Sheets sheets = book.WorkSheets; Worksheet ws = sheets[1]; 

ऑब्जेक्ट्स को जारी करने के बाद।

मुझे एक उपयोगी जेनेरिक टेम्पलेट मिला है जो COM ऑब्जेक्ट के लिए सही निपटान पैटर्न को लागू करने में मदद कर सकता है, जिसे मार्शल की आवश्यकता होती है। पुनराविक कॉम ऑब्जेक्ट को जब वे क्षेत्र से बाहर जाते हैं तो कहा जाता है:

उपयोग:

 using (AutoReleaseComObject<Application> excelApplicationWrapper = new AutoReleaseComObject<Application>(new Application())) { try { using (AutoReleaseComObject<Workbook> workbookWrapper = new AutoReleaseComObject<Workbook>(excelApplicationWrapper.ComObject.Workbooks.Open(namedRangeBase.FullName, false, false, missing, missing, missing, true, missing, missing, true, missing, missing, missing, missing, missing))) { // do something with your workbook.... } } finally { excelApplicationWrapper.ComObject.Quit(); } } 

टेम्पलेट:

 public class AutoReleaseComObject<T> : IDisposable { private T m_comObject; private bool m_armed = true; private bool m_disposed = false; public AutoReleaseComObject(T comObject) { Debug.Assert(comObject != null); m_comObject = comObject; } #if DEBUG ~AutoReleaseComObject() { // We should have been disposed using Dispose(). Debug.WriteLine("Finalize being called, should have been disposed"); if (this.ComObject != null) { Debug.WriteLine(string.Format("ComObject was not null:{0}, name:{1}.", this.ComObject, this.ComObjectName)); } //Debug.Assert(false); } #endif public T ComObject { get { Debug.Assert(!m_disposed); return m_comObject; } } private string ComObjectName { get { if(this.ComObject is Microsoft.Office.Interop.Excel.Workbook) { return ((Microsoft.Office.Interop.Excel.Workbook)this.ComObject).Name; } return null; } } public void Disarm() { Debug.Assert(!m_disposed); m_armed = false; } #region IDisposable Members public void Dispose() { Dispose(true); #if DEBUG GC.SuppressFinalize(this); #endif } #endregion protected virtual void Dispose(bool disposing) { if (!m_disposed) { if (m_armed) { int refcnt = 0; do { refcnt = System.Runtime.InteropServices.Marshal.ReleaseComObject(m_comObject); } while (refcnt > 0); m_comObject = default(T); } m_disposed = true; } } } 

संदर्भ:

http://www.deez.info/sengelha/2005/02/11/useful-idisposable-class-3-autoreleasecomobject/

मैं विश्वास नहीं कर सकता कि इस समस्या ने दुनिया को 5 साल तक झेल दिया है …. यदि आपने कोई एप्लिकेशन बनाया है, तो आपको लिंक को हटाने से पहले इसे बंद करना होगा।

 objExcel = new Excel.Application(); objBook = (Excel.Workbook)(objExcel.Workbooks.Add(Type.Missing)); 

जब बंद

 objBook.Close(true, Type.Missing, Type.Missing); objExcel.Application.Quit(); objExcel.Quit(); 

जब आप एक नया एक्सेल आवेदन करते हैं, तो यह पृष्ठभूमि में एक्सेल कार्यक्रम खोलता है। आपको यह आदेश देने की आवश्यकता है कि लिंक को रिलीज़ करने से पहले छोड़ने के लिए एक्सेल प्रोग्राम को छोड़ें क्योंकि यह एक्सेल प्रोग्राम आपके सीधी नियंत्रण का हिस्सा नहीं है। इसलिए, अगर लिंक जारी होता है तो यह खुला रहेगा!

अच्छा प्रोग्रामिंग हर ~ ~

आम डेवलपर्स, आपके कोई भी समाधान मेरे लिए काम नहीं करता, इसलिए मैं एक नई चाल को लागू करने का निर्णय करता हूं।

पहले, "हमारा लक्ष्य क्या है?" => "कार्य प्रबंधक में हमारी नौकरी के बाद एक्सेल ऑब्जेक्ट नहीं देख"

ठीक। इसे चुनौती देने और इसे नष्ट करने के लिए कोई भी न दें, लेकिन अन्य उदाहरणों ओएस एक्सेल को नष्ट न करें, जो समानांतर में चल रहे हैं।

इसलिए, वर्तमान प्रोसेसर की सूची प्राप्त करें और एक्सेल प्रोसेस की पीआईडी ​​प्राप्त करें, फिर आपका काम पूरा होने के बाद, हमारे पास एक अनूठी पीआईडी ​​के साथ प्रोसेस लिस्ट में एक नया अतिथि है, जो कि केवल एक ही ढूंढ और नष्ट कर सकता है।

<अपनी एक्सेल नौकरी के दौरान किसी भी नए उत्कृष्टता प्रक्रिया को ध्यान में रखें नए और नष्ट किए गए के रूप में पता लगाया जाएगा> <बेहतर समाधान यह है कि नई निर्मित एक्सेल ऑब्जेक्ट के पीआईडी ​​को कैद करना और उसे नष्ट करना है>

 Process[] prs = Process.GetProcesses(); List<int> excelPID = new List<int>(); foreach (Process p in prs) if (p.ProcessName == "EXCEL") excelPID.Add(p.Id); .... // your job prs = Process.GetProcesses(); foreach (Process p in prs) if (p.ProcessName == "EXCEL" && !excelPID.Contains(p.Id)) p.Kill(); 

यह मेरी समस्या का निराकरण करता है, आपके भी आशा करता है

सबसे पहले – आपको Marshal.ReleaseComObject(...) को कॉल करने की ज़रूरत नहीं है। Marshal.ReleaseComObject(...) या Marshal.FinalReleaseComObject(...) जब Excel इंटरॉप करते हैं यह एक भ्रामक विरोधी-पैटर्न है, लेकिन माइक्रोसॉफ्ट सहित, इसके बारे में कोई भी जानकारी, यह इंगित करता है कि आपको मैन्युअल रूप से COM संदर्भों को .NET से प्रकाशित करना गलत है। तथ्य यह है कि .NET रनटाइम और कचरा कलेक्टर सही ढंग से सीआर संदर्भों को ट्रैक करते हैं और साफ करते हैं। आपके कोड के लिए, इसका मतलब है कि आप शीर्ष पर जबकि (…) पाश को पूरे कर सकते हैं।

दूसरा, यदि आप यह सुनिश्चित करना चाहते हैं कि आपकी प्रक्रिया समाप्त होने पर (जब एक्सेल प्रक्रिया बंद हो जाएगी) एक आउट-ऑफ-प्रोसेस COM ऑब्जेक्ट के संदर्भ में कॉम को साफ किया जाता है, तो आपको यह सुनिश्चित करने की ज़रूरत है कि कचरा कलेक्टर चलाता है। आप GC.WaitForPendingFinalizers() को कॉल के साथ यह सही तरीके से करते हैं। GC.WaitForPendingFinalizers() और GC.WaitForPendingFinalizers() . GC.WaitForPendingFinalizers() इसे दो बार कॉल करना सुरक्षित है और यह सुनिश्चित करता है कि चक्र निश्चित रूप से भी साफ हो गए हैं (हालांकि मुझे यकीन नहीं है कि इसकी आवश्यकता है, और यह एक उदाहरण की सराहना करता है जो यह दिखाता है)

तीसरा, जब डीबगर के नीचे चलते हैं, स्थानीय संदर्भ कृत्रिम रूप से विधि के अंत तक जीवित रहेंगे (ताकि स्थानीय चर का निरीक्षण किया जा सके)। तो rng.Cellsrng.Cells GC.Collect() कॉल समान विधि से rng.Cellsrng.Cells जैसी वस्तु की सफाई के लिए प्रभावी नहीं है। आपको जीसी क्लीनअप से COM इंटरॉप को अलग तरीकों से अलग करना चाहिए। (यह मेरे लिए एक महत्वपूर्ण खोज थी, @ नाइटकोडर द्वारा यहां पोस्ट किए गए उत्तर के एक भाग से।)

इस प्रकार सामान्य पैटर्न होगा:

 Sub WrapperThatCleansUp() ' NOTE: Don't call Excel objects in here... ' Debugger would keep alive until end, preventing GC cleanup ' Call a separate function that talks to Excel DoTheWork() ' Now let the GC clean up (twice, to clean up cycles too) GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() End Sub Sub DoTheWork() Dim app As New Microsoft.Office.Interop.Excel.Application Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add() Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1") app.Visible = True For i As Integer = 1 To 10 worksheet.Cells.Range("A" & i).Value = "Hello" Next book.Save() book.Close() app.Quit() ' NOTE: No calls the Marshal.ReleaseComObject() are ever needed End Sub 

एमएसडीएन और स्टैक ओवरफ्लो (और विशेषकर इस सवाल पर!) पर कई पदों सहित इस मुद्दे के बारे में कई गलत सूचनाएं और भ्रम है।

आखिरकार मुझे आश्वस्त हुआ कि मुझे करीब से देखने और सही सलाह का पता लगाना ब्लॉग पोस्ट मार्शल था। रीलीज कॉम- ऑब्जेक्ट ने खतरे से संबंधित डिबगर के साथ इस मुद्दे को खोजने के साथ मिलकर रखा था जो कि मेरे पहले परीक्षण के बारे में उलझन में था।

यहां स्वीकार्य उत्तर सही है, लेकिन यह भी ध्यान रखना है कि न केवल "दो बिंदु" संदर्भों को टाला जाना चाहिए, लेकिन ये भी ऑब्जेक्ट जो सूचकांक के माध्यम से पुनर्प्राप्त किए जाते हैं। आपको इन ऑब्जेक्ट को साफ करने के लिए प्रोग्राम के साथ समाप्त होने तक भी इंतजार करने की ज़रूरत नहीं है, जब भी संभव हो, आप उन कार्यों को पूरा कर सकते हैं, जैसे कि आप उनके साथ समाप्त हो जाएंगे। यहाँ एक ऐसा फ़ंक्शन है जिसे मैंने बनाया है, जिसे xlStyleHeader नामक एक स्टाइल ऑब्जेक्ट के कुछ गुण xlStyleHeader :

 public Excel.Style xlStyleHeader = null; private void CreateHeaderStyle() { Excel.Styles xlStyles = null; Excel.Font xlFont = null; Excel.Interior xlInterior = null; Excel.Borders xlBorders = null; Excel.Border xlBorderBottom = null; try { xlStyles = xlWorkbook.Styles; xlStyleHeader = xlStyles.Add("Header", Type.Missing); // Text Format xlStyleHeader.NumberFormat = "@"; // Bold xlFont = xlStyleHeader.Font; xlFont.Bold = true; // Light Gray Cell Color xlInterior = xlStyleHeader.Interior; xlInterior.Color = 12632256; // Medium Bottom border xlBorders = xlStyleHeader.Borders; xlBorderBottom = xlBorders[Excel.XlBordersIndex.xlEdgeBottom]; xlBorderBottom.Weight = Excel.XlBorderWeight.xlMedium; } catch (Exception ex) { throw ex; } finally { Release(xlBorderBottom); Release(xlBorders); Release(xlInterior); Release(xlFont); Release(xlStyles); } } private void Release(object obj) { // Errors are ignored per Microsoft's suggestion for this type of function: // http://support.microsoft.com/default.aspx/kb/317109 try { System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); } catch { } } 

Notice that I had to set xlBorders[Excel.XlBordersIndex.xlEdgeBottom] to a variable in order to clean that up (Not because of the two dots, which refer to an enumeration which does not need to be released, but because the object I'm referring to is actually a Border object that does need to be released).

This sort of thing is not really necessary in standard applications, which do a great job of cleaning up after themselves, but in ASP.NET applications, if you miss even one of these, no matter how often you call the garbage collector, Excel will still be running on your server.

It requires a lot of attention to detail and many test executions while monitoring the Task Manager when writing this code, but doing so saves you the hassle of desperately searching through pages of code to find the one instance you missed. This is especially important when working in loops, where you need to release EACH INSTANCE of an object, even though it uses the same variable name each time it loops.

To add to reasons why Excel does not close, even when you create direct refrences to each object upon read, creation, is the 'For' loop.

 For Each objWorkBook As WorkBook in objWorkBooks 'local ref, created from ExcelApp.WorkBooks to avoid the double-dot objWorkBook.Close 'or whatever FinalReleaseComObject(objWorkBook) objWorkBook = Nothing Next 'The above does not work, and this is the workaround: For intCounter As Integer = 1 To mobjExcel_WorkBooks.Count Dim objTempWorkBook As Workbook = mobjExcel_WorkBooks.Item(intCounter) objTempWorkBook.Saved = True objTempWorkBook.Close(False, Type.Missing, Type.Missing) FinalReleaseComObject(objTempWorkBook) objTempWorkBook = Nothing Next 

This sure seems like it has been over-complicated. From my experience, there are just three key things to get Excel to close properly:

1: make sure there are no remaining references to the excel application you created (you should only have one anyway; set it to null )

2: call GC.Collect()

3: Excel has to be closed, either by the user manually closing the program, or by you calling Quit on the Excel object. (Note that Quit will function just as if the user tried to close the program, and will present a confirmation dialog if there are unsaved changes, even if Excel is not visible. The user could press cancel, and then Excel will not have been closed.)

1 needs to happen before 2, but 3 can happen anytime.

One way to implement this is to wrap the interop Excel object with your own class, create the interop instance in the constructor, and implement IDisposable with Dispose looking something like

 if (!mDisposed) { mExcel = null; GC.Collect(); mDisposed = true; } 

That will clean up excel from your program's side of things. Once Excel is closed (manually by the user or by you calling Quit ) the process will go away. If the program has already been closed, then the process will disappear on the GC.Collect() call.

(I'm not sure how important it is, but you may want a GC.WaitForPendingFinalizers() call after the GC.Collect() call but it is not strictly necessary to get rid of the Excel process.)

This has worked for me without issue for years. Keep in mind though that while this works, you actually have to close gracefully for it to work. You will still get accumulating excel.exe processes if you interrupt your program before Excel is cleaned up (usually by hitting "stop" while your program is being debugged).

After trying

  1. Release COM objects in reverse order
  2. Add GC.Collect() and GC.WaitForPendingFinalizers() twice at the end
  3. No more than two dots
  4. Close workbook and quit application
  5. Run in release mode

the final solution that works for me is to move one set of

 GC.Collect(); GC.WaitForPendingFinalizers(); 

that we added to the end of the function to a wrapper, as follows:

 private void FunctionWrapper(string sourcePath, string targetPath) { try { FunctionThatCallsExcel(sourcePath, targetPath); } finally { GC.Collect(); GC.WaitForPendingFinalizers(); } } 

I've traditionally followed the advice found in VVS's answer . However, in an effort to keep this answer up-to-date with the latest options, I think all my future projects will use the "NetOffice" library, available on CodePlex .

NetOffice is a complete replacement for the Office PIAs and is completely version-agnostic. It's a collection of Managed COM wrappers that can handle the cleanup that often causes such headaches when working with Microsoft Office in .NET.

Some key features are: – Mostly version-independent (and version-dependant features are documented) – No dependencies – No PIA – No registration – No VSTO

I am in no way affiliated with the project; I just genuinely appreciate the stark reduction in headaches.

You need to be aware that Excel is very sensitive to the culture you are running under as well.

You may find that you need to set the culture to EN-US before calling Excel functions. This does not apply to all functions – but some of them.

  CultureInfo en_US = new System.Globalization.CultureInfo("en-US"); System.Threading.Thread.CurrentThread.CurrentCulture = en_US; string filePathLocal = _applicationObject.ActiveWorkbook.Path; System.Threading.Thread.CurrentThread.CurrentCulture = orgCulture; 

This applies even if you are using VSTO.

For details: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q320369

"Never use two dots with COM objects" is a great rule of thumb to avoid leakage of COM references, but Excel PIA can lead to leakage in more ways than apparent at first sight.

One of these ways is subscribing to any event exposed by any of the Excel object model's COM objects.

For example, subscribing to the Application class's WorkbookOpen event.

Some theory on COM events

COM classes expose a group of events through call-back interfaces. In order to subscribe to events, the client code can simply register an object implementing the call-back interface and the COM class will invoke its methods in response to specific events. Since the call-back interface is a COM interface, it is the duty of the implementing object to decrement the reference count of any COM object it receives (as a parameter) for any of the event handlers.

How Excel PIA expose COM Events

Excel PIA exposes COM events of Excel Application class as conventional .NET events. Whenever the client code subscribes to a .NET event (emphasis on 'a'), PIA creates an instance of a class implementing the call-back interface and registers it with Excel.

Hence, a number of call-back objects get registered with Excel in response to different subscription requests from the .NET code. One call-back object per event subscription.

A call-back interface for event handling means that, PIA has to subscribe to all interface events for every .NET event subscription request. It cannot pick and choose. On receiving an event call-back, the call-back object checks if the associated .NET event handler is interested in the current event or not and then either invokes the handler or silently ignores the call-back.

Effect on COM instance reference counts

All these call-back objects do not decrement the reference count of any of the COM objects they receive (as parameters) for any of the call-back methods (even for the ones that are silently ignored). They rely solely on the CLR garbage collector to free up the COM objects.

Since GC run is non-deterministic, this can lead to the holding off of Excel process for a longer duration than desired and create an impression of a 'memory leak'.

उपाय

The only solution as of now is to avoid the PIA's event provider for the COM class and write your own event provider which deterministically releases COM objects.

For the Application class, this can be done by implementing the AppEvents interface and then registering the implementation with Excel by using IConnectionPointContainer interface . The Application class (and for that matter all COM objects exposing events using callback mechanism) implements the IConnectionPointContainer interface.

As others have pointed out, you need to create an explicit reference for every Excel object you use, and call Marshal.ReleaseComObject on that reference, as described in this KB article . You also need to use try/finally to ensure ReleaseComObject is always called, even when an exception is thrown. Ie instead of:

 Worksheet sheet = excelApp.Worksheets(1) ... do something with sheet 

you need to do something like:

 Worksheets sheets = null; Worksheet sheet = null try { sheets = excelApp.Worksheets; sheet = sheets(1); ... } finally { if (sheets != null) Marshal.ReleaseComObject(sheets); if (sheet != null) Marshal.ReleaseComObject(sheet); } 

You also need to call Application.Quit before releasing the Application object if you want Excel to close.

As you can see, this quickly becomes extremely unwieldy as soon as you try to do anything even moderately complex. I have successfully developed .NET applications with a simple wrapper class that wraps a few simple manipulations of the Excel object model (open a workbook, write to a Range, save/close the workbook etc). The wrapper class implements IDisposable, carefully implements Marshal.ReleaseComObject on every object it uses, and does not pubicly expose any Excel objects to the rest of the app.

But this approach doesn't scale well for more complex requirements.

This is a big deficiency of .NET COM Interop. For more complex scenarios, I would seriously consider writing an ActiveX DLL in VB6 or other unmanaged language to which you can delegate all interaction with out-proc COM objects such as Office. You can then reference this ActiveX DLL from your .NET application, and things will be much easier as you will only need to release this one reference.

I followed this exactly… But I still ran into issues 1 out of 1000 times. Who knows why. Time to bring out the hammer…

Right after the Excel Application class is instantiated I get a hold of the Excel process that was just created.

 excel = new Microsoft.Office.Interop.Excel.Application(); var process = Process.GetProcessesByName("EXCEL").OrderByDescending(p => p.StartTime).First(); 

Then once I've done all the above COM clean-up, I make sure that process isn't running. If it is still running, kill it!

 if (!process.HasExited) process.Kill(); 

¨°º¤ø„¸ Shoot Excel proc and chew bubble gum ¸„ø¤º°¨

 public class MyExcelInteropClass { Excel.Application xlApp; Excel.Workbook xlBook; public void dothingswithExcel() { try { /* Do stuff manipulating cells sheets and workbooks ... */ } catch {} finally {KillExcelProcess(xlApp);} } static void KillExcelProcess(Excel.Application xlApp) { if (xlApp != null) { int excelProcessId = 0; GetWindowThreadProcessId(xlApp.Hwnd, out excelProcessId); Process p = Process.GetProcessById(excelProcessId); p.Kill(); xlApp = null; } } [DllImport("user32.dll")] static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId); } 

When all the stuff above didn't work, try giving Excel some time to close its sheets:

 app.workbooks.Close(); Thread.Sleep(500); // adjust, for me it works at around 300+ app.Quit(); ... FinalReleaseComObject(app); 

Make sure that you release all objects related to Excel!

I spent a few hours by trying several ways. All are great ideas but I finally found my mistake: If you don't release all objects, none of the ways above can help you like in my case. Make sure you release all objects including range one!

 Excel.Range rng = (Excel.Range)worksheet.Cells[1, 1]; worksheet.Paste(rng, false); releaseObject(rng); 

The options are together here .

The two dots rule did not work for me. In my case I created a method to clean my resources as follows:

 private static void Clean() { workBook.Close(); Marshall.ReleaseComObject(workBook); excel.Quit(); CG.Collect(); CG.WaitForPendingFinalizers(); } 

You should be very careful using Word/Excel interop applications. After trying all the solutions we still had a lot of "WinWord" process left open on server (with more than 2000 users).

After working on the problem for hours, I realized that if I open more than a couple of documents using Word.ApplicationClass.Document.Open() on different threads simultaneously, IIS worker process (w3wp.exe) would crash leaving all WinWord processes open!

So I guess there is no absolute solution to this problem, but switching to other methods such as Office Open XML development.

A great article on releasing COM objects is 2.5 Releasing COM Objects (MSDN).

The method that I would advocate is to null your Excel.Interop references if they are non-local variables, and then call GC.Collect() and GC.WaitForPendingFinalizers() twice. Locally scoped Interop variables will be taken care of automatically.

This removes the need to keep a named reference for every COM object.

Here's an example taken from the article:

 public class Test { // These instance variables must be nulled or Excel will not quit private Excel.Application xl; private Excel.Workbook book; public void DoSomething() { xl = new Excel.Application(); xl.Visible = true; book = xl.Workbooks.Add(Type.Missing); // These variables are locally scoped, so we need not worry about them. // Notice I don't care about using two dots. Excel.Range rng = book.Worksheets[1].UsedRange; } public void CleanUp() { book = null; xl.Quit(); xl = null; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); } } 

These words are straight from the article:

In almost all situations, nulling the RCW reference and forcing a garbage collection will clean up properly. If you also call GC.WaitForPendingFinalizers, garbage collection will be as deterministic as you can make it. That is, you'll be pretty sure exactly when the object has been cleaned up—on the return from the second call to WaitForPendingFinalizers. As an alternative, you can use Marshal.ReleaseComObject. However, note that you are very unlikely to ever need to use this method.

My solution

 [DllImport("user32.dll")] static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId); private void GenerateExcel() { var excel = new Microsoft.Office.Interop.Excel.Application(); int id; // Find the Excel Process Id (ath the end, you kill him GetWindowThreadProcessId(excel.Hwnd, out id); Process excelProcess = Process.GetProcessById(id); try { // Your code } finally { excel.Quit(); // Kill him ! excelProcess.Kill(); } 

The accepted answer did not work for me. The following code in the destructor did the job.

 if (xlApp != null) { xlApp.Workbooks.Close(); xlApp.Quit(); } System.Diagnostics.Process[] processArray = System.Diagnostics.Process.GetProcessesByName("EXCEL"); foreach (System.Diagnostics.Process process in processArray) { if (process.MainWindowTitle.Length == 0) { process.Kill(); } } 

I think that some of that is just the way that the framework handles Office applications, but I could be wrong. On some days, some applications clean up the processes immediately, and other days it seems to wait until the application closes. In general, I quit paying attention to the details and just make sure that there aren't any extra processes floating around at the end of the day.

Also, and maybe I'm over simplifying things, but I think you can just…

 objExcel = new Excel.Application(); objBook = (Excel.Workbook)(objExcel.Workbooks.Add(Type.Missing)); DoSomeStuff(objBook); SaveTheBook(objBook); objBook.Close(false, Type.Missing, Type.Missing); objExcel.Quit(); 

Like I said earlier, I don't tend to pay attention to the details of when the Excel process appears or disappears, but that usually works for me. I also don't like to keep Excel processes around for anything other than the minimal amount of time, but I'm probably just being paranoid on that.

As some have probably already written, it's not just important how you close the Excel (object); it's also important how you open it and also by the type of the project.

In a WPF application, basically the same code is working without or with very few problems.

I have a project in which the same Excel file is being processed several times for different parameter value – eg parsing it based on values inside a generic list.

I put all Excel-related functions into the base class, and parser into a subclass (different parsers use common Excel functions). I didn't want that Excel is opened and closed again for each item in a generic list, so I've opened it only once in the base class and close it in the subclass. I had problems when moving the code into a desktop application. I've tried many of the above mentioned solutions. GC.Collect() was already implemented before, twice as suggested.

Then I've decided that I will move the code for opening Excel to a subclass. Instead of opening only once, now I create a new object (base class) and open Excel for every item and close it at the end. There is some performance penalty, but based on several tests Excel processes are closing without problems (in debug mode), so also temporary files are removed. I will continue with testing and write some more if I will get some updates.

The bottom line is: You must also check the initialize code, especially if you have many classes, etc.

उपयोग:

 [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 

Declare it, add code in the finally block:

 finally { GC.Collect(); GC.WaitForPendingFinalizers(); if (excelApp != null) { excelApp.Quit(); int hWnd = excelApp.Application.Hwnd; uint processID; GetWindowThreadProcessId((IntPtr)hWnd, out processID); Process[] procs = Process.GetProcessesByName("EXCEL"); foreach (Process p in procs) { if (p.Id == processID) p.Kill(); } Marshal.FinalReleaseComObject(excelApp); } }