vb.net 如何从创建的“Excel.Application”对象获取进程 ID?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17759085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get the Process ID from a created "Excel.Application" object?
提问by D_Bester
How do I get the Process ID from a running object?
如何从正在运行的对象中获取进程 ID?
Dim xlApp As Object = CreateObject("Excel.Application")
I need to use late binding because I can't guarantee which version I will get so using Microsoft.Office.Interop.Excel
won't work.
我需要使用后期绑定,因为我不能保证我会得到哪个版本,所以使用Microsoft.Office.Interop.Excel
将不起作用。
'do some work with xlApp
xlApp.Quit
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
xlApp = nothing
At this point Excel is still running in the background. I am familiar with all the recommendations to use variables and release them then use: System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
. This does not work reliably. The work that I'm doing is very complicated. I use for each loops etc using multiple files. It is impossible to release all of the resources in Excel. I need a better option.
此时 Excel 仍在后台运行。我熟悉使用变量并释放它们然后使用:的所有建议System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
。这不能可靠地工作。我正在做的工作非常复杂。我使用多个文件用于每个循环等。释放 Excel 中的所有资源是不可能的。我需要一个更好的选择。
I'd like to use Process.Kill
on Excel but I don't know how to get the process from the xlApp object. I don't want to kill all Excel processes because the user might have a workbook open.
我想Process.Kill
在 Excel上使用,但我不知道如何从 xlApp 对象获取进程。我不想终止所有 Excel 进程,因为用户可能打开了一个工作簿。
I tried using Dim xProc As Process = Process.Start(ExcelPath)
then using xProc.Kill()
That works sometimes except it's a little tricky to get the correct Excel object using XLApp = GetObject("Book1").Application
or XLApp = GetObject("", "Excel.Application")
if the user already has Excel windows open. I need a better option.
我尝试使用Dim xProc As Process = Process.Start(ExcelPath)
then using xProc.Kill()
That 有时有效,除非使用正确的 Excel 对象有点棘手,XLApp = GetObject("Book1").Application
或者XLApp = GetObject("", "Excel.Application")
如果用户已经打开了 Excel 窗口。我需要一个更好的选择。
I can't use GetActiveObject
or BindToMoniker
to get the Excel object because they only work with work when using early binding. E.g. Microsoft.Office.Interop.Excel
我无法使用GetActiveObject
或BindToMoniker
获取 Excel 对象,因为它们只能在使用早期绑定时处理工作。例如Microsoft.Office.Interop.Excel
How do I get the Process ID from a running object?
如何从正在运行的对象中获取进程 ID?
Edit: Actually I'm not really interested in a rehash on how to get Excel to nicely exit. Many other questions have addressed that. hereand hereI just want to kill it; cleanly, precisely and directly. I want to kill the exact process that I started and no other.
编辑:实际上,我对重新讨论如何让 Excel 很好地退出并不感兴趣。许多其他问题已经解决了这个问题。在这里和这里我只想杀死它;干净、准确、直接。我想杀死我开始的确切进程而不是其他进程。
采纳答案by D_Bester
Actually never mind; I figured it out. This is a very clean, precisely targeted solution that kills the exact process that was started. It doesn't interfere with any other process or file that the user might have open. In my experience killing the process after closing files and quitting Excel is the fastest and easiest way to deal with Excel. Here is a knowledge Base articledescribing the problem and Microsoft's recommended solution.
其实没关系;我想到了。这是一个非常干净、精确定位的解决方案,可以终止已启动的确切进程。它不会干扰用户可能打开的任何其他进程或文件。根据我的经验,关闭文件并退出 Excel 后终止进程是处理 Excel 的最快和最简单的方法。这是描述问题和 Microsoft 推荐解决方案的知识库文章。
Please note that this solution does NOT kill the Excel application. It only kills the empty process shell if any pointers have not been properly disposed. Excel itself DOES actually quit when we call xlApp.quit()
. This can be confirmed by trying to attach the running Excel application which will fail because Excel is not running at all.
请注意,此解决方案不会终止 Excel 应用程序。如果没有正确处理任何指针,它只会杀死空进程外壳。当我们调用xlApp.quit()
. 这可以通过尝试附加正在运行的 Excel 应用程序来确认,该应用程序将失败,因为 Excel 根本没有运行。
Many people don't recommend killing the process; See How to properly clean up Excel interop objectsand Understanding Garbage Collection in .net
很多人不建议杀死进程;请参阅 如何正确清理 Excel 互操作对象和了解 .net 中的垃圾收集
On the other hand many people don't recommend using GC.Collect. See What's so wrong about using GC.Collect()?
另一方面,很多人不推荐使用 GC.Collect。请参阅使用 GC.Collect() 有什么问题?
Be sure to Close any open workbooks, Quit the application, Release the xlApp object. Finally check to see if the process is still alive and if so then kill it.
确保关闭所有打开的工作簿,退出应用程序,释放 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
Once I realized that I could get the window handle from Excel, then I just needed the function to get the process ID from the window handle. Hence GetWindowThreadProcessId
If anyone knows a vb.net way to get that I would be grateful.
一旦我意识到我可以从 Excel 获取窗口句柄,那么我只需要从窗口句柄获取进程 ID 的函数。因此,GetWindowThreadProcessId
如果有人知道通过 vb.net 获得的方法,我将不胜感激。
回答by Hans Passant
Using Marshal.ReleaseComObject() or killing the Excel.exe process are ugly, error prone and unnecessary band-aids for this problem. And highly detrimental in the long run, this questionshows what can happen. The proper way to do it is by calling GC.Collect(), but read this answerto understand why this tends to not work when you debug your program.
对于这个问题,使用 Marshal.ReleaseComObject() 或杀死 Excel.exe 进程是丑陋的、容易出错和不必要的创可贴。从长远来看,这是非常有害的,这个问题表明可能会发生什么。正确的方法是调用 GC.Collect(),但请阅读此答案以了解为什么在调试程序时这往往不起作用。
The workaround is simple, you just need to make sure that you call GC.Collect() in a differentmethod. Which ensures that your Excel object references are no longer in scope. So the rough outline of a program that does this right would be:
解决方法很简单,您只需要确保以不同的方法调用 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
回答by ajsco
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)
回答by user3756502
I found a workaround (VB source):
我找到了一种解决方法(VB 源代码):
Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
First Marshal.ReleaseComObject() all more detailed objects and set them to Nothing.
首先 Marshal.ReleaseComObject() 所有更详细的对象并将它们设置为 Nothing。
Bring Excel window to top after closing last workbook and then wait 1000 ms (good value for working chance):
关闭最后一个工作簿后将 Excel 窗口置于顶部,然后等待 1000 毫秒(工作机会的价值):
exlBook.Close()
Dim exlProcId As Integer = Nothing
GetWindowThreadProcessId(exlApp.Hwnd, exlProcId)
AppActivate(exlProcId)
Threading.Thread.Sleep(1000)
Marshal.ReleaseComObject(exlBook)
exlBook = Nothing
At last:
最后:
exlApp.Quit()
exlApp = Nothing
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
I found it out when I stopped the programme after closing the working book, then opened the Excel window, then continued the programme and the Excel process was closed.
我在关闭工作簿后停止程序时发现的,然后打开Excel窗口,然后继续程序并关闭Excel进程。
Kind regards: Thorsten, Hanover, Germany
亲切的问候:Thorsten,汉诺威,德国
回答by Yowser
I ran into this problem when developing an unattended app that creates 50 to 100 Excel workbooks each day and emails them. No matter what I did, an instance of Excel hung around in Task Manager, sometimes even after GC.WaitForPendingFinalizers().
我在开发一个无人值守的应用程序时遇到了这个问题,该应用程序每天创建 50 到 100 个 Excel 工作簿并通过电子邮件发送它们。无论我做什么,任务管理器中都会出现一个 Excel 实例,有时甚至在 GC.WaitForPendingFinalizers() 之后。
As the app is unattended, I can't risk Excel eating up the machine, so I combined all the approaches into:
由于该应用程序无人值守,我不能冒险让 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
I try to exit gracefully, but if it fails, I kill the process, because I have to!
我尝试优雅地退出,但如果失败,我会终止进程,因为我必须这样做!
So far, it's behaved itself ;-)
到目前为止,它表现得很好;-)
回答by JimMoore
For C# use:
对于 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.
}