2014-05-13 34 views
3

我有一個用Delphi 7編寫的程序,它也是一個自動化服務器。自動化服務器與CreateOleObject啓動兩次,但不是所有的時間

自動化服務器註冊方式如下:

TAutoObjectFactory.Create(ComServer, TMyServer, Class_App, 
    ciMultiInstance, tmSingle); 

我有兩個COM加載項,一個字,一個用於Outlook。他們都使用自動化服務器從主程序獲取一些信息。下面的代碼是從加載項,即:被稱爲當用戶點擊加載項按鈕:

MyServerApp: Variant; 
begin 
MyServerApp := CreateOleObject('MyServer.App'); 
try 
    MyServerApp.DoSomething; 
finally 
    MyServerApp := UnAssigned; 
end– 

這裏的問題是: 大部分時間的代碼工作正常。如果主應用程序已經運行,那麼加載項將連接到自動化服務器並執行它們的操作,如果它沒有運行,則加載項將啓動主應用程序。

但是由於一些未知的情況,特別是對於Outlook,有時候可能會發生這樣的情況,即使主程序正在運行,加載項也不會連接到它,而是將第二次重新啓動主應用程序並連接到這個新實例的自動化服務器。這次災難來了:由於我的應用程序不允許它在兩個實例中運行,第二個應用程序實例將只顯示一條錯誤消息,我的加載項將凍結整個Outlook。

爲什麼會發生這種情況?爲什麼CreateOleObject會在大多數時間連接它,並不時重新啓動我的應用程序?

+0

什麼版本的Windows?我在XP中看到類似這樣的問題,但從未在Windows 7或Vista或Win8中看到這樣的問題。 –

+0

這是Windows 7與Outlook2010,但我記得看到與不同的Win和Outlook版本的錯誤。 – Steve

回答

1

你真的不應該在一個帖子中提出多個問題。

問題1

它發生在我身上了很多。 問題在於Office爲每個觸發加載項代碼的事件生成兩個調用。 我找到的解決方案只響應第一個電話。

我使用了Add-In Express作爲COM插件,它給了我一些我可以鏈接到的事件。
我不知道,如果你正在使用這一點,但這裏是我使用的代碼:

interface 

.... 

var 
    MyApp: TAddInModule = nil; 

implementation 

procedure TAddInModule.adxCOMAddInModuleAddInFinalize(Sender: TObject); 
begin 
    MyApp:= nil; 
end; 

procedure TAddInModule.adxCOMAddInModuleAddInInitialize(Sender: TObject); 
begin 
    if not(Assigned(MyApp)) then try 
    MyApp:= Self; 
    except 
    {ignore} 
    end; {if try} 
end; 

在事件處理程序,你必須測試,看看第一個實例引用,或鬼實例。 (有時會被打電話)。

procedure TAddInModule.adxCommandBar1Controls3Click(Sender: TObject); 
begin 
    if (Self <> MyApp) then exit; 
    //ToggleDisplay 
    if not(ExcelBezig(xbQuestion)) then try 
    ToggleDisplay; 
    except {ignore} 
    end; 
end; 

這是一個雜牌(我承認),但它解決了這個問題一勞永逸和加載項是搖滾穩定至今。

不要過度重新創建鏈接,並在
你不應該每次你需要查詢的應用程序時使用CreateOleObject('MyServer.App');。當插件被激活時,您可以調用CreateOleObject一次,存儲該實例並重新使用鏈接。喜歡的東西:

procedure TAddInModule.adxCOMAddInModuleAddInInitialize(Sender: TObject); 
begin 
    if not(Assigned(MyApp)) then try 
    MyApp:= Self; 
    MyServerApp:= CreateOleObject('MyServer.App'); 
    except 
    {ignore} 
    end; {if try} 
end; 


procedure TAddInModule.adxCommandBar1Controls3Click(Sender: TObject); 
begin 
    if (Self <> MyApp) then exit; 
    try 
    MyServerApp.DoSomething; 
    except 
    {ignore} 
    end; 
end; 

使用變種訪問的自動化服務器很慢!
因爲您使用變體來存儲對您的自動化服務的引用Delphi在編譯時無法解析您的調用。
它也不能幫助你避免錯別字和其他錯誤。
對通過變體訪問的服務器的任何調用都是有效的。

所以

MyServer.StupidTyyyypo('hallo').doesnotexist('should be integer'); 

將編譯沒有錯誤。
如果導入型的lib,使您的訪問變量的具體類型,例如:

type 
    TMyServer = IMyServer; 

你從你的Delphi自動化服務器導入類型庫得到IMyServer,請參閱:http://www.blong.com/Articles/Automation%20In%20Delphi/Automation.htm
科:Controlling Automation Servers Using Interfaces和下面。

問題2

爲什麼CreateOleObject連接到正在運行的應用程序實例,而不是創建一個單獨的實例所有的時間?

請參見官方文檔:http://docwiki.embarcadero.com/Libraries/XE2/en/System.Win.ComObj.CreateOleObject

它指出:

CreateOleObject創建通過className參數指定的類的一個初始化的對象。 ClassName指定Class ID(CLSID)的字符串表示形式。當CLSID已知時,以及當對象位於本地服務器或進程內服務器上時,CreateOleObject用於創建指定類型的對象。僅使用CreateOleObject創建不屬於聚合的對象。

注意:在Delphi代碼中,CreateOleObject被調用一次以創建每個新類的單個實例。 要創建同一個類的多個實例,建議使用類工廠。

問題3

請問tmSingle線程模型意味着到自動化服務器的所有調用的應用程序的主線程中執行?

你應該問一個單獨的問題。

+0

我不明白*答案1 *。我使用加載項快速,但我所有的主要應用程序的自動化服務器調用發生在用戶引發的事件 - 即。在你的例子adxCommandBar1Controls3Click事件。我知道可以多次調用AddInFinalize或AddInInitialize,但根據我的經驗,用戶引發的事件只會被調用一次。 *答案2:*我不想創建同一個類的多個實例,我想創建多個單個實例。這就是大部分時間發生的事情,在主應用程序中創建新實例,但爲什麼不總是這樣? – Steve

+0

我澄清了原始問題,請再閱讀一次 - 也許現在它更有意義。謝謝! – Steve

+0

我認爲創建一次自動化服務器並重用這個變量並不是一個好的做法,尤其是,在加載項中。例如,一個單詞加載項將有多個instaces,因爲Word可以以多個副本啓動。這些實例取決於外接程序的表達方式是否可以共享相同的地址空間,這意味着全局變量的使用可能會導致完全的混淆......甚至不會談論釋放變量的Ole接口,因爲它超出了範圍!關於CreateOleObject,請在我以前的評論中查看答案2,可以用不同的方式理解幫助文件... – Steve