问题描述
我正在尝试构建一个小工具来比较一堆程序集中的类型.为此,我创建了两个子文件夹并将相应的 dll 放在那里:
I'm trying to build a small tool for comparing types in a bunch of assemblies. For this purpose I created two subfolders and put the respective dlls there:
..Dllsv1.1
..Dllsv1.2
其中 ..
是应用程序文件夹
where ..
is the application folder
我还创建了一个代理对象:
I also created a proxy object:
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 并获取其中声明的所有类型:
and used it for loading in the following routine that should load an dll and get all types declared in it:
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
中的一个设置可能会有所帮助,所以我尝试添加两个探测路径:
So far nothing unusual... but it didn't work like that in this case (probably because of the subfolders) so I searched a bit and found that a settting in the app.config
might help so I tried to add two probing paths:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Dllsv1.1;Dllsv1.2" />
</assemblyBinding>
</runtime>
现在不再有任何 FileNotFoundExpection
,但由于 dll 具有相同的名称,它仅将第一个子目录 (v1.1
) 中的 dll 加载到两个域中所以我删除了它,而是尝试像这样实现 AppDomain.CurrentDomain.AssemblyResolve
事件处理程序:
Now there wasn't any FileNotFoundExpection
s anymore but as the dlls have the same names it loaded only dlls from the first subdirectory (v1.1
) into both domains so I removed it and instead tried to implement the AppDomain.CurrentDomain.AssemblyResolve
event handler like that:
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 时,它都会调用自己.我不知道我还能尝试什么.
But unfortunatelly with this event handler I created an infinite loop and it calls itself each time it tries to load a dll it cannot find. I have no more ideas what else I could try.
正如@SimonMourier 所建议的那样,我尝试为我的新 AppDomain 使用自定义 .config
并创建了另外两个 *.config
,例如:
As @SimonMourier suggested I tried to use a custom .config
for my new AppDomains and created two more *.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="Dllsv1.1" />
</assemblyBinding>
</runtime>
</configuration>
我将它们命名为 v1.1.config
和 v1.2.config
.然后我设置 new ConfigurationFile
属性:
I named them v1.1.config
and v1.2.config
. Then I set the new ConfigurationFile
property:
var appDomainSetup = new AppDomainSetup
{
ApplicationBase = Environment.CurrentDirectory,
ConfigurationFile = Path.Combine(Environment.CurrentDirectory, string.Format("{0}.config", version)),
};
我已将选项Copy to Output Directory
设置为Copy always
;-)
I've set the option Copy to Output Directory
to Copy always
;-)
它没有用,所以我用谷歌搜索并尝试了另一个建议:
It didn't work so I googled and tried another suggestion:
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", appDomainSetup.ConfigurationFile);
但这也无济于事.仍然 FileNotFoundException
就像我的自定义配置不存在一样.
but this didn't help either. Still FileNotFoundException
like my custom configs weren't there.
使用 SetConfigurationBytes
方法也没有效果:
Using the SetConfigurationBytes
method instead had also no effect:
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
方法,新的应用程序域将使用自定义 .config:
However if I call the GetData
method the new appdomain uses the custom .config:
Debug.WriteLine("Current config: " + AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE"));
它输出我通过ConfigurationFile
这真的很令人困惑.堆栈跟踪显示,尽管 GetData
返回了什么,Assembly.LoadFile
仍然使用原始 .config:
This is really confusing. The Stack Trace reveals that despite of what the GetData
returs the Assembly.LoadFile
still uses the original .config:
=== LOG:此绑定在默认加载上下文中开始.LOG:使用应用程序配置文件:
C:[...]inDebugMyApp.exe.Config
C:[...]inDebugMyApp.exe.Config
LOG:使用主机配置文件:LOG:使用机器配置文件来自
LOG: Using host configuration file: LOG: Using machine configuration file from
C:WindowsMicrosoft.NETFrameworkv4.0.30319configmachine.config.
C:WindowsMicrosoft.NETFrameworkv4.0.30319configmachine.config.
更新-3
好的,我做了更多的实验,发现通过实施@SimonMourier 的建议确实有效.FileNotFoundException
不是由 ProxyDomain
类中的 LoadFile
方法抛出的,而是在我的应用程序的 Main
方法中抛出的.我猜程序集和类型只允许存在于 ProxyDomain
上下文中,不能像我尝试的那样转移到主域中.
UPDATE-3
OK, I did some more experimenting and found out that by implementing @SimonMourier suggestion it indeed worked. The FileNotFoundException
wasn't thrown by the LoadFile
method in the ProxyDomain
class but in the Main
method of my app. I gues the assembly and the types are allowed to live only within the ProxyDomain
context and cannot be transfered into the main domain as I have tried.
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
}
我现在需要重写比较算法,至少可以加载程序集;-)
I'll need to rewrite the comparison algorithm now by at least the assemblies can be loaded ;-)
推荐答案
如果您不想使用不同的强名称重新编译程序集,则可以将它们加载到不同的 AppDomains、不同的设置配置和不同的 .配置文件,就像 IIS 对多个网站所做的一样,每个网站都在它的一个 AppDomain 中,完全独立,全部托管在一个 AppPool 进程中 - w3wp.exe.
If you don't want to recompile the assemblies with a different strong name, than you can load them into different AppDomains, with different setup configuration, and different .config files, exactly like IIS does with multiple web sites, each one in its one AppDomain, completely independent, all hosted in only one AppPool process - w3wp.exe.
这篇关于如何将两个版本的相同程序集从两个不同的子文件夹加载到两个不同的域中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!