感谢此线程中的所有提示。我认为我自己对这个错误的变体是出于稍微不同的原因,所以我会在这里发布以防万一。
在我的情况下,调用 window.ShowDialog() 时发生错误。更具体地说,我的窗口是在一个单独的类库程序集中定义的(我们称之为 AssemblyA.dll)。
我有多个版本的 AssemblyA,它们用于各种产品,其中一些是插件,有些不是。简而言之,结果是该过程最终可能会加载几个不同的强名称版本的 AssemblyA。因此,正如@VahidN 指出的那样,应用程序域中存在重复的程序集,但它们是严格不同版本的程序集,它们应该存在,并且仅共享相同的 AssemblyShortName。
WPF 为 InitializeComponent() 自动生成的代码如下所示:
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/AssemblyA;component/forms/mywindow.xaml", System.UriKind.Relative);
#line 1 "..\..\..\Forms\MyWindow.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
它只是指 AssemblyA 的简称,而不是指运行 InitializeComponent() 方法的 AssemblyA 的特定版本或公钥令牌。结果是代码似乎只是找到了加载到进程中的第一个 AssemblyA 程序集,搜索 XAML,但找不到它(因为它首先找到了旧版本的程序集),然后引发异常。或者它可能找到 something 但它可能从恰好也被加载的旧版本或新版本的程序集中提取了一个 不同 XAML 资源而不是它应该拥有的资源.
这并不完美,但我咨询了the Pack URI specification,并通过编写我自己的扩展方法来解决这个问题,确保使用适当的版本和公钥令牌找到程序集,而不是简单的 AssemblyShortName。
如果它对其他人有用,这是我最终得到的简化版本。
public static void AssemblySensitive_InitializeComponent(this ContentControl contentControl, string componentString)
{
// Strictly speaking this check from the generated code should also be
// implemented, but it doesn't fit directly into an extension method.
//if (_contentLoaded)
//{
// return;
//}
//_contentLoaded = true;
var asm = System.Reflection.Assembly.GetExecutingAssembly();
var shortName = asm.GetName().Name;
var publicKeyToken = GetPublicKeyTokenFromAssembly(asm);
var version = asm.GetName().Version.ToString();
System.Uri resourceLocater = new System.Uri($"/{shortName};V{version};{publicKeyToken};{componentString}", System.UriKind.Relative);
System.Windows.Application.LoadComponent(contentControl, resourceLocater);
}
/// <summary>
/// Gets a public key token from a provided assembly, and returns it as a string.
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
/// <remarks>Adapted from https://stackoverflow.com/questions/3045033/getting-the-publickeytoken-of-net-assemblies</remarks>
private static string GetPublicKeyTokenFromAssembly(System.Reflection.Assembly assembly)
{
var bytes = assembly.GetName().GetPublicKeyToken();
if (bytes == null || bytes.Length == 0)
return "None";
var publicKeyToken = string.Empty;
for (int i = 0; i < bytes.GetLength(0); i++)
publicKeyToken += string.Format("{0:x2}", bytes[i]);
return publicKeyToken;
}
_contentLoaded 位可能可以通过扩展属性完成,但我需要在 C# 7.3 中编译此库的代码,因此我有一个更长的解决方法,我将其删除以免分散注意力。
然后我像这样从构造函数中调用它:
public MyWindow()
{
// Don't use the auto-generated initialize, because if multiple different versions
// are loaded into the process, it can try to load the resource from the wrong one.
//InitializeComponent();
AssemblySensitive_InitializeComponent("component/forms/mywindow.xaml");
// ... do more constructor stuff ...
}
我花了很长时间试图弄清楚发生了什么感到沮丧,所以我希望这对其他人有所帮助。