Vlad
Vlad

Reputation: 3181

Get custom attribute data from assembly file and unlock it afterwise

Assembly A declares a custom assembly attribute which is applied in assembly B.

class MyAssemblyAttribute : Attribute { /* ... */ }

I need to get that attribute data from Assembly B from Assembly A method without keeping assembly B loaded.

Pseudo code in assembly 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);

How can I do it?

I can recompile both assemblies. I can't use 3rd party things like Mono.Cecil or IKVM.

Upvotes: 0

Views: 485

Answers (1)

Vlad
Vlad

Reputation: 3181

Finally I made this work:

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;
        }
    }

Upvotes: 1

Related Questions