Static libraries with managed code issue

Problem (simplified to simplify):

  • 1. there is one statically linked static.lib that has a function that increments:
extern int CallCount = 0; int TheFunction() { void *p = &CallCount; printf("Function called"); return CallCount++; } 
2. static.lib is associated with managed C ++ / CLI managed.dll, which wraps TheFunction method:
 int Managed::CallLibFunc() { return TheFunction(); } 
3. The test application has a link to managed.dll and creates several domains that invoke the C ++ / CLI shell:
 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 } 

Question:

Based on what I read in Don Box's Essential.NET Vol1 CLR, I would expect val2 to be zero, since a new copy of the manage.dll / static.lib file is loaded when CreateInstanceAndUnwrap is called. Do I really not understand what is happening? The static library does not seem to respect the boundaries of the application, as this is unmanaged code. Is there a way around this problem other than creating a completely new process for creating instances of Managed?

Thanks everyone!

+4
source share
6 answers

I suspected that unmanaged DLLs loaded in the context of the process, and not in the context of AppDomain, so any static data in unmanaged code is distributed between AppDomains.

This link shows someone with the same problem as yours, but not a 100% check for this, but probably it is.

This link is about creating a callback from unmanaged code in AppDomain using a trick. I'm not sure if this can help you, but maybe you will find it useful to create some kind of workaround.

+3
source

After the call

 Managed c1 = new Managed(); 

Your manage.dll shell will be loaded into the main application domain of your application. Until the unmanaged domain material is removed from static.lib, they will be transferred to other domains. Instead of creating a separate process, you just need to be sure (before each call) that the manage.dll file is not loaded into any application domain.

Compare with this

 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) } } ` 

IMPORTANT: If you add only one JIT compiler, download the manage.dll file and the magic will disappear.

 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(); } 

If you do not want to depend on such lines, you can create another ManagedIsolated.dll shell that will reference the manage.dll file and make each call in a separate domain with domain unloading immediately after the call. The main application will depend only on the types of ManagedIsolated.dll, and Managed.dll will not be loaded into the main domain of the application.

It seems like a trick, but maybe it will be useful for someone. `

+1
source

In short, maybe. AppDomains is a purely managed concept. When an instance of AppDomain is created, it does not appear in new copies of the underlying DLLs, it can reuse the code already in memory (for example, you do not expect it to download new copies of all System. * Assemblies, right?)

In a managed world, all static variables are covered by AppDomain, but as you note, this does not apply in an unmanaged world.

You can do something complicated that forces you to download a unique manage.dll file for each application domain, which will result in a new version of the static lib being delivered for the trip. For example, perhaps using Assembly.Load with a byte array will work, but I don’t know how the CLR will try to deal with the type conflict if the same assembly is loaded twice.

0
source

I don’t think we are getting to the actual problem here - see this DDJ article .

The default bootloader optimization attribute value is SingleDomain, which "forces AppDomain to load a personal copy of each required build code." Even if this is one of the values ​​of Multi domain, "each AppDomain always saves a separate copy of the static fields."

'managed.dll' (as the name implies) is a managed assembly. The code in static.lib was statically compiled (as an IL code) into "managed.dll", so I would expect the same behavior as Lenik expected ....

... if static.lib is not a static export library for an unmanaged DLL. Lenik says this is not the case, so I'm still not sure what is going on here.

0
source

Have you tried working in separate processes? A static library should not share memory instances outside its own process.

It can be a pain to manage, I know. I'm not sure what your other options will be in this case.

Edit: after a quick glance, I think you could do everything you need with the System.Diagnostics.Process class. You will have many options for communication at the moment, but .NET Remoting or WCF is likely to be a good and easy choice.

0
source

These are the best two articles I have found on this subject.

Main part:

RVA-based static fields are global processes. They are limited to scalars and value types, because we do not want objects to pass through the borders of the AppDomain. This will cause all kinds of problems, especially during the unloading of AppDomain. Some languages, such as ILASM and MC ++, allow you to define static fields based on RVA. Most languages ​​are not.

Ok, so if you manage the code in .lib, I would try

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

Because he said that RVA-based static fields are limited to scalars and value types. An int array may also work.

0
source

Source: https://habr.com/ru/post/1276752/


All Articles