2013-07-20 41 views
4

如何從運行對象獲取進程ID?如何從創建的「Excel.Application」對象獲取進程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()這工作有時但它是一個有點棘手使用XLApp = GetObject("Book1").ApplicationXLApp = GetObject("", "Excel.Application")如果用戶已經擁有了Excel中打開窗戶,以獲得正確的Excel對象。我需要一個更好的選擇。

我不能使用GetActiveObjectBindToMoniker來獲取Excel對象,因爲它們只能在使用早期綁定時使用工作。例如。 Microsoft.Office.Interop.Excel

如何從運行對象中獲取進程ID?

編輯:其實我並不真正感興趣的重新如何讓Excel很好地退出。許多其他問題解決了這個問題。 herehere我只是想殺死它;乾淨,準確和直接。我想殺死我開始的確切過程,而不是其他的。

+0

*不使用更可靠的工作* [您是否泄漏COM對象而不是一個'。'?](http://stackoverflow.com/questions/2191489/releasing-temporary-com-objects) –

+0

這是在「不是答案」的領土,但你有沒有考慮[EPPlus](http:///epplus.codeplex.com/)?它對Excel的功能有很好的支持,並且不會讓進程懸而未決[在Office自動化界面帶來的所有其他不好的事情中](http://support.microsoft.com/kb/257757)。 –

+0

@ ta.speot.is在Excel中聲明和釋放所有COM對象並不總是現實可行的。我花了不少時間在我的項目上嘗試。不可行。 –

回答

2

其實沒關係;我想到了。這是一個非常乾淨,精確針對性的解決方案,可以殺死已啓動的確切流程。它不會干擾用戶可能打開的任何其他進程或文件。根據我的經驗,在關閉文件並退出Excel之後終止進程是處理Excel的最快和最簡單的方法。 Here is a knowledge Base article描述了這個問題以及微軟推薦的解決方案。

請注意,此解決方案不殺死Excel應用程序。如果指針沒有正確處理,它只會殺死空的進程shell。 Excel本身在我們致電xlApp.quit()時確實退出。這可以通過嘗試附加正在運行的Excel應用程序來確認,該應用程序將失敗,因爲Excel根本沒有運行。

很多人不推薦殺死進程;見 How to properly clean up Excel interop objectsUnderstanding Garbage Collection in .net

在另一方面,許多人建議不要使用GC.Collect的。請參閱What's so wrong about using GC.Collect()?

務必關閉所有打開的工作簿,退出應用程序,釋放xlApp對象。最後檢查一下,看看這個過程是否還活着,如果是的話就殺了它。

​​

一旦我意識到,我可以從Excel中獲得的窗口句柄,然後我只需要該函數以獲取從窗口句柄的進程ID。因此GetWindowThreadProcessId如果有人知道一個vb.net的方式得到,我將不勝感激。

+4

這是**完全**是解決這個問題的錯誤方法。閱讀[這個答案](http://stackoverflow.com/a/17131389/17034)瞭解發生了什麼。 –

+0

@HansPassant非常有趣的一塊。它照亮了GC垃圾收集。但很多人都說使用GC.Collect(); GC.WaitForPendingFinalizers();實際上是一項昂貴的操作。我不知道爲什麼在關閉文件和退出應用程序之後關閉Excel過程比GC更糟糕。蒐集。我很樂意爲此提供答案。 –

+0

@HansPassant感謝您對此的意見。我編輯了我的答案,以包含一些處理Excel的推薦方式的鏈接。 –

6

使用Marshal.ReleaseComObject()或殺死Excel.exe進程很醜,容易出錯並且不必要的band-aids可以解決這個問題。從長遠來看,這是非常有害的,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 
+0

就個人而言,我不會打擾Office Interop的東西,除非EPPlus無法在某些情況下操作電子表格特殊的Excel專用方式。 –

+0

@Hans Passant:GC會不會最終垃圾收集Excel(儘管它會自行處理)?在那種情況下,是否有任何使用GC.Collect來強制它?這條語句的真實性如下:如果「調用COM對象的至少一個成員而不將其分配給變量」,Excel不會被釋放。請參閱http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects。 – Tarik

+0

它當然會。慢性問題是程序員實際上並不喜歡垃圾收集器的工作方式。他們做不明智的事情,比如停止分配內存,仍然期待GC收集垃圾。 –

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. 
    } 
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) 
相關問題