C# 从另一个文件夹解析程序集引用

C# 从另一个文件夹解析程序集引用,c#,.net,clr,assembly-resolution,C#,.net,Clr,Assembly Resolution,我正在开发一个应用程序,它引用并使用来自某个供应商的一些第三方程序集;在“开发”框中,我的源代码树中的引用文件夹中有这3个程序集,我可以引用它们并构建应用程序,应用程序生成但不运行,因为整个服务器应用程序未安装,但这很好 在要复制此自定义应用程序并运行其所引用的所有程序集的服务器上,这些程序集位于类似以下文件夹的位置: D:\ProgramFiles\VendorName\ProductName\Support\API\Bin64 如果我将我的小可执行文件复制到该文件夹中并运行它,它会工作得很

我正在开发一个应用程序,它引用并使用来自某个供应商的一些第三方程序集;在“开发”框中,我的源代码树中的引用文件夹中有这3个程序集,我可以引用它们并构建应用程序,应用程序生成但不运行,因为整个服务器应用程序未安装,但这很好

在要复制此自定义应用程序并运行其所引用的所有程序集的服务器上,这些程序集位于类似以下文件夹的位置:

D:\ProgramFiles\VendorName\ProductName\Support\API\Bin64
如果我将我的小可执行文件复制到该文件夹中并运行它,它会工作得很好,但是如果我将我的.exe放在我想要的更合适的文件夹中:

D:\ProgramFiles\MyCompanyName\MyProduct\bin\...
它无法工作,因为它无法解析这些程序集

我知道我可以使用app.config中的探测来指定我的exe必须在哪些文件夹中查找引用,但在我的情况下,程序集不在子文件夹中,更多的是在完全不同的位置

我不想复制我的应用程序文件夹中的所有供应商程序集,而且我不能只将我引用的3个程序集放在那里,因为它们也正在加载其他程序集,除非我拥有所有程序集(许多…),否则无法工作

我没有做任何特别的事情,没有创建应用程序域,也没有通过反射加载程序集,只是希望CLR在应用程序启动或执行时解析引用

谢谢

编辑:这里是最终工作代码

static System.Reflection.Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    Logger logger = new Logger();

    try
    {
        string RMSAssemblyFolder = ConfigurationManager.AppSettings["RMSAssemblyFolder"];

        Assembly MyAssembly = null;
        string strTempAssmbPath = string.Empty;

        Assembly objExecutingAssemblies = Assembly.GetExecutingAssembly();
        AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();

        AssemblyName myAssemblyName = Array.Find<AssemblyName>(arrReferencedAssmbNames, a => a.Name == args.Name);

        if (myAssemblyName != null)
        {
            MyAssembly = Assembly.LoadFrom(myAssemblyName.CodeBase);
        }
        else
        {
            strTempAssmbPath = Path.Combine(RMSAssemblyFolder, args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll");

            if (!string.IsNullOrEmpty(strTempAssmbPath))
            {
                if (File.Exists(strTempAssmbPath))
                {
                    logger.Information("Assembly to load: {0} - File was found in: {1}", args.Name, strTempAssmbPath);

                    // Loads the assembly from the specified path.                  
                    MyAssembly = Assembly.LoadFrom(strTempAssmbPath);
                }
            }
        }

        // Returns the loaded assembly.
        return MyAssembly;
    }
    catch (Exception exc)
    {
        logger.Error(exc);
        return null;
    }
}
static System.Reflection.Assembly currentDomain_AssemblyResolve(对象发送方,ResolveEventArgs args args)
{
记录器=新记录器();
尝试
{
字符串RMSAssemblyFolder=ConfigurationManager.AppSettings[“RMSAssemblyFolder”];
Assembly MyAssembly=null;
string strTempAssmbPath=string.Empty;
Assembly objExecutingAssemblies=Assembly.getExecutingGassembly();
AssemblyName[]arrReferencedAssmbNames=ObjExecutingAssemblys.GetReferencedAssemblys();
AssemblyName myAssemblyName=Array.Find(arrReferencedAssmbNames,a=>a.Name==args.Name);
如果(myAssemblyName!=null)
{
MyAssembly=Assembly.LoadFrom(myAssemblyName.CodeBase);
}
其他的
{
strTempAssmbPath=Path.Combine(RMSAssemblyFolder,args.Name.Substring(0,args.Name.IndexOf(“,”)+“.dll”);
如果(!string.IsNullOrEmpty(strTempAssmbPath))
{
if(File.Exists(strTempAssmbPath))
{
logger.Information(“要加载的程序集:{0}-在:{1}”、args.Name、strTempAssmbPath中找到了文件);
//从指定路径加载程序集。
MyAssembly=Assembly.LoadFrom(strTempAssmbPath);
}
}
}
//返回加载的程序集。
返回MyAssembly;
}
捕获(异常exc)
{
记录器错误(exc);
返回null;
}
}
使用:SN-T VendorAssembly.dll,这将返回一个十六进制数,该十六进制数是公钥令牌,然后从app.config引用程序集。要获取版本,请右键单击供应商程序集,并将其用作代码库版本值,即您提到的href=路径

  <configuration>
       <runtime>
          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
             <dependentAssembly>
                <assemblyIdentity name="VendorAssembly"  culture="neutral" publicKeyToken="307041694a995978"/>
                <codeBase version="1.1.1.1" href="FILE://D:/ProgramFiles/VendorName/ProductName/Support/API/Bin64/VendorAssembly.dll"/>
             </dependentAssembly>
          </assemblyBinding>
       </runtime>
    </configuration>

您应该首先找到安装这些DLL的文件夹,然后使用该文件夹挂接程序集解析并尝试从此文件夹加载请求的程序集

它看起来像这样(未测试,您需要检查
args.Name
确切包含的内容,可能包含版本和强名称以及类型名称):


嗨,希望它能工作,我仍然得到FileNotFound异常。。。我需要引用的程序集不是强名称,我想,SN-t不起作用,但我从异常消息中看到了PublicKeyToken,所以我从那里复制了它的值,仍然无法解析…:(谢谢,我已经修复了在编辑的问题中使用您的想法和我的代码的问题,现在效果很好:)当然我的解决方案可以优化,我不确定如果在自定义文件夹中找不到程序集,此方法是否必须返回null,如果是,CLR仍将在GAC中查找它,因为例如,我注意到这个方法也被像System.XML.Serializer这样的程序集调用。。。我正在一个控制台应用程序中测试这一点,该应用程序承载一个绑定netTcp的WCF服务,现在它可以工作了,我可以从任何文件夹运行该应用程序,最后我将创建一个WindowsService来承载我的WCF。再次感谢。))我发现这种方法不适合我。具有完整路径的Assembly.Load()失败;它再次调用AssemblyResolve(因为它无法解析DLL)。因此,我目前正在研究如何在AssemblyResolve处理程序中使用Assembly.LoadFrom()。'var dll=otherCompanyDlls.FirstOrDefault(fi=>fi.Name==args.Name)'-在每次命中或未命中时对列表进行Linear扫描。如果请求了大量oaf,并且存在程序集,则可能会出现性能问题。每次某个程序集未解析时(每个加载上下文中每个未知程序集最多一次),都会对一个小集合(文件夹中的DLL数)进行线性扫描。这听起来像是过早的优化(1.不要这样做。2.仅供专家使用!:现在不要这样做)。但无论如何,一个简单的
var otherCompanyDllsDict=otherCompanyDlls.ToDictionary(fi=>fi.Name)
然后通过字典访问应该可以解决问题。可能的
var otherCompanyDlls = new DirectoryInfo(companyFolder).GetFiles("*.dll");

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var dll = otherCompanyDlls.FirstOrDefault(fi => fi.Name == args.Name);
    if (dll == null)
    {
        return null;
    }

    return Assembly.Load(dll.FullName);
};