2013-06-03 12 views
5

У меня есть приложение C#, которое взаимодействует с некоторым оборудованием (устройством USB) следующим образом: C# application -> intermediate DLL -> hardware DLL -> hardware. Промежуточная DLL и аппаратная DLL поставляются с USB-устройством, поэтому я не могу контролировать их.Загрузка нескольких версий DLL

промежуточная DLL является единственной, которую мне нужно включить в проект VS, поскольку это то, что я называю. Аппаратная DLL находится в том же каталоге, поэтому ее необходимо найти автоматически.

Новая версия аппаратного устройства теперь выпускается с другой аппаратной DLL DLL. Старая DLL несовместима с новым оборудованием, и новая DLL несовместима со старым оборудованием.

Как я могу заставить приложение работать с обеими аппаратными средствами? Я предполагаю, что мне нужно загружать и выгружать каждую DLL по мере необходимости?

+2

Сначала вам нужно определить, какое оборудование вы планируете. Вы еще это решили? –

+0

Я могу либо сделать это с помощью раскрывающегося списка - или, в идеале, путем загрузки одной DLL и сканирования на аппаратное обеспечение, затем загрузки другой DLL и сканирования аппаратного обеспечения .. если возможно .. – Mark

+0

Является промежуточной DLL родной DLL? – Dennis

ответ

3

Вот что я делаю для аналогичной проблемы. У меня есть кусок кода, с которым я хочу работать, но мне нужно загрузить DLL во время выполнения. Поэтому я ссылаюсь на него в своем проекте, но я не помещаю его в тот же каталог, что и остальные мои сборки. Вместо этого, в потребляющей коде, у меня есть некоторый код, который выглядит следующим образом:

// constructor called from a static constructor elsewhere 
MyDllLoader(string hardwareFolder) { 
    _hardwareFolder = hardwareFolder; 
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
    SeeIfAlreadyLoaded(); 
} 


private void SeeIfAlreadyLoaded() { 
    // if the assembly is still in the current app domain then the AssemblyResolve event will 
    // never fire. 
    // Since we need to know where the assembly is, we have to look for it 
    // here. 
    Assembly[] assems = AppDomain.CurrentDomain.GetAssemblies(); 
    foreach (Assembly am in assems) 
    { 
     // if it matches, just mark the local _loaded as true and get as much 
     // other information as you need 
    } 
} 

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { 
    string name = args.Name; 
    if (name.StartsWith("Intermediate.dll,")) 
    { 
     string candidatePath = Path.Combine(_hardwareFolder, "Intermediate.dll"); 
     try { 
      Assembly assem = Assembly.LoadFrom(candidatePath); 
      if (assem != null) { 
       _location = candidateFolder; 
       _fullPath = candidatePath; 
       _loaded = true; 
       return assem; 
      } 
     } 
     catch (Exception err) { 
      sb.Append(err.Message); 
     } 
    } 
    return null; 
} 

Там другое решение тоже - это сложно, но я сделал это и сделал для вас работу. Вы объявляете абстрактный класс, скажем, MyHardwareAbstraction, который имеет подписи методов, которые вы хотите, и вы кодируете этот интерфейс. Затем вы пишете код, который задает путь к сборке, загружает его и динамически определяет новый класс, который соответствует MyHardwareAbstraction, и делает его сопоставлением с экземпляром фактического объекта, который вы хотите. I wrote a blog several years ago on how to do this.

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

+0

Спасибо, это дало самые важные подсказки, которые привели меня к окончательному решению. – Mark

0

Edit:

Если промежуточная DLL является сборкой .NET, вы можете использовать метод, упомянутым here указать, где искать для промежуточной DLL перед тем вызова любого метода, который использует промежуточный DLL , без необходимости менять существующий код.

то вы не должны напрямую ссылаться на DLL в своем проекте C#, потому что .Net Assemblies обнаружены и загружены до того, как ваш метод Main даже называется. Вместо этого вы должны динамически загружать промежуточную DLL с помощью AppDomain или другими способами, затем использовать библиотеку через отражение или используя объекты dynamic.

По-видимому, это сделало бы программирование очень громоздким. Однако существует альтернативный метод. Вы можете написать программу запуска , которая загружает ваше исходное приложение (вы можете загружать файлы .exe в виде библиотек) и вызывает рефлексивный метод вашей оригинальной программы Main. Чтобы загрузить правильную промежуточную DLL, вы можете использовать упомянутый метод here, в то время как ваша программа запуска загружает ваше оригинальное приложение.

Следующее обсуждение по-прежнему относится к аппаратной DLL.


Следующая является действительным, если:

  1. Вам нужно только одну версию DLL в то время (в течение всего периода запускает приложение), и
  2. Две версии промежуточного DLL имеют точно такой же API.

Согласно MSDN, путь поиска DLL включает каталоги, указанные в переменной среды PATH. (http://msdn.microsoft.com/en-us/library/7d83bc18%28v=vs.80%29.aspx).Таким образом, вы можете поместить две версии промежуточных библиотек DLL под отдельными подкаталогами в вашем каталоге приложения, но с точно таким же именем, под каждую директорию, например:

bin\ 
    hardware-intermediate-v1\ 
     intermediate.dll 
    hardware-intermediate-v2\ 
     intermediate.dll 

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

using System; 
using System.Reflection; 
using System.IO; 
... 
Environment.SetEnvironmentVariable(
    "PATH", 
    Environment.GetEnvironmentVariable("PATH") + ";" + 
     Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + 
     "\\hardware-intermediate-v1" 
); 

Тогда, вызовы методов P-Invoke (DllImport) приведет к соответствующей версии DLL к загружаться. Чтобы сразу загрузить все DLL-файлы, вы можете обратиться к DllImport, how to check if the DLL is loaded?.

Однако, если вы хотите использовать две версии DLL вместе, не перезагружая приложение, или если есть какая-либо разница API между двумя DLL на уровне имени метода и/или параметра count/type, вы должны создайте два отдельных набора методов P-Invoke, каждый из которых привязывается к соответствующей версии промежуточной DLL.

+0

Это не работает в моем проекте. Если я назову 'Marshal.PrelinkAll (Type)', как это предложено в ссылке, это тоже не помогает. Также нет никакой возвращаемой ценности, чтобы знать, действительно ли она что-то сделала. – Mark

+0

RE обновление, похожее на то, что я уже сделал, но не повезло :( – Mark

0

Я хочу, чтобы обе библиотеки dll сосуществовали в программе, вам придется использовать AppDomains, как объяснено here.

Кроме того, вы можете просто использовать LoadLibrary после того, как пользователь сделал четкий выбор о том, какая версия ему нужна?

+0

Я могу видеть, как это можно использовать для загрузки промежуточной DLL, но если это вызывает аппаратную DLL, есть ли способ указать, что он должен использовать данный AppDomain? – Mark

+0

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

+1

Является ли промежуточная DLL сборка .Net? – Interarticle