Tuesday, 21 July 2009

Excel Interop Bug

There is a bug in the Excel Interop libraries (at least in version 2003, I'm not sure about other versions) that means that when an Excel Application object is no longer referenced it's process is not ended. The Excel process will remain running on the host PC even after the application that created the COM object has ended too.

This can be a particular issue in a web application where a page might process data in an uploaded Excel file or dynamically produce an Excel file for download. Every time that page is requested a new process is started and the old processes quickly bog down the PC.

To kill a process is easy enough, just use the Process.Kill() method. However in order to kill the correct process you have to be able to find it. Enter the GetWindowThreadProcessId(IntPtr, int) Windows function.

To demonstrate its use here is a quick sample application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace ConsoleApplication4
{
class Program
{
[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

static void Main(string[] args)
{
var app = new Microsoft.Office.Interop.Excel.Application();
if (app.Workbooks.Count == 0)
{
app.Workbooks.Add(Type.Missing);
}
var book = (Workbook)app.Workbooks[1];
var sheet = (Worksheet)book.Worksheets[1];
var cells = (Range)sheet.Cells[1, 1];
cells.Value2 = "Hello Dave";
book.SaveAs("C:\\Users\\David Brunger\\Desktop\\Test.xlsx", Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
cells = null;
book.Close(false, Type.Missing, Type.Missing);
book = null;
var appid = app.Hwnd;
app = null;
int excelProcessId;
GetWindowThreadProcessId(new IntPtr(appid), out excelProcessId);
var excelProcess = Process.GetProcessById(excelProcessId);
excelProcess.Kill();
}
}
}
It might be adding a few checks around the GetWindowThreadProcessId function call, so that you don't try and kill the wrong or a non-existent process.

No comments:

Post a Comment