从程序集文件中获取自定义属性数据并在之后解锁
Get custom attribute data from assembly file and unlock it afterwise
程序集 A 声明了一个应用于程序集 B 的自定义程序集属性。
class MyAssemblyAttribute : Attribute { /* ... */ }
我需要从程序集 A 的方法中获取程序集 B 的属性数据,而不保持程序集 B 的加载。
程序集 A 中的伪代码:
var domain = AppDomain.Create();
MyAssemblyAttribute attr = null;
string path = "B.dll";
domain.DoCallback(() => {
attr = Assembly.Load(path)
.GetCustomAttributes()
.OfType<MyAssemblyAttribute>()
.First();
});
AppDomain.Unload(domain);
File.Delete(path);
File.WriteAllBytes(path, ...);
return expectedAttr.Equals(attr);
我该怎么做?
我可以重新编译这两个程序集。我不能使用 Mono.Cecil 或 IKVM 等第 3 方的东西。
我终于完成了这项工作:
var domain = AppDomain.CreateDomain(
"CompiledAssemblyCheck",
null,
new AppDomainSetup()
{
LoaderOptimization = LoaderOptimization.MultiDomainHost,
PrivateBinPath = Path.GetDirectoryName(Path.GetFullPath(otherAssembly)),
ShadowCopyFiles = "true"
});
try
{
var data = File.ReadAllBytes(otherAssembly);
string myPath = new Uri(executingAssembly.GetName().CodeBase).LocalPath;
var proxy = (AssemblyAnyLoadProxy)domain.CreateInstanceFromAndUnwrap(myPath, typeof(AssemblyAnyLoadProxy).FullName);
proxy.LoadFrom(myPath);
int outputAssemblyId = proxy.Load(data);
bool same = proxy.CompareAttribute(outputAssemblyId, typeof(MyAssemblyAttribute).FullName, expectedArgs);
}
finally
{
AppDomain.Unload(domain);
}
[Serializable]
class AssemblyAnyLoadProxy : MarshalByRefObject
{
List<Assembly> _assemblies=new List<Assembly>();
public int Load(byte[] raw)
{
Assembly asm = Assembly.ReflectionOnlyLoad(raw);
return Add(asm);
}
public int LoadFrom(string assemblyFile)
{
Assembly asm = Assembly.ReflectionOnlyLoadFrom(assemblyFile);
return Add(asm);
}
int Add(Assembly asm)
{
_assemblies.Add(asm);
return _assemblies.Count - 1;
}
public bool CompareAttribute(int assembly, string fullName, object[] args)
{
var attrs = CustomAttributeData.GetCustomAttributes(_assemblies[assembly]);
CustomAttributeData attr = attrs.FirstOrDefault(x => x.Constructor.DeclaringType.FullName == fullName);
return attr?.ConstructorArguments.Select(x => x.Value).SequenceEqual(args) ?? false;
}
}
程序集 A 声明了一个应用于程序集 B 的自定义程序集属性。
class MyAssemblyAttribute : Attribute { /* ... */ }
我需要从程序集 A 的方法中获取程序集 B 的属性数据,而不保持程序集 B 的加载。
程序集 A 中的伪代码:
var domain = AppDomain.Create();
MyAssemblyAttribute attr = null;
string path = "B.dll";
domain.DoCallback(() => {
attr = Assembly.Load(path)
.GetCustomAttributes()
.OfType<MyAssemblyAttribute>()
.First();
});
AppDomain.Unload(domain);
File.Delete(path);
File.WriteAllBytes(path, ...);
return expectedAttr.Equals(attr);
我该怎么做?
我可以重新编译这两个程序集。我不能使用 Mono.Cecil 或 IKVM 等第 3 方的东西。
我终于完成了这项工作:
var domain = AppDomain.CreateDomain(
"CompiledAssemblyCheck",
null,
new AppDomainSetup()
{
LoaderOptimization = LoaderOptimization.MultiDomainHost,
PrivateBinPath = Path.GetDirectoryName(Path.GetFullPath(otherAssembly)),
ShadowCopyFiles = "true"
});
try
{
var data = File.ReadAllBytes(otherAssembly);
string myPath = new Uri(executingAssembly.GetName().CodeBase).LocalPath;
var proxy = (AssemblyAnyLoadProxy)domain.CreateInstanceFromAndUnwrap(myPath, typeof(AssemblyAnyLoadProxy).FullName);
proxy.LoadFrom(myPath);
int outputAssemblyId = proxy.Load(data);
bool same = proxy.CompareAttribute(outputAssemblyId, typeof(MyAssemblyAttribute).FullName, expectedArgs);
}
finally
{
AppDomain.Unload(domain);
}
[Serializable]
class AssemblyAnyLoadProxy : MarshalByRefObject
{
List<Assembly> _assemblies=new List<Assembly>();
public int Load(byte[] raw)
{
Assembly asm = Assembly.ReflectionOnlyLoad(raw);
return Add(asm);
}
public int LoadFrom(string assemblyFile)
{
Assembly asm = Assembly.ReflectionOnlyLoadFrom(assemblyFile);
return Add(asm);
}
int Add(Assembly asm)
{
_assemblies.Add(asm);
return _assemblies.Count - 1;
}
public bool CompareAttribute(int assembly, string fullName, object[] args)
{
var attrs = CustomAttributeData.GetCustomAttributes(_assemblies[assembly]);
CustomAttributeData attr = attrs.FirstOrDefault(x => x.Constructor.DeclaringType.FullName == fullName);
return attr?.ConstructorArguments.Select(x => x.Value).SequenceEqual(args) ?? false;
}
}