C# 获取调用程序集的父级程序集名称

40

我正在开发一个C#单元测试应用程序,涉及到三个程序集- C#应用程序本身的程序集、应用程序使用的第二个程序集以及被第二个程序集使用的第三个程序集。

因此,调用顺序如下:

First Assembly ------> Second Assembly---------> Third Assembly.

在第三个程序集中,我需要获取调用第二个程序集的第一个程序集的名称。

Assembly.GetExecutingAssembly().ManifestModule.Name
Assembly.GetCallingAssembly().ManifestModule.Name

返回第二个程序集的名称。

Assembly.GetEntryAssembly().ManifestModule.Name

返回空值

有人知道如何获取第一个程序集的程序集名称吗?

按照其他用户要求,我放置了代码。这不是100%的代码,但是类似于此类的代码。

namespace FirstAssembly{
public static xcass A
{
        public static Stream OpenResource(string name)
        {
            return Reader.OpenResource(Assembly.GetCallingAssembly(), ".Resources." + name);
        }
}
}

using FirstAssembly;
namespace SecondAssembly{
public static class B 

{
public static Stream FileNameFromType(string Name)

{
return = A.OpenResource(string name);
}
}
}

以及测试项目方法

using SecondAssembly;
namespace ThirdAssembly{
public class TestC
{

 [TestMethod()]
        public void StremSizTest()
        {
            // ARRANGE
            var Stream = B.FileNameFromType("ValidMetaData.xml");
            // ASSERT
            Assert.IsNotNull(Stream , "The Stream  object should not be null.");
        }
}
}

这些答案中有没有帮助到您? - Chris Gessler
除了我的代码,它使用了StackFrames。 - Chris Gessler
@ChrisGessler:我还没有尝试你的代码。 - Saroop Trivedi
你需要提供更多信息,可能是加载第二个程序集的代码,这样人们才能重现你的行为。GetEntryAssembly 应该可以工作,除非初始进程是非托管的。你是否将第二个程序集作为 COM 对象加载了? - Panagiotis Kanavos
@PanagiotisKanavos:好的,我会在这里更新。 - Saroop Trivedi
显示剩余2条评论
9个回答

42

我猜你应该可以这样做:

using System.Diagnostics;
using System.Linq;

...

StackFrame[] frames = new StackTrace().GetFrames();
string initialAssembly = (from f in frames 
                          select f.GetMethod().ReflectedType.AssemblyQualifiedName
                         ).Distinct().Last();

这将获取包含当前线程中首先启动的方法的汇编。因此,如果您不在主线程中,则可能与EntryAssembly不同,如果我正确地理解了您的情况,这应该是您要查找的程序集。

您也可以像这样获取实际的Assembly而不仅仅是名称:

Assembly initialAssembly = (from f in frames 
                          select f.GetMethod().ReflectedType.Assembly
                         ).Distinct().Last();

编辑- 截至2015年9月23日

请注意,

GetMethod().ReflectedType

可能为null,因此检索其AssemblyQualifiedName可能会引发异常。例如,如果要检查专用于ORM(如linq2db等)POCO类的基本构造函数,则这很有趣。


这是一个很棒的答案!真的帮助我找到了我需要的东西。 - Piotr Kula
2
如果您使用.Last(),为什么还需要.Distinct()呢? - Dorus
@Dorus 因为否则你可能会得到错误的结果,例如我在没有使用.Distinct()的情况下得到了 mscorlib - Jack Miller
@JackMiller,这对我来说似乎非常奇怪,实际上就像最后一帧是“mscorlib”,而您的堆栈是类似于“aba”的东西,其中第二个“a”被distinct过滤掉,然后“last”取了“b”。 - Dorus
我完全同意你的观点。这确实非常奇怪。另一方面,我并没有尝试去了解.Distinct()的实际工作原理。 - Jack Miller
在同步调用中工作得很好。但是,在异步代码中,GetFrames() 中看到的堆栈与异步执行恢复的位置不匹配,因此我无法看到实际调用者。VS 调试器能够告诉异步执行从哪里恢复,因此有一种方法可以找出谁启动了异步执行。有人知道如何调整解决方案以使其也适用于异步调用者吗? - David Burg

16

这将返回引用您当前程序集的初始程序集的Assembly。

var currentAssembly = Assembly.GetExecutingAssembly();
var callerAssemblies = new StackTrace().GetFrames()
            .Select(x => x.GetMethod().ReflectedType.Assembly).Distinct()
            .Where(x => x.GetReferencedAssemblies().Any(y => y.FullName == currentAssembly.FullName));
var initialAssembly = callerAssemblies.Last();

13

我使用以下代码成功实现了:

System.Reflection.Assembly.GetEntryAssembly().GetName()

7

如果你是从nunit-console运行测试,那么Assembly.GetEntryAssembly()返回null。

如果你只想获取当前应用程序的名称,则可以使用:

 System.Diagnostics.Process.GetCurrentProcess().ProcessName 

或者

 Environment.GetCommandLineArgs()[0];

对于nunit-console,你将得到"nunit-console"和"C:\Program Files\NUnit 2.5.10\bin\net-2.0\nunit-console.exe"。

3

尝试:

Assembly.GetEntryAssembly().ManifestModule.Name

这应该是实际执行以启动您的进程的汇编程序。


0

不太确定您正在寻找什么,特别是当在单元测试的上下文中运行时,您将得到:

mscorlib.dll
Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration.dll

(或类似的内容,具体取决于您的测试运行程序)在导致任何方法被调用的程序集集合中。

下面的代码打印出调用涉及的每个程序集的名称。

var trace = new StackTrace();
var assemblies = new List<Assembly>();
var frames = trace.GetFrames();

if(frames == null)
{
    throw new Exception("Couldn't get the stack trace");
}

foreach(var frame in frames)
{
    var method = frame.GetMethod();
    var declaringType = method.DeclaringType;

    if(declaringType == null)
    {
        continue;
    }

    var assembly = declaringType.Assembly;
    var lastAssembly = assemblies.LastOrDefault();

    if(assembly != lastAssembly)
    {
        assemblies.Add(assembly);
    }
}

foreach(var assembly in assemblies)
{
    Debug.WriteLine(assembly.ManifestModule.Name);
}

我知道所有单元测试规则。现在我的问题是,我不想阅读所有的汇编代码,我只想要一个关于C语言的参考文献。这件事会让我的代码变得复杂。 - Saroop Trivedi

0
如果您知道堆栈中的帧数,可以使用StackFrame对象并跳过前面的帧数。
// You skip 2 frames
System.Diagnostics.StackFrame stack = new System.Diagnostics.StackFrame(2, false);
string assemblyName = stack.GetMethod().DeclaringType.AssemblyQualifiedName;

但是,如果你想要第一次调用,你需要获取所有的帧并取第一个。(参见AVee解决方案)


-1

试试使用Assembly.GetEntryAssembly()?它会返回进程的主可执行文件。

Process.GetCurrentProcess().MainModule.ModuleName也应该返回与ManifestModule名称相同的内容(“yourapp.exe”)。


啊,manifestmodule名称返回null,那么怎么样:Assembly.GetEntryAssembly().FullName(或GetName()根据您的需要) - Me.Name
@sarooptrivedi 我增加了另一个选项。 - Botz3000
@Botz3000:我在单元测试项目中使用了这个汇编调用。 - Saroop Trivedi
@sarooptrivedi 是什么类型的单元测试?如果程序集是由本机代码加载的(不确定您的单元测试框架如何设置),GetEntryAssembly() 将返回 null。您已经尝试过第二个选项了吗? - Botz3000
@Botz3000:我在测试项目中有一些资源文件。我不想通过DeploymentItem部署所有文件。因此,我创建了一个项目,在其中使用所有文件来进行单元测试,并创建了一个名为B的测试助手项目。但是,调用程序集的项目位于另一个助手项目C中。因此,当我将单元测试项目的程序集引用到项目C中时,对于GetEntryAssembly(),它返回NULL。 - Saroop Trivedi
如果您使用MStest,您可以将应该部署到所有测试的项目添加到您的testsettings文件中。 - habakuk

-4

当在 NUnit 测试中使用两个程序集时,此方法可用于获取原始程序集,而不返回 NULL。希望这能帮到你。

var currentAssembly = Assembly.GetExecutingAssembly();
var callerAssemblies = new StackTrace().GetFrames()
        .Select(x => x.GetMethod().ReflectedType.Assembly).Distinct()
        .Where(x => x.GetReferencedAssemblies().Any(y => y.FullName ==     currentAssembly.FullName));
var initialAssembly = callerAssemblies.Last();

发布仅包含代码的答案是不被赞同的,因为它对于帮助提问者理解问题或你的解决方案没有任何帮助。 - leigero
2
18个月后,你发表了一个与你上面的回答相同的答案? - dinotom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接