Как ссылаться на C# dll, расположенную в другой папке, на C# winforms exe в другой папке, которая использует интерфейс, предоставляемый этой dll
У меня есть формы C# windows, который использует Utility.dll, расположенный в другой папке, чем в папке EXE. Utility.dll содержит класс UtilityClass и интерфейс ILoadString. Когда я не наследую интерфейс ILoadString в своем классе Form1.cs, я успешно могу загрузить Utility.dll через событие AppDomain.AssemblyResolve в Program.cs.
Проблема возникает, когда я пытаюсь наследовать интерфейс ILoadString в Form1.cs. Когда я пытаюсь запустить проект, я получаю исключение FileNotFoundException от Visual Studio, в котором говорится: "Не удалось загрузить файл или сборку". Утилита, версия =1.0.0.0, культура = нейтральная, PublicKeyToken=null "или одна из ее зависимостей. Система не может найти указанный файл. " Элемент управления даже не попадает в статический void Main() в Program.cs. Я думаю, что CLR пытается загрузить Utility.dll в самом начале, так как мой Form1.cs наследует ILoadString.
Примечание. В поле Добавить ссылку на локальный экземпляр Utility.dll задано значение "false". поэтому в папке Exe нет этой dll.
Как мне загрузить Utility.dll из другой папки в этом случае? Любая помощь приветствуется.
Заранее спасибо.
Я вставляю свой код ниже.
using System.Reflection;
namespace SampleForm
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
Assembly MyAssembly, objExecutingAssemblies;
string strTempAssmbPath = "";
objExecutingAssemblies = Assembly.GetExecutingAssembly();
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
{
if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
{
strTempAssmbPath = "D:\\Ezhirko\\SampleForm\\bin\\Common\\" +
args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
}
}
MyAssembly = Assembly.LoadFrom(strTempAssmbPath);
return MyAssembly;
}
}
Моя форма Windows здесь....
using Utility;
namespace SampleForm
{
public partial class Form1 : Form, ILoadString
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string Name = string.Empty;
UtilityClass obj = new UtilityClass();
Name = obj.GetName();
this.lblHello.Text = Name;
ChangeName();
}
public void ChangeName()
{
this.lblHello.Text = "InterFace Called !";
}
}
Это UtilityClass.cs из Utility.dll
namespace Utility
{
public class UtilityClass
{
private string sName = string.Empty;
public UtilityClass()
{
sName = "My Name";
}
public string GetName()
{
return sName;
}
}
}
Это интерфейс ILoadString.cs из Utility.dll
namespace Utility
{
public interface ILoadString
{
void ChangeName();
}
}
3 ответа
Есть ли конкретная причина у вас есть copy local is set to "false"
?
Думаю, вам лучше установить его на true.
Другой вариант будет копировать его на ваш bin
папку, используя события сборки, или загружая ее динамически, используя отражение.
Но опять же, как я уже сказал, я думаю, что было бы лучше просто установить copy local
в true
,
РЕДАКТИРОВАТЬ: (согласно комментарию Ежирко)
Вы можете добавить загрузку сборок в статический конструктор Program
или в SampleForm
:
static class Program
{
static Program()
{
//Loads assemblies.
}
//the rest of your code...
}
Я не знаю, является ли это преднамеренным, но ваш цикл foreach генерирует путь для каждой сборки, но вы загружаете только последнюю сборку, так как Assembly.Load находится вне цикла.
foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
{
if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
{
strTempAssmbPath = "D:\\Ezhirko\\SampleForm\\bin\\Common\\" +
args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
}
}
MyAssembly = Assembly.LoadFrom(strTempAssmbPath);
return MyAssembly;
Я предполагаю, что вы пытаетесь загрузить определенную сборку в этом случае. Я использую этот код в своих собственных приложениях: он может быть проще в использовании.
Этот код был написан с целью позволить мне разрешить сборки из встроенных ресурсов в самой DLL/APPLICATION. Если все сделано правильно, вы можете даже сжать DLL и распаковать их во время выполнения.
Примечание: этот может быть проще в использовании, поскольку он избегает той стратегии зацикливания, которую вы использовали.
/// <summary>
/// This is the handler for failed assembly resolution attempts - when a failed resolve event fires, it will redirect the assembly lookup to internal
/// embedded resources. Not necessary for this method to ever be called by the user.
/// </summary>
/// <param name="sender">not important</param>
/// <param name="args">automatically provided</param>
/// <returns>returns the assembly once it is found</returns>
private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
string[] toSplitWith = { "," };
//New improvement - Allows for ANY DLL to be resolved to a local memory stream.
bool bCompressed = true;
string sFileName = args.Name.Split(toSplitWith, StringSplitOptions.RemoveEmptyEntries)[0] + ".dll";
string sPath = "NameSpace.Resources.";
Assembly thisAssembly = Assembly.GetExecutingAssembly(); //Gets the executing Assembly
using (Stream input = thisAssembly.GetManifestResourceStream(sPath + sFileName)) // Acquire the dll from local memory/resources.
{
return input != null ? Assembly.Load(StreamToBytes(input)) : null; // More bitwise operators - if input not Null, return the Assembly, else return null.
}
}
Пока я использую sPath = "NameSpace.Resources";
однако вы можете указать его в другом месте на вашем компьютере, а затем просто Assembly.LoadFrom, вместо того, чтобы беспокоиться о GetManifestResourceStream ();
Также - о том, что событие разрешения запускается немедленно. Если вы хотите убедиться, что событие запускается "после" установки обработчика разрешения, вам необходимо встроить свойство / поле в подкласс, который создается после установки обработчика разрешения. Если у вас есть это свойство в главной форме, эти свойства будут пытаться быть созданы во время создания родительского класса. Даже если они имеют значение null, тип используется, и если тип используется, он будет пытаться одновременно извлечь dll в память. Поэтому, если вы поместите объявление в подкласс, создаете его экземпляр только после того, как настроено разрешение, тогда оно не должно беспокоить вас по поводу отсутствия DLL.
Сначала загрузите сборку, а затем создайте экземпляры классов, которые зависят от нее. Ваш текущий код должен будет отложить создание Form1
пока вы не загрузите сборки (и не выполните это в отдельном методе, так как JIT для метода требуется класс ' metadata).
Другой вариант: вы можете настроить путь поиска, чтобы включить "другое местоположение" (если это подпапка вашего приложения) в качестве пути поиска, как описано в разделе Как добавить папку в путь поиска сборки во время выполнения в.NET?,