Excel interop 개체를 올바르게 정리하려면 어떻게해야합니까?
C # ( ApplicationClass
) 에서 Excel interop을 사용 하고 있으며 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 # 이 내 코드에 의해 릴리스되지 않은 Worksheets 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 응용 프로그램 개체 순입니다.
예를 들어라는 Range 개체 변수 xlRng
,라는 Worksheet 변수 xlSheet
,라는 Workbook 변수 xlBook
및라는 Excel 응용 프로그램 변수 가 있다고 가정하면 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으로 Office 프로그램 자동화
VB.NET 용으로 작성되었지만, 그것에 대해 미루지 마십시오. 원칙은 C #을 사용할 때와 정확히 동일합니다.
서문 : 내 대답에는 두 가지 해결책이 포함되어 있으므로 읽을 때주의하고 아무것도 놓치지 마십시오.
다음과 같이 Excel 인스턴스를 언로드하는 방법에 대한 여러 가지 방법과 조언이 있습니다.
Marshal.FinalReleaseComObject ()를 사용하여 모든 com 객체를 명시 적으로 해제합니다 (암시 적으로 생성 된 com 객체를 잊지 않음). 생성 된 모든 com 개체를 해제하려면 여기에 언급 된 점 2 개 규칙을 사용할 수 있습니다.
Excel interop 개체를 올바르게 정리하는 방법은 무엇입니까?GC.Collect () 및 GC.WaitForPendingFinalizers ()를 호출하여 CLR을 사용하지 않는 com-objects 릴리스 * (실제로 작동합니다. 자세한 내용은 두 번째 솔루션 참조)
com-server-application에 사용자의 응답을 기다리는 메시지 상자가 표시되는지 확인합니다 (Excel이 닫히는 것을 막을 수는 없지만 몇 번 들었습니다)
기본 Excel 창에 WM_CLOSE 메시지 보내기
별도의 AppDomain에서 Excel과 함께 작동하는 함수를 실행합니다. 어떤 사람들은 AppDomain이 언로드되면 Excel 인스턴스가 종료 될 것이라고 생각합니다.
엑셀 인터로 핑 코드가 시작된 후 인스턴스화 된 모든 엑셀 인스턴스를 종료합니다.
그러나! 때로는 이러한 모든 옵션이 도움이되지 않거나 적절하지 않을 수 있습니다!
예를 들어, 어제 내 기능 중 하나에서 (Excel과 함께 작동) Excel이 기능이 끝난 후에도 계속 실행된다는 것을 알았습니다. 나는 모든 것을 시도했다! 나는 전체 함수를 10 번 철저히 확인하고 모든 것에 대해 Marshal.FinalReleaseComObject ()를 추가했습니다! 또한 GC.Collect () 및 GC.WaitForPendingFinalizers ()가 있습니다. 숨겨진 메시지 상자를 확인했습니다. 메인 엑셀 창에 WM_CLOSE 메시지를 보내려고했습니다. 별도의 AppDomain에서 내 기능을 실행하고 해당 도메인을 언로드했습니다. 도움이되지 않았습니다! 모든 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();
}
보시다시피 Try-Parse 패턴에 따라 두 가지 방법을 제공했습니다 (여기서는 적절하다고 생각합니다). 하나의 방법은 프로세스를 종료 할 수없는 경우 예외를 throw하지 않습니다 (예 : 프로세스가 더 이상 존재하지 않음). , Process가 종료되지 않은 경우 다른 메서드에서 예외가 발생합니다. 이 코드에서 유일한 약점은 보안 권한입니다. 이론적으로 사용자는 프로세스를 종료 할 수있는 권한이 없을 수 있지만 모든 경우의 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가 가장 활동적이고 반응이 좋았습니다. 다음은 애플리케이션이 충돌하더라도 원래 게시물 인 Closing an Excel Interop process cleanly에 대한 링크 입니다. 다음은 게시물 요약과이 게시물에 복사 된 코드입니다.
- 와 상호 운용성 프로세스를 마감
Application.Quit()
하고하는 것은Process.Kill()
대부분의 경우 작동하지만, 응용 프로그램이 격변 충돌하면 실패합니다. 즉, 앱이 충돌하면 Excel 프로세스가 여전히 느슨하게 실행됩니다. - 해결책은 OS가 Win32 호출을 사용하여 Windows 작업 개체 를 통해 프로세스를 정리하도록하는 것 입니다. 주 응용 프로그램이 종료되면 관련 프로세스 (예 : 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;
호출됩니다.0x2000
는 ISJOB_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;
Excel COM 개체에 대한 모든 참조를 작업을 마치면 null 로 설정하는 것이 중요하다는 것을 배웠습니다 . 여기에는 셀, 시트 및 모든 것이 포함되었습니다.
Excel 네임 스페이스에있는 모든 항목을 해제해야합니다. 기간
당신은 할 수 없습니다 :
Worksheet ws = excel.WorkBooks[1].WorkSheets[1];
당신은해야합니다
Workbooks books = excel.WorkBooks;
Workbook book = books[1];
Sheets sheets = book.WorkSheets;
Worksheet ws = sheets[1];
그 다음에는 개체가 해제됩니다.
첫째 -Excel interop 을 호출 하거나 수행 할 때 필요 하지 않습니다 . 헷갈리는 안티 패턴이지만 Microsoft를 포함하여 .NET에서 COM 참조를 수동으로 해제해야 함을 나타내는 모든 정보가 올바르지 않습니다. 사실 .NET 런타임 및 가비지 수집기는 COM 참조를 올바르게 추적하고 정리합니다. 코드의 경우 맨 위에있는 전체`while (...) 루프를 제거 할 수 있습니다.Marshal.ReleaseComObject(...)
Marshal.FinalReleaseComObject(...)
둘째, 프로세스가 종료 될 때 out-of-process COM 개체에 대한 COM 참조를 정리하여 Excel 프로세스가 닫히도록하려면 가비지 수집기가 실행되는지 확인해야합니다. GC.Collect()
및에 대한 호출로이 작업을 올바르게 수행합니다 GC.WaitForPendingFinalizers()
. 이것을 두 번 호출하는 것은 안전하며 주기도 확실히 정리되도록합니다 (필요한지는 확실하지 않으며이를 보여주는 예제를 고맙게 생각합니다).
셋째, 디버거에서 실행할 때 메서드가 끝날 때까지 로컬 참조가 인위적으로 유지되어 로컬 변수 검사가 작동합니다. 따라서 같은 방법으로 GC.Collect()
물건을 청소할 때는 호출이 효과적이지 않습니다 rng.Cells
. GC 정리에서 COM interop을 수행하는 코드를 별도의 메서드로 분할해야합니다. (이것은 @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 및 Stack Overflow에 대한 많은 게시물 (특히이 질문!)을 포함하여이 문제에 대한 많은 잘못된 정보와 혼란이 있습니다.
마지막으로 자세히 살펴보고 올바른 조언을 찾아 내도록 설득 한 것은 블로그 게시물 Marshal.ReleaseComObject가 Dangerous로 간주 하고 이전 테스트를 혼란스럽게했던 디버거에서 활성 상태로 유지되는 참조 문제를 찾는 것입니다.
내가 발견 캔 도움이 COM 개체에 대한 올바른 처리 패턴, Marshal.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 프로그램이 열립니다. 해당 Excel 프로그램은 직접 제어의 일부가 아니므로 링크를 해제하기 전에 해당 Excel 프로그램을 종료하도록 명령해야합니다. 따라서 링크가 해제되면 계속 열려 있습니다!
좋은 프로그래밍 여러분 ~~
일반 개발자, 귀하의 솔루션 중 어느 것도 저에게 효과가 없었으므로 새로운 트릭 을 구현하기로 결정했습니다 .
먼저 "우리의 목표는 무엇입니까?" => "작업 관리자에서 작업 후 엑셀 개체를 볼 수 없음"
확인. 도전하고 파괴를 시작하지 말고 병렬로 실행되는 다른 인스턴스 OS Excel을 파괴하지 마십시오.
따라서 현재 프로세서 목록을 가져오고 EXCEL 프로세스의 PID를 가져 오면 작업이 완료되면 고유 한 PID가있는 프로세스 목록에 새 게스트가 생성되어 해당 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();
이것은 내 문제를 해결합니다.
이것은 확실히 너무 복잡해 보인다. 내 경험상 Excel을 제대로 닫으려면 세 가지 핵심 사항이 있습니다.
1 : 만든 Excel 응용 프로그램에 대한 나머지 참조가 없는지 확인합니다 (어쨌든 하나만 있어야합니다.로 설정 null
).
2 : 전화 GC.Collect()
3 : 사용자가 프로그램을 수동으로 닫거나 Quit
Excel 개체 를 호출 하여 Excel을 닫아야 합니다. ( Quit
사용자가 프로그램을 닫으려고 한 것처럼 작동하며 Excel이 보이지 않더라도 저장되지 않은 변경 사항이 있으면 확인 대화 상자가 표시됩니다. 사용자가 취소를 누르면 Excel이 닫히지 않습니다. )
1은 2보다 먼저 발생해야하지만 3은 언제든지 발생할 수 있습니다.
이를 구현하는 한 가지 방법은 interop Excel 개체를 자신의 클래스로 래핑하고, 생성자에서 interop 인스턴스를 만들고, Dispose를 사용하여 IDisposable을 구현하는 것입니다.
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"라이브러리를 사용할 것이라고 생각합니다.
NetOffice 는 Office PIA를 완전히 대체하며 버전에 구애받지 않습니다. .NET에서 Microsoft Office로 작업 할 때 종종 이러한 골치 아픈 문제를 일으키는 정리를 처리 할 수있는 관리되는 COM 래퍼 모음입니다.
몇 가지 주요 기능은 다음과 같습니다.
- 대부분 버전 독립적 (및 버전 종속 기능이 문서화 됨)
- 종속성 없음
- PIA 없음
- 등록 없음
- VSTO 없음
나는 프로젝트와 전혀 관련이 없습니다. 나는 진정으로 두통의 극명한 감소에 감사합니다.
여기에서 허용되는 대답은 정확하지만 "두 개의 점"참조뿐만 아니라 색인을 통해 검색되는 객체도 피해야합니다. 또한 이러한 개체를 정리하기 위해 프로그램을 마칠 때까지 기다릴 필요가 없습니다. 가능하면 개체를 다 사용하는 즉시 정리할 함수를 만드는 것이 가장 좋습니다. 다음은라는 Style 개체의 일부 속성을 할당하는 함수입니다 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]
이를 정리하기 위해 변수 를 설정 해야했습니다 (해제 할 필요가없는 열거 형을 나타내는 두 개의 점 때문이 아니라 실제로 참조하는 객체가 Border 객체이기 때문입니다). 해제해야합니다).
이런 종류의 것은 스스로 정리하는 표준 응용 프로그램에서는 실제로 필요하지 않지만 ASP.NET 응용 프로그램에서는 가비지 수집기를 호출하는 빈도에 관계없이 이러한 항목 중 하나라도 놓치면 Excel이 여전히 서버에서 실행 중입니다.
이 코드를 작성할 때 작업 관리자를 모니터링하는 동안 세부 사항과 많은 테스트 실행에 많은주의가 필요하지만 그렇게하면 놓친 인스턴스를 찾기 위해 코드 페이지를 필사적으로 검색해야하는 번거 로움을 덜 수 있습니다. 이것은 루프에서 작업 할 때 특히 중요합니다. 루프에서 작업 할 때마다 동일한 변수 이름을 사용하더라도 객체의 각 인스턴스를 해제해야합니다.
시도 후
- 역순으로 COM 개체 해제
- 끝에 추가
GC.Collect()
하고GC.WaitForPendingFinalizers()
두 번 - 2 개 이하의 점
- 통합 문서를 닫고 응용 프로그램을 종료합니다.
- 릴리스 모드에서 실행
나를 위해 작동하는 최종 솔루션은
GC.Collect();
GC.WaitForPendingFinalizers();
함수의 끝에 다음과 같이 래퍼에 추가했습니다.
private void FunctionWrapper(string sourcePath, string targetPath)
{
try
{
FunctionThatCallsExcel(sourcePath, targetPath);
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
나는 이것을 정확히 따랐다. 그러나 나는 여전히 1000 번 중 1 번 문제를 겪었다. 이유를 누가 압니다. 망치를 꺼낼 시간 ...
Excel Application 클래스가 인스턴스화 된 직후 방금 생성 된 Excel 프로세스를 가져옵니다.
excel = new Microsoft.Office.Interop.Excel.Application();
var process = Process.GetProcessesByName("EXCEL").OrderByDescending(p => p.StartTime).First();
그런 다음 위의 모든 COM 정리를 완료하면 프로세스가 실행되고 있지 않은지 확인합니다. 아직 실행 중이면 죽여라!
if (!process.HasExited)
process.Kill();
¨ ° º¤ø „¸ Excel proc을 촬영하고 풍선 껌을 씹는다 ¸ „ø¤º ° ¨
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 개체 모델의 COM 개체에 의해 노출되는 이벤트를 구독하는 것입니다.
예를 들어 Application 클래스의 WorkbookOpen 이벤트를 구독합니다.
COM 이벤트에 대한 몇 가지 이론
COM 클래스는 콜백 인터페이스를 통해 이벤트 그룹을 노출합니다. 이벤트를 구독하기 위해 클라이언트 코드는 콜백 인터페이스를 구현하는 개체를 등록하기 만하면 COM 클래스가 특정 이벤트에 대한 응답으로 해당 메서드를 호출합니다. 콜백 인터페이스는 COM 인터페이스이므로 이벤트 처리기에 대해 수신하는 COM 개체의 참조 횟수를 줄이는 것이 구현 개체의 의무입니다.
Excel PIA가 COM 이벤트를 노출하는 방법
Excel PIA는 Excel Application 클래스의 COM 이벤트를 기존 .NET 이벤트로 노출합니다. 클라이언트 코드 가 .NET 이벤트 ( 'a'강조)를 구독 할 때마다 PIA 는 콜백 인터페이스를 구현하는 클래스 의 인스턴스를 만들고 이를 Excel에 등록합니다.
따라서 .NET 코드의 다양한 구독 요청에 대한 응답으로 많은 콜백 개체가 Excel에 등록됩니다. 이벤트 구독 당 하나의 콜백 개체.
이벤트 처리를위한 콜백 인터페이스는 PIA가 모든 .NET 이벤트 구독 요청에 대해 모든 인터페이스 이벤트를 구독해야 함을 의미합니다. 골라서 선택할 수 없습니다. 이벤트 콜백을 수신하면 콜백 객체는 연결된 .NET 이벤트 핸들러가 현재 이벤트에 관심이 있는지 확인한 다음 핸들러를 호출하거나 콜백을 자동으로 무시합니다.
COM 인스턴스 참조 수에 미치는 영향
이러한 모든 콜백 개체는 콜백 메서드 (자동으로 무시되는 메서드의 경우에도)에 대해 (매개 변수로) 수신하는 COM 개체의 참조 횟수를 감소시키지 않습니다. COM 개체를 해제하기 위해 CLR 가비지 수집기 에만 의존 합니다.
GC 실행은 비 결정적이므로 원하는 것보다 더 긴 기간 동안 Excel 프로세스를 보류하고 '메모리 누수'라는 인상을 줄 수 있습니다.
해결책
현재 유일한 해결책은 COM 클래스에 대한 PIA의 이벤트 공급자를 피하고 COM 개체를 결정적으로 해제하는 자체 이벤트 공급자를 작성하는 것입니다.
Application 클래스의 경우 AppEvents 인터페이스를 구현 한 다음 IConnectionPointContainer 인터페이스 를 사용하여 Excel에 구현을 등록하면 됩니다 . Application 클래스 (및 콜백 메커니즘을 사용하여 이벤트를 노출하는 모든 COM 개체)는 IConnectionPointContainer 인터페이스를 구현합니다.
다른 사람들이 지적했듯이이 KB 문서에 설명 된대로 사용하는 모든 Excel 개체에 대한 명시 적 참조를 만들고 해당 참조에 대해 Marshal.ReleaseComObject를 호출해야합니다 . 또한 try / finally를 사용하여 예외가 발생하더라도 ReleaseComObject가 항상 호출되도록해야합니다. 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);
}
또한 Excel을 닫으려면 Application 개체를 해제하기 전에 Application.Quit을 호출해야합니다.
보시다시피, 적당히 복잡한 작업을 시도하자마자 매우 다루기 어려워집니다. Excel 개체 모델의 몇 가지 간단한 조작 (통합 문서 열기, 범위에 쓰기, 통합 문서 저장 / 닫기 등)을 래핑하는 간단한 래퍼 클래스로 .NET 응용 프로그램을 성공적으로 개발했습니다. 래퍼 클래스는 IDisposable을 구현하고 사용하는 모든 개체에서 Marshal.ReleaseComObject를 신중하게 구현하며 Excel 개체를 앱의 나머지 부분에 공개적으로 노출하지 않습니다.
그러나이 접근 방식은 더 복잡한 요구 사항에 맞게 확장되지 않습니다.
이것은 .NET COM Interop의 큰 결점입니다. 더 복잡한 시나리오의 경우 VB6 또는 Office와 같은 out-proc COM 개체와의 모든 상호 작용을 위임 할 수있는 기타 관리되지 않는 언어로 ActiveX DLL을 작성하는 것을 진지하게 고려합니다. 그런 다음 .NET 응용 프로그램에서이 ActiveX DLL을 참조 할 수 있으며이 참조 하나만 해제하면되므로 작업이 훨씬 쉬워집니다.
위의 모든 항목이 작동하지 않으면 Excel에 시트를 닫을 시간을주세요.
app.workbooks.Close();
Thread.Sleep(500); // adjust, for me it works at around 300+
app.Quit();
...
FinalReleaseComObject(app);
Excel과 관련된 모든 개체를 해제했는지 확인하십시오!
여러 가지 방법을 시도하면서 몇 시간을 보냈습니다. 모두 훌륭한 아이디어이지만 마침내 내 실수를 발견했습니다. 모든 개체를 해제하지 않으면 위의 방법 중 어느 것도 내 경우에 도움이 될 수 없습니다 . 범위 1을 포함하여 모든 물체를 놓아야합니다!
Excel.Range rng = (Excel.Range)worksheet.Cells[1, 1];
worksheet.Paste(rng, false);
releaseObject(rng);
옵션은 여기에 있습니다 .
COM 개체 해제에 대한 훌륭한 기사는 2.5 MSDN ( COM 개체 해제 )입니다.
내가 옹호하는 방법은 지역 변수가 아닌 경우 Excel.Interop 참조를 null로 설정 한 다음 GC.Collect()
and 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();
}
}
이 단어는 기사에서 직접 나온 것입니다.
거의 모든 상황에서 RCW 참조를 무효화하고 가비지 수집을 강제하면 제대로 정리됩니다. GC.WaitForPendingFinalizers도 호출하면 가비지 컬렉션은 가능한 한 결정적입니다. 즉, WaitForPendingFinalizers에 대한 두 번째 호출에서 반환 될 때 개체가 정리 된시기를 정확히 알 수 있습니다. 대안으로 Marshal.ReleaseComObject를 사용할 수 있습니다. 그러나이 방법을 사용할 필요는 거의 없습니다.
두 점 규칙이 저에게 효과가 없었습니다. 제 경우에는 다음과 같이 리소스를 정리하는 방법을 만들었습니다.
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 개발 과 같은 다른 방법으로 전환하는 것 같습니다 .
내 솔루션
[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(); }
}
저는 현재 Office 자동화에 대해 작업 중이며 매번 저에게 적합한 솔루션을 발견했습니다. 간단하며 프로세스를 종료하지 않습니다.
현재 활성 프로세스를 단순히 반복하고 어떤 방식 으로든 열려있는 Excel 프로세스에 '액세스'하면 길잃은 Excel 인스턴스가 제거됩니다. 아래 코드는 이름이 'Excel'인 프로세스를 확인한 다음 프로세스의 MainWindowTitle 속성을 문자열에 씁니다. 프로세스와의이 '상호 작용'은 Windows가 Excel의 고정 된 인스턴스를 따라 잡아 중단하는 것처럼 보입니다.
개발중인 추가 기능이 종료되기 직전에 아래 메서드를 실행하여 언로드 이벤트를 발생시킵니다. 매번 중단 된 Excel 인스턴스를 제거합니다. 솔직히 왜 이것이 작동하는지 완전히 확신하지 못하지만 잘 작동하며 이중 점, 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;
}
}
}
}
그 중 일부는 프레임 워크가 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();
앞서 말했듯이 Excel 프로세스가 나타나거나 사라지는시기에 대한 세부 사항에주의를 기울이지 않는 경향이 있지만 일반적으로 저에게 효과적입니다. 또한 최소한의 시간 외에는 Excel 프로세스를 유지하는 것을 좋아하지 않지만 아마 편집증에 빠진 것 같습니다.
일부는 이미 작성했듯이 Excel (개체) 을 닫는 방법 만이 중요하지 않습니다 . 여는 방법 과 프로젝트 유형에 따라 중요합니다 .
WPF 응용 프로그램에서는 기본적으로 동일한 코드가 거의 문제없이 작동합니다.
동일한 Excel 파일이 다른 매개 변수 값에 대해 여러 번 처리되는 프로젝트가 있습니다 (예 : 일반 목록 내의 값을 기반으로 구문 분석).
모든 Excel 관련 함수를 기본 클래스에 넣고 파서를 하위 클래스에 넣습니다 (다른 파서가 공통 Excel 함수를 사용함). 일반 목록의 각 항목에 대해 Excel이 다시 열리고 닫히는 것을 원하지 않았으므로 기본 클래스에서 한 번만 열고 하위 클래스에서 닫았습니다. 코드를 데스크톱 애플리케이션으로 이동할 때 문제가 발생했습니다. 위에서 언급 한 많은 솔루션을 시도했습니다. GC.Collect()
이전에 이미 구현되었으며 제안 된대로 두 배가되었습니다.
그런 다음 Excel을 여는 코드를 하위 클래스로 이동하기로 결정했습니다. 한 번만 여는 대신 이제 새 개체 (기본 클래스)를 만들고 모든 항목에 대해 Excel을 열고 끝에서 닫습니다. 약간의 성능 저하가 있지만 여러 테스트를 기반으로 Excel 프로세스가 문제없이 (디버그 모드에서) 종료되므로 임시 파일도 제거됩니다. 업데이트를 받으면 테스트를 계속하고 더 작성하겠습니다.
결론은 다음과 같습니다. 특히 클래스가 많은 경우 초기화 코드도 확인해야합니다.
참고 URL : https://stackoverflow.com/questions/158706/how-do-i-properly-clean-up-excel-interop-objects
'Development Tip' 카테고리의 다른 글
입력 필드에서 속성을 읽을 때 HTML 인코딩이 손실 됨 (0) | 2020.09.29 |
---|---|
++로 증가하는 파이썬 정수 (0) | 2020.09.29 |
-performSelector : withObject : afterDelay :와 같이 지연 후 블록을 어떻게 트리거합니까? (0) | 2020.09.29 |
CGI (Common Gateway Interface) 란 무엇입니까? (0) | 2020.09.29 |
POST와 PUT HTTP REQUEST의 차이점은 무엇입니까? (0) | 2020.09.29 |