문제

C#에서 Excel 상호 운용성을 사용하고 있습니다(ApplicationClass) 그리고 finally 절에 다음 코드를 배치했습니다.

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

이런 종류의 효과가 있지만, Excel.exe Excel을 닫은 후에도 프로세스가 여전히 백그라운드에 있습니다.애플리케이션을 수동으로 닫은 후에만 출시됩니다.

내가 뭘 잘못하고 있는 걸까요? 아니면 interop 개체를 적절하게 삭제하는 대안이 있나요?

도움이 되었습니까?

해결책

응용 프로그램이 여전히 COM 개체에 대한 참조를 보유하고 있기 때문에 Excel은 종료되지 않습니다.

COM 객체의 멤버를 변수에 할당하지 않고 적어도 한 명의 멤버를 호출한다고 생각합니다.

나에게 그것은 ExcelApp.worksheets 변수에 할당하지 않고 직접 사용한 객체 :

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

내부 C#이 워크 시트 내 코드에 의해 출시되지 않은 Com 객체 (나는 그것을 알지 못했기 때문에) Excel이 언로드되지 않은 원인이었습니다.

내 문제에 대한 해결책을 찾았습니다 이 페이지, C#의 COM 개체 사용에 대한 좋은 규칙도 있습니다.

COM 객체와 함께 두 개의 점을 사용하지 마십시오.


따라서이 지식으로 위의 올바른 방법은 다음과 같습니다.

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

사후 업데이트 :

나는 모든 독자가 Hans Passant 의이 답변을 매우 신중하게 읽기를 원하며 다른 많은 개발자들이 우연히 발견했다고 설명합니다. 몇 년 전에이 대답을 썼을 때 나는 디버거가 쓰레기 수집가에 미치는 영향에 대해 몰랐고 잘못된 결론을 도출했습니다. 나는 역사를 위해 내 대답을 변경하지 않지만이 링크를 읽고 ~하지 않다 "두 점"의 길을 가십시오. .NET의 쓰레기 수집 이해 그리고 idisposable으로 Excel Interop 객체를 정리하십시오

다른 팁

실제로 Excel 응용 프로그램 객체를 깨끗하게 해제 할 수는 있지만 조심해야합니다.

액세스하는 모든 COM 객체에 대한 명명 된 참조를 유지하고 Marshal.FinalReleaseComObject() 이론적으로는 정확하지만 불행히도 실제로 관리하기가 매우 어렵습니다. 어느 곳에서나 미끄러 져 "두 개의 점"을 사용하거나 셀을 반복하는 경우 for each 루프 또는 기타 유사한 종류의 명령은 COM 객체를 참조하지 않고 교수형을 당할 위험이 있습니다. 이 경우 코드에서 원인을 찾을 방법이 없습니다. 눈으로 모든 코드를 검토하고 대규모 프로젝트에 거의 불가능할 수있는 원인을 찾아야합니다.

좋은 소식은 실제로 사용하는 모든 COM 객체에 대한 명명 된 변수 참조를 유지할 필요가 없다는 것입니다. 대신 전화하십시오 GC.Collect() 그리고 GC.WaitForPendingFinalizers() 참조를 보유하지 않는 모든 (일반적으로 사소한) 객체를 해제 한 다음 이름이 지정된 변수 참조를 보유하는 객체를 명시 적으로 해제합니다.

또한 명명 된 참조를 반대 순서로 릴리스해야합니다. 먼저 범위 객체, 워크 시트, 통합 문서 및 마지막으로 Excel 응용 프로그램 개체를 릴리스해야합니다.

예를 들어, 범위 객체 변수가 xlRng, 워크 시트 변수 xlSheet, 통합 문서 변수 xlBook 및 Excel Application 변수 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() 전화는 다음과 같이 두 배로 이루어집니다.

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

그러나 VSTO (Visual Studio Tools for Office)를 사용하지 않는 한, 최종화 큐에서 홍보되는 최종 그래프를 사용하는 최종화기를 사용하지 않는 한 필요하지 않아야합니다. 그러한 객체는 그 때까지 출시되지 않을 것입니다 다음 쓰레기 수집. 그러나 VSTO를 사용하지 않으면 전화를 걸 수 있어야합니다. GC.Collect() 그리고 GC.WaitForPendingFinalizers() 딱 한번.

나는 명시 적으로 전화를한다는 것을 알고 있습니다 GC.Collect() 아니오 (그리고 확실히 두 번하는 것은 매우 고통스러운 소리)이지만, 정직하게는 그 주위에 방법이 없습니다. 일반 작업을 통해 귀하는 귀하가 전화를하는 것 외에 다른 수단을 통해 출시 할 수 없다는 참조를 가지고 있지 않은 숨겨진 개체를 생성합니다. GC.Collect().

이것은 복잡한 주제이지만, 이것은 실제로 전부입니다. 정리 절차를 위해이 템플릿을 설정하면 포장지 등없이 정상적으로 코딩 할 수 있습니다. :-)

여기에 튜토리얼이 있습니다.

vb.net / com interop으로 사무실 프로그램 자동화

VB.NET 용으로 작성되었지만 그로 인해 꺼지지 마십시오. 원칙은 C#을 사용할 때와 정확히 동일합니다.

서문 : 내 대답에는 두 가지 솔루션이 포함되어 있으므로 읽을 때 조심하고 아무것도 놓치지 마십시오.

Excel 인스턴스를 언로드하는 방법에 대한 다양한 방법과 조언이 있습니다.

  • 모든 COM 객체를 Marshal.FinalReleaseComObject ()로 명시 적으로 발매합니다 (암시 적으로 생성 된 com-objects를 잊지 않음). 생성 된 모든 COM 객체를 릴리스하려면 여기에 언급 된 2 개의 점의 규칙을 사용할 수 있습니다.
    Excel Interop 물체를 올바르게 정리하려면 어떻게해야합니까?

  • CLR을 사용하지 않는 com-objects *를 릴리스하려면 gc.collect () 및 gc.waitforpendingfinalizers () 호출 (실제로는 작동합니다. 자세한 내용은 두 번째 솔루션을 참조하십시오).

  • COM-Server-Application이 사용자가 대답하기를 기다리는 메시지 상자가 표시 될 수 있습니다 (Excel이 닫히는 것을 막을 수는 없지만 몇 번이나 들었습니다).

  • WM_CLOSE 메시지를 기본 Excel 창으로 보냅니다

  • 별도의 appDomain에서 Excel과 함께 작동하는 기능을 실행합니다. 어떤 사람들은 AppDomain이 언로드되면 Excel 인스턴스가 종료 될 것이라고 생각합니다.

  • Excel-Interoping 코드가 시작된 후 인스턴스화 된 모든 Excel 인스턴스를 죽입니다.

하지만! 때때로이 모든 옵션은 도움이되지 않거나 적절할 수 없습니다!

예를 들어, 어제는 내 함수 중 하나 (Excel과 함께 작동)에서 Excel이 함수가 종료 된 후에도 계속 실행된다는 것을 알았습니다. 나는 모든 것을 시도했다! 전체 기능을 10 번 철저히 확인하고 모든 것을 위해 Marshal.FinalReleaseComObject ()를 추가했습니다! 또한 gc.collect () 및 gc.waitforpendingfinalizers ()가있었습니다. 숨겨진 메시지 상자를 확인했습니다. WM_CLOSE 메시지를 기본 Excel 창에 보내려고했습니다. 별도의 appDomain에서 내 기능을 실행하고 해당 도메인을 내렸다. 아무것도 도움이되지 않았습니다! 모든 Excel 인스턴스를 닫는 옵션은 부적절합니다. 사용자가 다른 Excel 인스턴스를 수동으로 시작하면 Excel에서도 작동하는 기능을 실행하는 동안 해당 인스턴스가 내 함수에 의해 닫히기 때문입니다. 나는 사용자가 행복하지 않을 것입니다! 그래서 솔직히, 이것은 절름발이 옵션입니다 (범죄자 없음). 그래서 나는 좋은 것을 찾기 전에 몇 시간을 보냈습니다 (겸손한 의견으로) 해결책: 메인 창의 HWND에 의해 Excel 프로세스를 처치하십시오 (첫 번째 솔루션입니다).

간단한 코드는 다음과 같습니다.

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

/// <summary> Tries to find and kill process by hWnd to the main window of the process.</summary>
/// <param name="hWnd">Handle to the main window of the process.</param>
/// <returns>True if process was found and killed. False if process was not found by hWnd or if it could not be killed.</returns>
public static bool TryKillProcessByMainWindowHwnd(int hWnd)
{
    uint processID;
    GetWindowThreadProcessId((IntPtr)hWnd, out processID);
    if(processID == 0) return false;
    try
    {
        Process.GetProcessById((int)processID).Kill();
    }
    catch (ArgumentException)
    {
        return false;
    }
    catch (Win32Exception)
    {
        return false;
    }
    catch (NotSupportedException)
    {
        return false;
    }
    catch (InvalidOperationException)
    {
        return false;
    }
    return true;
}

/// <summary> Finds and kills process by hWnd to the main window of the process.</summary>
/// <param name="hWnd">Handle to the main window of the process.</param>
/// <exception cref="ArgumentException">
/// Thrown when process is not found by the hWnd parameter (the process is not running). 
/// The identifier of the process might be expired.
/// </exception>
/// <exception cref="Win32Exception">See Process.Kill() exceptions documentation.</exception>
/// <exception cref="NotSupportedException">See Process.Kill() exceptions documentation.</exception>
/// <exception cref="InvalidOperationException">See Process.Kill() exceptions documentation.</exception>
public static void KillProcessByMainWindowHwnd(int hWnd)
{
    uint processID;
    GetWindowThreadProcessId((IntPtr)hWnd, out processID);
    if (processID == 0)
        throw new ArgumentException("Process has not been found by the given main window handle.", "hWnd");
    Process.GetProcessById((int)processID).Kill();
}

보시다시피, 시도한 패턴에 따라 두 가지 방법을 제공했습니다. 프로세스가 죽지 않으면 다른 방법이 예외를 던집니다. 이 코드의 유일한 약한 장소는 보안 권한입니다. 이론적으로, 사용자는 프로세스를 죽일 권한이 없을 수도 있지만 모든 경우의 99.99%에서 사용자는 그러한 권한이 있습니다. 또한 게스트 계정으로 테스트했습니다. 완벽하게 작동합니다.

따라서 Excel로 작업하는 코드는 다음과 같이 보일 수 있습니다.

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

짜잔! Excel이 종료됩니다! :)

좋아, 게시물의 시작 부분에서 약속 한대로 두 번째 솔루션으로 돌아가 봅시다.두 번째 솔루션은 gc.collect () 및 gc.waitforpendingfinalizers ()를 호출하는 것입니다. 예, 그들은 실제로 작동하지만 여기서 조심해야합니다!
많은 사람들이 GC.collect ()에게 전화하는 것은 도움이되지 않는다고 말합니다. 그러나 그것이 도움이되지 않는 이유는 COM 개체에 대한 언급이 여전히 있다면! GC.collect ()의 가장 인기있는 이유 중 하나는 도움이되지 않는 것이 디버그 모드에서 프로젝트를 실행하는 것입니다. 더 이상 참조되지 않은 디버그 모드 객체에서는 방법이 끝날 때까지 수집되지 않습니다.
따라서 gc.collect () 및 gc.waitforpendingfinalizers ()를 시도하고 도움이되지 않은 경우 다음을 수행하십시오.

1) 릴리스 모드에서 프로젝트를 실행하고 Excel이 올바르게 닫힌 지 확인하십시오.

2) 별도의 방법으로 Excel로 작업하는 방법을 감싸십시오. 따라서 다음과 같은 대신 :

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# 코드 및 Windows 작업에 대한 링크가 추가되었습니다.

나는 이 문제를 파악하기 위해 언젠가 시간을 보냈고 당시 XtremeVBTalk가 가장 활발하고 반응이 좋았습니다.제가 쓴 원본글 링크입니다, 응용 프로그램이 중단되더라도 Excel Interop 프로세스를 깔끔하게 종료.아래는 게시물의 요약이며, 이 게시물에 복사된 코드입니다.

  • Interop 프로세스 종료 Application.Quit() 그리고 Process.Kill() 대부분의 경우 작동하지만 응용 프로그램이 심각하게 충돌하면 실패합니다.즉.앱이 충돌하더라도 Excel 프로세스는 여전히 느슨하게 실행됩니다.
  • 해결책은 OS가 다음을 통해 프로세스 정리를 처리하도록 하는 것입니다. Windows 작업 개체 Win32 호출을 사용합니다.기본 애플리케이션이 종료되면 관련 프로세스(예:Excel)도 종료됩니다.

OS가 실제 정리 작업을 수행하고 있기 때문에 이것이 깨끗한 솔루션이라는 것을 알았습니다.당신이해야 할 일은 등록하다 엑셀 과정.

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; 호출됩니다. 0x2000JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 열거형 값이며 이 값은 다음과 같이 정의됩니다. 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);

이것은 내가 작업하고있는 프로젝트에서 효과가있었습니다.

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

우리는 설정하는 것이 중요하다는 것을 배웠습니다 모든 당신이 그것을 완료했을 때 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(...) Excel Interop을 수행 할 때. 혼란스러운 방지 방지이지만 Microsoft를 포함한이 정보에 대한 정보는 .NET에서 COM 참조를 수동으로 릴리스해야한다는 것을 나타냅니다. 사실 .NET 런타임 및 쓰레기 수집기는 COM 참조를 올바르게 추적하고 정리합니다. 코드의 경우, 이는 상단의 전체 (...) 루프를 제거 할 수 있음을 의미합니다.

둘째, 프로세스가 종료 될 때 (Excel 프로세스가 닫히도록) 프로세스 외부 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 및 스택 오버플로에 대한 많은 게시물 (특히이 질문!)을 포함 하여이 문제에 대한 많은 잘못된 정보와 혼란이 있습니다.

마침내 내가 더 자세히 살펴보고 올바른 조언을 알아 내도록 설득 한 것은 블로그 게시물이었습니다. Marshal.releasecomobject는 위험한 것으로 간주됩니다 참고 문헌의 문제를 찾는 것과 함께 내 초기 테스트를 혼란스럽게하는 디버거 아래에 살아있었습니다.

설립하다 COM 개체의 올바른 처리 패턴을 구현하는 데 도움이되는 유용한 일반 템플릿, 마샬이 필요합니다.

용법:

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

주형:

public class AutoReleaseComObject<T> : IDisposable
{
    private T m_comObject;
    private bool m_armed = true;
    private bool m_disposed = false;

    public AutoReleaseComObject(T comObject)
    {
        Debug.Assert(comObject != null);
        m_comObject = comObject;
    }

#if DEBUG
    ~AutoReleaseComObject()
    {
        // We should have been disposed using Dispose().
        Debug.WriteLine("Finalize being called, should have been disposed");

        if (this.ComObject != null)
        {
            Debug.WriteLine(string.Format("ComObject was not null:{0}, name:{1}.", this.ComObject, this.ComObjectName));
        }

        //Debug.Assert(false);
    }
#endif

    public T ComObject
    {
        get
        {
            Debug.Assert(!m_disposed);
            return m_comObject;
        }
    }

    private string ComObjectName
    {
        get
        {
            if(this.ComObject is Microsoft.Office.Interop.Excel.Workbook)
            {
                return ((Microsoft.Office.Interop.Excel.Workbook)this.ComObject).Name;
            }

            return null;
        }
    }

    public void Disarm()
    {
        Debug.Assert(!m_disposed);
        m_armed = false;
    }

    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

    #endregion

    protected virtual void Dispose(bool disposing)
    {
        if (!m_disposed)
        {
            if (m_armed)
            {
                int refcnt = 0;
                do
                {
                    refcnt = System.Runtime.InteropServices.Marshal.ReleaseComObject(m_comObject);
                } while (refcnt > 0);

                m_comObject = default(T);
            }

            m_disposed = true;
        }
    }
}

참조:

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

나는이 문제가 5 년 동안 세상을 괴롭 혔다는 것을 믿을 수 없다 .... 당신이 응용 프로그램을 만들었다면, 링크를 제거하기 전에 먼저 종료해야합니다.

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

닫을 때

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

Excel 애플리케이션을 새로 사용하면 백그라운드에서 Excel 프로그램이 열립니다. Excel 프로그램이 직접 제어의 일부가 아니기 때문에 링크를 해제하기 전에 해당 Excel 프로그램을 명령해야합니다. 따라서 링크가 릴리스되면 열려 있습니다!

모든 사람이 좋은 프로그래밍 ~~

일반적인 개발자, 귀하의 솔루션 중 어느 것도 저에게 도움이되지 않았으므로 새로운 것을 구현하기로 결정했습니다. 장난.

먼저 "우리의 목표는 무엇입니까?" => "작업 관리자에서 작업 한 후 Excel 객체를 보지 마십시오"

확인. 도전하지 말고 파괴하기 시작하지 말고 병렬로 실행되는 다른 인스턴스 OS Excel을 파괴하지 마십시오.

따라서 현재 프로세서 목록을 가져 와서 Excel 프로세스의 PID를 가져 오면 작업이 완료되면 고유 한 PID가있는 프로세스 목록에 새로운 게스트가 있습니다.

<Excel 작업 중에 새로운 Excel 프로세스를 명심하십시오. 새롭고 파괴 된 상태로 감지됩니다.> <더 나은 솔루션은 새로운 생성 된 Excel 물체의 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();

이것은 내 문제를 해결합니다.

이것은 그것이 과도하게 복잡한 것처럼 보입니다. 내 경험을 통해 Excel이 올바르게 닫히는 세 가지 중요한 사항이 있습니다.

1 : 당신이 만든 Excel 응용 프로그램에 대한 남은 참조가 없는지 확인하십시오 (어쨌든 하나만 있어야합니다. null)

2 : 전화 GC.Collect()

3 : Excel은 사용자가 수동으로 프로그램을 닫거나 전화를 통해 닫아야합니다. Quit Excel 물체에. (그 점에 유의하십시오 Quit 사용자가 프로그램을 닫으려고 시도한 것처럼 작동하며 Excel이 보이지 않더라도 구축되지 않은 변경 사항이 있으면 확인 대화 상자를 제시합니다. 사용자는 취소를 누르면 Excel이 닫히지 않았습니다.)

1은 2 전에 발생해야하지만 3은 언제든지 발생할 수 있습니다.

이를 구현하는 한 가지 방법은 인터 로프 엑셀 객체를 자신의 클래스로 래핑하고 생성자에서 interop 인스턴스를 만들고,

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

그것은 당신의 프로그램 측면에서 탁월한 일을 정리할 것입니다. Excel이 닫히면 (사용자 또는 전화로 수동으로 Quit) 프로세스가 사라질 것입니다. 프로그램이 이미 폐쇄 된 경우 프로세스가 GC.Collect() 전화.

(나는 그것이 얼마나 중요한지 잘 모르겠지만 당신은 GC.WaitForPendingFinalizers() 다음에 전화하십시오 GC.Collect() 전화를 걸지만 Excel 프로세스를 제거 할 필요는 없습니다.)

이것은 몇 년 동안 문제없이 저에게 효과적이었습니다. 이 효과가있는 동안 실제로 작동하기 위해 우아하게 닫아야한다는 점을 명심하십시오. Excel이 정리되기 전에 프로그램을 중단하는 경우에도 Excel.exe 프로세스가 여전히 축적됩니다 (일반적으로 프로그램이 디버깅되는 동안 "정지"를 치면).

읽기, 생성시 각 객체에 직접 굴절을 생성하는 경우에도 Excel이 닫히지 않는 이유를 추가하는 것은 'for'루프입니다.

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"라이브러리를 사용할 것이라고 생각합니다.

네트워피스 사무실 PIAS를 완벽하게 대체하고 완전히 버전에 대한 정보를 제공합니다. .NET의 Microsoft Office와 함께 작업 할 때 종종 그러한 두통을 일으키는 청소를 처리 할 수있는 관리 된 COM 포장지 모음입니다.

몇 가지 주요 기능은 다음과 같습니다.

  • 대부분 버전 독립적 (및 버전 의존적 기능이 문서화 됨)
  • 의존성 없음
  • PIA 없음
  • 등록이 없습니다
  • 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 Will 여전히 서버에서 실행 중입니다.

이 코드를 작성할 때 작업 관리자를 모니터링하는 동안 세부 사항과 많은 테스트 실행에 많은주의를 기울여야하지만, 그렇게하면 코드 페이지를 필사적으로 검색하여 누락 된 인스턴스를 찾는 데 필사적으로 검색 할 수 있습니다. 이는 루프에서 작업 할 때 특히 중요합니다. 루프가 루프 할 때마다 동일한 변수 이름을 사용하더라도 객체의 각 인스턴스를 해제해야합니다.

시도한 후

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

나는 이것을 정확히 따랐다. 그러나 나는 여전히 1000 번 이슈를 달렸다. 왜 그런지 아는 사람. 망치를 꺼낼 시간 ...

Excel 응용 프로그램 클래스가 인스턴스화 된 직후에 방금 작성된 Excel 프로세스를 보유합니다.

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

그런 다음 위의 COM 정리를 모두 완료하면 프로세스가 실행되지 않도록합니다. 여전히 실행 중이면 죽여라!

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

¨ ° º¤Ø „¸ 엑스 엑스 엑스 엑스 엑스 프록과 씹어 버블 껌 ¸ „ؤº ° ¨

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

Excel은 당신이 달리는 문화에도 매우 민감하다는 것을 알고 있어야합니다.

Excel 함수를 호출하기 전에 문화를 EN-US로 설정해야 할 수도 있습니다. 이것은 모든 기능에 적용되지는 않지만 일부 기능에 적용됩니다.

    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 PIA는 첫눈에 명백한 것보다 더 많은 방식으로 누출을 초래할 수 있습니다.

이러한 방법 중 하나는 Excel Object Model의 COM 객체에 의해 노출 된 모든 이벤트에 가입하는 것입니다.

예를 들어, 응용 프로그램 클래스의 WorkbookOpen 이벤트를 구독합니다.

COM 사건에 대한 일부 이론

COM 클래스는 콜백 인터페이스를 통해 이벤트 그룹을 노출시킵니다. 이벤트를 구독하기 위해 클라이언트 코드는 단순히 콜백 인터페이스를 구현하는 객체를 등록 할 수 있으며 COM 클래스는 특정 이벤트에 대한 응답으로 메소드를 호출합니다. 콜백 인터페이스는 COM 인터페이스이므로 이벤트 핸들러에 대해 수신하는 COM 객체의 참조 수를 줄이는 것은 구현 객체의 의무입니다.

Excel PIA가 COM 이벤트를 노출시키는 방법

Excel PIA는 Excel 응용 프로그램 클래스의 COM 이벤트를 기존의 .NET 이벤트로 노출시킵니다. 클라이언트 코드가 가입 할 때마다 .NET 이벤트 ( 'a'에 중점을 둡니다), Pia는 생성합니다. an 클래스의 인스턴스 콜백 인터페이스를 구현하고 Excel에 등록합니다.

따라서, 많은 콜백 객체가 .NET 코드의 다른 구독 요청에 응답하여 Excel에 등록됩니다. 이벤트 구독 당 하나의 콜백 객체.

이벤트 처리를위한 콜백 인터페이스는 PIA가 모든 .NET 이벤트 구독 요청에 대해 모든 인터페이스 이벤트를 구독해야 함을 의미합니다. 선택하고 선택할 수 없습니다. 이벤트 콜백을 받으면 콜백 객체는 관련 .NET 이벤트 핸들러가 현재 이벤트에 관심이 있는지 여부를 확인한 다음 핸들러를 호출하거나 콜백을 조용히 무시합니다.

COM 인스턴스 참조 수에 미치는 영향

이러한 모든 콜백 객체는 콜백 방법 (조용히 무시되는 방법에 대해서도)에 대해 수신 한 COM 개체의 참조 수를 줄이지 않습니다. 그들은 전적으로 의존합니다 CLR 쓰레기 수집가가 COM 물체를 자유롭게합니다.

GC Run은 비 결정적이므로, 이는 원하는 것보다 더 긴 기간 동안 Excel 프로세스를 유지하고 '메모리 누출'의 인상을 유발할 수 있습니다.

해결책

현재 유일한 솔루션은 COM 클래스의 PIA 이벤트 제공 업체를 피하고 COM 개체를 결정적으로 출시하는 자신의 이벤트 제공 업체를 작성하는 것입니다.

응용 프로그램 클래스의 경우 Appevents 인터페이스를 구현 한 다음 사용하여 Excel에 구현을 등록하여 수행 할 수 있습니다. iConnectionPointContainer 인터페이스. 애플리케이션 클래스 (및 해당 문제에 대해 콜백 메커니즘을 사용하여 이벤트를 노출시키는 모든 COM 개체는 iconnectionPointContainer 인터페이스를 구현합니다.

다른 사람들이 지적했듯이, 당신은 당신이 사용하는 모든 Excel 객체에 대해 명시적인 참조를 만들고, 다음에 설명 된대로 그 참조에 대한 Marshal.releasecomobject를 호출해야합니다. 이 KB 기사. 또한 예외가 발생하더라도 릴리스에 릴리스가 호출되도록 시도/마침내 사용해야합니다. 즉, 대신 :

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

accel을 닫으려면 응용 프로그램 개체를 공개하기 전에 응용 프로그램을 호출해야합니다.

보시다시피, 이것은 단순한 복잡한 일을하려고하자마자 빠르게 다루기 힘들어집니다. Excel 객체 모델의 간단한 조작을 몇 가지 간단한 조작을 감지하는 간단한 래퍼 클래스를 사용하여 .NET 응용 프로그램을 성공적으로 개발했습니다 (통합 문서 열기, 범위에 쓰기, 통합 문서를 저장/닫기 등). 래퍼 클래스는 idisposable을 구현하고 신중하게 마샬을 구현합니다. Releasecomobject가 사용하는 모든 객체에 대한 releasecomobject이며, 엑셀 객체를 나머지 앱에 음모로 노출시키지 않습니다.

그러나이 접근법은보다 복잡한 요구 사항에 맞지 않습니다.

이것은 .net com interop의 큰 결함입니다. 보다 복잡한 시나리오의 경우 VB6 또는 기타 관리되지 않은 언어로 ActiveX DLL을 작성하여 사무실과 같은 외부 COM 객체와 모든 상호 작용을 위임 할 수 있습니다. 그런 다음 .NET 응용 프로그램 에서이 ActiveX DLL을 참조 할 수 있으며이 하나의 참조 만 해제하면됩니다.

위의 모든 것들이 작동하지 않았을 때, 엑셀을 잠시 닫을 시간을 주어보십시오.

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

...
FinalReleaseComObject(app);

Excel과 관련된 모든 개체를 출시해야합니다!

나는 여러 가지 방법을 시도함으로써 몇 시간을 보냈습니다. 모두 훌륭한 아이디어이지만 마침내 내 실수를 찾았습니다. 모든 객체를 공개하지 않으면 위의 방법 중 어느 것도 도움이 될 수 없습니다. 내 경우처럼. Range One을 포함한 모든 객체를 출시해야합니다!

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

Word/Excel Interop 응용 프로그램을 사용하여 매우주의해야합니다. 모든 솔루션을 시도한 후에도 여전히 서버에 많은 "Winword"프로세스가 열려있었습니다 (2000 명 이상의 사용자와 함께).

몇 시간 동안 문제를 해결 한 후에는 몇 가지 이상의 문서를 열면 Word.ApplicationClass.Document.Open() 다른 스레드에서 동시에 IIS 작업자 프로세스 (W3wp.exe)가 충돌하여 모든 Winword 프로세스가 열려 있습니다!

따라서이 문제에 대한 절대적인 해결책은 없지만 다음과 같은 다른 방법으로 전환합니다. Office Open XML 개발.

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.reneasecomobject를 사용할 수 있습니다. 그러나이 방법을 사용하지 않아도됩니다.

내 해결책

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

받아 들여진 대답은 저에게 효과가 없었습니다. 파괴자의 다음 코드가 작업을 수행했습니다.

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 프로세스에 '액세스'하는 경우 Excel의 길 잃은 인스턴스가 제거되는 것으로 보입니다. 아래 코드는 단순히 이름이 'Excel'인 프로세스를 확인한 다음 프로세스의 MainWindowTitle 속성을 문자열에 씁니다. 프로세스와의 이러한 '상호 작용'은 Windows가 냉동 된 Excel 인스턴스를 따라 잡고 중단시키는 것으로 보입니다.

언로드 이벤트를 발사하기 때문에 종료를 개발중인 추가 기능 직전에 아래 방법을 실행합니다. 매번 탁월한 탁월한 인스턴스를 제거합니다. 모든 정직에서 나는 이것이 왜 작동하는지 확실하지 않지만, 그것은 나에게 잘 작동하며 Double Dots, Marshal.releasecomobject 또는 킬링 프로세스에 대해 걱정할 필요없이 Excel 애플리케이션의 끝에 배치 될 수 있습니다. 나는 이것이 왜 효과적인 지에 대한 제안에 매우 관심이있을 것입니다.

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

그 중 일부는 프레임 워크가 사무실 응용 프로그램을 처리하는 방식 일 뿐이라고 생각하지만 잘못 될 수 있습니다. 며칠 동안 일부 응용 프로그램은 프로세스를 즉시 정리하고 다른 날에는 응용 프로그램이 닫힐 때까지 기다리는 것 같습니다. 일반적으로 세부 사항에주의를 기울이지 않고 하루가 끝날 때 떠 다니는 추가 프로세스가 없도록하십시오.

또한, 아마도 나는 물건을 단순화하고 있을지도 모르지만, 당신은 그냥 할 수 있다고 생각합니다 ...

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

앞서 말했듯이, 나는 Excel 프로세스가 나타나거나 사라질 때의 세부 사항에주의를 기울이는 경향이 없지만 일반적으로 저에게 효과적입니다. 나는 또한 최소한의 시간 이외의 다른 것에 대해 Excel 프로세스를 유지하는 것을 좋아하지 않지만 아마도 그것에 대해 편집증 일 것입니다.

일부는 이미 글을 썼을 것입니다. 닫다 Excel (물체); 당신이 어떻게 중요합니다 열려 있는 그것은 또한 프로젝트의 유형에 의해.

WPF 응용 프로그램에서 기본적으로 동일한 코드가 문제없이 또는 거의 작동하지 않습니다.

동일한 Excel 파일이 다른 매개 변수 값에 대해 여러 번 처리되는 프로젝트가 있습니다.

모든 Excel 관련 함수를 기본 클래스에 넣고 구문 분석기에 서브 클래스에 넣습니다 (다른 파서는 공통 Excel 함수를 사용합니다). Excel이 일반 목록의 각 항목에 대해 Excel이 다시 열리고 다시 닫히기를 원하지 않았으므로 기본 클래스에서 한 번만 열고 서브 클래스로 닫았습니다. 코드를 데스크탑 응용 프로그램으로 옮길 때 문제가있었습니다. 위에서 언급 한 많은 솔루션을 시도했습니다. GC.Collect() 이미 이전에 구현되었으며, 제안 된 두 배.

그런 다음 Excel을 열기위한 코드를 서브 클래스로 옮기기로 결정했습니다. 한 번만 열지 않고 이제 새 개체 (기본 클래스)를 만들고 모든 항목에 대해 Excel을 열고 끝에 닫습니다. 일부 성능 페널티가 있지만 여러 테스트를 기반으로 Excel 프로세스가 문제없이 (디버그 모드에서) 폐쇄되므로 임시 파일도 제거됩니다. 계속해서 테스트를 계속하고 업데이트를 받으면 좀 더 쓸 것입니다.

결론은 다음과 같습니다. 특히 클래스 등이 많은 경우 초기화 코드도 확인해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top