如何将位于不同文件夹中的C#dll引用到使用该dll公开的接口的不同文件夹中的C#winforms exe

时间:2013-10-22 06:10:50

标签: c# .net winforms dll-reference

我有一个C#窗体,它使用位于不同于EXE位置的文件夹中的Utility.dll。 Utility.dll包含一个类UtilityClass和一个接口ILoadString。当我没有在Form1.cs类中继承ILoadString接口时,我成功地能够通过Program.cs中的AppDomain.AssemblyResolve事件加载Utility.dll。

当我尝试在Form1.cs中继承ILoadString接口时出现问题。当我尝试运行该项目时,我从visual studio收到一个FileNotFoundException,说“无法加载文件或程序集”实用程序,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'或其依赖项之一。系统无法找到指定的文件。“ 该控件甚至没有在Program.cs中出现静态void Main()。我认为CLR正试图在开始时加载Utility.dll,因为我的Form1.cs继承了ILoadString。

注意:在Add reference Utility.dll中,copy local设置为“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 !";
    }
}

这是Utility.dll的UtilityClass.cs

namespace Utility
{
    public class UtilityClass
    {
        private string sName = string.Empty;

        public UtilityClass()
        {
            sName = "My Name";
        }

        public string GetName()
        {
            return sName;
        }
    }
}

这是来自Utility.dll的接口ILoadString.cs

namespace Utility
{
    public interface ILoadString
    {
        void ChangeName();
    }
}

3 个答案:

答案 0 :(得分:1)

您有copy local is set to "false"的具体原因吗? 它认为你最好把它设置为真 另一种选择是使用构建事件将其复制到bin文件夹,或使用反射动态加载它 但是,正如我所说,我认为将copy local设置为true会更好。

编辑:(根据Ezhirko的评论)

您可以在ProgramSampleForm的静态构造函数中添加加载程序集:

static class Program
{
    static Program()
    {
        //Loads assemblies. 
    }

   //the rest of your code...

}

答案 1 :(得分:0)

首先加载程序集,然后实例化依赖它的类。 您当前的代码必须延迟创建Form1,直到您加载程序集(并在单独的方法中执行它,因为JITing方法需要类'methadata)。

其他选项:您可以将搜索路径配置为probing path中涵盖的How to add folder to assembly search path at runtime in .NET?中的“其他位置”(如果它是您应用的子文件夹)。

答案 2 :(得分:0)

我不知道它是否是故意的,但是你的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();

另外 - 关于立即触发的resolve事件。 如果要确保在设置了解析处理程序之后“触发”事件,则需要将属性/字段嵌入在设置解析处理程序后实例化的子类中。 如果您将其作为主窗体上的属性,那么将尝试在父类创建时创建这些属性。 即使它们为null,也使用类型,如果使用该类型,它将尝试同时将dll拉入内存。 因此,如果您将声明放入子类中,只在设置了解析后对其进行实例化,那么它就不会打扰您丢失的DLL。