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


747

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

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

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

मैं क्या गलत कर रहा हूं, या क्या यह सुनिश्चित करने के लिए कोई विकल्प है कि इंटरोप ऑब्जेक्ट को ठीक से निपटारा किया जाए?


1
क्या आप अपने आवेदन को बंद किए बिना Excel.exe को बंद करने की कोशिश कर रहे हैं? यकीन नहीं होता कि मैं आपके सवाल को पूरी तरह से समझ गया हूँ।
ब्रायंट

3
मैं यह सुनिश्चित करने की कोशिश कर रहा हूं कि अनवांटेड इंटरोप ऑब्जेक्ट को ठीक से डिस्पोज किया जाए। ताकि उपयोगकर्ता द्वारा ऐप से बनाई गई एक्सेल स्प्रेडशीट के साथ समाप्त होने पर भी एक्सेल प्रक्रियाएं लटकी न रहें।
हेड्स

3
यदि आप XML एक्सेल फाइल का निर्माण करके इसे करने का प्रयास कर सकते हैं, तो कृपया VSTO संयुक्त राष्ट्र / प्रबंधित मेमोरी प्रबंधन पर विचार करें: jake.ginnivan.net/vsto-com-interop
जेरेमी थॉम्पसन

क्या यह एक्सेल में अच्छी तरह से अनुवाद करता है?
Coops

2
देखें (नीचे दिए गए उत्तरों के अलावा) Microsoft का यह समर्थन आलेख, जहाँ वे विशेष रूप से इस समस्या का समाधान देते हैं: support.microsoft.com/kb/317109
अर्जन

जवाबों:


684

Excel इसलिए नहीं छोड़ता क्योंकि आपका एप्लिकेशन अभी भी COM ऑब्जेक्ट के संदर्भ में है।

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

मेरे लिए यह excelApp.Worksheets ऑब्जेक्ट था जिसे मैंने सीधे एक वेरिएबल को असाइन किए बिना उपयोग किया था:

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

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

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

COM ऑब्जेक्ट्स के साथ दो डॉट्स का उपयोग कभी न करें।


तो इस ज्ञान के साथ उपरोक्त कार्य करने का सही तरीका है:

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

पोस्ट मॉर्टेम अद्यतन:

मैं चाहता हूं कि हर पाठक हंस पसंत के इस उत्तर को बहुत ध्यान से पढ़ें क्योंकि यह जाल I और बहुत से अन्य डेवलपर्स को डगमगाता है। जब मैंने सालों पहले यह उत्तर लिखा था तो मुझे इस बात की जानकारी नहीं थी कि डिबगर का कचरा कलेक्टर के पास होने का क्या असर है और उसने गलत निष्कर्ष निकाले। मैं अपना उत्तर इतिहास की खातिर अनछुए रखता हूं, लेकिन कृपया इस लिंक को पढ़ें और "दो डॉट्स" के रास्ते पर जाएं: .NET में कचरा संग्रह को समझना और पहचान योग्य के साथ एक्सेल इंटरोप ऑब्जेक्ट्स को साफ करना


16
फिर मैं सुझाव देता हूं कि COM से एक्सेल का उपयोग न करें और अपने आप को मुसीबत से बचाएं। एक्सेल 2007 के फॉर्मेट का इस्तेमाल एक्सेल को बिना खोले भव्य रूप से किया जा सकता है।
user7116

5
मुझे समझ नहीं आया कि "दो डॉट्स" का क्या मतलब है। क्या आप समझा सकते हैं?
a9S6

22
इसका मतलब है, आपको पैटर्न comObject.Property.PropertiesProperty का उपयोग नहीं करना चाहिए (आप दो बिंदु देखते हैं?)। इसके बजाय comObject असाइन करें। एक चर के लिए सक्रिय करें और उस चर का उपयोग और निपटान करें। उपरोक्त नियम का एक अधिक औपचारिक संस्करण sth हो सकता है। जैसे "कॉम ऑब्जेक्ट को वैरिएबल में असाइन करें इससे पहले कि आप उनका उपयोग करें। इसमें कॉम ऑब्जेक्ट शामिल हैं जो किसी अन्य कॉम ऑब्जेक्ट के गुण हैं।"
वीवीएस

5
@ निक: वास्तव में, आपको किसी भी तरह के सफाई की आवश्यकता नहीं है, क्योंकि कचरा कलेक्टर आपके लिए करेगा। केवल एक चीज जो आपको करने की ज़रूरत है वह है कि प्रत्येक COM ऑब्जेक्ट को अपने स्वयं के चर को असाइन करना ताकि GC को इसका पता चल सके।
वीवीएस

12
@ वीएसएस दैट्स बोल्ट, जीसी सब कुछ साफ कर देता है क्योंकि रैपर वैरिएबल .net फ्रेमवर्क द्वारा बनाए जाते हैं। जीसी को इसे साफ करने में बस हमेशा का समय लग सकता है। भारी अंतराल के बाद GC.Collect को कॉल करना एक खराब आइडेंटी नहीं है।
कोडिंगबारफील्ड

280

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

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

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

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

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

// 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.Collect()और GC.WaitForPendingFinalizers()कॉल को इस तरह से TWICE बनाया गया है:

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

जब तक आप Visual Studio Tools for Office (VSTO) का उपयोग नहीं कर रहे हैं, तब तक इसकी आवश्यकता नहीं होनी चाहिए, जो कि फाइनल का उपयोग करता है जिसके कारण वस्तुओं की एक पूरी ग्राफ को अंतिम रूप से कतार में बढ़ावा दिया जाता है। इस तरह की वस्तुओं को अगले कचरा संग्रहण तक जारी नहीं किया जाएगा । हालांकि, यदि आप वीएसटीओ का उपयोग नहीं कर रहे हैं, तो आपको कॉल करने में सक्षम होना चाहिए GC.Collect()और GC.WaitForPendingFinalizers()सिर्फ एक बार।

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

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

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

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

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


3
संबंधित चर्चा एक्सट्रीम वीबीटॉक। नेट ऑफिस ऑटोमेशन फोरम पर देखी जा सकती है, यहां: xtremevbtalk.com/showthread.php?t=303928
माइक रोसेनब्लम

2
और यदि बाकी सब विफल हो जाता है, तो Process.Kill () का उपयोग (अंतिम उपाय के रूप में) किया जा सकता है जैसा कि यहाँ वर्णित है: stackoverflow.com/questions/51462/…
माइक रोसेनब्लम

1
खुशी है कि यह रिचर्ड काम किया। :-) और यहाँ एक सूक्ष्म उदाहरण है जहाँ केवल "दो डॉट्स" से बचना किसी समस्या को रोकने के लिए पर्याप्त नहीं है: stackoverflow.com/questions/4964663/…
माइक रोसेनब्लम

इस विषय पर Microsoft की मीशा श्रेयर्सन की टिप्पणी दिनांक 7 अक्टूबर, 2010 के भीतर इस पर एक राय है। ( blogs.msdn.com/b/csharpfaq/archive/2010/09/28/… )
माइक रोसेनब्लिक

मैं उम्मीद कर रहा हूँ कि यह एक सतत समस्या है जो हमें हो रही है। क्या कचरा संग्रह करना और कॉल को अंततः ब्लॉक में जारी करना सुरक्षित है?
ब्रेट ग्रीन

213

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

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

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

  • GC.Collect () और GC.WaitForPendingFinalizers () को CLR रिलीज़ करने के लिए अप्रयुक्त कॉम-ऑब्जेक्ट को कॉल करना * (वास्तव में, यह काम करता है, विवरण के लिए मेरा दूसरा समाधान देखें)

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

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

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

  • सभी एक्सेल उदाहरणों को मारना, जो हमारे एक्सेल-इंटरोपिंग कोड शुरू होने के बाद त्वरित थे।

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

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

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

[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% सभी मामलों में, उपयोगकर्ता के पास ऐसी अनुमतियाँ होती हैं। मैंने इसे एक अतिथि खाते के साथ भी परीक्षण किया - यह पूरी तरह से काम करता है।

तो, एक्सेल के साथ काम करने वाला आपका कोड इस तरह दिख सकता है:

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

देखा! एक्सेल समाप्त हो गया है! :)

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

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

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

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


19
दुख की बात है कि धागा पहले से ही इतना पुराना है कि आपका उत्कृष्ट उत्तर नीचे दिखाई देता है, जो मुझे लगता है कि इसका एक ही कारण है कि इसे अधिक बार नहीं
उकेरा

15
मुझे स्वीकार करना होगा, जब मैंने पहली बार आपका जवाब पढ़ा, तो मुझे लगा कि यह एक विशाल कीचड़ है। इस के साथ कुश्ती के लगभग 6 घंटे के बाद (सब कुछ जारी किया गया है, मुझे कोई डबल डॉट्स नहीं मिला है, आदि ..), मुझे अब लगता है कि आपका जवाब प्रतिभाशाली है।
मार्क

3
इसके लिए धन्यवाद। एक एक्सेल के साथ कुश्ती हुई जो मुझे मिलने से पहले कुछ दिनों के लिए कोई फर्क नहीं पड़ता। अति उत्कृष्ट।
BBLake

2
@ रातकोडर: भयानक जवाब, वास्तव में विस्तृत। डीबग मोड के संबंध में आपकी टिप्पणी बहुत ही सही और महत्वपूर्ण है कि आपने इसे इंगित किया है। रिलीज़ मोड में समान कोड अक्सर ठीक हो सकता है। प्रक्रिया। हालांकि, स्वचालन का उपयोग करते समय केवल अच्छा है, न कि जब आपका प्रोग्राम एक्सेल के भीतर चल रहा हो, जैसे, एक प्रबंधित COM ऐड-इन के रूप में।
माइक रोसेनब्लम

2
@ डैनएम: आपका दृष्टिकोण 100% सही है और कभी असफल नहीं होगा। विधि के लिए अपने सभी चर स्थानीय रखते हुए, सभी संदर्भ .NET कोड द्वारा प्रभावी रूप से पहुंच से बाहर हैं। इसका मतलब यह है कि जब आपका अंतिम खंड GC.Collect () कॉल करता है, तो आपके सभी COM ऑब्जेक्ट्स के लिए फाइनल को निश्चितता के साथ बुलाया जाएगा। प्रत्येक फ़ाइनलीज़र तब मार्शल को बुलाता है। FinalReleaseComObject पर ऑब्जेक्ट को अंतिम रूप दिया जा रहा है। इसलिए आपका दृष्टिकोण सरल और मूर्खतापूर्ण है। इसका उपयोग करने में कोई डर नहीं है। (केवल चेतावनी: यदि आप वीएसटीओ का उपयोग कर रहे हैं, जो मुझे संदेह है कि आप हैं, तो आपको GC.Collect () और GC.WaitForPendingFinalizers TWICE को कॉल करना होगा।)
माइक रोसेनब्लम

49

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

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

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

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

विंडोज जॉब कोड

इंटरॉप प्रक्रियाओं को पंजीकृत करने के लिए 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_CLOSEenum मूल्य, और इस मूल्य द्वारा परिभाषित किया गया MSDN के रूप में:

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

प्रक्रिया ID (PID) प्राप्त करने के लिए अतिरिक्त 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);

2
स्पष्ट रूप से एक आउट-ऑफ-प्रोसेस COM सर्वर (जो अन्य COM क्लाइंट की सेवा कर सकता है) को मारना एक भयानक विचार है। यदि आप इसका सहारा ले रहे हैं, क्योंकि आपका COM प्रोटोकॉल टूट गया है। COM सर्वर को एक नियमित विंडो-ऐप के रूप में नहीं माना जाना चाहिए
मिकी डी

38

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

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

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


30

एक्सेल नेमस्पेस में जो कुछ भी है उसे जारी करने की आवश्यकता है। अवधि

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

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

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

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

वस्तुओं के विमोचन के बाद।


3
कैसे aobut xlRange.Interior.Color उदाहरण के लिए।
हेड्स

आंतरिक को रिलीज़ करने की आवश्यकता है (इसके नाम स्थान में) ... दूसरे हाथ पर रंग नहीं है (इसके कारण।
System.rawing.Color

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

यह सच नहीं है, टू-डॉट-बैड, वन-डॉट-गुड रूल बकवास है। इसके अलावा, आपको वर्कबुक, वर्कशीट, रेंज, को जारी नहीं करना है, जो कि जीसी का काम है। केवल एक चीज जिसे आपको कभी नहीं भूलना चाहिए, वह है एक और केवल Excel.Application ऑब्जेक्ट पर Quit को कॉल करना। Quit कॉल करने के बाद, Excel.Application ऑब्जेक्ट को null करें और GC.Collect () कॉल करें; GC.WaitForPendingFinalizers (); दो बार।
डिट्रीच बॉमगार्टन

29

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

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

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

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

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

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

अंतत: मुझे इस बात पर यकीन हुआ कि सही सलाह देने और सही सलाह देने के लिए ब्लॉग पोस्ट मार्शल था ।ReleaseComObject डेंजरस के साथ इस मुद्दे को खोजने के साथ-साथ इस डिबगर को जिंदा रखा, जो मेरे पहले परीक्षण को भ्रमित कर रहा था।


5
वस्तुतः इस पृष्ठ पर सभी उत्तरदाताओं को इस एक से अधिक गलत हैं। वे काम करते हैं लेकिन बहुत ज्यादा काम करते हैं। IGNORE "2 डॉट्स" नियम। GC आपके लिए अपना काम करने दें। .Net गुरू हंस पासेंट से पूरक साक्ष्य: stackoverflow.com/a/25135685/3852958
एलन बलजीउ

1
शासन, इसे करने का सही तरीका खोजने के लिए क्या राहत है। धन्यवाद।
डायट्रीच बॉमगार्टन

18

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

उपयोग:

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/


3
हाँ, यह एक अच्छा है। मुझे लगता है कि अब इस कोड को अपडेट किया जा सकता है, हालांकि फ़ाइनलरलाइज़कॉमओबजेक्ट उपलब्ध है।
बेनामी टाइप

यह अभी भी प्रत्येक वस्तु के लिए एक ब्लॉक का उपयोग करने के लिए कष्टप्रद है। यहां विकल्प के लिए stackoverflow.com/questions/2191489/… देखें ।
हेनरिक

16

मुझे विश्वास है कि इस समस्या ने 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(); 

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

सभी को अच्छी प्रोग्रामिंग ~~


नहीं, आप नहीं करते हैं, खासकर यदि आप COM अनुप्रयोग (इस मामले में एक्सेल के साथ उपयोगकर्ता इंटरैक्शन की अनुमति देना चाहते हैं - ओपी निर्दिष्ट नहीं करता है कि उपयोगकर्ता इंटरैक्शन का इरादा है, लेकिन इसे बंद करने का कोई संदर्भ नहीं है)। आपको केवल एक फ़ाइल को बंद करने, कहने, एक्सेल को छोड़ने में सक्षम होने के लिए उपयोगकर्ता को जाने देने के लिए कॉलर को प्राप्त करना होगा।
डाउनवॉच

यह मेरे लिए पूरी तरह से काम करता है - जब मैं केवल objExcel.Quit () था तो दुर्घटनाग्रस्त हो गया, लेकिन जब मैंने भी objExcel.Application.Quit () पहले से किया, तो सफाई से बंद हो गया।
mcmillab

13

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

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

ठीक है। चुनौती न दें और इसे नष्ट करना शुरू करें, लेकिन समानांतर में चल रहे अन्य एक्सेल एक्सेल को नष्ट नहीं करने पर विचार करें।

तो, वर्तमान प्रोसेसर की सूची प्राप्त करें और EXCEL प्रक्रियाओं के PID प्राप्त करें, फिर एक बार आपकी नौकरी लगने के बाद, हमारे पास एक अद्वितीय PID के साथ प्रक्रियाओं की सूची में एक नया अतिथि है, बस एक को ढूंढें और नष्ट करें।

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

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();

यह मेरा मुद्दा हल करता है, आशा है कि आपका भी।


1
.... यह एक स्लेजघमर दृष्टिकोण का एक सा है? - यह उसी तरह है जो मैं भी उपयोग कर रहा हूं :( लेकिन बदलने की जरूरत है। इस दृष्टिकोण का उपयोग करने के साथ समस्या यह है कि अक्सर एक्सेल खोलते समय, मशीन पर जहां यह भाग गया था, यह बाएं हाथ की पट्टी में अलर्ट है, कुछ ऐसा कह रहा है "एक्सेल को गलत तरीके से बंद कर दिया गया था": बहुत सुरुचिपूर्ण नहीं है। मुझे लगता है कि इस धागे में पहले के सुझावों में से एक को प्राथमिकता दी जानी है।
Whytheq

11

यह सुनिश्चित लगता है कि यह अति जटिल हो गया है। मेरे अनुभव से, एक्सेल को ठीक से बंद करने के लिए सिर्फ तीन प्रमुख चीजें हैं:

1: सुनिश्चित करें कि आपके द्वारा बनाए गए एक्सेल एप्लिकेशन में कोई शेष संदर्भ नहीं हैं (आपको केवल एक ही होना चाहिए; इसे सेट करें null)

2: कॉल करें GC.Collect()

3: एक्सेल को प्रोग्राम को मैन्युअल रूप से बंद करके या Quitएक्सेल ऑब्जेक्ट पर कॉल करके उपयोगकर्ता द्वारा बंद किया जाना है । (ध्यान दें कि Quitजैसे ही उपयोगकर्ता प्रोग्राम को बंद करने का प्रयास करेगा, और यदि एक्सेल दिखाई नहीं दे रहा है, तो भी एक पुष्टि डायलॉग प्रस्तुत करेगा। उपयोगकर्ता रद्द कर सकता है, और तब एक्सेल बंद नहीं होगा। )

1 को 2 से पहले होना चाहिए, लेकिन 3 कभी भी हो सकता है।

इसे लागू करने का एक तरीका यह है कि इंटरॉप एक्सेल ऑब्जेक्ट को अपनी कक्षा के साथ लपेटें, कंस्ट्रक्टर में इंटरोप इंस्टेंस बनाएं, और डिस्पोज़ को लागू करने के लिए डिस्पोज़ के साथ कुछ इस तरह देखें

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

जो कि आपके प्रोग्राम की चीजों से उत्कृष्टता प्राप्त करेगा। एक बार एक्सेल बंद हो जाने पर (उपयोगकर्ता द्वारा या आपको कॉल करके Quit) प्रक्रिया समाप्त हो जाएगी। यदि प्रोग्राम पहले से ही बंद हो गया है, तो GC.Collect()कॉल पर प्रक्रिया गायब हो जाएगी ।

(मुझे यकीन नहीं है कि यह कितना महत्वपूर्ण है, लेकिन आप GC.WaitForPendingFinalizers()कॉल के बाद कॉल कर सकते हैं GC.Collect()लेकिन एक्सेल प्रक्रिया से छुटकारा पाने के लिए यह कड़ाई से आवश्यक नहीं है।)

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


1
यह उत्तर काम करता है और स्वीकृत उत्तर की तुलना में असीम रूप से अधिक सुविधाजनक है। COM वस्तुओं के साथ "दो डॉट्स" के बारे में कोई चिंता नहीं है, इसका कोई उपयोग नहीं है Marshal
andrew.cuthbert

9

एक्सेल बंद नहीं होने के कारणों को जोड़ने के लिए, यहां तक ​​कि जब आप रीड, क्रिएशन पर प्रत्येक ऑब्जेक्ट के लिए प्रत्यक्ष रेफरल बनाते हैं, तो 'फॉर' लूप होता है।

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

और एक अन्य कारण, पिछले मूल्य को पहले जारी किए बिना एक संदर्भ का फिर से उपयोग करना।
ग्रिमफोर्ट

धन्यवाद। मैं भी एक "प्रत्येक के लिए" का उपयोग कर रहा था आपके समाधान ने मेरे लिए काम किया।
चाड ब्रौन-दुइन

+1 धन्यवाद। इसके अलावा, ध्यान दें कि यदि आपके पास एक्सेल रेंज के लिए ऑब्जेक्ट है, और आप ऑब्जेक्ट को जीवनकाल के दौरान रेंज में बदलना चाहते हैं, तो मैंने पाया कि मुझे इसे री-साइन करने से पहलेComComObject करना था, जो कोड को थोड़ा बेकार बनाता है!
AjV Jsy

9

मैंने पारंपरिक रूप से वीवीएस के जवाब में मिली सलाह का पालन किया है । हालांकि, नवीनतम विकल्पों के साथ इस उत्तर को अप-टू-डेट रखने के प्रयास में, मुझे लगता है कि मेरी सभी भविष्य की परियोजनाएं "नेटऑफिस" पुस्तकालय का उपयोग करेंगी।

NetOffice कार्यालय लिए एक पूर्ण प्रतिस्थापन है और पूरी तरह से संस्करण-अज्ञेयवादी है। यह प्रबंधित COM रैपर का एक संग्रह है, जो .NET में Microsoft Office के साथ काम करते समय अक्सर ऐसे सिरदर्द का कारण बन सकता है।

कुछ प्रमुख विशेषताएं हैं:

  • अधिकतर संस्करण-स्वतंत्र (और संस्करण-निर्भर विशेषताएँ प्रलेखित हैं)
  • कोई निर्भरता नहीं
  • कोई पी.आई.ए.
  • कोई पंजीकरण नहीं
  • कोई वी.एस.टी.ओ.

मैं परियोजना से संबद्ध किसी भी तरह से नहीं हूं; मैं वास्तव में सिर दर्द में कमी की सराहना करता हूं।


2
यह जवाब के रूप में चिह्नित किया जाना चाहिए, वास्तव में। नेटऑफिस इस सारी जटिलता को दूर कर देता है।
सी। ऑगस्टो प्रोएटी

1
मैं काफी समय से NetOffice का उपयोग कर रहा हूं और एक एक्सेल ऐड-इन लिख रहा हूं और यह पूरी तरह से काम करता है। विचार करने के लिए एकमात्र बात यह है कि यदि आप स्पष्ट रूप से उपयोग की गई वस्तुओं का निपटान नहीं करते हैं, तो यह तब होगा जब आप आवेदन से बाहर निकल जाएंगे (क्योंकि यह वैसे भी उन पर नज़र रखता है)। तो NetOffice के साथ अंगूठे का नियम हमेशा सेल, रेंज या शीट आदि जैसे हर एक्सेल ऑब्जेक्ट के साथ "पैटर्न" का उपयोग करना है
Stas Ivanov

8

यहां स्वीकृत उत्तर सही है, लेकिन यह भी ध्यान रखें कि न केवल "दो डॉट" संदर्भों से बचने की आवश्यकता है, बल्कि उन वस्तुओं को भी जिन्हें सूचकांक के माध्यम से पुनर्प्राप्त किया जाता है। जब तक आप इन वस्तुओं को साफ करने के लिए कार्यक्रम के साथ समाप्त नहीं हो जाते, तब तक आपको प्रतीक्षा करने की आवश्यकता नहीं है, यह उन कार्यों को बनाने के लिए सबसे अच्छा है जो जितनी जल्दी हो सके आप उन्हें समाप्त कर लेंगे। यहाँ एक फ़ंक्शन है जो मैंने बनाया है जो स्टाइल ऑब्जेक्ट के कुछ गुणों को बताता है 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 { } 
}

ध्यान दें कि मुझे सेट करना था xlBorders[Excel.XlBordersIndex.xlEdgeBottom] सफाई करने के लिए एक चर करना (न कि दो बिंदुओं के कारण, जो एक गणना का उल्लेख करता है जिसे जारी करने की आवश्यकता नहीं है, लेकिन क्योंकि मैं जिस वस्तु का उल्लेख कर रहा हूं वह वास्तव में एक सीमा वस्तु है यह जारी करने की आवश्यकता है)।

मानक अनुप्रयोगों में इस तरह की बात वास्तव में आवश्यक नहीं है, जो खुद के बाद सफाई का एक बड़ा काम करते हैं, लेकिन ASP.NET अनुप्रयोगों में, यदि आप इनमें से एक को भी याद करते हैं, तो आप कितनी बार कचरा कलेक्टर, एक्सेल करेंगे अभी भी आपके सर्वर पर चल रहा है।

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


रिलीज के अंदर, नल (जो जवाब) के लिए जाँच करें। यह अनावश्यक अशक्त अपवादों से बच जाएगा। मैंने बहुत सारे तरीकों का परीक्षण किया है। यह एक्सेल को प्रभावी ढंग से जारी करने का एकमात्र तरीका है।
गेरहार्ड पॉवेल

8

कोशिश करने के बाद

  1. COM ऑब्जेक्ट को रिवर्स ऑर्डर में रिलीज़ करें
  2. अंत में जोड़ें GC.Collect()और GC.WaitForPendingFinalizers()दो बार
  3. दो से अधिक डॉट्स नहीं
  4. कार्यपुस्तिका बंद करें और आवेदन छोड़ दें
  5. रिलीज़ मोड में चलाएँ

अंतिम समाधान जो मेरे लिए काम करता है, उसके एक सेट को स्थानांतरित करना है

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

हमने फ़ंक्शन के अंत में एक आवरण में जोड़ा, जो निम्नानुसार है:

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

मैंने पाया कि मुझे ComObjects को बंद करने की आवश्यकता नहीं थी, केवल Quit () - Close () और FinalReleaseComObject। मुझे विश्वास नहीं हो रहा है कि यह सब काम करने के लिए मेरे अंत में गायब था। महान!
कैरोल

7

मैंने इसे ठीक से पालन किया ... लेकिन मैं अभी भी 1000 में से 1 बार मुद्दों में भाग गया। कौन जाने क्यों। हथौड़े को बाहर लाने का समय ...

एक्सेल एप्लीकेशन क्लास के तुरंत बाद मुझे एक्सेल प्रोसेस की एक पकड़ मिलती है जो अभी बनाई गई थी।

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

फिर एक बार जब मैंने उपरोक्त सभी COM क्लीन-अप किया है, तो मुझे यकीन है कि यह प्रक्रिया नहीं चल रही है। यदि यह अभी भी चल रहा है, तो इसे मार डालो!

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

7

¨ ° ¨¤ø º and गोली मारो एक्सेल खरीदो और बबल गम चबाओ „ø¤¤¨ ° º

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

6

आपको इस बात से अवगत होना चाहिए कि Excel आपके द्वारा चलाए जा रहे संस्कृति के प्रति बहुत संवेदनशील है।

एक्सेल फ़ंक्शन को कॉल करने से पहले आप पा सकते हैं कि आपको एन-यूएस को संस्कृति सेट करने की आवश्यकता है। यह सभी कार्यों पर लागू नहीं होता है - लेकिन उनमें से कुछ।

    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;

यह तब भी लागू होता है जब आप VSTO का उपयोग कर रहे हों।

विवरण के लिए: http://support.microsoft.com/default.aspx?scid=kb;en-us-Q320369


6

"COM ऑब्जेक्ट्स के साथ दो डॉट्स का उपयोग कभी न करें" COM संदर्भों के रिसाव से बचने के लिए अंगूठे का एक बड़ा नियम है, लेकिन Excel PIA पहली बार में स्पष्ट रूप से अधिक तरीकों से रिसाव का कारण बन सकता है।

इन तरीकों में से एक एक्सेल ऑब्जेक्ट मॉडल के COM ऑब्जेक्ट द्वारा उजागर किसी भी घटना की सदस्यता है।

उदाहरण के लिए, अनुप्रयोग वर्ग की वर्कबुकऑपन ईवेंट की सदस्यता लेना।

COM घटनाओं पर कुछ सिद्धांत

COM कक्षाएं कॉल-बैक इंटरफेस के माध्यम से घटनाओं के एक समूह को उजागर करती हैं। घटनाओं की सदस्यता के लिए, क्लाइंट कोड कॉल-बैक इंटरफ़ेस को लागू करने वाली एक वस्तु को पंजीकृत कर सकता है और COM वर्ग विशिष्ट घटनाओं के जवाब में अपने तरीकों को लागू करेगा। चूंकि कॉल-बैक इंटरफ़ेस एक COM इंटरफ़ेस है, इसलिए किसी भी ईवेंट हैंडलर के लिए प्राप्त किसी भी COM ऑब्जेक्ट के संदर्भ की संख्या में कमी (पैरामीटर के रूप में) को लागू करना ऑब्जेक्ट का कर्तव्य है।

एक्सेल पीआईए कॉम इवेंट्स को कैसे उजागर करता है

एक्सेल पीआईए एक्सेल एप्लीकेशन क्लास के COM ईवेंट्स को पारंपरिक .NET इवेंट्स के रूप में उजागर करता है। जब भी ग्राहक कोड एक .NET इवेंट ('ए' पर जोर देता है) के लिए सब्सक्राइब करता है, तो PIA एक बनाता है एक वर्ग कॉल-बैक इंटरफेस और यह रजिस्टरों Excel के साथ लागू करने के उदाहरण।

इसलिए, .NET कोड से विभिन्न सदस्यता अनुरोधों के जवाब में कई कॉल-बैक ऑब्जेक्ट एक्सेल के साथ पंजीकृत होते हैं। प्रति घटना सदस्यता के लिए एक कॉल-बैक ऑब्जेक्ट।

इवेंट हैंडलिंग के लिए कॉल-बैक इंटरफ़ेस का अर्थ है कि, PIA को प्रत्येक .NET इवेंट सब्सक्रिप्शन अनुरोध के लिए सभी इंटरफ़ेस इवेंट की सदस्यता लेनी होगी। यह चुन और चुन नहीं सकता। ईवेंट कॉल-बैक प्राप्त करने पर, कॉल-बैक ऑब्जेक्ट की जांच करता है कि संबंधित .NET ईवेंट हैंडलर वर्तमान ईवेंट में रुचि रखता है या नहीं और फिर हैंडलर को आमंत्रित करता है या चुपचाप कॉल-बैक को अनदेखा करता है।

COM उदाहरण संदर्भ मायने रखता है पर प्रभाव

ये सभी कॉल-बैक ऑब्जेक्ट कॉल-बैक तरीकों में से किसी के लिए भी (जो भी चुपचाप नजरअंदाज किए जाते हैं) के लिए उन्हें प्राप्त होने वाली COM वस्तुओं में से किसी की संदर्भ संख्या को नहीं घटाते हैं। वे पूरी तरह से COM ऑब्जेक्ट्स को खाली करने के लिए CLR कचरा संग्रहकर्ता पर भरोसा करते हैं।

चूंकि जीसी रन गैर-नियतात्मक है, इससे एक्सेल प्रक्रिया को वांछित से अधिक लंबी अवधि के लिए होल्ड किया जा सकता है और 'मेमोरी लीक' की छाप बन सकती है।

समाधान

अब तक का एकमात्र समाधान COM वर्ग के लिए PIA के ईवेंट प्रदाता से बचने और अपने स्वयं के ईवेंट प्रदाता को लिखना है, जो निर्दिष्‍ट रूप से दो ऑब्जेक्ट्स को रिलीज़ करता है।

अनुप्रयोग वर्ग के लिए, यह AppEvents इंटरफ़ेस को लागू करने और फिर IConnectionPointContainer इंटरफ़ेस का उपयोग करके Excel के साथ कार्यान्वयन को पंजीकृत करके किया जा सकता है । अनुप्रयोग वर्ग (और उस मामले के लिए सभी COM ऑब्जेक्ट कॉलबैक तंत्र का उपयोग करके घटनाओं को उजागर करता है) IConnectionPointContainer इंटरफ़ेस को लागू करता है।


4

जैसा कि अन्य लोगों ने बताया है, आपको अपने द्वारा उपयोग किए जाने वाले प्रत्येक Excel ऑब्जेक्ट के लिए एक स्पष्ट संदर्भ बनाने की आवश्यकता है, और उस संदर्भ पर Mars.R.ReleaseComObject को कॉल करें, जैसा कि इस KB आलेख में वर्णित है । आपको यह भी सुनिश्चित करने के लिए कोशिश / अंत में उपयोग करने की आवश्यकता है कि जब एक अपवाद फेंक दिया जाता है, तब भी इसे जारी किया जाए। इसके बजाय Ie:

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

आपको कुछ ऐसा करने की आवश्यकता है:

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

यदि आप एक्सेल को बंद करना चाहते हैं तो आपको एप्लिकेशन ऑब्जेक्ट जारी करने से पहले Application.Quit पर कॉल करना होगा।

जैसा कि आप देख सकते हैं, जैसे ही आप कुछ भी करने की कोशिश करते हैं, तो यह बहुत ही जल्दी हो जाता है। मैंने एक सरल आवरण वर्ग के साथ .NET अनुप्रयोग सफलतापूर्वक विकसित किया है जो एक्सेल ऑब्जेक्ट मॉडल के कुछ सरल जोड़तोड़ को खोलता है (एक कार्यपुस्तिका खोलें, एक सीमा तक लिखें, कार्यपुस्तिका को सहेजना / बंद करना आदि)। रैपर क्लास आईडिसेप्‍टिकल को लागू करता है, सावधानीपूर्वक मार्शल का उपयोग करता है। यह प्रयोग करने वाले हर ऑब्जेक्ट पर करता है।

लेकिन यह दृष्टिकोण अधिक जटिल आवश्यकताओं के लिए अच्छा नहीं है।

यह .NET COM इंटरॉप की एक बड़ी कमी है। अधिक जटिल परिदृश्यों के लिए, मैं गंभीरता से VB6 या अन्य अप्रबंधित भाषा में एक ActiveX DLL लिखने पर विचार करूंगा, जिसमें आप ऑफिस जैसे आउट-कॉम COM ऑब्जेक्ट्स के साथ सभी इंटरैक्शन को प्रस्तुत कर सकते हैं। आप अपने .NET अनुप्रयोग से इस ActiveX DLL को संदर्भित कर सकते हैं, और चीजें बहुत आसान हो जाएंगी क्योंकि आपको केवल इस एक संदर्भ को जारी करने की आवश्यकता होगी।


3

जब ऊपर का सारा सामान काम नहीं आया, तो एक्सेल को अपनी शीट बंद करने के लिए कुछ समय देने का प्रयास करें:

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

...
FinalReleaseComObject(app);

2
मुझे मनमाने इंतजार से नफरत है। लेकिन +1 क्योंकि आप कार्यपुस्तिकाओं को बंद करने के लिए प्रतीक्षा करने के बारे में सही हैं। कार्यपुस्तिका संग्रह को लूप में चुनने और लूप टाइमआउट के रूप में मनमाने प्रतीक्षा का उपयोग करने के लिए एक विकल्प है।
dFlat

3

सुनिश्चित करें कि आप एक्सेल से संबंधित सभी वस्तुओं को जारी करते हैं!

मैंने कई घंटों की कोशिश करके कुछ घंटे बिताए। सभी महान विचार हैं, लेकिन मुझे अंततः मेरी गलती मिली: यदि आप सभी वस्तुओं को जारी नहीं करते हैं, तो ऊपर दिए गए तरीकों में से कोई भी आपके मामले में मेरी तरह मदद नहीं कर सकता है । सुनिश्चित करें कि आप एक सीमा सहित सभी वस्तुओं को जारी करते हैं!

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

विकल्प यहां एक साथ हैं


बहुत अच्छी बात! मुझे लगता है कि क्योंकि मैं कार्यालय 2007 का उपयोग कर रहा हूं, मेरी ओर से कुछ सफाई की गई है। मैंने आगे सलाह का उपयोग किया है, लेकिन आपके द्वारा सुझाए गए चर को संग्रहीत नहीं किया है और EXCEL.EXE बाहर निकलता है, लेकिन मैं सिर्फ भाग्यशाली हो सकता हूं और अगर मुझे आगे कोई समस्या है तो मैं निश्चित रूप से अपने कोड के इस भाग को
देखूंगा

3

COM ऑब्जेक्ट को रिलीज़ करने पर एक महान लेख 2.5 रिलीज़ है COM ऑब्जेक्ट (MSDN)।

जिस विधि की मैं वकालत करूंगा, वह आपके Excel.Interop संदर्भों को शून्य करने के लिए है यदि वे गैर-स्थानीय चर हैं, और फिर कॉल करें GC.Collect()और GC.WaitForPendingFinalizers()दो बार। स्थानीय रूप से स्कोप किए गए इंटरोप चर को स्वचालित रूप से ध्यान रखा जाएगा।

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

यहाँ एक उदाहरण लेख से लिया गया है:

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

ये शब्द लेख से सीधे हैं:

लगभग सभी स्थितियों में, RCW संदर्भ को बंद करना और एक कचरा संग्रह को मजबूर करना ठीक से साफ हो जाएगा। अगर आप GC.WaitForPendingFinalizers भी कॉल करते हैं, तो कचरा संग्रह नियतात्मक होगा जितना कि आप इसे बना सकते हैं। यही है, जब आप ऑब्जेक्ट को साफ कर दिया गया है, तो आप निश्चित रूप से निश्चित रूप से आश्वस्त होंगे - वेटफोरपेंडिंगफाइंडर्स के लिए दूसरी कॉल से वापसी पर। एक विकल्प के रूप में, आप Marshal.ReleaseComObject का उपयोग कर सकते हैं। हालाँकि, ध्यान दें कि इस पद्धति का उपयोग करने के लिए आपको कभी भी इसकी आवश्यकता नहीं है।


2

मेरे लिए दो बिंदुओं वाला नियम काम नहीं आया। मेरे मामले में मैंने अपने संसाधनों को साफ करने के लिए एक तरीका बनाया है:

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

2

मेरा समाधान

[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();
}

2

आपको वर्ड / एक्सेल इंटरॉप एप्लिकेशन का उपयोग करके बहुत सावधान रहना चाहिए। सभी समाधानों की कोशिश करने के बाद भी हमारे पास सर्वर पर (2000 से अधिक उपयोगकर्ताओं के साथ) खुली "विनवर्ल्ड" प्रक्रिया का एक बहुत कुछ था।

घंटों तक समस्या पर काम करने के बाद, मुझे एहसास हुआ कि अगर मैं एक Word.ApplicationClass.Document.Open()साथ विभिन्न थ्रेड्स का उपयोग करते हुए एक से अधिक दस्तावेज खोलता हूं , तो IIS वर्कर प्रक्रिया (w3wp.exe) सभी WinWord प्रक्रियाओं को खुला छोड़कर दुर्घटनाग्रस्त हो जाएगी!

इसलिए मुझे लगता है कि इस समस्या का कोई पूर्ण समाधान नहीं है, लेकिन Office Open XML विकास जैसे अन्य तरीकों पर स्विच करना ।


1

स्वीकृत जवाब मेरे काम नहीं आया। विध्वंसक में निम्नलिखित कोड ने काम किया।

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


1

मैं वर्तमान में ऑफिस ऑटोमेशन पर काम कर रहा हूं और इसके लिए एक समाधान तैयार किया है जो हर बार मेरे लिए काम करता है। यह सरल है और इसमें किसी भी प्रक्रिया को मारना शामिल नहीं है।

ऐसा लगता है कि वर्तमान सक्रिय प्रक्रियाओं के माध्यम से केवल लूपिंग और किसी भी तरह से एक खुली एक्सेल प्रक्रिया को 'एक्सेस' करने से एक्सेल के किसी भी आवारा हैंगिंग इंस्टेंस को हटा दिया जाएगा। नीचे दिया गया कोड केवल उन प्रक्रियाओं की जांच करता है जहां नाम 'एक्सेल' है, फिर एक स्ट्रिंग के लिए प्रक्रिया की MainWindowTitle संपत्ति लिखता है। प्रक्रिया के साथ यह 'इंटरैक्शन' विंडोज को एक्सेल के जमे हुए उदाहरण को पकड़ने और निरस्त करने के लिए लगता है।

मैं ऐड-इन के ठीक पहले नीचे की विधि चलाता हूं जिसमें मैं क्विट्स विकसित कर रहा हूं, क्योंकि यह इसे अनलोड करने की घटना है। यह हर बार एक्सेल के किसी भी हैंगिंग इंस्टेंसेस को हटाता है। सभी ईमानदारी में मुझे पूरी तरह से यकीन नहीं है कि यह क्यों काम करता है, लेकिन यह मेरे लिए अच्छी तरह से काम करता है और किसी भी एक्सेल एप्लिकेशन के अंत में डबल डॉट्स, मार्शल के बारे में चिंता किए बिना रखा जा सकता है। मुझे किसी भी सुझाव में बहुत दिलचस्पी होगी क्योंकि यह प्रभावी क्यों है।

public static void SweepExcelProcesses()
{           
            if (Process.GetProcessesByName("EXCEL").Length != 0)
            {
                Process[] processes = Process.GetProcesses();
                foreach (Process process in processes)
                {
                    if (process.ProcessName.ToString() == "excel")
                    {                           
                        string title = process.MainWindowTitle;
                    }
                }
            }
}

1

मुझे लगता है कि उस में से कुछ ही तरीका है कि रूपरेखा कार्यालय अनुप्रयोगों को संभालती है, लेकिन मैं गलत हो सकता है। कुछ दिनों में, कुछ एप्लिकेशन तुरंत प्रक्रियाओं को साफ करते हैं, और अन्य दिनों तक आवेदन बंद होने तक इंतजार करना पड़ता है। सामान्य तौर पर, मैंने विवरणों पर ध्यान देना छोड़ दिया और यह सुनिश्चित कर लिया कि दिन के अंत में कोई अतिरिक्त प्रक्रिया नहीं चल रही है।

इसके अलावा, और शायद मैं चीजों को सरल कर रहा हूं, लेकिन मुझे लगता है कि आप सिर्फ ...

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();

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


1

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

एक WPF आवेदन में, मूल रूप से एक ही कोड बिना या बहुत कम समस्याओं के साथ काम कर रहा है।

मेरे पास एक प्रोजेक्ट है जिसमें एक ही एक्सेल फाइल को कई बार अलग-अलग पैरामीटर वैल्यू के लिए प्रोसेस किया जा रहा है - जैसे कि यह एक जेनरेटिंग लिस्ट के अंदर वैल्यू के आधार पर पार्स करना।

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

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

लब्बोलुआब यह है: आपको इनिशियलाइज़ कोड को भी देखना होगा, खासकर यदि आपके पास कई कक्षाएं हैं, आदि।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.