Excel interop 객체를 적절하게 정리하려면 어떻게 해야 하나요?
에서 Excel 을 사용하고 있습니다(C# ApplicationClass
마지막 절
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();
이런 것도 가능하지만Excel.exe
Excel을 닫아도 프로세스가 백그라운드에서 진행됩니다.응용 프로그램을 수동으로 닫은 후에만 해제됩니다.
무엇을 잘못하고 있는지, 또는 interop 오브젝트를 적절히 폐기할 수 있는 대체 방법이 있습니까?
응용 프로그램이 아직 COM 개체에 대한 참조를 유지하고 있기 때문에 Excel이 종료되지 않습니다.
COM 오브젝트를 변수에 할당하지 않고 적어도1개의 멤버를 호출하고 있는 것 같습니다.
저는 Excel App이었습니다.변수에 할당하지 않고 직접 사용한 워크시트 개체:
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의 답변을 매우 주의 깊게 읽어주셨으면 합니다.수년 전 이 답을 썼을 때 디버거가 가비지 컬렉터에 미치는 영향을 몰랐고 잘못된 결론을 도출했습니다.저는 역사를 위해 변함없는 답변을 유지하고 있지만, 이 링크를 읽고 "두 점"의 길을 가지 마십시오.의 가비지 수집에 대해IDisposable을 사용한 NET 및 Excel Interop 객체 정리
Excel 어플리케이션오브젝트를 깔끔하게 해방할 수 있지만 주의해야 합니다.
한 후 COM 오브젝트를 통해 으로 .Marshal.FinalReleaseComObject()
이론적으로는 맞지만 불행히도 실제로는 관리하기 매우 어렵습니다.한 '두 개의 점을 사용하거나 '두 개의 점'을 for each
참조되지 않은 COM 객체가 발생하여 행업할 위험이 있습니다.이 경우 코드에서 원인을 찾을 수 없습니다.모든 코드를 눈으로 확인하고 원인을 찾아야 합니다.대규모 프로젝트에서는 거의 불가능한 작업입니다.
좋은 소식은 실제로 사용하는 모든 COM 개체에 대해 명명된 변수 참조를 유지할 필요가 없다는 것입니다. 「」를 호출해 .GC.Collect()
다음에 또 한 번.GC.WaitForPendingFinalizers()
참조를 보유하지 않는 모든 (보통 마이너) 오브젝트를 해제하고 지정된 변수 참조를 보유하는 오브젝트를 명시적으로 해제합니다.
또한 중요한 역순으로 명명된 참조를 릴리스해야 합니다. 먼저 개체 범위를 지정하고 다음으로 워크시트, 워크북, 마지막으로 Excel 응용 프로그램 개체입니다.
들어 객체 변수 이 "로 되어 있다고 가정하면 "Range"는 "Range"로 되어 .xlRng
, " " " " "xlSheet
, " " " "xlBook
Excel이라는 이름의 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);
대부분의 코드 예에서는 에서 COM 오브젝트를 정리하는 방법을 볼 수 있습니다. NET,GC.Collect()
★★★★★★★★★★★★★★★★★」GC.WaitForPendingFinalizers()
TWICE를 하다
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
그러나 VSTO(Visual Studio Tools for Office)를 사용하는 경우 이외에는 이 작업이 필요하지 않습니다. VSTO(Visual Studio Tools for Office)는 최종화 대기열에서 개체의 전체 그래프를 승격시킵니다.이러한 개체는 다음 가비지 수집 때까지 릴리스되지 않습니다.단, VSTO를 사용하지 않을 경우 다음 주소로 전화할 수 있습니다.GC.Collect()
★★★★★★★★★★★★★★★★★」GC.WaitForPendingFinalizers()
아, 아, 아, 아, 아, 아, 아, 아, 아.
으로 콜을 발신하고 있는 것은 있습니다.GC.Collect()
(두 번 하는 것은 확실히 매우 고통스럽게 들립니다만) 솔직히 말해서 방법이 없습니다.에서는, 숨겨진 됩니다.따라서, 콜 으로는 릴리스 할 수.GC.Collect()
.
이것은 복잡한 주제이지만, 실제로는 이것뿐입니다.청소 절차용으로 이 템플릿을 설정하면 래퍼 등을 사용하지 않고 정상적으로 코드화할 수 있습니다. :- )
여기에 튜토리얼이 있습니다.
VB에 의한 Office 프로그램 자동화넷/COM 인터op
VB를 위해 쓰여져 있어요.NET, 그러나 그것을 미루지 마세요.원칙은 C#을 사용할 때와 똑같습니다.
서문: 제 답변에는 두 가지 해결책이 포함되어 있습니다. 그러니 읽을 때 주의하고 빠뜨리지 마세요.
Excel 인스턴스를 언로드하는 방법 및 조언은 다음과 같습니다.
보안관과 함께 모든 com 객체를 명시적으로 공개합니다.FinalReleaseComObject() ('com-objects')작성된 모든 COM 개체를 해제하려면 다음 2개의 점 규칙을 사용합니다.
Excel interop excel excel excel excel excel excel excel excel excel excel excel excel excel excel excel excel excel excel excel excel?GC를 호출합니다.Collect() 및 GC.Wait For Pending Finalizers()를 사용하여 CLR을 사용하지 않는 com-objects*로 만듭니다(실제로 동작합니다.자세한 내용은 두 번째 솔루션을 참조하십시오).
com-server-application이 사용자의 답변을 기다리는 메시지 박스가 표시되는지 확인하면 (Excel이 닫히지 않도록 할 수 있을지 모르겠지만 몇 번 들은 적이 있습니다.
메인 Excel 창에 WM_CLOSE 메시지 보내기
별도의 AppDomain에서 Excel과 연동되는 기능을 실행합니다.AppDomain이 언로드되면 Excel 인스턴스가 닫힐 것이라고 믿는 사람도 있습니다.
엑셀 인터로핑 코드가 시작된 후 인스턴스화된 엑셀 인스턴스를 모두 삭제합니다.
하지만! 이 모든 옵션이 도움이 되지 않거나 적절하지 않을 때도 있습니다!
예를 들어 어제 제가 알고 있는 기능 중 하나(엑셀과 연동)는 기능이 종료된 후에도 Excel이 계속 실행되고 있습니다.다 해봤어요!나는 전체 기능을 10번 꼼꼼히 체크하고 마샬을 추가했다.모든 것을 위한 Final Release Com Object()!저도 GC 했어요.Collect() 및 GC.Wait For Pending Finalizers().숨겨진 메시지 상자를 확인했습니다.메인 엑셀창에 WM_CLOSE 메시지를 보내려고 했습니다.다른 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();
}
보시는 바와 같이 Try-Parse 패턴에 따라 두 가지 방법을 제공했습니다(여기서는 적절하다고 생각합니다). 한 가지 방법은 프로세스를 종료할 수 없는 경우 예외를 발생시키지 않으며(예를 들어 프로세스가 존재하지 않는 경우), 다른 방법은 프로세스를 종료하지 않은 경우 예외를 발생시킵니다.이 코드의 유일한 취약점은 보안 권한입니다.이론적으로는 사용자에게 프로세스를 종료할 수 있는 권한이 없을 수 있지만 전체 사례의 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);
엑셀 종료!:)
자, 두 번째 해결책으로 돌아가자, 게시물 처음에 약속했던 대로.두 번째 해결책은 GC를 호출하는 것입니다.Collect() 및 GC.Wait For Pending Finalizers().네, 실제로 작동하지만 여기서 조심해야 해요!
GC를 사용하다Collect()는 Collect()에 해당합니다.그러나 COM 오브젝트에 대한 참조가 아직 남아 있으면 도움이 되지 않습니다.GC를 사용합니다.Collect()는 Collect()에 해당합니다.프로젝트를 디버깅모드로 실행하고 있습니다.디버깅 모드에서는 실제로 참조되지 않는 오브젝트는 메서드가 종료될 때까지 가비지 수집되지 않습니다.
「GC」입니다.Collect()는 GC입니다.Wait For Pending Finalizers()는 보류 중인 최종 사용자입니다.음음음음음음음
1) Release 모드로 프로젝트를 실행하여 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 작업 링크
이 문제를 해결하기 위해 잠시 시간을 보냈는데, 당시 Xtreme VBTalk가 가장 적극적이고 응답성이 뛰어났습니다.이 링크는 원래 게시물인 Excel Interop 프로세스를 깔끔하게 종료합니다.어플리케이션이 크래쉬 해도 마찬가지입니다.투고의 개요와 이 투고에 카피된 코드를 이하에 나타냅니다.
- 를 종료합니다.
Application.Quit()
★★★★★★★★★★★★★★★★★」Process.Kill()
엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀 엑셀. - 해결책은 Windows 작업오브젝트를 통해 Windows 32 콜을 사용하여 프로세스를 정리하는 것입니다.메인 어플리케이션이 종료되면 관련 프로세스(Excel)도 종료됩니다.
OS가 실제로 청소를 하고 있기 때문에 클린 솔루션이라고 생각했습니다.엑셀 프로세스만 등록하면 됩니다.
Windows 작업 코드
Win32 API Calls를 랩하여 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에 의해 다음과 같이 정의됩니다.
작업의 마지막 핸들이 닫힐 때 작업과 관련된 모든 프로세스가 종료됩니다.
프로세스 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로 설정하는 것이 중요하다는 것을 배웠습니다.여기에는 셀, 시트 및 모든 것이 포함됩니다.
첫 번째 - 전화할 필요가 없습니다.Marshal.ReleaseComObject(...)
★★★★★★★★★★★★★★★★★」Marshal.FinalReleaseComObject(...)
★★★★★★★★★★★★★★★★★★★★★★★★★★★이것은 혼란스러운 안티 패턴이지만, Microsoft 의 정보를 포함한 이것에 관한 정보는, 에서 수동으로 COM 레퍼런스를 릴리스 할 필요가 있는 것을 나타냅니다.NET ★★★★★★★★★★★★★★★★★★★★★★★★」사실은 입니다.NET 런타임 및 가비지 컬렉터는 COM 참조를 올바르게 추적하고 정리합니다.이것은, 코드의 상부에 있는 「while (...)」루프 전체를 삭제할 수 있는 것을 의미합니다.
다음으로 프로세스가 종료되었을 때 프로세스 외 COM 오브젝트에 대한 COM 참조를 확실하게 정리하고(Excel 프로세스가 종료되도록), 가비지 콜렉터를 실행할 필요가 있습니다.은 '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', '아까', 하면 됩니다.GC.Collect()
★★★★★★★★★★★★★★★★★」GC.WaitForPendingFinalizers()
·이것을 2회 호출하는 것은 안전하며, 사이클도 확실히 청소할 수 있습니다(필요한지는 모르겠지만, 예를 들어 주시면 감사하겠습니다).
셋째, 디버거에서 실행할 때 로컬 참조는 메서드가 끝날 때까지 인위적으로 유지됩니다(로컬 변수 검사가 작동하도록). ★★★★★★★★★★★★★★★★★.GC.Collect()
은 오브젝트 이지 않습니다.rng.Cells
같은 방법으로.COM interop을 실행하는 코드를 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가 위험하다고 간주됨과 동시에 이전 테스트를 혼란스럽게 하는 디버거에서 계속 유지되는 참조의 문제를 발견했습니다.
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이 필요한 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 프로그램이 열립니다.엑셀 프로그램은 직접 제어할 수 없기 때문에 링크를 해제하기 전에 해당 엑셀 프로그램을 종료하도록 명령해야 합니다.따라서 링크가 해제되어도 열린 상태로 유지됩니다!
여러분 프로그래밍 잘했어요~
일반적인 개발자들이여, 귀사의 솔루션 중 어느 것도 제게 효과가 없었기 때문에 새로운 기술을 도입하기로 결정했습니다.
먼저 "우리의 목표는 무엇입니까?" => "작업 관리자 작업 후 Excel 객체를 표시하지 않음"을 지정합니다.
OK. 도전을 하고 파기하는 것을 시작하지만 병렬로 실행되고 있는 다른 인스턴스 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을 올바르게 종료하기 위해서는 다음 3가지 주요 사항만 있습니다.
1: 있지 있어야 ). 1: Excel 으로 설정합니다.로 설정합니다).null
)
:콜 2: »GC.Collect()
Excel은 닫거나 가 3으로 .Quit
Excel ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★Quit
사용자가 프로그램을 닫으려고 한 것처럼 기능하며 Excel이 표시되지 않더라도 저장되지 않은 변경사항이 있는 경우 확인 대화 상자가 나타납니다.엑셀을 사용하다)
1은 2보다 먼저 발생해야 하지만 3은 언제든지 발생할 수 있습니다.
이를 구현하기 위한 한 가지 방법은 인터op Excel 객체를 사용자 자신의 클래스로 랩핑하고 컨스트럭터에서 인터op 인스턴스를 만들고 Dispose를 사용하여 IDisposable을 구현하는 것입니다.
if (!mDisposed) {
mExcel = null;
GC.Collect();
mDisposed = true;
}
그것은 당신의 프로그램 측면에서 탁월함을 없앨 것입니다. Excel에 의한 )Quit
프로세스가 없어집니다. 종료된 , 이는 프그그 the the if, 음 if if if if if if, 음 if if the the the the if the the the the the the the the .GC.Collect()
discl.discl.discl을 클릭합니다.
GC.WaitForPendingFinalizers()
을 GC.Collect()
하지만 Excel 프로세스를 완전히 없앨 필요는 없습니다.)
이것은 몇 년 동안 문제없이 나에게 효과가 있었다.다만, 이 조작이 동작하는 동안은, 실제로는 정상적으로 닫아야만 동작하는 것에 주의해 주세요.Excel이 삭제되기 전에 프로그램을 중단하면(보통 프로그램 디버깅 중에 "Stop"을 눌러) Excel.exe 프로세스가 누적됩니다.
나는 전통적으로 VVS의 답변에 있는 조언을 따라왔다.그러나 최신 옵션으로 이 답변을 최신 상태로 유지하기 위해 향후 모든 프로젝트에서는 "NetOffice" 라이브러리를 사용할 예정입니다.
NetOffice는 Office PIA를 완전히 대체하는 제품으로 버전에 구애받지 않습니다.이것은 에서 Microsoft Office와 함께 작업할 때 자주 발생하는 정리를 처리할 수 있는 Managed COM 래퍼 모음입니다.그물.
주요 기능은 다음과 같습니다.
- 대부분 버전에 의존하지 않음(및 버전에 의존한 기능이 문서화되어 있음)
- 의존관계 없음
- PIA 없음
- 등록 없음
- VSTO 없음
나는 그 프로젝트에 전혀 관여하지 않았다. 단지 골칫거리가 완전히 줄어든 것에 진심으로 감사할 뿐이다.
게다가 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
여기서 허용되는 답변은 맞지만 "두 개의 점" 참조뿐만 아니라 인덱스를 통해 검색되는 개체도 피해야 합니다.또한 이러한 오브젝트를 정리하는 프로그램이 완료될 때까지 기다릴 필요가 없습니다.가능한 한 오브젝트를 정리하는 즉시 오브젝트를 정리하는 기능을 작성하는 것이 가장 좋습니다.을 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]
(해제할 필요가 없는 열거를 참조하는 두 개의 점이 아니라 실제로 해제해야 하는 보더 오브젝트이기 때문입니다.
이러한 기능은 표준 어플리케이션에서는 필요하지 않습니다.표준 어플리케이션에서는 필요 없습니다.표준 어플리케이션에서는 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 어플리케이션 클래스가 인스턴스화된 직후에 저는 방금 만든 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 개체 모델의 COM 개체에 의해 노출되는 이벤트에 가입하는 것입니다.
예를 들어 응용 프로그램 클래스의 WorkbookOpen 이벤트에 가입하는 것입니다.
COM 이벤트에 관한 몇 가지 이론
COM 클래스는 콜백인터페이스를 통해 이벤트 그룹을 표시합니다.이벤트에 가입하기 위해 클라이언트코드는 단순히 콜백인터페이스를 실장하는 오브젝트를 등록하기만 하면 COM 클래스는 특정 이벤트에 응답하여 메서드를 호출합니다.콜백 인터페이스는 COM 인터페이스이기 때문에 이벤트핸들러에 대해서 (파라미터로서) 수신하는 COM 객체의 참조 카운트를 줄이는 것은 실장 오브젝트의 의무입니다.
Excel PIA가 COM 이벤트를 표시하는 방법
Excel PIA는 Excel 어플리케이션클래스의 COM 이벤트를 종래와 같이 공개합니다.NET 이벤트클라이언트 코드가 에 서브스크라이브 될 때마다NET 이벤트('a'의 강조), PIA는 콜백인터페이스를 구현하는 클래스의 인스턴스를 생성하여 Excel에 등록합니다.
따라서로부터의 다른 서브스크립션 요구에 응답하여 다수의 콜백오브젝트가 Excel에 등록됩니다.NET 코드이벤트 서브스크립션당 1개의 콜백오브젝트
이벤트 처리용 콜백인터페이스는 PIA가 모든 의 인터페이스이벤트에 가입해야 함을 의미합니다.NET 이벤트 서브스크립션 요구고르고 고를 수 없다.이벤트 콜백을 수신하면 Call-back 객체는 관련지어져 있는지 여부를 확인합니다.NET 이벤트핸들러는 현재 이벤트에 관심이 있는지 여부에 관계없이 핸들러를 호출하거나 콜백을 자동으로 무시합니다.
COM 인스턴스 참조 수에 대한 영향
이러한 모든 Call-back 객체는 콜백 방식(사일런트하게 무시되는 방식이라도)으로 수신되는 COM 객체의 참조 카운트를 감소시키지 않습니다.COM 오브젝트를 해방하기 위해 CLR 가비지 컬렉터에만 의존합니다.
GC 실행은 결정적이지 않기 때문에 Excel 프로세스를 원하는 시간보다 더 오래 지연시키고 '메모리 누출'의 느낌을 줄 수 있습니다.
솔루션
현시점에서의 유일한 해결책은 COM 클래스에 대한 PIA의 이벤트 공급자를 피하고 COM 개체를 결정적으로 릴리스하는 자체 이벤트 공급자를 작성하는 것입니다.
Application 클래스의 경우 AppEvents 인터페이스를 구현한 후 IConnectionPointContainer 인터페이스를 사용하여 Excel에 구현을 등록하면 됩니다.응용 프로그램클래스(및 콜백메커니즘을 사용하여 이벤트를 노출하는 모든 COM 객체)는 IConnectionPointContainer 인터페이스를 구현합니다.
COM 오브젝트의 릴리스에 관한 중요한 기사는 2.5 릴리스 COM 오브젝트(MSDN)입니다.
제가 추천하고 싶은 방법은 당신의 엑셀을 무효화하는 것입니다. interop 하고 interop 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에도 전화하면.Wait For Pending Finalizers, 가비지 컬렉션은 최대한 확정적입니다.즉, 두 번째 호출에서 Wait For Pending Finalizers로 돌아오면 오브젝트가 정리되었을 때 정확히 알 수 있습니다.대신 마샬을 사용할 수도 있습니다.ReleaseComObject.단, 이 방법을 사용할 필요는 없습니다.
다른 사람들이 지적한 바와 같이 사용하는 모든 Excel 객체에 대해 명확한 참조를 작성하여 Marshal에 문의해야 합니다.ReleaseComObject를 참조해 주세요(이 KB 문서에 기재되어 있습니다).또한 예외가 발생하더라도 ReleaseComObject가 항상 호출되도록 하려면 try/finally를 사용해야 합니다.즉, 다음과 같은 경우:
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을 닫으려면 응용 프로그램 개체를 해제하기 전에 종료하십시오.
보시다시피, 이 작업은 어느 정도 복잡한 작업이라도 시도하면 순식간에 매우 다루기 어려워집니다.저는 성공적으로 개발되었습니다.Excel 객체 모델의 간단한 조작을 랩하는 간단한 래퍼 클래스가 있는 NET 응용 프로그램(워크북 열기, 범위에 쓰기, 워크북 저장/닫기 등)래퍼 클래스는 IDisposable을 구현하고 신중하게 Marshal을 구현합니다.ReleaseComObject는 사용하는 모든 오브젝트에 대해 Excel 오브젝트를 공개하지 않습니다.
그러나 이 접근 방식은 더 복잡한 요구사항에 맞게 확장되지 않습니다.
이것은 의 큰 결점입니다.NET COM 상호 운용보다 복잡한 시나리오에서는 ActiveX DLL을 VB6 또는 Office 등의 Out-Proc COM 오브젝트와의 모든 상호작용을 위임할 수 있는 기타 관리대상외 언어로 기술하는 것을 진지하게 검토하겠습니다.그러면 에서 이 ActiveX DLL을 참조할 수 있습니다.NET 어플리케이션에서는 이 레퍼런스 1개만 공개하면 되기 때문에 작업이 훨씬 쉬워집니다.
위의 모든 작업이 작동하지 않을 경우 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);
옵션은 여기에 함께 있습니다.
나는 두 개의 점 규칙이 통하지 않았다.내 경우 리소스를 정리하는 방법을 다음과 같이 만들었습니다.
private static void Clean()
{
workBook.Close();
Marshall.ReleaseComObject(workBook);
excel.Quit();
CG.Collect();
CG.WaitForPendingFinalizers();
}
나의 솔루션
[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();
}
Word/Excel interop 어플리케이션을 사용할 때는 매우 주의해야 합니다.모든 솔루션을 사용해 본 결과, 서버상에 아직 많은 「WinWord」프로세스가 열려 있었습니다(2000명 이상의 유저).
몇 시간 동안 문제를 해결한 후, 만약 내가 몇 개 이상의 문서를 열었을 때Word.ApplicationClass.Document.Open()
동시에 다른 스레드에서 IIS 워커 프로세스(w3wp.exe)가 크래시되어 모든 WinWord 프로세스가 열립니다.
따라서 이 문제에 대한 절대적인 해결책은 없지만 Office Open XML 개발 등의 다른 방법으로 전환하는 것은 있을 수 없습니다.
받아들여진 답변은 나에게 효과가 없었다.파괴자의 다음 코드가 작동했습니다.
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 Automation에 종사하고 있으며, 매번 유효한 솔루션을 우연히 발견했습니다.단순하고 프로세스를 종료하지 않습니다.
현재의 액티브한 프로세스를 루프 하는 것만으로, 오픈 엑셀 프로세스에 「액세스」하는 것만으로, 엑셀의 행잉 인스턴스가 없어지는 것 같습니다.아래 코드는 단순히 이름이 'Excel'인 프로세스를 확인한 다음 프로세스의 MainWindowTitle 속성을 문자열에 씁니다.프로세스와의 '상호작용'으로 인해 Windows가 동결된 Excel 인스턴스를 따라잡고 중단하는 것처럼 보입니다.
개발 중인 add-in이 언로드 이벤트를 발생시키기 때문에 바로 직전에 아래 방법을 실행합니다.매번 Excel의 행인스턴스를 삭제합니다.솔직히 왜 이게 효과가 있는지 잘 모르겠습니다만, 저는 잘 작동하며, 엑셀 어플리케이션의 끝에 두 개의 점 걱정 없이 배치할 수 있습니다, 보안관님.ReleaseComObject 또는 Killing 프로세스.왜 이것이 효과적인지에 대한 어떠한 제안도 매우 흥미로울 것입니다.
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(개체)을 닫는 방법뿐만 아니라 여는 방법이나 프로젝트의 종류도 중요합니다.
WPF 어플리케이션에서는 기본적으로 같은 코드가 문제가 없거나 거의 없는 상태로 동작합니다.
동일한 Excel 파일이 다른 파라미터 값에 대해 여러 번 처리되는 프로젝트가 있습니다.예를 들어, 범용 리스트 내의 값에 근거해 해석하는 것입니다.
엑셀 관련 함수를 모두 베이스 클래스에 넣고 파서는 서브 클래스에 넣습니다(파서마다 공통 엑셀 함수를 사용).범용 리스트의 항목마다 Excel이 다시 열리고 닫히는 것을 원치 않았기 때문에 기본 클래스에서 한 번만 열고 서브 클래스에서 닫았습니다.코드를 데스크톱 어플리케이션으로 옮길 때 문제가 발생하였습니다.저는 위에서 언급한 많은 해결책을 시도했습니다. GC.Collect()
이미 구현되어 있습니다.두 번째
그래서 엑셀 오픈 코드를 서브클래스로 이동하기로 했습니다.한 번만 여는 것이 아니라 새로운 오브젝트(베이스 클래스)를 생성하여 모든 아이템에 대해 Excel을 열고 마지막에 닫습니다.성능 저하가 있지만 몇 가지 테스트를 통해 Excel 프로세스가 문제 없이 종료되므로(디버깅모드), 임시 파일도 삭제됩니다.테스트를 계속하고 업데이트가 있으면 몇 가지 더 쓰도록 하겠습니다.
결론은 초기화 코드도 체크해야 한다는 것입니다.특히 클래스가 많은 경우 등입니다.
언급URL : https://stackoverflow.com/questions/158706/how-do-i-properly-clean-up-excel-interop-objects
'programing' 카테고리의 다른 글
MongoDB 범위 페이지 설정 (0) | 2023.04.02 |
---|---|
간격 데이터 유형에서 총 초수 추출 (0) | 2023.04.02 |
React에서 Link 컴포넌트를 사용할 때 jsx-a11y/anchor-is-valid를 수정하려면 어떻게 해야 합니까? (0) | 2023.04.02 |
mongodb는 실행 중입니까? (0) | 2023.03.28 |
React Native Flat List의 List Header Component를 스틱으로 만들려면 어떻게 해야 합니까? (0) | 2023.03.28 |