Как ссылаться на 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?,

Другие вопросы по тегам