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

9

我正在开发一个应用程序,引用并使用某个供应商的一些第三方程序集;在开发机上,我将这3个程序集放在我的源代码树中的一个参考文件夹中,我可以引用它们并构建应用程序,应用程序构建成功,但无法运行,因为整个服务器应用程序没有安装,但这没关系。

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

D:\ProgramFiles\VendorName\ProductName\Support\API\Bin64

如果我将我的小型可执行文件复制到该文件夹中并运行它,它可以完美运行,但是如果我将我的.exe文件放在一个更合适的文件夹中,比如我想要的那样:

D:\ProgramFiles\MyCompanyName\MyProduct\bin\...

由于它无法解析这些程序集,所以它不能工作。

我知道我可以在app.config中使用probing来指定我的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;
    }
}

可能是 https://dev59.com/vXM_5IYBdhLWcg3wcSx_ 的重复问题。 - nawfal
2个回答

14

你应该首先找到安装这些dll的文件夹,然后使用AppDomain.AssemblyResolve来挂钩程序集解析并尝试从该文件夹加载所需的程序集。

它会类似于这样(未经测试,并且您需要检查args.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);
};

谢谢,我已经使用你的想法和我的代码在编辑后的问题中进行了修复,现在运行良好 :) - Davide Piras
我的解决方案肯定可以进行优化,我不确定如果自定义文件夹中找不到程序集,是否需要返回 null。如果是这样,CLR仍将在 GAC 中查找它,因为例如我已经注意到此方法也用于像System.XML.Serializer之类的程序集...我正在控制台应用程序中测试这个方法,该应用程序托管了一个 netTcp 绑定的 WCF 服务,现在它可以工作并且我可以从任何文件夹运行该应用程序,在最终我将创建一个 WindowsService 来托管我的 WCF。再次感谢.)) - Davide Piras
我发现这种方法对我不起作用。使用完整路径的Assembly.Load()失败了;它再次调用了AssemblyResolve(因为它无法解析DLL)。所以我目前正在研究在AssemblyResolve处理程序中使用Assembly.LoadFrom()。 - Per Lundberg
'var dll = otherCompanyDlls.FirstOrDefault(fi => fi.Name == args.Name)' - 每次命中或未命中都会导致列表的线性扫描。对于请求和现有程序集数量较大的情况可能会造成性能问题。 - George Mamaladze
1
每次某个程序集无法解析(每个加载上下文中最多一次),都会在一个小集合(文件夹中的dll数量)上进行线性扫描。这听起来像是过早优化(_1.不要这样做。2.仅限专家!:暂时不要这样做)。但是,如果发生这种情况,一个简单的var otherCompanyDllsDict = otherCompanyDlls.ToDictionary(fi => fi.Name),然后通过字典访问应该可以解决它。 - Julien Roncaglia
AssemblyResolve 是针对 CurrentDomain 的,不适用于另一个域 AppDomain.CreateDomain - Kiquenet

0
使用 SN.exe :SN -T VendorAssembly.dll,这将返回一个十六进制数字,即公钥令牌,然后从 app.config 引用程序集。要获取版本,请右键单击供应商程序集并使用该值作为 codeBase 版本值,href=path 你提到的。
  <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>

嗨,希望它能工作,但我仍然遇到了“FileNotFound”异常... 我需要引用的程序集可能没有强名称,SN -t 没有起作用,但我从异常消息中看到了PublicKeyToken,所以我从那里复制了它的值,但仍然无法解决... :( - Davide Piras

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接