2010-09-16 139 views
11

我錯過了Excel.Application.QuitExcel.Application.BeforeQuit事件。 有沒有人知道模擬這些事件的解決方法?防止Excel退出

我從通過COM互操作一個C#WinForms應用程序訪問Excel。給定一個Excel.Application對象,我怎麼能:

  1. 最好防止Excel退出?
  2. 如果這是不可能的,我怎麼能至少通知當Excel退出?

請注意:因爲我有一個COM參考Excel.ApplicationExcel進程退出當Excel「跳槽」用戶。雖然這聽起來矛盾,但事實就是這樣。通過「退出」,我的意思是用戶點擊窗口右上角的「退出」或「十字按鈕」。窗口關閉,文件被卸載,加載項被卸載,以及Excel除了我不知道的東西之外的任何東西。但是我仍然可以使用Application對象來「恢復」該進程並使Excel再次可見,儘管加載項然後丟失了,並且我還不確定尚未定義狀態的其他內容。

爲了擺脫這個問題,我希望在一開始就取消退出(如果它存在,請考慮一個BeforeQuitCancel = true),或者至少在Excel退出時通知,以便我可以釋放COM對象並使流程真正退出,下一次我再次需要Excel時,我會知道我需要首先啓動它。

不幸的是這是一個惡性循環:只要Excel運行,我需要COM對象。所以我不能在 Excel退出之前處理它們。另一方面,只要COM對象存在,即使Excel假裝退出,進程也不會退出,所以我不能等待進程退出事件或類似事件。

我有我會來砸我的頭撞牆的感覺令人不快...

+0

請使用枕頭,磚塊已知很難:) – Marko 2010-09-16 07:08:44

+0

是否可以選擇監視Process.Exit事件,並在隱藏狀態下重新啓動Excel(如果用戶手動關閉它)? – 2010-09-16 07:12:20

+0

@Marko:你的意思是我根本不應該使用Excel? :-)/:-( – chiccodoro 2010-09-16 07:35:59

回答

7

請注意,我還沒有嘗試過。

創建一個工作簿,其中有代碼BeforeClose。例如

Option Explicit 

Private Sub Workbook_BeforeClose(Cancel As Boolean) 
    Cancel = True 
End Sub 

打開工作簿非常久遠,你有&其他工作簿中它並沒有被隱藏(如果整個應用程序是不可見的)。

因此,如果您嘗試退出Excel實例,它將強制關閉此隱藏工作簿,這會提高其BeforeClose事件&您可以編寫代碼以阻止其關閉。

請注意,上面的代碼是在VB6(VBA)中,它將需要轉換爲C#。
發表評論,如果發現任何疑問,請轉換。

如果你想隱藏的工作簿,你可以做

Workbooks("my workbook").Windows(1).Visible = False 

注:工作簿有一個Windows集合。上面的代碼嘗試隱藏第一個窗口。
我不知道,一個工作簿可以有多個窗口?如果是這樣,怎麼樣?

+1

這種方法聽起來很有希望,也可以用來注意Quit,因爲我可以注意到一個工作簿 - 關閉通過觀察工作簿集合(http://stackoverflow.com/questions/2767439/excel-automation-close-event-missing) – chiccodoro 2010-09-28 15:16:56

+0

嗨shahkalpesh。我試過了,它工作正常!我覺得給予這種骯髒的方法讓人感到不舒服,但實際上它是唯一能夠真正實現所討論內容的答案。 – chiccodoro 2010-09-29 06:57:13

+0

@chiccodoro:是的,它很髒。如果您碰巧遇到了更好的方法,請隨時發佈。這肯定會幫助他人嘗試黑客:)。感謝您接受答案。 – shahkalpesh 2010-09-29 06:59:50

1

你爲什麼不只是執行System.Diagnostics.Process.Start(@"SomeWorkbook.xlsx");,以確保啓動Excel。如果它已經開始,那麼這不會創建一個新的過程。

+0

因爲(1)我需要通過COM與工作簿進行通信,(2)我需要以編程方式使用密碼打開工作簿,因此用戶不必輸入它。 – chiccodoro 2010-09-16 07:34:46

+0

@'chiccodoro' - 您可以(1)通過COM進行通信 - 使用'Process.Start'確保Excel正在運行。 (2)在你的問題中,你沒有提及密碼的任何內容,但你確實說過你可以通過'Process.Start'啓動它。也許你需要提供更多的細節? – Enigmativity 2010-09-16 08:36:45

+0

抱歉讓您困惑,會相應更新我的問題。 – chiccodoro 2010-09-16 08:46:45

5

有一個KB文章,How to automate Excel and then know the user closed it,在C++。我沒有將這個移植到C#,但它可能不是很多工作。

+0

如果你用'Marshal.ReleaseComObject'正確地清理你的COMObject,就像Otaku在他的回答中指出的那樣,這個過程將退出onQuit。釋放Office-Interop對象在此問題中進一步描述:http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c我沒有Excel- 2007版本,所以我不確定天氣是否有ApplicationQuit的EventHandler。 Process Process似乎是您最好的選擇。 – marg 2010-09-23 06:48:56

+0

對不起,我很難過。只是沮喪地盡我所能來制定一個精確的問題,150分鐘的獎金切片,然後看到一個答案被頂級投票,這不能解決我的問題,並可能阻止其他用戶調查此線程。 - 我意識到誤解來自於我,並不是說我需要Excel運行的所有對象對事件作出反應,並且因爲如果Excel沒有退出,我想讓'Application'對象在下次它重用它時需要提高性能。不過,我可能會把你的建議與西蒙的變種結合起來。 – chiccodoro 2010-09-27 07:44:02

+0

@chiccodoro:沒問題,基於閱讀所有的編輯,我看到更清晰你在這裏後。我已經使用MSFT唯一已知的解決方案編輯了上述內容。 – 2010-09-27 17:23:51

3

這當然是一個黑客,但不能使用帶有WH_SHELL或WH_CBT在Windows調用SetWindowsHookEx API至少得到通知的Excel的主窗口被破壞?

注:當然有安全隱患,即需要做跨進程魔法一些管理員權限。

+0

嗨西蒙。你有一些點。相反,我也可以選擇觀察'Excel.Application.Visible'屬性。到目前爲止,我不想使用它,因爲假設VBA宏出於任何原因設置了「Visible = false」,它會將其識別爲Quit。儘管結合Otaku的提議,它可能是有希望的。 – chiccodoro 2010-09-24 06:06:04

+0

這是白盒測試應用程序所做的。他們使用Windows hook API調用來獲取父窗口並通過鼠標/鍵盤自動控制它。然而,您會發現我認爲使用這種方法存在的問題......例如在進程終止後卸載資源,在這種情況下,您可能仍然遇到關閉加載項的問題。 – 2010-09-29 23:29:18

3

您試圖在這裏解決的問題不會通過監視程序退出來解決。 在你說我沒有回答你的問題之前,你在問題中說明,即使用戶退出excel,你也可以恢復excel。因此,excel.exe進程仍在運行,因爲您有一個.net對象,其中包含對excel.application的com互操作引用。

所以,你有三種選擇:

  1. 避免退出Excel的用戶。 正如你所說保持Excel不退出,但我不知道一種方法,你可以防止用戶導致Excel退出,因此,正如你已經正確指出卸載你的和任何其他插件,等等。以這種方式設計用戶交互,他們希望用戶有能力關閉他們的應用程序。你的插件需要能夠處理這個問題,如果不能,我會說你的插件不是Excel的問題。我可能錯了,因爲我對應用程序的要求不夠了解。

  2. 在用戶退出之前清除所有非託管資源。 您需要做的是在用戶手動退出Excel之前清除對alll Excel和Office非託管資源的引用,以便在他們退出應用程序代碼時不會留下任何現在指向excel實例的剩餘資源更長的插件等加載。步驟(a)應該隨時執行,只要您不再需要某個特定資源,或者即使將其用於其他目的(即Excel.Range類型)時,也應該使用步驟(b),而步驟一個贏的應用程序,而不是一個插件,可能更頻繁,這一切都取決於你的應用程序和窗口oppurtunity,你有(時間),用戶可能會完成任務關閉之前。顯然,使用插件可以將其放入關機事件中,或者在代碼中進行任意處理。

    a。正如Otaku指出的那樣,在每個使用後的!= null的非託管資源上使用Marshal.FinalReleaseCOMObject。

    if (ComObject != null) 
        { 
         Marshal.FinalReleaseComObject(ComObject); 
         ComObject = null; 
        } 
    

    b。爲COM資源使用GC清理模式。

    GC.Collect(); 
        GC.WaitForPendingFinalizers(); 
        GC.Collect(); 
        GC.WaitForPendingFinalizers(); 
    
  3. 刷新加載項 如果您在充分跟蹤和卸載所有非託管資源,由於這項任務的複雜性沒有興趣,時間的限制(雖然我會推薦它),你可以看任何需要重裝你大概已經知道你的環境中的插件。這隻有在你控制環境時纔有效。 有手動加載Excel和COM加載項的技術。至於其他的東西,我沒有意識到這一點,但如果你在啓動/ XLSTART dirs中使用XLL或XLT,但可能會加載它,但反正會加載它。

+0

也許還有另一個「黑客」選項,但是這需要你強制用戶在Excel.exe的一個單獨實例中打開工作簿,然後關閉對該實例的引用。 – 2010-09-27 06:27:48

+0

嗨匿名類型,謝謝你的詳細回覆!你的「介紹」很好地說明了我一直在試圖解釋的東西。關於1,我沒有開發加載項,但是需要它們來處理我的應用程序管理的文件。所有加載項在退出時都由Excel卸載,即如果我恢復Excel,它們不再可用。至於3,我試圖重新加載插件,但它沒有工作,並導致容易出錯。關於2:問題是,只要Excel運行*,我需要對象*,因爲我必須觀察一些事件,例如, BeforeSave事件,並說Cancel = true ... – chiccodoro 2010-09-27 07:22:38

+0

好吧我明白你需要觀察某些事件的狀態,如BeforeSave等,但是如果我假設你有一個(外部Excel.exe)應用程序運行監視對於每個Excel實例上的事件,如果Excel.exe實例已關閉,是否可以將這些事件的當前狀態串行化並將其寫入config/xml文件?你說你應該在Excel中有一個關於事件的雞蛋情節,但這不是一個真正可行的程序場景。在某些時候,您需要能夠處理用戶從Excel脫機的事實。exe – 2010-09-28 00:29:05

-1

爲什麼不直接使用Application.ApplicationExit事件知道它何時關閉?

+1

有沒有這樣的事件,或者至少我找不到它。你指的是哪個版本的Excel? – chiccodoro 2011-04-05 08:32:42