C# Runtime DLL loading and ref parameters

2.9k views Asked by At

In short, I wish to load a .DLL file at runtime, and ask it to modify a ref value that I passed to it as a parameter. (the .DLL would be written in C#) but I don't know what a suitable technique would be.

I have a class "ALotOfData" which contains ~1 Gigabyte worth of variables in it.

I want to dynamically load a .DLL file which contains a method

"DoWork(ref ALotOfData thedata){ thedata.value = ... }

then execute the method, and then unload the DLL, and do the same with another DLL. (VERY important to have the ability to load/unload DLLs at runtime)

Obviously, a solution would be to pass a copy of the value itself, return the modified copy, and put it back into my class.

However, this is not possible, if the DLL file will have to make a decision. based on the data, which data to modify (consider: it potentially needs access to all the data).

Merely copying the entire package of data is... an absolutely horrible idea, as the whole entirety of the data is about 1 gigabyte large.

How can I go about importing a method from a .DLL dynamically (at run time) and pass a parameter to it, by ref, and I mean, actually pass a reference, not just copy it? (very important to pass a ref to the class, without copying)

A psuedo-code might help explain my point:

class ALotOfData{ ... } // ~ about 1GB of initialized values inside

Main(){
DLL = loadDLL("mydll.dll");
DLL.Invoke("DoWork",ref AlotOfData); // This needs to actually change my class's contents
DLL.unload();
}

Inside the dll:

DoWork(ref ALotOfData data){
    if(data.value){
        foreach( value A in data.value ){ ... } // ~100 iterations
    }
}

I could add this decision making in my main program, but that would defeat the purpose of being able to load/unload DLL files.

3

There are 3 answers

0
Paul Turner On BEST ANSWER

Loading an assembly at runtime is pretty easy:

Assembly.LoadFrom("mydll.dll");

Unfortunately, there is no way to unload an assembly. Instead you have to unload the entire AppDomain the assembly was loaded into. Normally you have a single AppDomain which contains all your running code; it unloads when your application exits.

If you want to be able to unload assemblies whilst running, you must create a second AppDomain and load your assembly there:

// Create a new domain with the dynamic assembly.
var domain = AppDomain.Create("Dynamic Assembly Domain");
domain.Load("mydll.dll");

// Do some work with the dynamic domain...

// Unload the domain.
AppDomain.Unload(domain);

The trouble here is that there is a boundary between AppDomains which has to be crossed to communicate with the objects in each domain.

The problem (as you seem to be aware of) is that the default mechanism for communicating between domains is to create a copy of the object. A complete clone is made and passed into the other domain, which is not suitable when you are working with large amounts of information.

The answer to this problem is the type MarshalByRefObject:

MarshalByRefObject is the base class for objects that communicate across application domain boundaries by exchanging messages using a proxy. Objects that do not inherit from MarshalByRefObject are implicitly marshal by value. When a remote application references a marshal by value object, a copy of the object is passed across application domain boundaries.

MarshalByRefObject objects are accessed directly within the boundaries of the local application domain. The first time an application in a remote application domain accesses a MarshalByRefObject, a proxy is passed to the remote application. Subsequent calls on the proxy are marshaled back to the object residing in the local application domain.

So, in order to pass your data between domains without creating a bulk copy, the class providing the data needs to inherit from MarshalByRefObject. You should also create a type can be loaded in the remote domain which also inherits from MarshalByRefObject, so the local domain has a proxy to the remote domain:

// Load your 1GB of data.
var data = ALotOfData.Load();

// Create a new domain with the dynamic assembly.
var domain = AppDomain.Create("Dynamic Assembly Domain");
domain.Load("primary.dll"); // Assembly containing your primary code.
domain.Load("mydll.dll");

// Create the remote proxy.
var remote = domine.CreateInstanceAndUnwrap("primary", "primary.RemoteControl");

// Invoke the logic in the loaded dll.
remote.Execute("mydll", "mydll.DLL", data);

// Unload the domain.
AppDomain.Unload(domain);
2
nvoigt On

You probably come from other languages than C#.

The ref keyword does not do what you think it does. A class is always passed by reference. Your code would work if you just removed the ref keyword altogether.

Now loading and unloading code dynamically is done using Reflection. You should probably start out from Assembly.LoadFrom. However, you cannot unload an Assembly. Once loaded, it's there. What you can do is use multiple AppDomains and drop those that contain the dlls you don't need.

0
The Vermilion Wizard On

The only time you ever need to pass a class with ref is in a situation like the following:

void DoWork(ref ALotOfData data)
{
    data = SomethingElse(); // or
    data = new ALotOfData();
}

If you're not replacing the object with another object and instead just accessing its properties/methods, then you don't need ref.

As others have mentioned, you will need to use multiple AppDomains to load/unload assemblies. You will want to make sure your ALotOfData class and any objects returned from properties/methods inherit from MarshalByRefObject so that they are not copied across app domains.