问题
I developed a DLL in Managed C++ which loads some plugins (implemented in any .NET language) at runtime using System.Reflection.Assembly.LoadFile. The interface which is implemented by all plugins is implemented in C#. It's used by the Managed C++ code like this:
#using <IMyPluginInterface.dll> // Make the 'IMyPluginInterface' type available
ref class PluginManager {
List<IMyPluginInterface ^> ^m_plugins;
// Load all plugins in a well-known directory.
void load() {
for ( string dllFile in Directory.GetFiles( .., "*.dll" ) ) {
// Lookup the type of the plugin object using Reflection
Type pluginType = ...;
// Finally, instantiate the plugin and add it to our list.
m_plugins.Add( (IMyPluginInterface ^)Activator.CreateInstance( pluginType ) );
}
}
}
Loading the plugins works well; the problem I'm facing is that at runtime, the IMyPlugnInterface.dll
file might not be in the same directory as the Managed C++ DLL. This means that the 'IMyPluginInterface' type is not available at runtime, and an exception is thrown.
I previously asked whether it was maybe possible to influence the lookup path used when resolving DLLs referenced via the #using
statement. Unfortunately, this didn't yield any result.
Is there maybe a different approach to this? Can types which are referenced via #using
be compiled into the Managed C++ DLL? Maybe anybody else has an entirely different solution?
回答1:
You can use several options - if you know in advance where the assembly will be located, you can add that path to your application's configuration file:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyPath"/>
</assemblyBinding>
</runtime>
</configuration>
If you want to search for the assembly at runtime, you can implement a handler for AppDomain::CurrentDomain->AssemblyResolve
event:
ref class AssemblyResolver
{
public:
/// The path where the assemblies are searched
property String^ Path
{
String^ get()
{ return path_; }
}
explicit AssemblyResolver(String^ path)
: path_(path)
{ /* Void */ }
Assembly^ ResolveHandler(Object^ sender, ResolveEventArgs^ args)
{
// The name passed here contains other information as well
String^ dll_name = args->Name->Substring(0, args->Name->IndexOf(','));
String^ path = System::IO::Path::Combine(path_, dll_name+".dll");
if ( File::Exists(path) )
return Assembly::LoadFile(path);
return nullptr;
}
private:
String^ path_;
};
and you can wire it using something like this:
AssemblyResolver^ resolver = gcnew AssemblyResolver(path);
AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler(
resolver,
&AssemblyResolver::ResolveHandler
);
Just make sure this is done before calling any methods that may use types from the assembly that has to be resolved.
回答2:
Is it not the standard .net assembly resolution strategy? See here for a detailed introduction.
来源:https://stackoverflow.com/questions/1503266/how-do-i-change-the-lookup-path-for-net-libraries-referenced-via-using-in-mana