2008-09-16 53 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将不会被加载到主应用程序域。

这看起来像一个伎俩,但可能会对某人有用。 `