【问题标题】:assembly discovery from library code从库代码中发现汇编
【发布时间】:2012-01-29 12:09:27
【问题描述】:

我在一个公共库中有一些代码来支持国际化。基本思想是,给定 RESX 文件位置的完全限定名称,您可以使用标记扩展名查找值:

    resx:ResxProperty.Name="SampleApp.Common.Resources.MainWindow"
    Title="{Resx Key=Window.Title}" 
    Icon="{Resx Key=Window.Icon}"

要查找 RESX 文件,有一个搜索所有程序集的例程,如下所示,当 RESX 文件与 xaml 位于同一程序集中时,它可以正常工作。但如果不是,它就会崩溃。

考虑下面的解决方案结构,其中 SampleApp.Wpf 具有调用 XAML 并依赖于 LocalizationLib 和 SampleApp.Common。

AppDomain.CurrentDomain.GetAssemblies() 在运行时没有 SampleApp.Common(尽管在设计时有)。

如何修改此代码以便它在运行时了解 SampleApp.Common?

干杯,
浆果

库代码

/// <summary>
/// Find the assembly that contains the type
/// </summary>
/// <returns>The assembly if loaded (otherwise null)</returns>
public static Assembly FindResourceAssembly(string resxName)
{
    // check the entry assembly first - this will short circuit a lot of searching
    //
    var assembly = Assembly.GetEntryAssembly();
    if (assembly != null && HasSpecifiedResx(assembly, resxName))
        return assembly;

    var assemblies = AppDomain.CurrentDomain.GetAssemblies();
    foreach (var searchAssembly in assemblies)
    {
        // skip system assemblies
        var name = searchAssembly.FullName;
        if (_isSystemAssembly(name)) continue;

        if (HasSpecifiedResx(searchAssembly, resxName))
            return searchAssembly;
    }
    return null;
}

更新

更多细节。

SampleApp.Wpf 的目标是 .net 4.0。下面是它在 Visual Studio 中显示的参考。

其中唯一的项目引用是 Infralution.Localization.Wpf 和 SampleApp.Common,它们都同样针对 .Net 4.0。

根据 Metro Smurf 的输入,我尝试使用 assembly.GetReferencedAssemblies()。当我使用此方法时,VS 设计器找不到 SampleApp.Common,并且在运行时调试器中已知以下 AssemblyNames:

{System.Reflection.AssemblyName[6]}
    [0]: {PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35}
    [1]: {mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [2]: {System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [3]: {Infralution.Localization.Wpf, Version=2.1.2.0, Culture=neutral, PublicKeyToken=547ccae517a004b5}
    [4]: {System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [5]: {PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35}

当我使用原始的 AppDomain.CurrentDomain.GetAssemblies() 时,设计器确实知道 SampleApp.Common,并且这些程序集在运行时是已知的:

?AppDomain.CurrentDomain.GetAssemblies()
{System.Reflection.RuntimeAssembly[21]}
    [0]: {mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [1]: {Microsoft.VisualStudio.HostingProcess.Utilities, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
    [2]: {System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [3]: {System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
    [4]: {System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [5]: {Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
    [6]: {Microsoft.VisualStudio.Debugger.Runtime, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
    [7]: {vshost32, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
    [8]: {System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [9]: {System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [10]: {System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [11]: {Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
    [12]: {System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [13]: {System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [14]: {System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}
    [15]: {WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35}
    [16]: {PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35}
    [17]: {PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35}
    [18]: {SampleApp.Wpf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null}
    [19]: {System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
    [20]: {Infralution.Localization.Wpf, Version=2.1.2.0, Culture=neutral, PublicKeyToken=547ccae517a004b5}

在这两种情况下,SampleApp.Common 在运行时都是未知的。为了好玩,我在调试器中加载了该程序集并得到:

?Assembly.Load("SampleApp.Common")
{SampleApp.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null}
    [System.Reflection.RuntimeAssembly]: {SampleApp.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null}
    CodeBase: "file:///C:/.../SampleApp.Wpf/bin/Release/SampleApp.Common.DLL"
    EntryPoint: null
    EscapedCodeBase: "file:///C:.../SampleApp.Wpf/bin/Release/SampleApp.Common.DLL"
    Evidence: {System.Security.Policy.Evidence}
    FullName: "SampleApp.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    GlobalAssemblyCache: false
    HostContext: 0
    ImageRuntimeVersion: "v4.0.30319"
    IsDynamic: false
    IsFullyTrusted: true
    Location: "C:\\...\\SampleApp.Wpf\\bin\\Release\\SampleApp.Common.dll"
    ManifestModule: {SampleApp.Common.dll}
    PermissionSet: {<PermissionSet class="System.Security.PermissionSet"
version="1"
Unrestricted="true"/>
}
    ReflectionOnly: false
    SecurityRuleSet: Level2

我还尝试了 Metro Smurf 的建议,即在 SampleApp.Wpf 中引用来自 SampleApp.Common 的静态字符串。那个没有变化,虽然我想它进一步证明了 SampleApp.Common 被正确引用。

在 SampleApp.Common 上尝试的其他操作可能包括添加 EntryPoint、SNK 或 HashAlgorithm。

修复

asm.GetReferencedAssemblies 听起来像票据,但它只返回加载到内存中的程序集,如果程序集没有在代码中被引用,则不会加载程序集 em>(令人困惑的是,虽然说一个项目引用了另一个项目也很恰当,但这不是该方法返回的那种引用)。

事实证明 AppDomain.CurrentDomain.GetAssemblies() 的工作方式相同。

Metro Smurf 在正确的轨道上,但显然静态引用不会加载程序集,但从程序集创建类型实例会。所以:

Console.WriteLine(Class1.MyDummyString)  // this won't do it
Console.WriteLine(new Class1())  // finally, the assembly is loaded

尽管我不喜欢创建一个虚拟类,但在这种情况下,它很容易解决;我可以保留原来的库代码。

【问题讨论】:

  • 很好奇...为什么应用程序会在设计时知道 Common 而不是运行时?
  • 同意。也许在设计时 Common 以某种方式加载但在运行时它以某种方式未加载?

标签: c# .net wpf reflection


【解决方案1】:

您可能需要考虑使用Assembly.GetReferencedAssemblies Method。但是,如果我没记错的话,您还需要排除框架程序集。

【讨论】:

  • 我尝试了 GetReferencedAssemblies 并得到了相同的结果。我看到另一篇文章似乎说 Visual Studio 引用的程序集不一定是反射已知的程序集,但我还没有理解。
  • SampleApp.Wpf 是否有对 SampleApp.Common 的直接引用?如果是这样,SampleApp.Wpf 项目中引用的 SmapleApp.Common 程序集的“复制本地”属性是否设置为 True。 (在SampleApp.Wpf的引用中右键SampleApp.Common,选择属性查看Copy Local属性)
  • 复制本地已经是真的了。我猜 SampleApp.Common 是由设计器加载的,因此可用,但在运行时未加载或可用。但为什么?一定有我们在这里遗漏的东西。干杯
  • 可能是很小的东西,以至于它会是一个手掌对脸的时刻。您能否尝试在Common 中创建一个类(比如静态字符串),然后使用该字符串在 WPF 项目中显示某些内容。如果这在运行时有效,那么我们知道 Common 被正确引用。此外,请仔细检查 HasSpecifiedResx 方法,以根据 Common 中使用的命名空间/命名约定验证逻辑。最后,我相信您将不得不使用GetReferencedAssemblies 方法。
  • 是的,手掌对脸的时刻会很好,因为现在感觉就像是锤子对脸的时刻!到目前为止,我可以看到帖子的更新。干杯
猜你喜欢
  • 2012-12-14
  • 1970-01-01
  • 2012-03-04
  • 1970-01-01
  • 2023-04-10
  • 2023-04-06
  • 2011-08-27
  • 2016-06-01
  • 1970-01-01
相关资源
最近更新 更多