2015-07-24 2 views
2

После нескольких часов, потраченных на эту тему (а также десятки посещенных страниц), я вынужден обратиться за помощью. Я видел много сообщений на эту тему, но я не мог исправить проблему, которую я получаю.Загрузка сборок во время выполнения, пример плагина Autocad

В принципе, я хочу сделать очень простую вещь: загрузить сборку из папки в приложение.


Вот вопрос короче (все остальные детали объясняются в остальной части текста) Я хочу использовать метод Assembly.LoadFrom, чтобы загрузить мои сборки (тот же файл сборки с привязкой проектом, но с CopyLocal лжи), но хотя сборка загружается при вызове метода, использующего его, программа пытается загрузить сборку из местоположений по умолчанию. Если он найден, то загружаются две одинаковые сборки, а программа использует последнюю, если она не найдена, тогда возникает событие FileNotFoundException. Но, когда я использую ту же идею в плагине Autocad, все работает, и исключение не возникает, хотя файлы не найдены.


У меня есть тестовое решение с двумя простыми проектами: TestExe (консольное приложение) и TestDll (библиотека DLL). Я хочу использовать типы из TestDll в TestExe, поэтому я добавил ссылку на TestDll (я не ссылаюсь на проект TestDll, но файл в указанном месте), но я хочу загрузить TestDll вручную во время выполнения. Почему это необходимо, объясняется в конце текста, с примером Autocad.

Насколько я понимаю, для этой цели может быть использован метод Assembly.LoadFrom. Итак, основная идея заключается в следующем: Load TestDll перед тем, как метод в TestExe использует его, поэтому, когда метод вызывается, мы уже загрузили сборку. Таким образом, независимо от того, существует ли ссылка dll в каталогах по умолчанию или нет, у нас уже есть сборка, и она будет использоваться.

Из MSDN:

Если сборка с той же идентичностью уже загружена, LoadFrom возвращает загруженную сборку, даже если другой путь был указан.

Так что я понимаю, что если я загружу dll один раз, каждый следующий груз такой же сборки (также из другого места) будет знать, что он уже добавлен, поэтому первый будет использоваться.


Проект: TestExe

//File: Program.cs 
using System; 
namespace TestExe 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine(new ClassExe().ExeMethod()); 
     } 
    }  
} 
//File: ClassExe.cs 
namespace TestExe 
{ 
    class ClassExe 
    { 
     public string ExeMethod() 
     { 
      return new TestDll.ClassDll().DllMethod(); 
     } 
    } 
} 

Проект TestDll

using System.Reflection; 

namespace TestDll 
{ 
    public class ClassDll 
    { 
     public string DllMethod() 
     { 
      return Assembly.GetExecutingAssembly().Location; 
     } 
    } 
} 

Как вы можете видеть, задача проста: показать местоположение вызываемого узла.

Скажем, TestDll.dll скопирован в папку приложения Extern \ TestDll.dll.

Если я установил свойство CopyLocal: false для ссылки на TestDll в проекте TestExe, программа завершится неудачно с FileNotFoundException. (1) Это потому, что сборка выполняется только в каталогах по умолчанию (application dir и TestDll \ TestDll.dll)?

Затем я попытался загрузить сборку вручную, прежде чем использовать его:

static void Main(string[] args) 
    { 
     Assembly.LoadFrom(@"Extern\TestDll.dll"); 
     Console.WriteLine(new ClassExe().ExeMethod()); 
    } 

Но я получаю же FileNotFoundException, хотя TestDll был загружен enter image description here

Когда я установить атрибут CopyLocal: верно, то программа работает. Но он снова загружает TestDll.dll из местоположения по умолчанию и игнорирует сборку, которая уже загружена. enter image description here

Единственный способ, которым я сумел программу для работы, как я хотел (чтобы использовать сборки Экстерн \ TestDll.dll) является с помощью AppDomain.AssemblyResolve событие.

У меня есть плагин Autocad, который использует 10 различных dll, из разных решений. Поэтому у меня есть plugin.dll, который ссылается на 10 файлов DLL в папке Documents и устанавливает CopyLocal: true. Но при развертывании для клиента я держу только файл plugin.dll в папке приложения, а все остальные DLL находятся в подкаталоге Libraries. В статическом конструкторе класса plugin я помещаю Assembly.LoadFrom для загрузки из подкаталога Libraries, и все работает нормально.

Извините за длинный пост, но я хотел объяснить его подробностями.

И спасибо за любую обратную связь :)

+0

Не уверен, что в плагине AutoCAD в конце вы разрабатываете встроенный или внешний (вне процесса) EXE? –

+0

@AugustoGoncalves In-process exe. Все делается на поток exe, скажем, простейшее консольное приложение. – miki

+0

Когда я говорю «in-process», я имею в виду DLL, которая является NETLOADed внутри AutoCAD и может использовать как .NET, так и COM API. Теперь EXE (вне процесса) будет запускаться в отдельном потоке и вызывать AutoCAD через COM API (Interop). Тем не менее, когда вы говорите «in-process» EXE, это не имеет смысла. –

ответ

1

Вы можете попробовать проверив контекст нагрузки сборки. Here пример и here - статья.

Основой является то, что AutoCad загружает свои сборки и сборки плагинов в том же AppDomain, где выполняется процесс acad.exe, поэтому они видны для всех других сборок, которые загружаются, потому что они являются ссылками на DLL подключаемого модуля. Но когда вы загружаете dll в процесс acad, он не будет виден для .exe и наоборот - они оба работают в разных app domains и не видят друг друга.

+0

Спасибо за ответ :) В этой статье я нашел единственное (для меня) разумное объяснение. Для ** Ни ** контекста: * «Ничто не может связываться с этой сборкой, если вы не подписались на событие AssemblyResolve». * Итак, если это так, мне нужно найти объяснение, почему оно загружено ни в контекст, когда я использую метод LoadFrom ... – miki

1

Для всех, кого интересует эта тема, я нашел ответ.

Я еще раз оглянусь назад: идея заключалась в том, чтобы ссылаться на dll из проекта и использовать его в коде, но позже, когда приложение развернуто для загрузки одной и той же dll вручную. Таким образом, загрузка ссылки dll завершится неудачно, но до этого dll будет загружаться вручную, поэтому все должно работать. Это было не так, потому что, хотя dll загружался вручную, он по-прежнему искал ссылку, и это создавало проблемы.

В this обширной статье о CLR Binder, этот пункт объясняет мою проблему:

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

Спасибо всем за ваше время и помощь.

Смежные вопросы