如何从正在运行的对象中获取进程ID?
Dim xlApp As Object = CreateObject("Excel.Application")
我需要使用后期绑定,因为我无法保证我将使用哪个版本,因此使用Microsoft.Office.Interop.Excel
将无效。
'do some work with xlApp
xlApp.Quit
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
xlApp = nothing
此时Excel仍在后台运行。我熟悉使用变量并释放它们的所有建议,然后使用:System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
。这不能可靠地工作。我正在做的工作非常复杂。我使用多个文件用于每个循环等。无法在Excel中释放所有资源。我需要一个更好的选择。
我想在Excel上使用Process.Kill
,但我不知道如何从xlApp对象获取进程。我不想杀死所有Excel进程,因为用户可能打开了工作簿。
我尝试使用Dim xProc As Process = Process.Start(ExcelPath)
然后使用xProc.Kill()
这有时会起作用,但如果用户已经拥有Excel,则使用XLApp = GetObject("Book1").Application
或XLApp = GetObject("", "Excel.Application")
获取正确的Excel对象有点棘手窗户打开。我需要一个更好的选择。
我无法使用GetActiveObject
或BindToMoniker
来获取Excel对象,因为它们仅在使用早期绑定时才能使用。例如。 Microsoft.Office.Interop.Excel
如何从正在运行的对象中获取进程ID?
编辑:实际上我并没有真正对如何让Excel很好地退出的改变感兴趣。许多其他问题都解决了这个问题here和here我只想杀了它;干净,准确,直接。我想要杀死我开始的确切过程而不是其他过程。
答案 0 :(得分:6)
使用Marshal.ReleaseComObject()或杀死Excel.exe进程是非常丑陋,容易出错并且对此问题不必要的创可贴。从长远来看,这是非常有害的,this question显示了可能发生的事情。正确的方法是调用GC.Collect(),但是阅读this answer以了解为什么在调试程序时这种方法无效。
解决方法很简单,您只需要确保在不同的方法中调用GC.Collect()。这可确保您的Excel对象引用不再在范围内。因此,执行此操作的程序的粗略轮廓将是:
Sub Main()
DoOfficeStuff()
GC.Collect()
GC.WaitForPendingFinalizers()
'' Excel.exe will now be gone
'' Do more work
''...
End Sub
Sub DoOfficeStuff()
Dim xlApp As Object = CreateObject("Excel.Application")
'' etc..
End Sub
答案 1 :(得分:4)
其实没关系;我想到了。这是一个非常干净,精确定位的解决方案,可以杀死已启动的确切过程。它不会干扰用户可能已打开的任何其他进程或文件。根据我的经验,在关闭文件和退出Excel之后终止进程是处理Excel的最快速,最简单的方法。 Here is a knowledge Base article描述了问题和Microsoft推荐的解决方案。
请注意,此解决方案不会终止Excel应用程序。如果没有正确处理任何指针,它只会杀死空进程shell。 Excel本身在我们调用xlApp.quit()
时实际上已退出。这可以通过尝试附加正在运行的Excel应用程序来确认,因为Excel根本没有运行,将会失败。
许多人不建议杀死这个过程;看到 How to properly clean up Excel interop objects 和Understanding Garbage Collection in .net
另一方面,很多人不建议使用GC.Collect。见What's so wrong about using GC.Collect()?
确保关闭所有打开的工作簿,退出应用程序,释放xlApp对象。最后检查过程是否仍然存在,如果是,则将其杀死。
Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, _
ByRef lpdwProcessId As Integer) As Integer
Sub testKill()
'start the application
Dim xlApp As Object = CreateObject("Excel.Application")
'do some work with Excel
'close any open files
'get the window handle
Dim xlHWND As Integer = xlApp.hwnd
'this will have the process ID after call to GetWindowThreadProcessId
Dim ProcIdXL As Integer = 0
'get the process ID
GetWindowThreadProcessId(xlHWND, ProcIdXL)
'get the process
Dim xproc As Process = Process.GetProcessById(ProcIdXL)
'Quit Excel
xlApp.quit()
'Release
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
'set to nothing
xlApp = Nothing
'kill it with glee
If Not xproc.HasExited Then
xproc.Kill()
End If
End Sub
一旦我意识到我可以从Excel获取窗口句柄,那么我只需要该函数从窗口句柄中获取进程ID。因此GetWindowThreadProcessId
如果有人知道vb.net的方式,我会很感激。
答案 2 :(得分:1)
Public Declare Function GetWindowThreadProcessId Lib "user32" _
(ByVal hwnd As Long, _
ByRef lpdwProcessId As Long) As Long
Function KillProcess(hwnd As Long)
Dim CurrentForegroundThreadID As Long
Dim strComputer As String
Dim objWMIService
Dim colProcessList
Dim objProcess
Dim ProcIdXL As Long
ProcIdXL = 0
CurrentForegroundThreadID = GetWindowThreadProcessId(hwnd, ProcIdXL)
strComputer = "."
Set objWMIService = GetObject _
("winmgmts:\\" & strComputer & "\root\cimv2")
Set colProcessList = objWMIService.ExecQuery _
("Select * from Win32_Process Where ProcessID =" & ProcIdXL)
For Each objProcess In colProcessList
objProcess.Terminate
Next
End Function
KillProcess (ExcelApplication.hwnd)
答案 3 :(得分:0)
对于C#使用:
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
uint iProcessId = 0;
//Get the process ID of excel so we can kill it later.
GetWindowThreadProcessId((IntPtr)ExcelObj.Hwnd, out iProcessId);
try
{
Process pProcess = Process.GetProcessById((int)iProcessId);
pProcess.Kill();
}
catch (System.Exception)
{
//just ignore any failure.
}
答案 4 :(得分:0)
我找到了解决方法(VB源代码):
Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
首先将Marshal.ReleaseComObject()的所有详细对象设置为Nothing。
关闭最后一个工作簿后,将Excel窗口移至顶部,然后等待1000毫秒(工作机会的价值):
exlBook.Close()
Dim exlProcId As Integer = Nothing
GetWindowThreadProcessId(exlApp.Hwnd, exlProcId)
AppActivate(exlProcId)
Threading.Thread.Sleep(1000)
Marshal.ReleaseComObject(exlBook)
exlBook = Nothing
最后:
exlApp.Quit()
exlApp = Nothing
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
我在关闭工作簿后停止程序,然后打开Excel窗口,然后继续执行程序并关闭Excel进程时发现了这一点。
亲切的问候: 德国汉诺威,托尔斯滕
答案 5 :(得分:0)
在开发无人值守的应用程序时,我每天都会遇到此问题,该应用程序每天创建50至100个Excel工作簿并通过电子邮件发送给他们。无论我做什么,有时甚至在GC.WaitForPendingFinalizers()之后,任务管理器中都会挂起一个Excel实例。
由于该应用程序无人值守,因此我不能冒用Excel吞噬机器的风险,因此我将所有方法组合为:
Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
Dim XLApp As Excel.Application
Dim XLHwnd As Integer
Dim XLProcID As Integer
Dim XLProc As Process
XLApp = New Excel.Application
XLHwnd = XLApp.Hwnd
GetWindowThreadProcessId(CType(XLHwnd, IntPtr), XLProcID)
XLProc = Process.GetProcessById(XLProcID)
DoStuffWithExcel()
Try
XLApp.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(XLApp)
XLApp = Nothing
GC.Collect()
GC.WaitForPendingFinalizers()
If Not XLProc.HasExited Then
XLProc.Kill()
End If
Catch ex As Exception
XLApp = Nothing
Finally
GC.Collect()
End Try
我尝试优雅地退出,但是如果失败,我将终止进程,因为必须这样做!
到目前为止,它本身就是行为;-)