【问题标题】:How to force WPF to use resource URIs that use assembly strong name? Argh!如何强制 WPF 使用使用程序集强名称的资源 URI?啊!
【发布时间】:2009-09-21 06:34:39
【问题描述】:

好的,这真的很烦人,我之前注意到 WPF 生成的用于加载 XAML 资源的代码似乎没有使用强名称,因此对于需要支持并行版本的 WPF 程序集的场景可能会出现问题.

事实证明是这样的,现在它给我带来了问题 - 我有一个插件系统,它应该支持并排安装插件,这些插件的不同之处仅在于它们的版本号(它们的程序集版本)。这当然可以由 .NET 支持,因为即使它们具有相同的 DLL 文件名,程序集也被确定具有不同的身份,前提是它们具有强命名并且具有不同的公钥/私钥或具有不同的程序集版本号。

现在,如果我们查看 Visual Studio 为窗口和用户控件生成的代码,我们会在自动生成的文件中看到以下内容:

/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
    if (_contentLoaded) {
        return;
    }
    _contentLoaded = true;
    System.Uri resourceLocater = new System.Uri("/Sensormatic.AMK1000.Panel;component/views/servicepanelui.xaml", System.UriKind.Relative);

    #line 1 "..\..\..\Views\ServicePanelUI.xaml"
    System.Windows.Application.LoadComponent(this, resourceLocater);

    #line default
    #line hidden
}

注意创建资源定位器的行 - 它使用的是相对 URI,它没有指定强名称或包含 xaml 资源的程序集的版本。

我想也许 LoadComponent 会检查调用程序集的身份并使用它的公钥和版本详细信息,或者可能检查包含“this”参数类型的程序集的身份。

看起来情况并非如此 - 如果您有两个版本号不同(但文件名相同)的程序集,那么您可能会得到一个 IOException 并显示消息“无法找到资源 X”(对于上面的示例“无法找到资源”视图/servicepanelui.xaml'。

更糟糕的是,我很确定这也意味着具有相同文件名但不同公钥/私钥的程序集(即来自不同发布者)也会导致此错误。

那么,有谁知道如何解决这个问题?如何使 WPF 强名称兼容。

注意,就我而言,这是一个 WPF 错误。您不必为了避免这种情况而使用 Appdomain 隔离。

【问题讨论】:

  • 嗨菲尔,我面临同样的问题。你能找到任何解决方案吗?
  • 使用主题(通过资源字典)的 WPF 自定义控件是否有人遇到过类似的问题?如果是,有什么解决方案吗?
  • 这方面有进展吗?当然,人们能够创建 WPF 库而没有这个问题。我需要但有这个确切的问题。

标签: .net wpf xaml resources strongname


【解决方案1】:

我遇到过同样的问题,这可能是一个可能的解决方案

每次使用 .xaml 页面创建控件时,在附加的 .cs 文件构造函数中,在 InitializeComponent() 调用之前,添加以下行:

内容加载=真; var assemblyName = GetType().Assembly.GetName(); System.Windows.Application.LoadComponent(GetType(),新的 Uri( string.Format("/{0};v{1};component{2}/{3}.xaml", 程序集名称.名称, 程序集名称.版本, [[[命名空间]]], 类型.名称 ), UriKind.Relative))

其中 as [[[namespace]]] 输入类的完整命名空间,Visual Studio 项目默认命名空间除外

(注意:连接上有一个打开的打勾 https://connect.microsoft.com/VisualStudio/feedback/details/668914/xaml-generated-code-uses-resource-uri-without-assembly-strong-name)

【讨论】:

  • 在 [[[namespace]]] 中,它不是命名空间,而是相对于您尝试加载的页面项目的位置(通常与去除根的命名空间一致)
【解决方案2】:

您可以在项目文件中设置以下内容以更改生成代码中的 URI:

<PropertyGroup>
  <AssemblyVersion>1.0.0.0</AssemblyVersion>
  <AssemblyPublicKeyToken>[YOUR_PUBLIC_KEY_TOKEN]</AssemblyPublicKeyToken>
</PropertyGroup>

【讨论】:

  • 你怎么知道的?这太……深奥了。这甚至没有得到官方支持,但它确实有效!
  • 我不喜欢这个解决方案,如果我更改版本(没有自动重构)或者我的调试机器和构建服务器中有不同的密钥怎么办?
  • 我已经在这个问题上苦苦挣扎了一个多星期,而且我非常痛苦!您的回答对我很有帮助,对我帮助很大!谢谢!! (另请注意this site,它引用了这个线程)
  • 我遇到了这个问题,表现为Could not load file or assembly '&lt;AssemblyName&gt;, Version=1.0.0.0, Culture=neutral' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)。事实证明,我正在覆盖 AssemblyVersion 属性。
【解决方案3】:

我倾向于同意这可能是一个错误,或者至少是 XAML 工具中的一个缺陷。也许你应该在Connect 上报告。

我没有尝试过,但这里有几个潜在的解决方法:

  1. 注入预构建步骤以自动修改 .g.cs 文件以使用指定完整程序集信息的pack URIs (AssemblyShortName[;Version][;PublicKey];component/Path )
  2. 附加到 AppDomain.AssemblyResolve 以帮助 CLR 找到正确的程序集

【讨论】:

  • 谢谢肯特,虽然我希望有一些内置的方法来处理这个! (虽然我还没有找到它)。我认为 #2 不会有帮助 - 很确定程序集解析请求只会使用弱名称,因为这是 URI 指定的名称,然后我不知道应该使用哪个程序集。 #1 可能会起作用,尽管显然有点难看。顺便说一句,在桁架上做得很好。在我当前的项目中使用它。
  • 附加到 assemblyresolve,不知道要加载哪个对我来说似乎没有用,如果指定了请求程序集,也许您可​​以使用它
【解决方案4】:

我在 VS2012 中一直在努力解决这个问题。我无法让 Riccardo 的解决方案在这种环境下工作。他的代码的这个变种......

_contentLoaded = true;
var assemblyName = GetType().Assembly.GetName();
Application.LoadComponent(this, new Uri(String.Format("/{0};v{1};component/CustomersFrame.xaml", assemblyName.Name, assemblyName.Version), UriKind.Relative));

... 确实解决了“无法定位资源”问题,但随后我在子元素中进一步遇到了以下错误:“无法注册命名对象。无法在此范围内注册重复名称“搜索”。

Aaron Marten 的解决方案确实对我有用。抱歉,我无法评论或投票,但我没有代表。

【讨论】:

    【解决方案5】:

    如果您的构建是自动化的,您还可以将 /p:AssemblyVersion=$version 参数传递给 msbuild 进程。

    【讨论】:

    • 我已经在这个问题上苦苦挣扎了一个多星期,而且我非常痛苦!您的回答对我很有帮助,而且帮助很大!谢谢!! (另请注意this site,它引用了您的答案)
    【解决方案6】:

    这段代码基于 Riccardo 的回答,在 VS2010 中为我工作。

    首先我定义了一个加载器方法,我可以从我的 XAML 构造函数中调用它。

    namespace Utility
    {
        public class Utility
        {
            public static void LoadXaml(Object obj)
            {
                var type = obj.GetType();
                var assemblyName = type.Assembly.GetName();
                var uristring = string.Format("/{0};v{1};component/{2}.xaml",
                    assemblyName.Name,
                    assemblyName.Version,
                    type.Name);
                var uri = new Uri(uristring, UriKind.Relative);
                System.Windows.Application.LoadComponent(obj, uri);
            }
        }
    }
    

    然后在每个 XAML 控件的构造函数中,我将 InitializeComponent() 替换为:

            _contentLoaded = true;
            Utility.Utility.LoadXaml(this);
            InitializeComponent();
    

    我确实注意到我的一些 RelativeSource 绑定停止工作,但我能够解决这个问题。

    【讨论】:

    • 通过这样做我得到这个异常:找不到资源'basicwizard.xaml'。其中 basicwizard 是我的 Window 类
    【解决方案7】:

    我们也遇到了同样的问题,但我们只需要在我们的解决方案中为某些特定项目设置程序集版本。

    因为我喜欢像 user195275 推荐的那样为构建设置版本号的想法,所以我对如何为单个 csproj 文件进行了一些研究。

    所以结合下面的线程How to read the assemblyversion from assemblyInfo.cs?我们想出了下面的解决方案:

    <Target Name="BeforeBuild">
        <ReadLinesFromFile File="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs">
            <Output TaskParameter="Lines"
                    ItemName="ItemsFromFile"/>
        </ReadLinesFromFile>
    
        <PropertyGroup>
            <Pattern>\[assembly: AssemblyVersion\(.(\d+)\.(\d+)\.(\d+)\.(\d+)</Pattern>
            <In>@(ItemsFromFile)</In>
            <Out>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</Out>
        </PropertyGroup>
    
        <CreateProperty Value="$(Out.Remove(0, 28))">
            <Output TaskParameter="Value" PropertyName="AssemblyVersion"/>
        </CreateProperty>
    </Target>
    

    它的作用:它从 AssemblyInfo.cs 中解析版本号,并将其设置为 Property,如 Aaron Martens 的回答。这为我们带来了版本号的单点维护。

    【讨论】:

    • 感谢您的回答,在对我们遇到的类似问题(基于 COM 的插件,加载同一 WPF 程序集的不同版本)进行了 3 天的激烈谷歌搜索后,我终于有了一个可行的解决方案!这段代码还会阻止我们在每次增加程序集版本时编辑 csproj 文件!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多