كيف يمكنني تنظيف بشكل صحيح Excel interop الكائنات ؟

StackOverflow https://stackoverflow.com/questions/158706

  •  03-07-2019
  •  | 
  •  

سؤال

أنا باستخدام Excel إمكانية التشغيل المتداخل في C# (ApplicationClass) و قد وضعت التعليمة البرمجية التالية في بلدي أخيرا شرط:

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

على الرغم من أن هذا النوع من الأعمال ، Excel.exe العملية لا تزال في الخلفية حتى بعد إغلاق Excel.يتم تحريرها فقط مرة واحدة بلدي التطبيق يدويا مغلقة.

ما الذي أفعله خطأ ، أو هل هناك بديل لضمان إمكانية التشغيل المتداخل الكائنات يتم التخلص منها بشكل صحيح ؟

هل كانت مفيدة؟

المحلول

Excel لا إنهاء التطبيق الخاص بك لا يزال يحمل إشارات إلى كائنات COM.

أعتقد أنك تحتج على الأقل عضو واحد من كائن COM دون تعيين متغير.

بالنسبة لي كان excelApp.أوراق عمل الكائن التي لا تستخدم مباشرة دون تعيين متغير:

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

لم أكن أعرف أن داخليا C# إنشاء مجمع عن أوراق عمل كائن COM التي لم تحصل على صدر قانون بلدي (لأنني لم أكن على علم به) وكان سبب Excel لم يتم إلغاء تحميله.

لقد وجدت الحل لمشكلتي على هذه الصفحة, التي لديها أيضا لطيفة حكم استخدام كائنات COM في C#:

أبدا استخدام اثنين من النقاط مع كائنات COM.


حتى مع هذه المعرفة في الطريق الصحيح من القيام أعلاه هو:

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

بعد الوفاة التحديث:

أريد من كل قارئ أن يقرأ هذا الجواب من قبل هانز بسنت بعناية فائقة كما يشرح فخ أنا و الكثير من المطورين الآخرين تعثرت في.عندما كتبت هذا الجواب سنوات مضت لم أكن أعرف عن تأثير المصحح أن جامع القمامة ولفت استنتاجات خاطئة.أحافظ على الإجابة دون تغيير أجل من التاريخ ولكن يرجى قراءة هذا الرابط لا تمضي في طريقك من "نقطتين": فهم جمع القمامة في .صافي و تنظيف Excel إمكانية التشغيل المتداخل مع الكائنات IDisposable

نصائح أخرى

ويمكنك فعلا الافراج عن الكائن تطبيق Excel الخاصة بك نظيفة، ولكن لديك لرعاية.

والنصيحة للحفاظ على المرجعية التي سميت باسم كل كائن COM تماما لك الوصول ومن ثم الإفراج عنها صراحة عبر Marshal.FinalReleaseComObject() صحيحة من الناحية النظرية، ولكن، للأسف، من الصعب جدا لإدارة عمليا. إذا كان أحد من أي وقت مضى ينزلق في أي مكان ويستخدم "اثنين من النقاط"، أو خلايا بالتكرار عبر حلقة for each، أو أي نوع آخر مماثل من الأوامر، ثم سيكون لديك كائنات COM غير مرجعية وخطر على تعليق. في هذه الحالة، لن يكون هناك أي وسيلة لمعرفة السبب في التعليمات البرمجية. قد تضطر إلى مراجعة كل ما تبذلونه من الشفرة عن طريق العين ونأمل أن تجد هذه القضية، وهي المهمة التي يمكن أن يكون من المستحيل تقريبا لمشروع كبير.

والخبر السار هو ان لم يكن لديك فعلا للحفاظ على مرجع متغير اسمه إلى كل كائن COM التي تستخدمها. بدلا من ذلك، ندعو GC.Collect() ثم GC.WaitForPendingFinalizers() لاطلاق سراح جميع (عادة قاصر) تعترض الذي لا يحملون المرجعية، ثم حرر صراحة الكائنات التي لم تعقد مرجع متغير اسمه.

ويجب أيضا إطلاق سراح المراجع الخاصة بك وردت أسماؤهم في ترتيب عكسي من أهمية: مجموعة التحف أولا، ثم أوراق العمل والمصنفات، ثم أخيرا لكم الكائن تطبيق إكسل

وعلى سبيل المثال، على افتراض أن لديك كائن نطاق متغير اسمه xlRng، وxlSheet ورقة عمل متغير اسمه، مصنف متغير اسمه xlBook وxlApp متغير تطبيق Excel باسم، ثم رمز تنظيف الخاص بك يمكن أن تبدو شيء كما يلي:

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

في معظم الأمثلة رمز سترى لتنظيف كائنات COM من .NET، يتم إجراء المكالمات GC.Collect() وGC.WaitForPendingFinalizers() مرتين كما في:

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

لا ينبغي أن يطلب هذا، ومع ذلك، إلا إذا كنت تستخدم أدوات Visual Studio للمكتب (VSTO)، والذي يستخدم أدوات الإنهاء التي تسبب الرسم البياني بأكمله من الأشياء التي ينبغي تشجيعها في قائمة انتظار الانتهاء. لن يتم الافراج هذه الأجسام حتى المقبل جمع القمامة. ومع ذلك، إذا كنت لا تستخدم VSTO، يجب أن تكون قادرا على استدعاء GC.Collect() وGC.WaitForPendingFinalizers() مرة واحدة فقط.

وأنا أعلم أن GC.Collect() يدعو صراحة هو لا لا (وبالتأكيد فعل ذلك مرتين يبدو مؤلمة جدا)، ولكن لا توجد وسيلة من حوله، وإلى أن نكون صادقين. من خلال العمليات العادية سوف تولد الأشياء الخفية التي كنت تحمل أي إشارة أنك، لذلك، لا يمكن اطلاق سراحهم من خلال أية وسيلة أخرى غير الدعوة GC.Collect().

وهذا هو موضوع معقد، ولكن هذا حقا هو كل ما في الامر. بمجرد إنشاء هذا القالب لإجراء تنظيف الخاص بك يمكنك رمز بشكل طبيعي، دون الحاجة إلى مغلفة، الخ: -)

ولدي البرنامج التعليمي على هذا هنا:

برامج Office أتمتة مع VB.Net / COM إمكانية التشغيل المتداخل

ولقد كتب عن VB.NET، ولكن لا يمكن تأجيل ذلك، فإن المبادئ هي نفسها تماما كما هو الحال عندما باستخدام C #.

مقدمة:جوابي يحتوي على اثنين من الحلول ، لذا كن حذرا عند القراءة لا تفوت أي شيء.

هناك طرق مختلفة و المشورة في كيفية جعل Excel سبيل المثال تفريغ مثل:

  • الإفراج عن كل كائن com صراحة مع المشير.FinalReleaseComObject() (لا ينسون ضمنا خلق الكائنات com).إلى الإفراج عن كل إنشاء كائن com, يمكنك استخدام وسيادة 2 النقاط المذكورة هنا:
    كيف يمكنني تنظيف بشكل صحيح Excel interop الكائنات ؟

  • داعيا GC.جمع () ، GC.WaitForPendingFinalizers() لجعل CLR الإفراج غير المستخدمة الكائنات com * (في الواقع, يعمل, أرى الحل الثاني لمزيد من التفاصيل)

  • التحقق إذا com-server-تطبيق ربما يظهر مربع رسالة انتظار المستخدم للرد (على الرغم من أنني لا متأكد من أنها يمكن أن تمنع من Excel إغلاق, لكن سمعت عنه قليلة مرات)

  • إرسال رسالة WM_CLOSE الرئيسية نافذة Excel

  • تنفيذ الوظيفة التي يعمل مع التفوق في فصل AppDomain.بعض الناس يعتقدون Excel سبيل المثال سوف يكون مغلقا عندما AppDomain هو تفريغ.

  • مما أسفر عن مقتل جميع excel الحالات التي تم مثيل بعد excel-interoping رمز بدأت.

ولكن! في بعض الأحيان كل هذه الخيارات فقط لا تساعد أو لا يمكن أن يكون من المناسب!

على سبيل المثال ، أمس اكتشفت أن في واحد من بلدي وظائف (الذي يعمل مع excel) Excel تحافظ على التوالي بعد تنتهي الدالة.لقد حاولت كل شيء!لقد فحص دقيق كل وظيفة من 10 مرات و أضاف المشير.FinalReleaseComObject() على كل شيء!كما كان GC.جمع() GC.WaitForPendingFinalizers().بحثت عن رسالة مخفية مربعات.حاولت إرسال رسالة WM_CLOSE الرئيسية نافذة Excel.لقد أعدم بلدي وظيفة منفصلة AppDomain وتفريغها هذا المجال.ساعد شيئا!الخيار مع إغلاق excel الحالات هو غير مناسب لأنه إذا كان المستخدم يبدأ آخر Excel سبيل المثال يدويا أثناء التنفيذ من الوظيفة التي يعمل أيضا مع Excel, ثم تلك الحالة أيضا سوف تكون مغلقة من خلال وظيفة.أراهن أن المستخدم لن يكون سعيدا!بصراحة هذا هو عرجاء الخيار (لا جرم الرجال).لذلك قضيت بضع ساعات قبل العثور على جيد (في رأيي المتواضع) الحل: قتل excel العملية 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);

فويلا!Excel يتم إنهاء!:)

حسنا, دعونا نعود إلى الحل الثاني ، كما وعدت في بداية هذا المنصب.الحل الثاني هو استدعاء GC.جمع() GC.WaitForPendingFinalizers(). نعم أنها تعمل في الواقع ، ولكن عليك أن تكون حذرا هنا!
كثير من الناس يقولون (قلت) أن يدعو GC.جمع() لا يساعد.ولكن السبب لن تساعد إذا لا تزال هناك إشارات إلى كائنات COM!واحد من الأسباب الأكثر شعبية GC.جمع() لا تساعد على تشغيل المشروع في تصحيح الوضع.في تصحيح الوضع من الأشياء التي ليست حقا المشار إليها بعد الآن لن تكون القمامة التي تم جمعها حتى نهاية الأسلوب.
لذا ، إذا كنت حاولت GC.جمع() GC.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)
    ...
  }
}

الآن Excel قريب =)

التحديث:إضافة التعليمات البرمجية C#, و رابط ويندوز وظائف

لقد قضيت بعض الوقت في محاولة معرفة هذه المشكلة, و في الوقت XtremeVBTalk كان الأكثر نشاطا واستجابة.هنا هو ارتباط إلى الموقع الأصلي ، إغلاق Excel Interop عملية نظيفة, حتى إذا تعطل التطبيق.فيما يلي ملخص الوظيفة ، رمز نسخها إلى هذا المنصب.

  • إغلاق العملية مع إمكانية التشغيل المتداخل Application.Quit() و Process.Kill() يعمل معظم الأحيان, ولكن إذا فشل التطبيقات تحطم كارثي.أولا-هاء.إذا تعطل التطبيق ، Excel العملية سوف يكون لا يزال طليقا.
  • الحل هو السماح نظام التشغيل التعامل مع التنظيف من العمليات الخاصة بك من خلال ويندوز وظيفة الكائنات باستخدام استدعاءات Win32.عندما الخاص بك التطبيق الرئيسي يموت العمليات المرتبطة بها (أيExcel) سوف تحصل على إنهاء كذلك.

وجدت هذا الحل نظيفة لأن نظام التشغيل هو القيام بعمل حقيقي من تنظيف.كل ما عليك القيام به هو تسجيل Excel العملية.

Windows رمز الوظيفة

يلتف استدعاءات Win32 API التسجيل Interop العمليات.

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 التعداد قيمة وهذه القيمة يحددها MSDN كما:

يسبب كل العمليات المرتبطة وظيفة إنهاء عندما آخر المقبض إلى عمل مغلقة.

إضافية Win32 API الاتصال للحصول على معرف العملية (PID)

    [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 كائن COM لاغية عندما كنت فعلت مع ذلك.وشملت هذه الخلايا ، أوراق ، و كل شيء.

أي شيء في Excel مساحة يحتاج إلى أن يطلق سراحه.الفترة

لا يمكنك أن تفعل:

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

عليك أن تفعل

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

تليها الإفراج عن الكائنات.

أولا - أنت <م> لا يجب أن ندعو Marshal.ReleaseComObject(...) أو Marshal.FinalReleaseComObject(...) عند القيام إمكانية التشغيل المتداخل إكسل. وهو الخلط بين نموذج مضاد، ولكن أي معلومات حول هذا الموضوع، بما في ذلك من مايكروسوفت، والذي يشير لديك لاطلاق سراح المراجع COM يدويا من NET هو غير صحيح. والحقيقة هي أن وقت التشغيل .NET وجمع القمامة حفاظ بشكل صحيح مسار وتنظيف المراجع COM. لرمز، وهذا يعني أنه يمكنك إزالة كاملة `بينما (...) حلقة في الجزء العلوي.

وثانيا، إذا كنت تريد التأكد من أن المراجع COM إلى كائن COM خارج العملية هي تنظيف عندما تنتهي عملية (بحيث أن عملية اكسل سيغلق)، تحتاج إلى التأكد من أن يدير جمع القمامة. يمكنك القيام بذلك بشكل صحيح مع المكالمات إلى GC.Collect() وGC.WaitForPendingFinalizers(). يدعو هذا مرتين آمنة، ويضمن أن يتم تنظيف دورات بالتأكيد يصل جدا (على الرغم من أنني لست متأكدا من الحاجة إليه، وسوف نقدر مثال يوضح ذلك).

وثالثا، عندما يعمل تحت المصحح والمراجع المحلية سيتم الاحتفاظ بشكل مصطنع على قيد الحياة حتى نهاية الأسلوب (بحيث التفتيش متغير محلي يعمل). حتى المكالمات GC.Collect() ليست فعالة لتنظيف الجسم مثل rng.Cells من نفس الأسلوب. يجب تقسيم رمز القيام إمكانية التشغيل المتداخل COM من تنظيف GC إلى طرق منفصلة. (كان هذا الاكتشاف الرئيسي بالنسبة لي، من جزء واحد من الجواب نشرت هنا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 وعلى تجاوز المكدس (وخصوصا هذا السؤال!).

وماذا أخيرا أقنعني لإلقاء نظرة فاحصة ومعرفة كانت المشورة الصحيحة بلوق وظيفة <م> <وأ href = "https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal -releasecomobject-مدروسة خطير / "يختلط =" noreferrer "> Marshal.ReleaseComObject تعتبر خطرة جنبا إلى جنب مع إيجاد هذه المسألة مع الاحتفاظ المراجع على قيد الحياة تحت المصحح الذي كان مربكا لي اختبار في وقت سابق.

أنا وجدت مفيدة عامة قالب التي يمكن أن تساعد في تنفيذ الصحيحة التخلص من نمط كائنات COM التي تحتاج المشير.ReleaseComObject يسمى عندما يخرجون من نطاق:

الاستخدام:

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

عند تطبيق جديد التفوق، فإنه يفتح برنامج اكسل في الخلفية. تحتاج إلى قيادة هذا البرنامج اكسل لإنهاء قبل الافراج عن الارتباط لأن هذا برنامج اكسل ليست جزءا من التحكم الخاصة بك مباشرة. وبالتالي، فإنه سيبقى مفتوحا إذا تم تحرير الرابط!

وجيد البرمجة الجميع ~~

المشتركة للمطورين شيء من الحلول عملت بالنسبة لي ، لذلك قررت أن تنفذ جديد خدعة.

دعونا أولا تحديد "ما هو هدفنا؟" => "لا أرى كائن excel بعد عملنا في إدارة المهام"

موافق.دعونا لا للطعن والبدء في تدميرها ، ولكن النظر ليس إلى تدمير الأخرى مثيل نظام التشغيل Excel التي يتم تشغيلها في نفس الوقت.

لذا على قائمة المعالجات الحالية وجلب PID EXCEL العمليات ، ثم مرة واحدة يتم العمل الخاص بك, لدينا ضيف جديد في العمليات القائمة مع PID فريدة من نوعها ,العثور على وتدمير واحد فقط.

< نضع في اعتبارنا أي excel عملية خلال excel الوظيفة سيتم الكشف عن الجديد و دمرت > < أفضل حل هو الاستيلاء PID جديدة إنشاء كائن excel فقط تدمير هذا>

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

هذا حل مشكلتي, أتمنى لك أيضا.

وهذا يبدو بالتأكيد مثل فقد كان الإفراط معقدة. من تجربتي، هناك فقط ثلاثة أشياء رئيسية للحصول على Excel لإغلاق بشكل صحيح:

1: تأكد من عدم وجود ما يشير المتبقية لتطبيق التفوق قمت بإنشائه (يجب أن يكون لديك واحد فقط على أي حال، تعيين إلى null)

2: دعوة GC.Collect()

و3: يتضمن Excel أن تكون مغلقة، إما من قبل المستخدم إغلاق البرنامج يدويا، أو يمكنك استدعاء Quit على كائن Excel. (لاحظ أن Quit سوف تعمل كما لو حاول المستخدم لإغلاق البرنامج، وسيقدم حوار تأكيد ما إذا كانت هناك تغييرات غير محفوظة، حتى إذا Excel غير مرئية. ويمكن للمستخدم الضغط على إلغائها، ثم لن أغلقت إكسل .)

1 يجب أن يحدث قبل 2، ولكن 3 يمكن أن يحدث في أي وقت.

وأحد الطرق لتنفيذ ذلك هي التفاف كائن Excel إمكانية التشغيل المتداخل مع الفئة الخاصة بك، وخلق سبيل المثال إمكانية التشغيل المتداخل في منشئ، وتنفيذ IDisposable مع تخلص تبحث عن شيء مثل

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

وهذا تنظيف التفوق من جانب البرنامج الخاص بك من الأشياء. بمجرد أن يتم إغلاق اكسل (يدويا من قبل المستخدم أو يمكنك استدعاء Quit) فإن عملية يذهب بعيدا. إذا تم بالفعل إغلاق البرنامج، ثم هذه العملية سوف تختفي على الدعوة GC.Collect().

و(لست متأكدا كم هو مهم، ولكن قد ترغب مكالمة GC.WaitForPendingFinalizers() بعد المكالمة GC.Collect() ولكن ليس من الضروري للتخلص من عملية اكسل بدقة).

لقد عملت هذا بالنسبة لي من دون قضية لسنوات. نأخذ في الاعتبار على الرغم من أنه في حين أن هذا يعمل، لديك فعلا لإغلاق بأمان من أجل أن تعمل. وسوف لا يزال الحصول على تراكم العمليات EXCEL.EXE في حالة مقاطعة البرنامج قبل أن يتم تنظيفها اكسل حتى (عادة عن طريق ضرب "وقف" بينما يتم تصحيحه البرنامج).

لإضافة إلى الأسباب إكسل لا ثيق، حتى عند إنشاء المراجع مباشرة إلى كل كائن على القراءة، وخلق، هو 'ل' حلقة.

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

لقد تقليديا اتبعت نصيحة وجدت في VVS الجواب.ومع ذلك ، في مسعى إلى الحفاظ على هذا الجواب ما يصل إلى موعد مع أحدث الخيارات أعتقد أن كل المشاريع المستقبلية سوف تستخدم "NetOffice" المكتبة.

NetOffice هو الاستبدال الكامل مكتب PIAs تماما الإصدار-الملحد.انها تمكنت مجموعة من COM الأغلفة التي يمكن التعامل مع التنظيف التي غالبا ما يسبب هذا الصداع عند العمل مع Microsoft Office في .صافي.

بعض الميزات الرئيسية هي:

  • في الغالب الإصدار مستقلة (و النسخة تعتمد على الميزات هي موثقة)
  • أي تبعيات
  • لا بيا
  • أي تسجيل
  • لا VSTO

أنا في أي وسيلة تابعة مع المشروع ؛ أنا فقط حقا نقدر ستارك في الحد من الصداع.

والجواب المقبول هنا هو الصحيح، ولكن أيضا تأخذ نلاحظ أنه ليست إلا إشارات "اثنين من نقطة" لا بد من تجنبها، ولكن أيضا الكائنات التي تم استردادها عبر المؤشر. كنت أيضا لا تحتاج إلى الانتظار حتى يتم الانتهاء مع برنامج لتنظيف هذه الكائنات، فمن الأفضل لخلق وظائف من شأنها أن تنظيفها بمجرد الانتهاء معهم، عندما يكون ذلك ممكنا. هنا هي وظيفة أنا خلقت أن يعين بعض خصائص كائن نمط يسمى 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، إذا كنت أفتقد حتى واحد من هؤلاء، بغض النظر عن عدد المرات التي كنت استدعاء جامع القمامة و Excel ستظل تعمل على الخادم الخاص بك.

وهذا يتطلب الكثير من الاهتمام بالتفاصيل والعديد من عمليات الإعدام اختبار في حين رصد إدارة المهام عند كتابة هذا الرمز، لكنه لا يفعل ذلك يوفر عليك مشقة البحث يائسة من خلال صفحات من التعليمات البرمجية للعثور على حالة واحدة فاتك. هذا مهم بشكل خاص عند العمل في الحلقات، حيث كنت في حاجة لاطلاق سراح كل مثيل كائن، على الرغم من أنه يستخدم نفس اسم المتغير في كل مرة الحلقات.

بعد محاولة

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

وتابعت هذا بالضبط ... ولكن ما زلت اجهت قضايا 1 من 1000 مرة. من يدري لماذا. الوقت لاخراج مطرقة ...

ومباشرة بعد إنشاء مثيل من فئة تطبيق إكسل أحصل على عقد من عملية Excel الذي تم إنشاؤه للتو.

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

وبعد ذلك مرة واحدة لقد فعلت كل COM أعلاه تنظيف، أتأكد من أن عملية ليست قيد التشغيل. إذا كان لا يزال قيد التشغيل، وقتل ذلك!

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

°O¤ø" النار Excel proc و مضغ العلكة "ø¤o°

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

وعليك أن تكون على علم بأن التفوق هو حساس جدا لثقافة تقوم بتشغيل تحت كذلك.

وأنت قد تجد أنك تحتاج إلى تعيين الثقافة لEN-US قبل استدعاء دالات 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

"أبدا استخدام اثنين من النقاط مع كائنات COM" كبيرة لتجنب تسرب COM المراجع ، ولكن Excel بيا يمكن أن يؤدي إلى تسرب بطرق أكثر من واضحة للوهلة الأولى.

واحدة من هذه الطرق هو الاشتراك في أي حال تعرض أي من كائن Excel نموذج كائنات COM.

على سبيل المثال الاشتراك في فئة التطبيق هو WorkbookOpen الحدث.

بعض نظرية COM الأحداث

COM فصول تعرض مجموعة من الأحداث خلال الإتصال الواجهات.من أجل الاشتراك في الأحداث ، رمز العميل يمكنك ببساطة تسجيل كائن تنفيذ الإتصال واجهة COM فئة استدعاء أساليب ردا على أحداث معينة.منذ استدعاء واجهة واجهة COM, فمن واجب تنفيذ كائن إلى إنقاص عدد مرجع من أي كائن COM يتلقى (كمعلمة) أي من معالجات الأحداث.

كيف Excel بيا فضح COM الأحداث

Excel بيا يعرض COM الأحداث من تطبيق Excel الطبقة التقليدية .صافي الأحداث.كلما رمز العميل يشترك أ .صافي الحدث (التركيز على 'a'), بيا يخلق وهو سبيل المثال من فئة تنفيذ الإتصال واجهة ويسجل ذلك مع Excel.

وبالتالي ، فإن عددا من الإتصال الكائنات الحصول على تسجيل مع Excel ردا على الاشتراك مختلفة من طلبات .صافي رمز.مكالمة واحدة إلى الوراء الكائن في الحدث الاشتراك.

معاودة الاتصال واجهة التعامل مع الحدث يعني بيا إلى الاشتراك في كل واجهة الأحداث من أجل كل .صافي حال طلب الاشتراك.فإنه لا يمكن انتقاء واختيار.على تلقي الحدث الإتصال ، استدعاء كائن يتحقق إذا المرتبطة بها .صافي معالج الحدث هو مهتم في الحدث الحالي أم لا ثم إما استدعاء معالج أو بصمت يتجاهل الإتصال.

تأثير COM على سبيل المثال الإشارة التهم

كل هذه استدعاء كائنات لا تنقيص مرجع أي من كائنات COM التي يتلقونها (كما المعلمات) أي من الإتصال الطرق (حتى تلك التي بصمت تجاهل).أنها تعتمد فقط على CLR جامع القمامة إلى كائنات COM.

منذ GC تشغيل غير القطعية ، وهذا يمكن أن يؤدي إلى عقد من Excel عملية لمدة أطول من المطلوب و خلق انطباع من تسرب الذاكرة'.

الحل

الحل الوحيد الآن هو تجنب بيا حال مزود COM الدرجة كتابة الحدث الخاص بك موفر التي deterministically النشرات كائنات COM.

عن فئة التطبيق ، وهذا يمكن أن يتم من خلال تنفيذ AppEvents واجهة ثم تسجيل التنفيذ مع Excel باستخدام IConnectionPointContainer واجهة.فئة التطبيق (وكذلك جميع كائنات COM تعريض الأحداث باستخدام آلية الاستدعاء) تنفذ واجهة IConnectionPointContainer.

وكما أشار آخرون، تحتاج إلى إنشاء إشارة صريحة لكل كائن Excel التي تستخدمها، واستدعاء Marshal.ReleaseComObject على أن الإشارة، كما هو موضح في <لأ href = "http://support.microsoft.com/ default.aspx / كيلو بايت / 317109 "يختلط =" نوفولو noreferrer "> هذا KB المقالة . تحتاج أيضا إلى استخدام محاولة / أخيرا لضمان يسمى ReleaseComObject دائما، حتى عندما يتم طرح استثناء. بمعنى آخر. بدلا من:

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 قبل الافراج عن كائن التطبيق إذا كنت تريد Excel لإغلاقه.

وكما ترون، هذا يصبح بسرعة غير عملي للغاية في أقرب وقت كما كنت في محاولة لفعل أي شيء حتى معقد نسبيا. لقد نجحت في تطوير تطبيقات .NET مع فئة المجمع البسيطة التي يلتف عدد قليل من التلاعب بسيطة من طراز كائن Excel (فتح مصنف، الكتابة إلى المدى، حفظ / إغلاق المصنف الخ). الفئة المجمع تنفذ IDisposable، تنفذ بعناية Marshal.ReleaseComObject على كل كائن يستخدم، ولا تعرض pubicly أي كائنات Excel إلى بقية التطبيق.

ولكن هذا النهج لا مقياس جيد لمتطلبات أكثر تعقيدا.

وهذا هو نقص كبير في صافي COM إمكانية التشغيل المتداخل. لسيناريوهات أكثر تعقيدا، وأود أن تنظر بجدية في كتابة DLL ActiveX في VB6 أو لغة أخرى غير المدارة التي يمكنك تفويض كل تفاعل مع الكائنات COM مهلة بروك مثل مكتب. ثم يمكنك الرجوع إلى هذه DLL ActiveX من تطبيق NET الخاص بك، والأمور ستكون أسهل بكثير كما ستحتاج فقط لإطلاق سراح هذا إشارة واحدة.

عند كل الاشياء لم أعلاه لا يعمل، حاول إعطاء إكسل بعض الوقت لإغلاق ورقة بها:

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

...
FinalReleaseComObject(app);

تأكد من أن إطلاق سراح جميع الأشياء المتعلقة Excel!

قضيت بضع ساعات قبل محاولة عدة طرق.كلها أفكار رائعة لكن أخيرا وجدت خطأ: إذا كنت لا سراح جميع الكائنات أيا من الطرق المذكورة أعلاه يمكن أن تساعدك كما في حالتي.تأكد الإفراج عن جميع الكائنات بما في ذلك مجموعة واحدة!

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

الخيارات معا هنا.

والقاعدة اثنين من النقاط لم تنجح بالنسبة لي. في حالتي أنا خلقت طريقة لتنظيف مواردي كما يلي:

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

ويجب أن تكون حذرا للغاية باستخدام التطبيقات إمكانية التشغيل المتداخل وورد / اكسل. بعد محاولة كل الحلول ما زال لدينا الكثير من "ينوورد" عملية ترك الباب مفتوحا على الخادم (مع أكثر من 2000 مستخدم).

وبعد العمل على حل المشكلة لساعات، وأنا أدرك أنني إذا فتح أكثر من بضع المستندات باستخدام Word.ApplicationClass.Document.Open() على المواضيع المختلفة في وقت واحد، العملية المنفذة IIS (w3wp.exe) من شأنه ان تحطم ترك كل عمليات ينوورد مفتوحة!

ولذا أعتقد أنه لا يوجد حل المطلق لهذه المشكلة، ولكن التحول إلى وسائل أخرى مثل فتح مكتب تطوير XML .

مقالة رائعة على الإفراج عن كائنات COM هو 2.5 الإفراج عن كائنات COM (MSDN).

الطريقة التي أود أن الدعوة إلى null Excel الخاص بك.إمكانية التشغيل المتداخل المراجع إذا كانوا غير المتغيرات المحلية ، GC.Collect() و GC.WaitForPendingFinalizers() مرتين.محليا راقب Interop المتغيرات سوف تتخذ الرعاية من تلقائيا.

وهذا يزيل الحاجة إلى إبقاء اسمه مرجع كل كائن 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();
    }
}

هذه الكلمات هي على التوالي من المقال:

تقريبا في جميع الحالات ، nulling RCW المرجعية إجبار جمع القمامة تنظيف بشكل صحيح.إذا كنت أيضا استدعاء GC.WaitForPendingFinalizers, جمع القمامة سوف يكون مثل القطعية كما كنت يمكن أن تجعل من.التي سوف تكون متأكد بالضبط عند الكائن تم تنظيفه—على العودة من الدعوة الثانية إلى WaitForPendingFinalizers.كبديل يمكنك استخدام المشير.ReleaseComObject.ولكن لاحظ أنك المستبعد جدا أن تحتاج من أي وقت مضى إلى استخدام هذا الأسلوب.

وبلدي الحل

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

والجواب المقبول لم تنجح بالنسبة لي. التعليمات البرمجية التالية في destructor لم المهمة.

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

وأنا أعمل حاليا على التشغيل الآلي للمكاتب وقد تعثرت عبر حل لهذا أن يعمل في كل مرة بالنسبة لي. انها بسيطة وليس فيها قتل أي عمليات.

ويبدو أن من مجرد حلقات من خلال العمليات النشطة الحالية، وبأي شكل من الأشكال "الوصول إلى" عملية اكسل مفتوحة، سيتم إزالة أي مثيل شنقا طائشة من Excel. رمز أدناه ببساطة يتحقق للعمليات حيث الاسم هو "إكسل"، ثم يكتب الملكية MainWindowTitle العملية إلى سلسلة. هذا "التفاعل" مع عملية يبدو لجعل ويندوز اللحاق بالركب وإجهاض المثال المجمدة من Excel.

وأركض أدناه طريقة قبيل إنهاء التي أنا النامية الوظيفة، كما انها تطلق عليه تفريغ الحدث. فهو يزيل أي حالات اعدام من Excel في كل مرة. بكل أمانة أنا لست متأكدا تماما لماذا هذا يعمل، لكنه يعمل بشكل جيد بالنسبة لي ويمكن وضعها في نهاية أي تطبيق إكسل دون الحاجة إلى القلق بشأن النقاط مزدوجة، Marshal.ReleaseComObject، ولا عمليات القتل. سأكون مهتما جدا في أي اقتراحات لماذا هذا هو فعالة.

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

وأعتقد أن بعض من هذا هو مجرد وسيلة أن الإطار يعالج تطبيقات Office، ولكن يمكن أن أكون مخطئا. في بعض الأيام، بعض التطبيقات تنظيف العمليات فورا، وأيام أخرى على ما يبدو الانتظار حتى إغلاق التطبيق. بشكل عام، وإنهاء دفع الانتباه إلى التفاصيل وفقط للتأكد من أنه لا توجد أي عمليات إضافية تطوف في نهاية اليوم.

وأيضا، وربما أنا أكثر من تبسيط الأمور، ولكن أعتقد يمكنك فقط ...

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

وكما قلت سابقا، أنا لا أميل إلى إيلاء الاهتمام لتفاصيل يبدو فيه أن عملية إكسل أو يختفي، ولكن هذا عادة ما يعمل بالنسبة لي. أنا أيضا لا أحب للحفاظ على العمليات إكسل حول أي شيء آخر سوى كمية ضئيلة من الوقت، ولكن أنا على الأرجح مجرد كونها بجنون العظمة على ذلك.

وأما البعض وربما كتب بالفعل، انها ليست مهمة فقط كيف <م> قريب اكسل (كائن)؛ من المهم أيضا كيف <م> فتح عليه، وكذلك حسب نوع المشروع.

في تطبيق WPF، في الأساس يعمل نفس رمز بدون أو مع مشاكل قليلة جدا.

ولدي مشروع في أي ملف إكسل نفسه يتم معالجة عدة مرات لقيمة المعلمة مختلفة - على سبيل المثال تحليل ذلك على أساس القيم داخل قائمة عامة.

وأضع كل المهام المتعلقة اكسل في الفئة الأساسية، ومحلل في فئة فرعية (موزعي مختلفة استخدام وظائف اكسل المشتركة). لم أكن أريد أن Excel يتم فتحه وإغلاقه مرة أخرى لكل عنصر في قائمة عامة، وبالتالي لقد فتحت مرة واحدة فقط في الفئة الأساسية وإغلاقه في فئة فرعية. كان لي مشاكل عند نقل التعليمات البرمجية في تطبيقات سطح المكتب. لقد حاول العديد من الحلول المذكورة أعلاه. تم تنفيذ GC.Collect() بالفعل من قبل، ضعف المقترحة.

وبعد ذلك لقد قررت أنني سوف تتحرك رمز لفتح Excel إلى فئة فرعية. بدلا من فتح مرة واحدة فقط، وأنا الآن إنشاء كائن جديد (الفئة الأساسية) وفتح إكسل لكل بند وإغلاقه في نهاية المطاف. هناك بعض جزاء الأداء، ولكن على أساس عدة اختبارات العمليات إكسل وتغلق دون مشاكل (في وضع التصحيح)، وذلك كما تتم إزالة الملفات المؤقتة. سأستمر مع الاختبار وكتابة بعض أكثر إذا كنت سوف تحصل على بعض التحديثات.

وخلاصة القول هي: يجب عليك أيضا التحقق من رمز تهيئة، وخاصة إذا كان لديك العديد من الفئات، الخ

.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top