2

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

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

注意:在添加引用 Utility.dll 的复制本地设置为“假”。所以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();
    }
}
4

3 回答 3

1

你有什么特别的原因copy local is set to "false"吗?
它认为您最好将其设置为true。
另一种选择是使用构建事件将其复制到您的bin文件夹,或使用反射动态加载它。
但是,正如我所说,我认为最好简单地设置copy localtrue.

编辑:(根据 Ezhirko 的评论)

Program您可以在或的静态构造函数中添加加载程序集SampleForm

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

   //the rest of your code...

}
于 2013-10-22T06:14:33.873 回答
0

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

其他选项:您可以配置搜索路径以包含“其他位置”(如果它是您的应用程序的子文件夹)作为探测路径,如如何在 .NET 运行时将文件夹添加到程序集搜索路径?.

于 2013-10-22T06:20:25.223 回答
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();

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

于 2016-01-26T10:53:34.850 回答