Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在.NET运行时将文件夹添加到程序集搜索路径?_.net_Search_Assemblies_Path - Fatal编程技术网

如何在.NET运行时将文件夹添加到程序集搜索路径?

如何在.NET运行时将文件夹添加到程序集搜索路径?,.net,search,assemblies,path,.net,Search,Assemblies,Path,我的DLL由第三方应用程序加载,我们无法自定义。我的程序集必须位于它们自己的文件夹中。我无法将它们放入GAC(我的应用程序需要使用XCOPY进行部署)。 当根DLL尝试从另一个DLL(在同一文件夹中)加载资源或类型时,加载失败(FileNotFound)。 是否可以通过编程方式(从根DLL)将DLL所在的文件夹添加到程序集搜索路径?我不允许更改应用程序的配置文件。您可以在应用程序的.config文件中添加,但只有当探测路径包含在应用程序的基本目录中时,它才会起作用。听起来您可以使用AppDoma

我的DLL由第三方应用程序加载,我们无法自定义。我的程序集必须位于它们自己的文件夹中。我无法将它们放入GAC(我的应用程序需要使用XCOPY进行部署)。 当根DLL尝试从另一个DLL(在同一文件夹中)加载资源或类型时,加载失败(FileNotFound)。
是否可以通过编程方式(从根DLL)将DLL所在的文件夹添加到程序集搜索路径?我不允许更改应用程序的配置文件。

您可以在应用程序的.config文件中添加,但只有当探测路径包含在应用程序的基本目录中时,它才会起作用。

听起来您可以使用AppDomain.AssemblyResolve事件并从DLL目录手动加载依赖项

编辑(从评论中):


查看AppDomain.AppendPrivatePath(已弃用)或AppDomainSetup.PrivateBinPath

最佳解释:

框架4的更新

由于Framework4也为资源引发AssemblyResolve事件,因此该处理程序实际上工作得更好。它基于这样一个概念,即本地化是应用程序内的子目录(其中一个用于本地化,名称为区域性,即C:\MyApp\It for Italian) 里面有资源文件。 如果本地化是国家/地区,即it或pt BR,则处理程序也可以工作。在这种情况下,处理程序“可能被调用多次:对回退链中的每个区域性调用一次”[来自MSDN]。这意味着,如果我们为“it-it”资源文件返回null,框架将引发请求“it”的事件

事件挂钩

        AppDomain currentDomain = AppDomain.CurrentDomain;
        currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);
事件处理程序

    Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        //This handler is called only when the common language runtime tries to bind to the assembly and fails.

        Assembly executingAssembly = Assembly.GetExecutingAssembly();

        string applicationDirectory = Path.GetDirectoryName(executingAssembly.Location);

        string[] fields = args.Name.Split(',');
        string assemblyName = fields[0];
        string assemblyCulture;
        if (fields.Length < 2)
            assemblyCulture = null;
        else
            assemblyCulture = fields[2].Substring(fields[2].IndexOf('=') + 1);


        string assemblyFileName = assemblyName + ".dll";
        string assemblyPath;

        if (assemblyName.EndsWith(".resources"))
        {
            // Specific resources are located in app subdirectories
            string resourceDirectory = Path.Combine(applicationDirectory, assemblyCulture);

            assemblyPath = Path.Combine(resourceDirectory, assemblyFileName);
        }
        else
        {
            assemblyPath = Path.Combine(applicationDirectory, assemblyFileName);
        }



        if (File.Exists(assemblyPath))
        {
            //Load the assembly from the specified path.                    
            Assembly loadingAssembly = Assembly.LoadFrom(assemblyPath);

            //Return the loaded assembly.
            return loadingAssembly;
        }
        else
        {
            return null;
        }

    }
Assembly currentDomain\u AssemblyResolve(对象发送方,ResolveEventArgs args args)
{
//只有当公共语言运行库尝试绑定到程序集并失败时,才会调用此处理程序。
Assembly ExecutionGassembly=Assembly.getExecutionGassembly();
字符串applicationDirectory=Path.GetDirectoryName(executingAssembly.Location);
string[]fields=args.Name.Split(',');
字符串assemblyName=字段[0];
串文化;
如果(字段长度<2)
assemblyCulture=null;
其他的
assemblyCulture=字段[2]。子字符串(字段[2]。IndexOf('=')+1);
字符串assemblyFileName=assemblyName+“.dll”;
字符串组装路径;
if(assemblyName.EndsWith(“.resources”))
{
//特定资源位于应用程序子目录中
字符串resourceDirectory=Path.Combine(applicationDirectory,assemblyCulture);
assemblyPath=Path.Combine(resourceDirectory,assemblyFileName);
}
其他的
{
assemblyPath=Path.Combine(applicationDirectory,assemblyFileName);
}
if(File.Exists(assemblyPath))
{
//从指定路径加载程序集。
Assembly loadingAssembly=Assembly.LoadFrom(assemblyPath);
//返回加载的程序集。
返回装载组件;
}
其他的
{
返回null;
}
}

对于C++/CLI用户,这里是@Mattias S的答案(对我来说很有用):


我用了@Mattias S的解决方案。如果您确实想要解析来自同一文件夹的依赖项,您应该尝试使用请求程序集位置,如下所示参数。应检查RequestingAssembly是否为空

System.AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
{
    var loadedAssembly = System.AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName == args.Name).FirstOrDefault();
    if(loadedAssembly != null)
    {
        return loadedAssembly;
    }

    if (args.RequestingAssembly == null) return null;

    string folderPath = Path.GetDirectoryName(args.RequestingAssembly.Location);
    string rawAssemblyPath = Path.Combine(folderPath, new System.Reflection.AssemblyName(args.Name).Name);

    string assemblyPath = rawAssemblyPath + ".dll";

    if (!File.Exists(assemblyPath))
    {
        assemblyPath = rawAssemblyPath + ".exe";
        if (!File.Exists(assemblyPath)) return null;
    } 

    var assembly = System.Reflection.Assembly.LoadFrom(assemblyPath);
    return assembly;
 };
我是从关于将探测标记添加到App.Config文件来这里的

我想在此添加一个旁注-Visual studio已经生成了一个App.config文件,但是将探测标记添加到预生成的运行时标记不起作用!您需要一个包含探测标记的单独运行时标记。简而言之,您的App.Config应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

  <!-- Discover assemblies in /lib -->
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="lib" />
    </assemblyBinding>
  </runtime>
</configuration>

谢谢你,马蒂亚斯!这是有效的:AppDomain currentDomain=AppDomain.currentDomain;currentDomain.AssemblyResolve+=新的ResolveEventHandler(LoadFromSameFolderResolveEventHandler);静态程序集LoadFromSameFolderResolveEventHandler(对象发送方,ResolveEventArgs args args){string folderPath=Path.GetDirectoryName(Assembly.GetExecutionGassembly().Location);string assemblyPath=Path.Combine(folderPath,args.Name+“.dll”);程序集程序集=Assembly.LoadFrom(assemblyPath);return assembly;}如果要“回退”到基本解析程序,您会怎么做。e、 如果(!File.Exists(asmPath))返回searchInGAC(…)这起作用了,我还没有找到任何替代方案。ThanksFrom:更改AppDomainSetup实例的属性不会影响任何现有AppDomain。当使用AppDomainSetup实例作为参数调用CreateDomain方法时,它只能影响新AppDomain的创建。文档似乎建议它应该支持动态扩展
AppDomain
的搜索路径,只是不推荐使用该功能。如果它能工作,它是一个比重载
AssemblyResolve
更干净的解决方案。作为参考,它看起来像
AppDomain.AppendPrivatePath
和.Yea,因此它在.Net 4.7.2中工作得很好,但在.Net core中将停止工作。如果你还记得AppDomains的概念已经不再是一个东西,这是有道理的。谢谢你添加这个。我已经多次看到了
AssemblyResolve
解决方案,很高兴有另一个(更简单的)选项。如果将应用复制到其他地方,请不要忘记将App.config文件与应用一起移动。
AssemblyResolve
用于CurrentDomain,对另一个域无效
AppDomain.CreateDomain
您可以使用
AssemblyName
构造函数来解码程序集名称,而不是依赖于解析程序集字符串。这是我在C++/CLI中唯一适用的答案。Whe
using namespace System;
using namespace System::IO;
using namespace System::Reflection;

static Assembly ^LoadFromSameFolder(Object ^sender, ResolveEventArgs ^args)
{
    String ^folderPath = Path::GetDirectoryName(Assembly::GetExecutingAssembly()->Location);
    String ^assemblyPath = Path::Combine(folderPath, (gcnew AssemblyName(args->Name))->Name + ".dll");
    if (File::Exists(assemblyPath) == false) return nullptr;
    Assembly ^assembly = Assembly::LoadFrom(assemblyPath);
    return assembly;
}

// put this somewhere you know it will run (early, when the DLL gets loaded)
System::AppDomain ^currentDomain = AppDomain::CurrentDomain;
currentDomain->AssemblyResolve += gcnew ResolveEventHandler(LoadFromSameFolder);
System.AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
{
    var loadedAssembly = System.AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName == args.Name).FirstOrDefault();
    if(loadedAssembly != null)
    {
        return loadedAssembly;
    }

    if (args.RequestingAssembly == null) return null;

    string folderPath = Path.GetDirectoryName(args.RequestingAssembly.Location);
    string rawAssemblyPath = Path.Combine(folderPath, new System.Reflection.AssemblyName(args.Name).Name);

    string assemblyPath = rawAssemblyPath + ".dll";

    if (!File.Exists(assemblyPath))
    {
        assemblyPath = rawAssemblyPath + ".exe";
        if (!File.Exists(assemblyPath)) return null;
    } 

    var assembly = System.Reflection.Assembly.LoadFrom(assemblyPath);
    return assembly;
 };
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

  <!-- Discover assemblies in /lib -->
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="lib" />
    </assemblyBinding>
  </runtime>
</configuration>
:: Moves files to a subdirectory, to unclutter the application folder
:: Note that the new subdirectory should be probed so the dlls can be found.
SET path=$(TargetDir)\lib
if not exist "%path%" mkdir "%path%"
del /S /Q "%path%"
move /Y $(TargetDir)*.dll "%path%"
move /Y $(TargetDir)*.xml "%path%"
move /Y $(TargetDir)*.pdb "%path%"