我正在尝试构建一个小工具来比较一堆程序集中的类型。为此,我创建了两个子文件夹并将各自的 dll 放在那里:
..\Dlls\v1.1
..\Dlls\v1.2
where ..
是应用程序文件夹
我还创建了一个代理对象:
public class ProxyDomain : MarshalByRefObject
{
public Assembly LoadFile(string assemblyPath)
{
try
{
//Debug.WriteLine("CurrentDomain = " + AppDomain.CurrentDomain.FriendlyName);
return Assembly.LoadFile(assemblyPath);
}
catch (FileNotFoundException)
{
return null;
}
}
}
并将其用于加载以下例程,该例程应加载 dll 并获取其中声明的所有类型:
private static HashSet<Type> LoadAssemblies(string version)
{
_currentVersion = version;
var path = Path.Combine(Environment.CurrentDirectory, Path.Combine(_assemblyDirectory, version));
var appDomainSetup = new AppDomainSetup
{
ApplicationBase = Environment.CurrentDirectory,
};
var evidence = AppDomain.CurrentDomain.Evidence;
var appDomain = AppDomain.CreateDomain("AppDomain" + version, evidence, appDomainSetup);
var proxyDomainType = typeof(ProxyDomain);
var proxyDomain = (ProxyDomain)appDomain.CreateInstanceAndUnwrap(proxyDomainType.Assembly.FullName, proxyDomainType.FullName);
_currentProxyDomain = proxyDomain;
var assemblies = new HashSet<Type>();
var files = Directory.GetFiles(path, "*.dll");
foreach (var file in files)
{
try
{
var assembly = proxyDomain.LoadFile(file);
if (assembly != null)
{
assemblies.UnionWith(assembly.DefinedTypes.Where(t => t.IsPublic));
}
}
catch (Exception)
{
}
}
return assemblies;
}
到目前为止没有什么不寻常的......但在这种情况下它并没有像这样工作(可能是因为子文件夹)所以我搜索了一下并发现了一个设置app.config
可能有帮助,所以我尝试添加两个探测路径:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Dlls\v1.1;Dlls\v1.2" />
</assemblyBinding>
</runtime>
现在没有了FileNotFoundExpection
s 了,但由于 dll 具有相同的名称,因此它仅加载第一个子目录中的 dll (v1.1
)进入两个域,所以我删除了它并尝试实现AppDomain.CurrentDomain.AssemblyResolve
像这样的事件处理程序:
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
{
var path = Path.Combine(Environment.CurrentDirectory, Path.Combine(_assemblyDirectory, _currentVersion));
path = Path.Combine(path, e.Name.Split(',').First());
path = path + ".dll";
var assembly = _currentProxyDomain.LoadFile(path);
return assembly;
};
但不幸的是,通过这个事件处理程序,我创建了一个无限循环,它称自己为每次它尝试加载找不到的 dll 时。我不知道我还能尝试什么。
UPDATE-1
正如 @SimonMourier 建议我尝试使用自定义.config
为我的新 AppDomains 并创建了另外两个*.config
s like:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Dlls\v1.1" />
</assemblyBinding>
</runtime>
</configuration>
我给他们起了名字v1.1.config
and v1.2.config
。然后我设置了new ConfigurationFile
财产:
var appDomainSetup = new AppDomainSetup
{
ApplicationBase = Environment.CurrentDirectory,
ConfigurationFile = Path.Combine(Environment.CurrentDirectory, string.Format("{0}.config", version)),
};
我已经设置了选项Copy to Output Directory
to Copy always
;-)
它不起作用,所以我用谷歌搜索并尝试了另一个建议:
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", appDomainSetup.ConfigurationFile);
但这也没有帮助。仍然FileNotFoundException
就像我的自定义配置不存在一样。
使用SetConfigurationBytes
方法也没有效果:
var domainConfig = @"
<configuration>
<startup>
<supportedRuntime version=""v4.0"" sku="".NETFramework,Version=v4.5"" />
</startup>
<runtime>
<assemblyBinding xmlns=""urn:schemas-microsoft-com:asm.v1"">
<probing privatePath=""Dlls\{0}"" />
</assemblyBinding>
</runtime>
</configuration>";
domainConfig = string.Format(domainConfig, version).Trim();
var probingBytes = Encoding.UTF8.GetBytes(domainConfig);
appDomainSetup.SetConfigurationBytes(probingBytes);
但是如果我打电话给GetData
新的 appdomain 使用自定义 .config 的方法:
Debug.WriteLine("Current config: " + AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE"));
它输出我通过设置的路径ConfigurationFile
UPDATE-2
这确实令人困惑。堆栈跟踪显示,尽管GetData
返回Assembly.LoadFile
仍然使用原来的.config:
=== 日志:此绑定在默认加载上下文中启动。日志:使用应用程序配置文件:
C:[...]\bin\Debug\MyApp.exe.Config
日志:使用主机配置文件: 日志:使用来自以下位置的计算机配置文件:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config。
UPDATE-3
好的,我做了一些更多的实验,发现通过实施 @SimonMourier 建议,它确实有效。这FileNotFoundException
没有被抛出LoadFile
方法中的ProxyDomain
类但在Main
我的应用程序的方法。我猜想程序集和类型只允许存在于ProxyDomain
正如我所尝试的那样,无法将其转移到主域中。
public IEnumerable<Type> LoadFile(string assemblyPath)
{
//try
{
// does't throw any exceptions
var assembly = Assembly.LoadFile(assemblyPath);
// returning the assembly itself or its types will throw an exception in the main application
return assembly.DefinedTypes;
}
//catch (FileNotFoundException)
{
// return null;
}
}
主域中的方法:
private static HashSet<Type> LoadAssemblies(string version)
{
// omitted
foreach (var file in files)
{
//try
{
// the exception occurs here, when transfering types between domains:
var types = proxyDomain.LoadFile(file);
assemblies.UnionWith(types.Where(t => t.IsPublic));
}
}
// omitted
}
我现在需要重写比较算法,至少可以加载程序集;-)