从库代码发现程序集

发布于 2024-12-29 05:36:51 字数 6478 浏览 1 评论 0原文

我在公共库中有一些代码来支持国际化。基本思想是,给定 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?

干杯,
Berryl

库代码

/// <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,并且运行时调试器中已知以下 AssemblyName:

{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。

FIX

asm.GetReferencedAssemblies 听起来像票,但它只返回内存中加载的程序集,如果代码中未引用程序集,则不会加载程序集> (令人困惑的是,虽然说一个项目引用另一个项目也很正确,但这不是此方法返回的那种引用)。

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

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

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

尽管我不喜欢创建一个虚拟类,但在这种情况下这是一个简单的修复;我可以保留原始库代码不变。

I have some code in a common library to support internationalization. The basic idea is that given the fully qualified name of a RESX file location, you can look up values using a markup extension:

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

To find the RESX file there is a routine to search all assemblies as shown below, and it works fine when the RESX file is in the same assembly as the xaml. BUT it breaks down when it is not.

Consider the solution structure below, where SampleApp.Wpf has the calling XAML and has a dependency on both the LocalizationLib and SampleApp.Common.

enter image description here

AppDomain.CurrentDomain.GetAssemblies() does not have SampleApp.Common at run time (although it does at design time).

How can I modify this code so it will know about SampleApp.Common at runtime?

Cheers,
Berryl

Library code

/// <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;
}

UPDATE

More details.

SampleApp.Wpf is targeting .net 4.0. Below are the references for it as displayed in Visual Studio.

enter image description here

The only project references in there are Infralution.Localization.Wpf and SampleApp.Common, both of which also target .Net 4.0.

Based on Metro Smurf's input I tried using assembly.GetReferencedAssemblies(). When I use this method, the VS designer does NOT find SampleApp.Common, and the following AssemblyNames are known in the debugger at runtime:

{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}

And when I use the origional AppDomain.CurrentDomain.GetAssemblies(), the designer DOES know SampleApp.Common, and these assemblies are known at runtime:

?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}

In both cases, SampleApp.Common isn't known at runtime. For the fun of it, I loaded that assembly in the debugger and got:

?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

I also tried Metro Smurf's suggestion to reference a static string from SampleApp.Common in SampleApp.Wpf. No change on that one, although I guess it further proves that SampleApp.Common is properly referenced.

Other things to try on SampleApp.Common might include adding an EntryPoint, a SNK, or a HashAlgorithm.

the FIX

asm.GetReferencedAssemblies sounds like the ticket, but it only returns assemblies which are loaded in the memory, and assemblies aren't loaded if they aren't referenced in the code (as in it's confusing that while also quite proper to say one project has a reference to another, but that isn't the sort of reference this method returns).

It turns out that AppDomain.CurrentDomain.GetAssemblies() works the same way.

Metro Smurf was on the right track, but apparently a static reference won't get the assembly loaded, but creating a type instance from the assembly will. So:

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

As much as I dislike creating a dummy class, in this case it is an easy fix; I can leave the original library code as is.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

暮年 2025-01-05 05:36:51

You may want to consider using the Assembly.GetReferencedAssemblies Method. However, if I'm not mistaken, you'll need to exclude the framework assemblies as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文