2008-09-16 41 views
4

問題(簡化,以使事情更清晰):靜態庫使用託管代碼問題

    1.有一個有一個遞增函數靜態鏈接static.lib:
    
        extern int CallCount = 0; 
        int TheFunction() 
        { 
         void *p = &CallCount; 
         printf("Function called"); 
         return CallCount++; 
        } 
    
    2.靜態的。 LIB被鏈接到託管C++/CLI managed.dll一個包裝TheFunction方法:
    
        int Managed::CallLibFunc() 
        { 
         return TheFunction(); 
        } 
    
    3.測試的應用程序有一個參考managed.dll並創建調用C++/CLI包裝多個域:
    
        static void Main(string[] args) 
        { 
         Managed c1 = new Managed(); 
         int val1 = c1.CallLibFunc(); 
         // value is zero 
    
         AppDomain ad = AppDomain.CreateDomain("NewDomain"); 
         Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed; 
         int val2 = c.CallLibFunc(); 
         // value is one 
        } 
    

問:

基於我已經基本.NET VOL1上的CLR由唐盒讀,我希望VAL2是因爲managed.dll的一個全新的副本,零/ static.lib是裝當調用CreateInstanceAndUnwrap時。我誤解了正在發生的事情嗎?因爲它是非託管代碼,所以靜態庫似乎並不尊重AppDomain邊界。除了通過創建一個用於實例化Managed的全新流程之外,是否有辦法解決此問題?

非常感謝大家!

+0

幾年前我們在這裏嘗試了相同的結果。我們只能獲得非託管代碼的一個實例。 – 2008-09-16 14:25:23

回答

3

我的預感是,如您所懷疑的那樣,非託管DLL在進程的上下文中加載,而不是在AppDomain的上下文中加載,因此非託管代碼中的任何靜態數據都在AppDomain中共享。

This link顯示有人和你有同樣的問題,這仍然不是100%驗證,但可能是這種情況。

This link是關於使用一個置信轉換特技產生從非託管代碼回調到一個AppDomain。我不確定這可以幫助你,但也許你會發現這有助於創建某種解決方法。

0

總之,也許。 AppDomain純粹是一個受管理的概念。當一個AppDomain被實例化時,它不會映射到底層DLL的新副本中,它可以重用已經存在於內存中的代碼(例如,您不會期望它會加載所有System。*程序集的新副本, ?)

內管理世界上所有的靜態變量的作用域的AppDomain,但正如你指出,這並不在不可控制的世界應用。

你可以做一些複雜的,迫使一個獨特managed.dll爲每個應用程序域,這將導致湊湊熱鬧帶來的靜態庫之中的新版本的負載。例如,也許使用Assembly.Load和byte數組可以工作,但我不知道如果相同的程序集加載兩次,CLR將如何嘗試處理類型中的衝突。

0

我不認爲我們在這裏得到實際問題 - see this DDJ article

加載程序優化屬性的默認值是SingleDomain,它使「AppDomain加載每個必需程序集代碼的私有副本」。即使它是多域值之一「每個AppDomain始終保持靜態字段的獨特副本」。

'managed.dll'(顧名思義)是一個託管程序集。在static.lib的代碼已經被靜態編譯(如IL代碼)到「managed.dll」,所以我希望同樣的行爲Lenik預計....

...除非static.lib是一個非託管DLL的靜態導出庫。 Lenik說這並非如此,所以我仍然不確定這裏發生了什麼。

+0

LoaderOptimizationAttribute的MSDN文檔聲明「此屬性只是加載器的提示,不會影響程序行爲。」所以無論它做什麼,它都不應該*能夠改變觀察到的行爲,所以不能在這裏扮演一個角色。 – 2008-09-16 15:11:26

0

您是否嘗試過在單獨的進程中運行?靜態庫不應共享它自己進程之外的內存實例。

我知道這可能是一種痛苦。我不確定在這種情況下您的其他選項是什麼。

編輯:稍微環顧一下後,我認爲你可以用System.Diagnostics.Process這個類來做你需要的一切。在這一點上你會有很多選擇進行溝通,但.NET Remoting或WCF可能是很好的選擇。

0

以上是我關於這個問題找到了最好的兩篇文章

最重要的部分是:

基於RVA靜態字段的過程-全球。這些僅限於標量和值類型,因爲我們不希望對象跨AppDomain邊界流血。這會導致各種問題,尤其是在AppDomain卸載期間。有些語言如ILASM和MC++可以方便地定義基於RVA的靜態字段。大多數語言不會。

好了,如果你在的.lib控制的代碼,我想嘗試

class CallCountHolder { 
    public: 
    CallCountHolder(int i) : count(i) {} 
    int count; 
}; 

static CallCountHolder cc(0); 
int TheFunction() 
{ 
    printf("Function called"); 
    return cc.count++; 
} 

既然他表示,基於RVA靜態字段被限制爲標量和值類型。一個int數組也可能工作。

1

在調用

Managed c1 = new Managed(); 

你managed.dll包裝將被加載到你的應用程序的主要應用領域。直到它將有域名非託管的東西從static.lib將與其他域共享。 而不是創建單獨的進程,只需確保(每次調用之前)managed.dll未加載到任何應用程序域。

比較與

static void Main(string[] args) 
{ 

    {  
       AppDomain ad = AppDomain.CreateDomain("NewDomain"); 
       Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed; 
       int val2 = c.CallLibFunc(); 
       // Value is zero 

       AppDomain.Unload(ad) 
    } 
    {  
       AppDomain ad = AppDomain.CreateDomain("NewDomain"); 
       Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed; 
       int val2 = c.CallLibFunc(); 
       // I think value is zero 

       AppDomain.Unload(ad) 
    } 


} 
` 

重要:如果只添加一行JIT編譯器將加載managed.dll和神奇消失。

static void Main(string[] args) 
{ 

    {  
       AppDomain ad = AppDomain.CreateDomain("NewDomain"); 
       Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed; 
       int val2 = c.CallLibFunc(); 
       // Value is zero 

       AppDomain.Unload(ad) 
    } 
    {  
       AppDomain ad = AppDomain.CreateDomain("NewDomain"); 
       Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed; 
       int val2 = c.CallLibFunc(); 
       // I think value is one 

       AppDomain.Unload(ad) 
    } 
Managed c1 = new Managed(); 


} 

如果你不想依賴於這樣的線,你可以創造一個又一個的包裝ManagedIsolated.dll將引用managed.dll,只是調用之後將在與域卸載單獨的域的每個呼叫。主要應用程序將僅取決於ManagedIsolated.dll類型和Managed.dll將不會被加載到主應用程序域。

這看起來像一個伎倆,但可能會對某人有用。 `