Dynamically loading a dll in C#

扶醉桌前 提交于 2019-12-30 07:33:24

问题


I have a window to use for editing. The editor should load a dll (which I have full control of) in response to the user's selection to know how to display the information visually. (They're dll's, as a user will not necessarily want or need every single display model, and also allow new ones to be added without messing around with the main project)

They will all simply be stored in a subdirectory (for now anyway) I'm pretty sure I can enumerate the available dlls but I need to do 2 more things that I'm not sure on

1) Some way to get metadata from\on the dll, so I can build the lists of possible display selections...

2) Load the selected dll, and unload it as necessary

Any suggestions would be greatly appreciated.


回答1:


If you are using raw dll's and not .NET assemblies then here are some handy P/Invokes for you:

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr LoadLibrary(string lpFileName);

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern void SetDllDirectory(string lpPathName);

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
privatestatic extern int GetModuleFileName(IntPtr module, [Out] StringBuilder fileName, int size);

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static bool FreeLibrary(IntPtr module);

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

Note that SetDllDirectory may need some protection as it is not available on all versions of windows (Windows 2000, in particular doesn't have it).

And in use:

SetDllDirectory(candidateFolder);
IntPtr dllHandle = LoadLibrary(dllName);
if (dllHandle != IntPtr.Zero)
{
    _dllHandle = dllHandle;
    _location = candidateFolder;
    _fullPath = Path.Combine(candidateFolder, dllName);
    IntPtr p = GetProcAddress(_dllHandle, procName);
    if (p == IntPtr.Zero)
        throw new ArgumentException("procName");
    SomeDelegateType d = (SomeDelegateType)Marshal.GetDelegateForFunctionPointer(p, typeof(SomeDelegateType));
    d(/* args */);
}

otherwise, you will be using Assembly methods. Looking at assembly level attributes or object level attributes is a good way to get extra information, although if what you want is a plug-in system, you should use a plug-in system, like the Managed Add-In Framework at CodePlex. See also this SO question and answer.




回答2:


Take a look at the Castle Windsor framework. It is designed to handle all of your requirements including DLL unloading. It's also free and open source.




回答3:


I don't know if changing how your program works is an option, but, you could use dependency injection for this, as long as they adhere to a certain interface.

The user selects, you dynamically set class to be loaded, and then just get an instance of the class.

I am not dealing with the unloading, I am just thinking about how you could possibly get classes, and since plinth already gave links to the functions for actually handling the dll, I think I will just end here.




回答4:


For a native module, the simplest way to get "metadata" would be to define some C-exported (non-name-mangled) functions that return the information you want. At their simplest, these would return pointers to static data within the modules, e.g.:

extern "C" const char* GetModuleDescription();
...
const char* GetModuleDescription() { return "Dummy Module"; }

You would then load each ".dll" file in the directory using LoadLibrary, load and call your known exports from it using GetProcAddress. If you can't load a file or find the exports, then it's not a valid plugin module, so skip it.

Once you're done with a module, you can call FreeLibrary. Windows will then unload the module from your address space.




回答5:


OK, I;ve figured out I need to use a second AppDomain, load the dll into that, and then I can unload the AppDomain as required.

string SignalSystemDLLPath = AppDomain.CurrentDomain.BaseDirectory +  MyApp.Properties.Resources.SystemModuleFolder;     
AppDomainSetup info = new AppDomainSetup();
info.ApplicationBase = DLLPath;
DLLDomain = AppDomain.CreateDomain("EditorDomain", null, info);

DLLPath is set to the subdir that holds the dll's.

I then foreach on all the dll's to get the AssemblyName, then later I use

DLLDomain.Load(SelectedAssemblyName)

to load the DLL. I keep getting FileNotFound exceptions though. After much googling I've decided its to much work at the moment, and I can refactor it later If I really need to do it...

Thank you for your replies though!




回答6:


Found out how to do this very easy using MEF, simply use a DirectoryCatalog pointed at your plugin dir, and as long as you have matching [Export]s and [Import]s it works great.



来源:https://stackoverflow.com/questions/1750901/dynamically-loading-a-dll-in-c-sharp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!