我有一个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();
}
}
答案 0 :(得分:1)
您有copy local is set to "false"
的具体原因吗?
它认为你最好把它设置为真
另一种选择是使用构建事件将其复制到bin
文件夹,或使用反射动态加载它
但是,正如我所说,我认为将copy local
设置为true
会更好。
编辑:(根据Ezhirko的评论)
您可以在Program
或SampleForm
的静态构造函数中添加加载程序集:
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。