Powershell - 在应用程序配置文件中找不到程序集绑定重定向

Posted

技术标签:

【中文标题】Powershell - 在应用程序配置文件中找不到程序集绑定重定向【英文标题】:Powershell - Assembly binding redirect NOT found in application configuration file 【发布时间】:2014-12-26 10:44:08 【问题描述】:

这里有问题...

我有一个 Powershell CmdLet,它在 32 位模式下运行时工作,在 64 位模式下失败。 问题是原因是什么以及如何解决。

情况

引用“OutlookHelper.Common.dll”的 Powershell CmdLet。最新版本是 2.0.0.0 CmdLet 还使用日志记录并引用“Logging.dll”。 Logging.dll 还引用了“OutlookHelper.Common.dll”,仅针对版本 1.0.0.0 编译。

我是如何让它工作的,这部分工作

在Powershell的应用程序配置文件中使用程序集绑定重定向:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true"> 
    <supportedRuntime version="v4.0.30319"/> 
    <supportedRuntime version="v2.0.50727"/> 
  </startup> 
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="OutlookHelper.Common" publicKeyToken="5e4553dc0df45306"/>
        <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

Powershell 32 位运行良好

在 64 位机器上运行时,使用“Windows Powershell (x86)”可以正常工作。程序集管理器发现程序集绑定重定向:

The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = MYDOMAIN\testuser
LOG: DisplayName = OutlookHelper.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5e4553dc0df45306
 (Fully-specified)
LOG: Appbase = file:///C:/Windows/syswow64/Windowspowershell/v1.0/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = powershell.exe
Calling assembly : OutlookHelper.Data.Common, Version=1.0.5295.26925, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Redirect found in application configuration file: 1.0.0.0 redirected to 2.0.0.0.
LOG: Post-policy reference: OutlookHelper.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5e4553dc0df45306
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///C:/Windows/syswow64/Windowspowershell/v1.0/OutlookHelper.Common.DLL.
LOG: Attempting download of new URL file:///C:/Windows/syswow64/Windowspowershell/v1.0/OutlookHelper.Common/OutlookHelper.Common.DLL.
LOG: Attempting download of new URL file:///C:/Windows/syswow64/Windowspowershell/v1.0/OutlookHelper.Common.EXE.
LOG: Attempting download of new URL file:///C:/Windows/syswow64/Windowspowershell/v1.0/OutlookHelper.Common/OutlookHelper.Common.EXE.
LOG: Attempting download of new URL file:///D:/SampleApps/_Common/Bin/Outlook.Extensions.Sample/OutlookHelper.Common.DLL.
LOG: Assembly download was successful. Attempting setup of file: D:\SampleApps\_Common\Bin\Outlook.Extensions.Sample\OutlookHelper.Common.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: OutlookHelper.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5e4553dc0df45306
LOG: Where-ref bind Codebase does not match what is found in default context. Keep the result in LoadFrom context.
LOG: Binding succeeds. Returns assembly from D:\SampleApps\_Common\Bin\Outlook.Extensions.Sample\OutlookHelper.Common.dll.
LOG: Assembly is loaded in LoadFrom load context.

这是 Powershell 关于程序集标识的说明:

Windows PowerShell (x86)
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\testuser> ([xml](gc $([System.AppDomain]::CurrentDomain.SetupInformation.ConfigurationFile))).configuratio
n.runtime.assemblyBinding.dependentAssembly.assemblyIdentity

name                                                        publicKeyToken
----                                                        --------------
OutlookHelper.Common                                        5e4553dc0df45306

PS C:\Users\testuser>

这就是麻烦的开始......

在 64 位机器上运行时,使用“Windows Powershell”无法正常工作。程序集管理器未找到程序集绑定重定向:

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
Running under executable  C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = MYDOMAIN\testuser
LOG: DisplayName = OutlookHelper.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5e4553dc0df45306
 (Fully-specified)
LOG: Appbase = file:///C:/WINDOWS/system32/WindowsPowerShell/v1.0/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = powershell.exe
Calling assembly : OutlookHelper.Data.Common, Version=1.0.5295.26925, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Post-policy reference: OutlookHelper.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5e4553dc0df45306
LOG: The same bind was seen before, and was failed with hr = 0x80070002.
ERR: Unrecoverable error occurred during pre-download check (hr = 0x80070002).

这是 Powershell 关于程序集标识的说明:

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\testuser> ([xml](gc $([System.AppDomain]::CurrentDomain.SetupInformation.ConfigurationFile))).configuratio
n.runtime.assemblyBinding.dependentAssembly.assemblyIdentity
PS C:\Users\ccontent01>

当我让 Powershell 获取它自己的应用程序配置文件的内容时,我得到以下输出:

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\testuser> gc C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe.Config
<?xml version="1.0"?>
<configuration>
    <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0.30319"/>
        <supportedRuntime version="v2.0.50727"/>
    </startup>
</configuration>
PS C:\Users\testuser>

我尝试了什么...

检查“特定版本”是否设置为“假”-> 是这样。 删除并重新添加了应用程序配置文件 -> 没有修复它。 从 SysWOW64 文件夹中的新应用程序配置文件开始 -> 没有修复它。 仔细检查加载的文件(powershell.exe.Config 和 machine.config)的内容 -> 它们是相同的。

我的猜测

程序集管理器找不到程序集重定向绑定。

有什么解决办法吗?

为什么 64 位实例的 Fusion 日志没有提及“在应用程序配置文件中找到重定向:1.0.0.0 重定向到 2.0.0.0。”之类的内容? 这一切的原因是什么? 您能想到任何解决方案吗?

【问题讨论】:

磁盘上的dll在哪里?他们出现在C:/WINDOWS/system32/WindowsPowerShell/v1.0/ 还是其他地方? ddls,如 PSEvents.dll、pspluginwkr.dll、pspluginwkr-v3.dll、pwrshmsg.dll、pwrshsip.dll 存在于 system32 和 syswow64 文件夹中。但是文件内容不一样。 【参考方案1】:

并非 100% 与 32/64 位问题相关,但如果有人对 工作 程序集重定向解决方案感兴趣,请在此处查看 Powershell config assembly redirect。

您可以使用类似的 PowerShell 代码进行自定义程序集重定向

$FSharpCore = [reflection.assembly]::LoadFrom($PSScriptRoot + "\bin\LIBRARY\FSharp.Core.dll") 

$OnAssemblyResolve = [System.ResolveEventHandler] 
  param($sender, $e)

  # from:FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  # to:  FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  if ($e.Name -eq "FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")  return $FSharpCore 

  foreach($a in [System.AppDomain]::CurrentDomain.GetAssemblies())
  
    if ($a.FullName -eq $e.Name)
    
      return $a
    
  
  return $null


[System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)

我首先从某处加载正确版本的 FSharp.Core,因为 GAC 中的版本很旧(我想这也可能是你的情况)

【讨论】:

对不起,该项目已被删除。【参考方案2】:

基于@davidpodhola 非常有用的答案,我开始在我的 psm1 模块文件中添加类似的内容。如果您的新程序集已经加载(例如通过 Import-Module),这应该可以工作:

if (!("Redirector" -as [type]))

$source = 
@'
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

public class Redirector

    public readonly string[] ExcludeList;

    public Redirector(string[] ExcludeList = null)
    
        this.ExcludeList  = ExcludeList;
        this.EventHandler = new ResolveEventHandler(AssemblyResolve);
    

    public readonly ResolveEventHandler EventHandler;

    protected Assembly AssemblyResolve(object sender, ResolveEventArgs resolveEventArgs)
    
        Console.WriteLine("Attempting to resolve: " + resolveEventArgs.Name); // remove this after its verified to work
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        
            var pattern  = "PublicKeyToken=(.*)$";
            var info     = assembly.GetName();
            var included = ExcludeList == null || !ExcludeList.Contains(resolveEventArgs.Name.Split(',')[0], StringComparer.InvariantCultureIgnoreCase);

            if (included && resolveEventArgs.Name.StartsWith(info.Name, StringComparison.InvariantCultureIgnoreCase))
            
                if (Regex.IsMatch(info.FullName, pattern))
                
                    var Matches        = Regex.Matches(info.FullName, pattern);
                    var publicKeyToken = Matches[0].Groups[1];

                    if (resolveEventArgs.Name.EndsWith("PublicKeyToken=" + publicKeyToken, StringComparison.InvariantCultureIgnoreCase))
                    
                        Console.WriteLine("Redirecting lib to: " + info.FullName); // remove this after its verified to work
                        return assembly;
                    
                
            
        

        return null;
    

'@

    $type = Add-Type -TypeDefinition $source -PassThru 


#exclude all powershell related stuff, not sure this strictly necessary
$redirectExcludes = 
    @(
        "System.Management.Automation", 
        "Microsoft.PowerShell.Commands.Utility",
        "Microsoft.PowerShell.Commands.Management",
        "Microsoft.PowerShell.Security",
        "Microsoft.WSMan.Management",    
        "Microsoft.PowerShell.ConsoleHost",
        "Microsoft.Management.Infrastructure",
        "Microsoft.Powershell.PSReadline",
        "Microsoft.PowerShell.GraphicalHost"
        "System.Management.Automation.HostUtilities",

        "System.Management.Automation.resources",
        "Microsoft.PowerShell.Commands.Management.resources",
        "Microsoft.PowerShell.Commands.Utility.resources",
        "Microsoft.PowerShell.Security.resources",
        "Microsoft.WSMan.Management.resources",
        "Microsoft.PowerShell.ConsoleHost.resources",
        "Microsoft.Management.Infrastructure.resources",
        "Microsoft.Powershell.PSReadline.resources",
        "Microsoft.PowerShell.GraphicalHost.resources",
        "System.Management.Automation.HostUtilities.resources"
    )
try

    $redirector = [Redirector]::new($redirectExcludes)
    [System.AppDomain]::CurrentDomain.add_AssemblyResolve($redirector.EventHandler)

catch

    #.net core uses a different redirect method
    write-warning "Unable to register assembly redirect(s). Are you on ARM (.Net Core)?"

更新: Powershell 似乎有一个错误,即在调用某些命令(如 Out-GridView)时,简单地注册程序集解析脚本块可能会导致 ***Exception。我更新了代码以使用使用 Add-Type 编译的版本,似乎可以解决问题。

【讨论】:

效果很好,但是退出时如何避免堆栈溢出?如果我将 AssemblyResolve 处理程序留在其中,则 powershell 在退出时会崩溃。以下内容会阻止它[System.AppDomain]::CurrentDomain.remove_AssemblyResolve($OnAssemblyResolve),但这会破坏封装,并且意味着程序集的用户必须知道取消注册事件处理程序。我尝试在 AppDomain 卸载和进程退出处理程序中执行此操作,但没有成功。 你知道我偶尔会遇到这个异常,我认为它隐藏在我正在编写的所有互操作代码中,因为它会使进程崩溃。不幸的是,我认为问题不仅仅是删除解析器,因为在我的机器上我可以通过简单地注册一个什么都不做的解析器并调用 out-gridview 来 100% 地重现它。 AssemblyResolve 的文档表明,如果解析处理程序导致 AppDomain.Load 或 Assembly.Load 被调用,您可以获得 ***Exception,但我不确定这点代码会如何发生: $OnAssemblyResolve = [System.ResolveEventHandler] param($sender, $resolveEventArgs) Write-Host -fore Green "试图解决问题" 返回 null; [System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve); #this 将使用 ***Exception 使 powershell 崩溃 try 1..10 | out-gridview catch #this 不会被命中 @ben 我在 MS 内部有一个线程,他们建议我使用 Add-Type 编译解析器来避免堆栈溢出。我不能说我完全确定它为什么会有所作为,但它似乎确实有效。我将更新我的示例...【参考方案3】:

在 64 位机器上有两个配置文件:

C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe.Config
C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe.Config

您是否在 64 位机器上编辑过它们?

在 64 位版本的 Windows 上。 32位进程(如notepad++)被操作系统透明地从C:\WINDOWS\System32重定向到C:\WINDOWS\SysWOW64。

您需要确保使用内置的 notepad.exe 等 64 位文本编辑器编辑这两个文件。这将保证您不会遇到这个可能导致混淆的微妙问题。

【讨论】:

是的,我做了,尝试编辑两者之一,然后操作系统更改/同步对另一个文件的更改。因此,我尝试更改 system32 中的那个-> 无效。尝试更改 syswow64 中的那个。尝试删除两者,然后测试 2 个场景,我将在第一个场景中将文件添加到 system32 中,然后添加在 syswow64 文件夹中添加文件 -> 不起作用。 您能否在 Powershell 和 Powershell (x86) 中运行以下命令并告诉我每个中的输出:([xml](gc $([System.AppDomain]::CurrentDomain.SetupInformation.ConfigurationFile)) ).configuration.runtime.assemblyBinding.dependentAssembly.assemblyIdentity 感谢您的评论丹!我已经用您要求的信息更新了我的问题。似乎 64 位缺少一些东西。 您好 mathijsuitmegen,您能否在 64 位 powershell 控制台中运行 gc C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe.Config 并发布输出。谢谢 你在做某事,丹! Powershell 不会读取整个文件。当我启动 cmd.exe 并在同一个文件上执行“类型”时,会显示文件的全部内容(包括元素“运行时”)。

以上是关于Powershell - 在应用程序配置文件中找不到程序集绑定重定向的主要内容,如果未能解决你的问题,请参考以下文章

MinGW gcc 在 /local/include 中找不到头文件

Xcode Archive 在子项目中找不到头文件

exec:“powershell”:在 Terraform 中运行 local-exec 时在 $PATH 中找不到可执行文件

脑洞题目 - 改自从一组无序数组中找不存在的最小正整数

在 Visual Studio 2019 的 PowerShell 窗口中找不到 ILDASM

maven-shade-plugin 错误:在“资源”的 org.apache.maven.plugins.shade.resource.ManifestResourceTransformer 中找不