SimpleIsBest.NET

유경상의 닷넷 블로그

Excel.exe 죽이기

by 블로그쥔장 | 작성일자: 2005-05-31 오후 1:33:00
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
많은 경우, 업무 프로그램에서 엑셀(Excel)과의 연동은 필수 불가결한 것이라 할 수 있다. 엑셀의 매력은 비정형적인 데이터를 손쉽게 다룰 수 있다는 것이다. 그래서 많은 고객들이 업무용 프로그램의 요구사항을 간단히 이렇게 명세하곤 한다.

"뭐... 엑셀 처럼 해주세요..."

대략 뷁스럽기 이를데 없는 경우다. 이야기가 좀 옆으로 샜지만 닷넷과 엑셀의 연동에서 골치거리 중 하나가 이놈의 Excel.exe 프로세스가 잘 죽지 않는다는 것이다. Excel.exe 프로세스를 확실하게 죽이는 방법에 대해 썰을 좀 풀어볼까 한다. (죽인다는 표현이 좀 쌀벌하다...)

다음은 엑셀을 액세스하는 전형적인 간단한 C# 코드이다. 뭐 설명할 필요도 없이 Sheet1의 A1 셀에 Hello Excel Interop 이란 문자열을 박아 넣는 코드로서 매우 잘 작동한다.

static void ExcelWillNotDie()
{
    Excel.ApplicationClass app = new Excel.ApplicationClass();
    Excel.Workbook workbook = app.Workbooks.Add("");
    Excel.Worksheet sheet = (Excel.Worksheet)workbook.Sheets["Sheet1"];
    Excel.Range range = (Excel.Range)sheet.Cells.get_Item("1", "A");
    range.Value2 = "Hello Excel Interop";

    // Excel 파일 저장 코드... (생략)

    app.DisplayAlerts = false; // 저장할 것인가 확인하지 않도록 설정
    app.Quit();
}

위 코드의 문제는 작업을 모두 마쳤음에도 불구하 Excel.exe 프로세스가 죽지 않는다는 것이다. 작업 관리자로 현재 수행중인 프로세스를 살펴보면 떡하니 Excel.exe가 버티고 있다. Quit() 메쏘드까지 호출해 줬는데도 말이다!

비슷한 코드를 VB 6.0이나 VBS(Visual Basic Script)로 수행해보면 메쏘드를 종료하는 시점에 Excel.exe 프로세스는 제깍 제깍 죽는다. 그런데 동등한 코드를 닷넷 환경에서 수행시키면 메쏘드 호출이 끝났어도 죽지 않는다. 원인은 COM 객체와 닷넷의 가비지 컬렉션이 서로 궁합이 잘 맞지 않는다는 것이며 Excel 프로세스가 종료하기 위해서는 생성된 모든 엑셀 COM 객체들(Application, Workbook Sheet 등등)이 해제(release)되어야 하기 때문이다.

닷넷에서 엑셀 객체들과 같은 COM 객체를 액세스 할 때는 항상 RCW(Runtime Callable Wrapper)를 통해서 액세스를 한다는 것은 알고 있을 것이다. 이 RCW는 Finalizer를 정의하고 있고 이 Finalizer가 COM 객체를 해제하도록 구성되어 있다. 따라서 엑셀 프로세스가 종료되는 시점은 다음(next) 가비지 컬렉션이 수행된 후가 될 것이다. 이것도 좀 문제가 있는 것이... 비록 가비지 컬렉션이 수행되더라도 곧바로 RCW의 Finalizer가 호출된다는 보장이 없다. Finalizer 메쏘드는 별도의 Finalizer 만을 위한 쓰레드가 호출하도록 되어 있기 때문에 언젠가는 호출되겠지만 그 언젠가가 당췌 언제인지는 모르는 것이다.

Dispose 패턴이 그러하듯이 명시적으로 엑셀 객체를 해제해 주면 되지 않을까? 그렇다. 명시적으로 엑셀 객체들을 해제 해주면 엑셀 프로세스는 곧바로 종료하게 된다. 요것이 포인트가 되겠다. COM 객체를 다룰 때는 두 가지만 알면 된다. 북치기 박치기~~ ^__^

웃자고 해본 소리고... COM 객체를 닷넷에서 다루고자 한다면 참조한 모든 COM 객체는 죄다 명시적으로 해제를 하면 된다. COM 객체를 해제하는 구체적인 방법은 System.Runtime.InteropServices 네임스페이스의 Marshal 클래스가 제공하는 ReleaseComObject 메쏘드를 호출하면 된다.

위 코드를 어떻게 수정하면 될까? 뇌리를 스치는 것은 app, workbook, sheet, range 변수에 대해 ReleaseComObject 를 호출하면 되지 않을까? 자... 그렇다면 위 메쏘드의 마지막에 다음 코드를 추가하고 Excel.exe 가 종료되나 살펴보자.

static void ExcelWillNotDie()
{
    // 기존 코드 동일 (생략)

    Marshal.ReleaseComObject(range);
    Marshal.ReleaseComObject(sheet);
    Marshal.ReleaseComObject(workbook);
    Marshal.ReleaseComObject(app);
}

테스트를 수행해 보면 예상과 달리 Excel.exe 프로세스는 종료되지 않는다. 왜일까? 필자가 붉은 색으로 강조하고 침 튀기며 외쳐댄 "참조한 COM 객체를 명시적으로 해제"를 수행 했는데 왜 Excel.exe 프로세스가 종료되지 않았을까? 둘 중 하나다. 필자가 구라를 쳤던가 아니면 코드에서 해제되지 않은 COM 객체 참조가 있던지.

당근 필자가 구라를 친 것은 아니다. 해제되지 않은 COM 객체 참조가 있기 때문이다. 메쏘드의 두 번째 라인을 잘 살펴보자. Application 객체의 Workbooks 속성의 Add 메쏘드를 호출하고 있다. 이 코드를 좀 풀어서 써 보자면 다음과 같다.

// 원래 코드
Excel.WorkSheet sheet = (Excel.WorkSheet)app.Workbooks.Add("");

// 풀어쓴 코드
Excel.WorkBooks books = app.Workbooks;
Excel.WorkSheet sheet = (Excel.WorkSheet)books.Add("");

이제 감이 오는가? Application 객체의 Workbooks 속성이 또 다른 Workbooks 라는 COM 컬렉션 객체를 반환하고 있고, 이 COM 객체에 대한 참조는 암시적으로 이루어지고 있음을 알아야 한다.

그렇다면 명시적으로 COM 객체를 해제해 주려면 엑셀에 접근하는 코드 자체를 다음과 같이 수정해주어야 한다.

static void ExcelWillDie()
{
    Excel.ApplicationClass app = new Excel.ApplicationClass();
    Excel.Workbooks workbooks = app.Workbooks;
    Excel.Workbook workbook = workbooks.Add("");
    Excel.Sheets sheets = workbook.Worksheets;
    Excel.Worksheet sheet = (Excel.Worksheet)sheets["Sheet1"];
    Excel.Range cells = (Excel.Range)sheet.Cells;
    Excel.Range range = (Excel.Range)cells.get_Item("1", "A");

    range.Value2 = "Hello Excel Interop";

    // Excel 파일 저장 코드... (생략)
    app.DisplayAlerts = false; // 저장할 것인가 확인하지 않도록 설정

    app.Quit();

    Marshal.ReleaseComObject(range);
    Marshal.ReleaseComObject(cells);
    Marshal.ReleaseComObject(sheet);
    Marshal.ReleaseComObject(sheets);
    Marshal.ReleaseComObject(workbook);
    Marshal.ReleaseComObject(workbooks);
    Marshal.ReleaseComObject(app);
}

이 코드는 확실이 메쏘드 종료와 더불어 Excel.exe 프로세스를 종료시켜 준다.

VB 6.0이나 VBS 등으로 동등한 코드를 만들면 위와 같이 명시적인 해제가 없더라도 엑셀 프로세스는 잘 죽곤 한다. 왜 일까? 그건 VB 6.0 이나 VBS의 런타임이 메쏘드의 scope를 벗어나면 메쏘드에서 사용된 로컬 변수와 암시적으로 사용된 변수(app.WorkBooks.Add 의 경우 처럼)에 대해 해제 코드를 모두 호출해 주기 때문이다.

닷넷은 왜 VB 6.0 처럼 하지 못하는 것일까? 닷넷 개발팀이 닭대가리들이라서? 닷넷 개발팀이 닭대가리라면 나는 접시물에 코박고 죽어야할 운명이다... 근본적인 원인은 닷넷의 가비지 컬렉션이 그 이유라고 보면 되겠다. 가비지 컬렉션은 많은 장점을 가지고 있지만 COM 객체 접근이나 데이터베이스 연결(connection)과 같이 unmanaged 자원에 접근하는 경우 역 기능을 가지곤 한다.

보다 많은 코드를 작성해야 하고 ReleaseComObject 도 호출하는 위와 같은 방법 말고 Excel.exe를 우아하게 종료하는 방법은 없을까? 우아하지 않은 방법은 있다. 강제로 가비지 컬렉션을 수행하고 RCW의 Finalizer 메쏘드가 호출되도록 하면 Excel.exe가 종료되긴 한다.

GC.Collect();
GC.WaitForPendingFinalizer();

위와 같은 방법은 벼룩 잡을려고 초가삼간 태우는 격이 되겠다. 그 누구도 GC.Collect 호출을 권장하지 않는다. 가비지 컬렉션은 닷넷 CLR이 판단하여 스스로 수행하도록 하는 것이 가장 좋다. 그래도 위와 같이 GC.Collect와 GC.WaitForPendingFinalizer 호출을 수행하여 Excel 연동의 결과로 수행된 Excel.exe 프로세스를 종료시키는 방법이 있다는 것만 알아 두자.

마지막으로 당부할 것은 COM 객체를 반복문 안에서 참조하는 경우를 조심해야 한다. 반복문(for, while 등) 내에서 COM 객체의 참조를 얻었다면 해제 역시 반복문 내에서 이루어져야 한다는 점이다.

for(int i=1; i < 10; i++) {
   Excel.Range r = (Excel.Range)cells.get_Item(i, 2):
   r.Value2 = i;
   Marshal.ReleaseComObject(r);
}

왜 반복문 안에서 얻은 COM 참조를 반복문 내에서 해제해야 하는 가에 대해서는 상세히 설명하지 않겠다. 잘 생각해 보기를 바란다.



Comments (read-only)
#re: Excel.exe 죽이기 / 강희기 / 2007-04-09 오전 8:34:00
옛날 포스트지만 굿굿... 찬찬히 하나하나 읽어 가면서 ~~
역시 대단하셔요~~
#re: Excel.exe 죽이기 / 오잉? / 2007-07-21 오전 3:28:00
도대체 이런것 어떻게 알았죠?

Quit로 도대체 죽지 않는것 같은 녀석을.. 덕분에 시간을 절약 했습니다.

감사합니다.


#re: Excel.exe 죽이기 / 짱짱이 / 2007-08-13 오후 4:55:00
작년에 이것때문에 고생했었는데..명쾌한 해결법..감사합니다~^^
#re: Excel.exe 죽이기 / 한성웅 / 2008-02-11 오후 5:05:00
한가지 문제가 있습니다..

잘 되다가 안되는 경우가 있네요

// 실패
range2.Font.Bold
range2.Interior.ColorIndex
range2.Font.Size

// 성공
range2.NumberFormatLocal = "@";
range2.RowHeight = 14;
#re: Excel.exe 죽이기 / 조승태 / 2008-03-05 오후 4:25:00
흠....
이런 방법이 있다는 것은 다행이지만,,
코드 작성하기가 상당히 귀찮겠군요..
GC.Collect()를 강제 호출하면 왜 좋지 않은지 궁금해 집니다.
좋을 글, 감사합니다.
#re: Excel.exe 죽이기 / 블로그쥔장 / 2008-03-05 오후 6:42:00
가비지 컬렉션은 CLR에 의해 가장 필요한 시점에서 수행되도록 하는 것이
메모리 관리와 성능 면에서 가장 좋으며 권장되는 방법입니다.
명시적으로 GC.Collect를 호출하면 CLR가 가비지 컬렉션을 수행하는 시점을 흩어 뜨려서
다음 가비지 컬렉션 수행 시점 등의 혼선이 오게 됩니다.
따라서 전체적인 메모리 관리 효율이 떨어지며 성능 역시 저하되는 현상이 발생하곤 합니다.
가급적 GC.Collect 호출은 수행하지 않는 것이 가장 좋지만 매우 간단한 어플리케이션
(성능이나 효율이 중요하지 않은)에서는 종종 호출하기도 합니다만 그래도 좋지 않은 코딩 습관으로 알고 있습니다.
#re: Excel.exe 죽이기 / park / 2008-03-30 오후 10:22:00
Excel.ApplicationClass app = new Excel.ApplicationClass();
이부분에서 권한 에러 나는데요 정확히 어떻게 해야 되나요?
#re: Excel.exe 죽이기 / 블로그쥔장 / 2008-03-31 오전 11:03:00
권한 에러라 함은 SecurityException을 말씀하시는건가요?
그렇다면 CAS 권한 문제입니다. 스마트 클라이언트를 사용하시거나
네트워크 드라이브에서 EXE를 구동하시는 경우에는 CAS 권한이 낮게 설정됩니다.
이 상황에서는 COM 객체 호출(Interop)에 대한 권한이 없기 때문에 권한 예외가 발생할 수
있습니다.
FullTrust 권한을 갖도록 설정을 조정하신 후에 다시 시도해 보시기 바랍니다.
CAS 권한에 대한 내용은 다음 URL을 참고 하십시요.

http://www.simpleisbest.net/archive/2006/04/28/612.aspx

도움이 되셨기를...
#re: Excel.exe 죽이기 / Yang / 2008-07-11 오후 3:08:00
잘 봤습니다.

해보니까 로컬에서 구동할때는 걍 팍팍 죽습니다.

그런데 iis6.0 인 웹서버에서 제가 구동 시킬때 웹서버 작업관리자를 열어두고 돌려보니까 웹서버에서는 엑셀프로세스가 계속 남더군요.

혹시 다르게 셋틷을 해야 하는지 아신다면 알려주세요`~ ㅠ_ㅠ
잘 부탁드립니다.
#re: Excel.exe 죽이기 / 블로그쥔장 / 2008-07-15 오후 7:43:00
제가 Windows 2003에서 테스트 할 때는 아무런 문제가 없었습니다만...
작업 프로세스(어플리케이션 풀)를 System 계정으로 사용해 보십시요.
혹 디버깅 모드에서 프로세스가 죽지 않는 경우도 있었습니다.
-_-;
#re: Excel.exe 죽이기 / LEE / 2008-08-13 오후 5:54:00
이 C# 코드를 어떤 파일에 작성해야하나요?
그리고 비쥬얼 베이직을 써서 작성 하면 되는건가요?
답변 좀 부탁드립니다..ㅜㅜ
#re: Excel.exe 죽이기 / 블로그쥔장 / 2008-08-14 오후 2:11:00
LEE//
블로그에서 다루는 내용은 프로그램적으로 Excel을 수행시키고 Excel 데이터시트를 액세스 한 후에
엑셀이 죽지 않는 현상을 다루는 내용입니다.
따라서 님과 같이 단순히 수동으로 액셀을 수행시킨 후에 액셀이 종료되지 않는 현상하고는
상관이 없으며 블로그에 나와 있는 코드도 무관합니다. -_-;
#re: Excel.exe 죽이기 / wlee / 2009-03-06 오전 10:48:00
EXCEL.EXE와 한창 전투중인데 좋은 정보 잘 얻어가게 되었네요
감사드립니다 ^^

질문 한가지 드리자면,
app.Quit() 후에 Marshal.ReleaseComObject() 코드를 수행하면 된다고 알려주셨는데요.
일단 프로그램적으로 Excel을 수행시킨 후, Excel데이터 시트에 값을 쓴 상태로 화면에 Excel 창을 띄웁니다.
여기서 만약 app.Quit() 코드를 수행하지 않고 Marshal.ReleaseComObject() 코드만 수행하고 메소드가 끝난 후에,

사용자가 원하는 순간에 화면에 띄워진 Excel 창을 종료시키게 되면,
그때도 EXCEL.EXE 프로세스가 종료되는지 궁금합니다.
#re: Excel.exe 죽이기 / wlee / 2009-03-06 오전 11:50:00
자문자답 같아서 댓글을 쓰지 않으려고 했습니다만.
[[[ 제가 말씀드린 조건에서도 잘 동작하는 것으로 관찰된다. ]]]
는 사실을 필요하신 다른 분들도 아시면 좋을 것 같아서 짧게 달아보았습니다.

(블로그쥔장님// 좋은 정보 주셔서 다시 한번 정말 감사드립니다.)
#re: Excel.exe 죽이기 / 남처리 / 2009-08-13 오후 1:49:00
엑셀이 안죽어서 난감해 하고 있었는데 정말 감사합니다.
(_ _) (_ _) 꾸벅 꾸벅
#re: Excel.exe 죽이기 / 안죽어요 ㅠ / 2009-09-23 오후 4:08:00
글을 참고 해서 아래 처럼 코딩 했는데 ㅠㅠ
엑셀프로 세스가 죽지 않네요.. 여기서 암시적 참조가 이루어 지는 놈이 어떤놈인지ㅠㅠ

Excel.ApplicationClass app = new Excel.ApplicationClass();
Excel.Workbooks workbooks = app.Workbooks;
Excel.Workbook workbook = null;
Excel.Sheets sheets = null;//workbook.Worksheets;
Excel.Worksheet sheet = null; //(Excel.Worksheet)sheets["Sheet1"];
Excel.Range cells = null;//(Excel.Range)sheet.Cells;
Excel.Range range = null;//(Excel.Range)cells.get_Item("1", "A");

if (System.IO.File.Exists(strname))
{
// 엑셀 실행후 -> Workbook에 담는다.
workbook = app.Workbooks.Open(strname,
Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlNoChange,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing);

sheets = workbook.Worksheets;
sheet = (Excel.Worksheet)sheets[1];
cells = (Excel.Range)sheet.Cells;


int intCurRows = ((Excel.Range)cells.CurrentRegion).Rows.Count;
int intCurCols = ((Excel.Range)cells.CurrentRegion).Columns.Count;


string strAvalue="";
string strBvalue="";
int intMergeCnt = 0;

int intGrdFixed = grdCurrency.Rows.Fixed;

for (int intRow = 1; intRow < intGrdFixed; intRow++)
{
for (int intCol = 1; intCol < intCurCols; intCol++)
{

strAvalue = ((Excel.Range)cells[intRow, intCol]).Value2 ==null ? "" : ((Excel.Range)cells[intRow, intCol]).Value2.ToString();
strBvalue = ((Excel.Range)cells[intRow, intCol + 1]).Value2 == null ? "" : ((Excel.Range)cells[intRow, intCol + 1]).Value2.ToString();

if (strAvalue == strBvalue)
{
intMergeCnt++;
}
else
{
if (intMergeCnt > 0)
{
for (int cLoop = intCol - intMergeCnt; cLoop <= intCol; cLoop++)
{
((Excel.Range)cells[intRow, cLoop]).Value2 = null;
}
range = cells.get_Range(cells[intRow, intCol -intMergeCnt], cells[intRow, intCol]);
range.Merge(Type.Missing);
range.Value2 = strAvalue;
intMergeCnt = 0;
}
}
}
}

app.DisplayAlerts = false; // 저장할 것인가 확인하지 않도록 설정
workbook.SaveAs(strname, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

app.Quit();


System.Runtime.InteropServices.Marshal.ReleaseComObject(range);
System.Runtime.InteropServices.Marshal.ReleaseComObject(cells);
System.Runtime.InteropServices.Marshal.ReleaseComObject(sheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
#re: Excel.exe 죽이기 / 자답!! / 2009-09-23 오후 8:54:00
"
마지막으로 당부할 것은 COM 객체를 반복문 안에서 참조하는 경우를 조심해야 한다. 반복문(for, while 등) 내에서 COM 객체의 참조를 얻었다면 해제 역시 반복문 내에서 이루어져야 한다는 점이다."

이걸 주의 깊게 안봤더니 ㅡㅡㅋ

For 문안에서 사용하고 바로 COM 객체를 명시적으로 해제 해죠야 하네요
#Excel.exe 절대 죽지 않습니다. / 철이베베 / 2010-03-05 오전 10:42:00
먼저 다음과 같이 해보면, 절대 죽지 않는걸 알 수 있습니다.
블로그쥔장님의 코드도 역시 안됩니다.
뭐 환경이 저는 2008서버에 IIS 7 입니다만, 아무런 상관이 없는줄 압니다.

Excel.Application excelApp = new Excel.Application();
Excel.Workbooks books = excelApp.Workbooks;
Excel.Workbook excelWorkbook = books.Add("");

Marshal.ReleaseComObject(excelWorkbook);
Marshal.ReleaseComObject(books);
Marshal.ReleaseComObject(excelApp);

그래서.. 왜 안죽는지 하나하나 Marshal.ReleaseComObject 를 통해서 죽여봤습니다.
books 와 excelApp 는 죽는걸 확인했습니다. 그런데 excelWorkbook이 안죽어서 EXCEL.exe가 죽지 않더군요.


excelWorkbook <-- 이거 도데체 왜 안죽는지..

저와 비슷한 경험에 해결방법을 아시는분은 좀 답변좀 주세요....
#Excel.exe 프로세스 강제로 죽이기 / 철이베베 / 2010-03-05 오전 10:51:00
EXCEL.exe 안죽는 분들을 위해.. 결국 선택한 제 방법입니다.
EXCEL에 볼일 다본 후, 마지막에 프로세스 전부 강제로 종료시키는 방법입니다.

System.Diagnostics.Process [] PROC = System.Diagnostics.Process.GetProcessesByName("EXCEL");
foreach(System.Diagnostics.Process PK in PROC)
{
if(PK.MainWindowTitle.Length==0)
PK.Kill();
}

후후.. 여하튼 죽으면 되기때문에.. 이방법도 괜찮군요.
#네.. Excel이 절대 죽지 않도록 코딩하셨네요... ^^ / 블로그쥔장 / 2010-03-05 오전 11:23:00
철이베베//
먼저... 만약 IIS 에서 Excel을 구동하신다면... 그건 결코 권장되는 방법이 아닙니다.
다수의 사용자 계정으로 수행될 수 있는 IIS 환경에서 Excel을 구동시키면 다양한 보안 문제와
더불어 DCOM 서버 문제를 야기할 수 있습니다. 예전에 Microsoft Support 사이트에서
관련 문서가 있었는데 지금도 존재하는지는 알 수 없네요... -_-;

Excel이 절대 죽지 않는 건... 철이베베님이 excelApp.Quit를 호출하지 않으셔서 그런겁니다.
Excel은 DCOM 서버 역할을 합니다. 따라서 일단 구동되면 추가적인 요청이 올것을 기대하고
모든 Excel 객체들이 release 되더라도 Excel.exe 프로세스는 남아 있습니다.
일정 시간동안 요청이 없으면 종료하거나 Quit 호출과 더불어 모든 Excel 객체가 release 되면 종료합니다.
#re: 블로그쥔장님~ 이경우도 좀 봐주세요. / 철이베베 / 2010-03-08 오전 10:19:00
먼저 excelApp.Quit() 해주니까.. 확실히 죽네요.. 감사드립니다.
그런데 또다른 난관에 부딪쳤습니다.

아래와 같이 엑셀 저장을 하려하려고, SaveAs를 호출하니.. 저장은 됩니다. 그런데 역시 저기서 Excel.exe 가 죽지 않습니다.
저장 한 후, 처리해야할 것이 따로 있는지요....

Excel.Application excelApp = new Excel.Application();
Excel.Workbooks books = excelApp.Workbooks;
Excel.Workbook excelWorkbook = books.Add("");

excelWorkbook.SaveAs(SavedFile, Excel.XlFileFormat.xlWorkbookNormal, null, null, false, false, Excel.XlSaveAsAccessMode.xlExclusive, false, false, null, null, null);

excelApp.Quit();

Marshal.ReleaseComObject(excelWorkbook);
Marshal.ReleaseComObject(books);
Marshal.ReleaseComObject(excelApp);
#re: Excel.exe 죽이기 / 블로그쥔장 / 2010-03-18 오후 4:02:00
철이베베//
답변이 늦었습니다. 죄송합니다.
일반적으로 종료가 되어야 합니다만 SaveAs 호출시 저장여부를 묻는 대화 상자가 나타날 수도 있습니다.
이때 Excel을 호출하는 프로세스가 IIS와 같이 UI를 표시할 수 없는 프로세스의 경우
엑셀 프로세스가 종료되지 않을 수도 있습니다.
다음 코드를 포함시켜 이러한 UI가 나타나지 않도록 시도 해보십시요.

excelApp.DisplayAlerts = false;

도움이 되셨기를......
#re: Excel.exe 죽이기 / 안녕하세요! 질문이 있습니다만, / 2010-04-07 오후 5:40:00
excel 파일을 만들던 중 프로세스가 죽지 않아; 찾게 되었습니다.
위에서 알려주신 방법대로 사용한 컴오브젝트를 모두 릴리즈했는데요;
Excel.Range를 이용해서 셀 속성을 수정중인데요.
살짝 소스를 적어봅니다;

Excel.Application app = new Excel.Application();
Excel.Workbooks workbooks = app.workbooks;
Excel.Workbook workbook = workbooks.add("");
Excel.Sheets sheets = Workbook.Sheets["Sheet1"];
Excel.Range cells = (Excel.Range)sheet.Cells;
Excel.Range column = cells.get_Range("A1","A1");
Excel.Range merge = cells.get_Range("A1","C1");

column.ColumnWidth = 25;
column.RowHeight = 16;
merge.Merge(Type.Missing);
//이런 속성들은 위의 변수들을 ReleaseComobject() 함수이용하면 프로세스 죽는데요,
column.Font.size = 15;
column.Font.Name = "굴림";
column.Borders.LineStyle= BorderStyle.FixdSingle;
//Range의 속성의 속성을 이용해서 그런지, 프로세스가 살아있고 죽질 않네요;
//위의 속성들을 사용하려면 Range의 변수를 다시 이용해야 하는건지;

app.Quit();//함수 이용했구요
Marshal.ReleaseComObject() //함수 이용해서 위에 해당되는거 전부 릴리즈했습니다.

-_-
속성을 사용하고 싶은데 어떻게 하면 좋을까요;;질문해봅니다;
#re: Excel.exe 죽이기 / Goo / 2010-04-07 오후 5:48:00
위에 Name에다 엉뚱한 것을 써놨네요;
#re: Excel.exe 죽이기 / 블로그쥔장 / 2010-04-07 오후 8:14:00
column.Font 속성이나 column.Borders 속성도 COM 객체를 반환합니다.
따라서 요 녀석들도 ReleaseComObject로 해제하셔야 할 듯 싶네요.

??? font = column.Font;
font.size = 15;
...

Marshal.RelaseComObject(font);

도움이 되셨기를...
#re: Excel.exe 죽이기 / Goo / 2010-04-09 오전 9:25:00
답변 감사드립니다.
오우, 전부 바꾸려고 하니까 완전 노.가.다.라는 느낌이;
font, size, name, border 이런 속성들 사용하기 위해선 이렇게 밖에 안된다니;;;
어서 호환성?이 좋아졌으면 좋겠네요;;;
그럼 즐거운 하루 되십시오!