我有一个Windows服务,可以通过Microsoft.Office.Interop.Excel.Application对象打开Excel电子表格。
Application xlApp = new Application();
Workbook workbook = xlApp.Workbooks.Open(fileName, 2, false);
...
...
workbook.Close();
xlApp.Quit();
我想杀掉使用工作簿完成后继续运行的EXCEL.exe进程。
我尝试了以下但没有成功......
// This returns a processId of 0
IntPtr processId;
GetWindowThreadProcessId(new IntPtr(xlApp.Hwnd), out processId);
Process p = Process.GetProcessById(processId.ToInt32());
p.Kill();
任何人对如何通过Windows服务执行此操作有任何想法?
答案 0 :(得分:10)
正确关闭打开的Excel工作簿并退出应用程序非常困难。如果我能找到我发布的链接,但基本上你必须清理对你创建的任何COM对象的所有引用。这包括ODBCConnections(数据连接),工作表,工作簿和Excel应用程序中的所有内容。我开始工作的组合涉及垃圾收集和System.Runtime.InteropServices.Marshal
对象:
// Garbage collecting
GC.Collect();
GC.WaitForPendingFinalizers();
// Clean up references to all COM objects
// As per above, you're just using a Workbook and Excel Application instance, so release them:
workbook.Close(false, Missing.Value, Missing.Value);
xlApp.Quit();
Marshal.FinalReleaseComObject(workbook);
Marshal.FinalReleaseComObject(xlApp);
正如您所提到的,循环并终止每个Excel进程通常不是一个好主意,因为如果您将其作为Windows应用程序运行,您可以关闭用户的Excel,或者在服务中也关闭Excel的实例通过其他程序运行。
修改:有关详细信息,请参阅this question。
答案 1 :(得分:7)
您需要检查文件句柄并获取由进程打开然后将其终止的PID。它对我有用。
private void genExcel(
{
int pid = -1;
//Get PID
xlApp = new Excel.Application();
HandleRef hwnd = new HandleRef(xlApp, (IntPtr)xlApp.Hwnd);
GetWindowThreadProcessId(hwnd, out pid);
.
.
.
.
//Finally
KillProcess(pid,"EXCEL");
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);
private void KillProcess(int pid, string processName)
{
// to kill current process of excel
System.Diagnostics.Process[] AllProcesses = System.Diagnostics.Process.GetProcessesByName(processName);
foreach (System.Diagnostics.Process process in AllProcesses)
{
if (process.Id == pid)
{
process.Kill();
}
}
AllProcesses = null;
}
答案 2 :(得分:6)
经过多次阅读和挫折后,我找到了解决方案!
所有赠送金额均转到dotNetkow,nightcoder和Mike Rosenblum,以获取此帖子中的解决方案:How do I properly clean up Excel interop objects?
这是我做的......
1.将项目的构建模式更改为“Release”(在DEBUG模式下,COM对象很难处理其引用。
2.删除所有双点表达式(所有COM对象应绑定到变量,以便它们可以被释放)
3.在finally块中显式调用GC.Collect(),GC.WaitForPendingFinalizers()和Marshal.FinalReleaseComObject()
以下是我正在使用的实用代码:
Application xlApp = null;
Workbooks workbooks = null;
Workbook workbook = null;
Worksheet sheet = null;
Range r = null;
object obj = null;
try
{
xlApp = new Application();
xlApp.DisplayAlerts = false;
xlApp.AskToUpdateLinks = false;
workbooks = xlApp.Workbooks;
workbook = workbooks.Open(fileName, 2, false);
sheet = workbook.Worksheets[1];
r = sheet.get_Range("F19");
obj = r.get_Value(XlRangeValueDataType.xlRangeValueDefault);
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (value != null) Marshal.FinalReleaseComObject(value);
if (r != null) Marshal.FinalReleaseComObject(r);
if (sheet != null) Marshal.FinalReleaseComObject(sheet);
if (workbooks != null) Marshal.FinalReleaseComObject(workbooks);
if (workbook != null)
{
workbook.Close(Type.Missing, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(workbook);
}
if (xlApp != null)
{
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
}
}
答案 3 :(得分:3)
我不知道我的答案是不是你正在寻找的......如果是这样告诉我,我会删除它。无论如何我用过这个:
Application xlApp = new Application();
xlApp.DisplayAlerts = false;
xlApp.Visible = true; // Only for debug purposes
Workbook workbook = xlApp.Workbooks.Open(filename, 2, false);
...
...
workbook.Close();
xlApp.Quit();
关闭工作簿并退出xlApp会从我的电脑内存中删除EXCEL.EXE 我正在使用Windows XP 32位和Microsoft Office 2007。
在使用这个测试应用程序之前,我还尝试打开另一个excel文件:第二个EXCEL.EXE被打开并且(使用Quit)在结束时关闭,第一个实例保持不变。
答案 4 :(得分:3)
我使用了一个简单但有效的解决方案
finally {
GC.Collect();
GC.WaitForPendingFinalizers();
if (xlApp != null)
{
xlApp .Quit();
int hWnd = xlApp .Application.Hwnd;
uint processID;GetWindowThreadProcessId((IntPtr)hWnd, out processID);
Process[] procs = Process.GetProcessesByName("EXCEL");
foreach (Process p in procs)
{
if (p.Id == processID)
p.Kill();
}
Marshal.FinalReleaseComObject(xlApp );
}
}
查找所有Excell.exe进程。然后获取我的excelApplication的进程ID。只杀死id匹配的进程。 用于在类中声明GetWindowThreadProcessId:
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
答案 5 :(得分:2)
我的解决方案
[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 Process Id
GetWindowThreadProcessId(excel.Hwnd, out id);
Process excelProcess = Process.GetProcessById(id);
try
{
// Your code
}
finally
{
excel.Quit();
// Kill him !
excelProcess.Kill();
}
答案 6 :(得分:1)
以下是打开和删除Excel实例的代码。我们只需要确保关闭与Excel相关的所有对象。
string strFilePath = @"C:\Sample.xlsx";
try
{
Excel.Application excelApp = null;
Excel.Workbook excelWorkbook = null;
Excel.Sheets excelSheets = null;
Excel.Worksheet excelWorksheet = null;
Excel.Workbooks excelWorkbooks = null;
Excel.Range excelUsedRange = null;
excelApp = new Microsoft.Office.Interop.Excel.Application();
int nData = excelApp.Hwnd;
// excelApp = new Excel.ApplicationClass();
//excelApp.Visible = true;
excelWorkbooks = excelApp.Workbooks;
excelWorkbook = excelWorkbooks.Add(System.Reflection.Missing.Value);
excelWorkbook = excelApp.Workbooks.Open(strFilePath, 2, false);
//excelWorkbook = excelApp.Workbooks.Open(strFilePath,
//
Type.Missing, Type.Missing,
//
Type.Missing, Type.Missing,
//
Type.Missing, Type.Missing,
//
Type.Missing, Type.Missing,
//
Type.Missing, Type.Missing,
//
Type.Missing, Type.Missing,
//
Type.Missing, Type.Missing);
excelSheets = excelWorkbook.Worksheets;
// excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(1);
excelWorksheet = (Excel.Worksheet)excelWorkbook.Worksheets["Dem0_1"];
excelUsedRange = excelWorksheet.UsedRange;
//Excel.Range lastCell = usedRange.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
//int lastRow = lastCell.Row;
//int lastCol = lastCell.Column;
//int rowMin = lastRow + 1;
//int colMin = lastCol + 1;
int nRowsCount = excelUsedRange.Rows.Count;
int nColCount = excelUsedRange.Columns.Count;
int N_Quality_Header = -1;
int N_Measurement_Name = -1;
int N_Lower_Tolerance = -1;
int N_Upper_Tolerance = -1;
//Read the Columns Index
for (int nColIndex = 1; nColIndex <= nColCount; nColIndex++)
{
Excel.Range cell = usedRange.Cells[1, nColIndex] as Excel.Range;
String strCellValue = cell.Value2.ToString();
if (strCellValue == "Quality Header")
N_Quality_Header = nColIndex;
else if (strCellValue.IndexOf("Measurement Name", StringComparison.OrdinalIgnoreCase) > -1)
N_Measurement_Name = nColIndex;
else if (strCellValue.IndexOf("Lower Tolerance", StringComparison.OrdinalIgnoreCase) > -1)
N_Lower_Tolerance = nColIndex;
else if (strCellValue.IndexOf("Upper Tolerance", StringComparison.OrdinalIgnoreCase) > -1)
N_Upper_Tolerance = nColIndex;
}
//Read all rows to get the values
for (int nRowIndex = 2; nRowIndex <= nRowsCount; nRowIndex++)
{
Excel.Range cellQualityHeader = usedRange.Cells[nRowIndex, N_Quality_Header] as Excel.Range;
String strValue = cellQualityHeader.Value2.ToString();
if (strValue == String_Empty)
continue;
}
}
catch (Exception oException)
{
}
finally
{
excelUsedRange.Clear();
//excelWorkbook.Save();
excelWorkbook.Close(false, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
excelWorkbooks.Close();
excelApp.Quit();
Marshal.ReleaseComObject(excelUsedRange);
Marshal.ReleaseComObject(excelWorksheet);
Marshal.ReleaseComObject(excelSheets);
Marshal.ReleaseComObject(excelWorkbooks);
Marshal.ReleaseComObject(excelWorkbook);
Marshal.ReleaseComObject(excelApp);
excelUsedRange = null;
excelWorksheet = null;
excelSheets = null;
excelWorkbooks = null;
excelWorkbook = null;
excelApp = null;
GC.GetTotalMemory(false);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.GetTotalMemory(true);
}
答案 7 :(得分:0)
我会使用Process.GetProcess并查找那里的exe我不相信与服务世界中的窗口有任何关系,因为我认为windows是在桌面站上创建的,你无法访问。
答案 8 :(得分:0)
我正在使用:
Process[] AllProcesses = Process.GetProcessesByName("EXCEL.EXE");
将该进程杀死。
答案 9 :(得分:0)
也许它不是那么优雅,但我最终得到了公认的解决方案和Safrin的解决方案。所以首先我尝试以优雅的方式做到这一点,如果它失败了我使用蛮力。原因是代码是批处理过程的一部分,即使一个Excel刷新操作失败,它也必须能够继续。我的问题是,有些故障与PowerPivot模型中的故障有关,这会导致出现带有错误消息的对话框。此对话框不可见,因为它作为后台进程运行,似乎Excel不会关闭,我的进程将无法继续,直到对话框关闭(?!)。因此,如果退出不起作用,则在具有超时机制的单独线程中启动进程并在处理我的工作对象时终止Excel是我能想到的唯一解决方案(可行)...
public void Dispose()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (workbook != null)
{
try
{
workbook.Close(false);
Marshal.FinalReleaseComObject(workbook);
}
catch { }
}
if (excel != null)
{
try { excel.Quit(); }
catch {
int hWnd = excel.Application.Hwnd;
uint processID;
GetWindowThreadProcessId((IntPtr)hWnd, out processID);
Process[] procs = Process.GetProcessesByName("EXCEL");
foreach (Process p in procs)
{
if (p.Id == processID) p.Kill();
}
}
Marshal.FinalReleaseComObject(excel);
}
}
答案 10 :(得分:0)
这是我删除所有未使用的Excel进程的代码
Process[] process = Process.GetProcessesByName("excel");
foreach (Process excel in process)
{
if (excel.HasExited)
{
excel.Kill();
}
}
process = null;
答案 11 :(得分:0)
这就是我正在做的..也许是杀了..但是对我来说很好。
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
private void KillProcess(uint pid, string processName) {
// to kill current process of excel
System.Diagnostics.Process[] AllProcesses = System.Diagnostics.Process.GetProcessesByName(processName);
foreach (System.Diagnostics.Process process in AllProcesses) {
if (process.Id == pid) {
process.Kill();
}
}
AllProcesses = null;
}
public void ReleaseObject(object obj) {
try {
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex) {
obj = null;
MessageBox.Show("Exception Occured while releasing object " + ex.ToString());
}
finally { GC.Collect(); }
}
xlWorkBook.Save();
xlWorkBook.Close();
xlApp.Quit();
uint pid;
HandleRef hwnd = new HandleRef(xlApp, (IntPtr)xlApp.Hwnd);
GetWindowThreadProcessId((IntPtr)xlApp.Hwnd, out pid);
//GetWindowThreadProcessId(hwnd, out pid);
KillProcess(pid, "EXCEL");
ReleaseObject(worksheets);
ReleaseObject(xlWorkBook);
ReleaseObject(xlApp);