Загрузка сборки Bytes теряет место - программирование
Подтвердить что ты не робот

Загрузка сборки Bytes теряет место

Я хочу загрузить сборку с помощью следующих

var loadedAssembly = Assembly.Load(File.ContentsAsBytes);

File.ContentAsBytes возвращает dll как byte[], используя следующие

System.IO.File.ReadAllBytes("dll location");

Проблема - загруженная сборка (loadedAssembly) теряет свое физическое местоположение

  • loadedAssembly.CodeBase - устанавливается в сборку, которая загружает его (что неверно)
  • loadedAssembly.Location - пусто.

Есть ли способ загрузить из byte[] и получить аналогичный результат в Assembly.LoadFile, так как мне нужен результат для работы с AppDomain.CurrentDomain.AssemblyResolve

4b9b3361

Ответ 1

Байт-массив byte[] - это просто поток байтов в памяти. Он не имеет никакого отношения ни к одному файлу. Этот массив байтов мог быть прочитан из файла, загружен с веб-сервера или создан спонтанно генератором случайных чисел. Нет дополнительных данных, которые "идут с ним".

Если вы хотите сохранить местоположение файла, из которого первоначально был загружен массив байтов, вы должны сохранить эти данные отдельно в другой переменной. Невозможно "прикрепить" дополнительные данные к переменной byte[].

Когда вы используете Assembly.Load для загрузки массива байтов в качестве сборки, он не знает, откуда пришел этот массив байтов, поскольку дополнительные данные не предоставляются функции Load.

В качестве обходного пути вы можете сохранить свой байт-массив во временный файл, используйте Assembly.LoadFile, чтобы предоставить вам нужные данные, и привяжите Location к исходному массиву байтов?

Ответ 2

Конечно, вы бы подумали, что у местоположения будет какой-то метод, который вы могли бы получить, или каким-либо другим способом его настройки. Это не так. Что происходит (я сбросил mscorlib.dll в IL DASM), так это то, что при загрузке из файла есть собственный дескриптор, связанный с сборкой в ​​классе RuntimeAssembly. Когда вы вызываете получателя местоположения, он захватывает этот дескриптор и дает вам местоположение из встроенного дескриптора, но только если он есть. Нет ручки, нет места.

Здесь IL:

.method public hidebysig specialname virtual 
    instance string  get_Location() cil managed
{
  .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       37 (0x25)
  .maxstack  3
  .locals init (string V_0)
  IL_0000:  ldnull
  IL_0001:  stloc.0
  IL_0002:  ldarg.0
  IL_0003:  call       instance class System.Reflection.RuntimeAssembly System.Reflection.RuntimeAssembly::GetNativeHandle()
  IL_0008:  ldloca.s   V_0
  IL_000a:  call       valuetype System.Runtime.CompilerServices.StringHandleOnStack System.Runtime.CompilerServices.JitHelpers::GetStringHandleOnStack(string&)
  IL_000f:  call       void System.Reflection.RuntimeAssembly::GetLocation(class System.Reflection.RuntimeAssembly,
                                                                       valuetype System.Runtime.CompilerServices.StringHandleOnStack)
  IL_0014:  ldloc.0
  IL_0015:  brfalse.s  IL_0023
  IL_0017:  ldc.i4.8
  IL_0018:  ldloc.0
  IL_0019:  newobj     instance void System.Security.Permissions.FileIOPermission::.ctor(valuetype System.Security.Permissions.FileIOPermissionAccess,
                                                                                     string)
  IL_001e:  call       instance void System.Security.CodeAccessPermission::Demand()
  IL_0023:  ldloc.0
  IL_0024:  ret
} // end of method RuntimeAssembly::get_Location

Ответ 3

Как только вы передадите метод byte[] в метод Assembly.Load, этот массив байтов не имеет абсолютно никакой информации, чтобы даже намекнуть на метод Load, откуда он пришел, - это всего лишь куча байтов. То же самое применимо, если вы скопировали файл в отдельное место:

File.Copy(dllLocation, anotherLocation);
var asm = Assembly.LoadFile(anotherLocation);

Место сборки будет указывать на anotherLocation, хотя сборка первоначально была на dllLocation. Аналогично, когда вы загружаете байты сборки (по существу копируя сборку с диска на память), "местоположение" этих байтов теперь является памятью.

Ответ 4

Я столкнулся с подобной ситуацией. Редко, что мне действительно нужно место, но для случаев, которые я делаю, я создал вспомогательный класс (AssemblyUtilities), который я использую для загрузки байтов для сборки и просто сохраняю местоположение в статическом словаре. Дополнительный вспомогательный метод (GetLocation) заглядывает в фактическое местоположение сборки, а если его нет, обратитесь к словарю. Это отлично работает, так как я несу ответственность за загрузку необработанных байтов в любом случае, а заглядывание обрабатывает сборки, загруженные "традиционным" способом. Так вот...

public static class AssemblyUtilities {
    private static Dictionary<Assembly, string> locationByAssembly =
        new Dictionary<Assembly, string>();

    private static Dictionary<string, Assembly> assemblyByLocation =
        new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);

    public static Assembly LoadFile(string location) {
        Assembly assembly;
        lock (locationByAssembly) {
            if (!assemblyByLocation.TryGetValue(location, out assembly)) {
                byte[] bytes = ReadAllBytes(location);
                if (bytes == null) return null;
                byte[] pdb = ReadAllBytes(Path.ChangeExtension(location, ".pdb"));
                assembly = ((pdb == null)? Assembly.Load(bytes): Assembly.Load(bytes, pdb));
                locationByAssembly[assembly] = location;
                assemblyByLocation[location] = assembly;
            }
            return assembly;
        }
    }

    public static string GetLocation(Assembly assembly) {
        if (assembly == null) return null;
        string location = assembly.Location;
        if (location == null) locationByAssembly.TryGetValue(assembly, out location);
        return location;
    }

    private static byte[] ReadAllBytes(string path) {
        try { return File.ReadAllBytes(path); }
        catch { return null; }
    }
}

// And if you prefer extensions...
public static class AssemblyExtensions {
    public static string GetLocation(this Assembly self) {
        return AssemblyUtilities.GetLocation(self);
    }
}