使用汇编语言与AppDomain查找我的主执行文件路径的区别

28

我是一个.NET用户,我的目标很简单,就是找到我的主执行程序集(即EXE文件)所在的绝对路径。

我有几个候选项:

  • Assembly.GetExecutingAssembly().CodeBase
  • Assembly.GetExecutingAssembly().Location
  • AppDomain.CurrentDomain.BaseDirectory

如果根据.NET文档判断 - 我倾向于使用CodeBase。 有人能详细解释一下这三个相对于.NET文档更具体的区别吗?也许可以提供一个示例来说明它们之间的差异吗?


我发现这篇文章是对不同程序集方法的很好解释。 - mcont
5个回答

17

我建议使用GetEntryAssembly()而不是GetExecutingAssembly()

为了看到原因,请按照以下步骤进行:

  • 创建一个新的控制台项目
  • 将类库项目(ClassLibrary1)添加到该解决方案中,并从控制台项目中引用它。

将下面的内容放入ClassLibrary1中:

namespace ClassLibrary1
{
    using System;
    using System.IO;
    using System.Reflection;

    public class Class1
    {
        public void GetInfo(int n)
        {
            Assembly asm = Assembly.GetEntryAssembly();
            Console.WriteLine("[GetEntryAssembly {0}] Location:{1}", n, Path.GetDirectoryName(asm.Location));
            asm = Assembly.GetExecutingAssembly();
            Console.WriteLine("[GetExecutingAssembly() {0}] Location:{1}", n, Path.GetDirectoryName(asm.Location));
        }
    }
}

将以下内容放入控制台的 Program.cs 文件中:

namespace ConsoleApplication4
{
    using System;
    using System.IO;
    using System.Reflection;
    using ClassLibrary1;

    class Program
    {
        static void Main(string[] args)
        {
            Assembly asm = Assembly.GetEntryAssembly();
            Console.WriteLine("[GetEntryAssembly() 1] Location:{0}", Path.GetDirectoryName(asm.Location));
            asm = Assembly.GetExecutingAssembly();
            Console.WriteLine("[GetExecutingAssembly() 1] Location:{0}", Path.GetDirectoryName(asm.Location));

            Class1 obj1 = new Class1();
            obj1.GetInfo(2);

            asm = Assembly.LoadFile(@"C:\temp\ClassLibrary1.dll");
            Type t = asm.GetType("ClassLibrary1.Class1");
            object obj2 = asm.CreateInstance("ClassLibrary1.Class1");
            t.GetMethod("GetInfo").Invoke(obj2, new object[] { 3 });

            Console.ReadKey();
        }
    }
}

建立解决方案,将ClassLibrary1.dll复制到c:\temp并运行。
正如您所看到的,在某些条件下GetExecutingAssembly()可能会欺骗您。
最后注意一点,如果您的应用程序是Windows Forms应用程序,则可以直接使用Application.ExecutablePath

好的,谢谢,我接受这个更改!但是Assembly.CodeBase和AppDomain.BaseDirectory怎么样? - tsemer
17
不妨直接发布输出结果,而不是要求读者自行编译和运行代码才能理解您的观点 :-) - Pat
17
如果应用程序在非托管环境下启动,Assembly.GetEntryAssembly不可靠,具体而言,它将返回null。因此必须对结果进行空值检查。 - clemahieu
输出如下:[GetEntryAssembly() 1] 位置:C:\Users\bob\Documents\Visual Studio 2017\Projects\myapp\myapp\bin\Debug [GetExecutingAssembly() 1] 位置:C:\Users\bob\Documents\Visual Studio 2017\Projects\myapp\myapp\bin\Debug[GetEntryAssembly() 2] 位置:C:\Users\bob\Documents\Visual Studio 2017\Projects\myapp\myapp\bin\Debug [GetExecutingAssembly()2] 位置:C:\Users\bob\Documents\Visual Studio 2017\Projects\myapp\myapp\bin\Debug[GetEntryAssembly() 3] 位置:C:\Users\bob\Documents\Visual Studio 2017\Projects\myapp\myapp\bin\Debug [GetExecutingAssembly() 3] 位置:C:\temp - Antoine Meltzheim

16

我不确定AppDomain.CurrentDomain.BaseDirectory,但是Assembly.GetExecutingAssembly().CodeBaseAssembly.GetExecutingAssembly().Location之间的区别在这篇博客文章中有解释

CodeBase是文件被发现的URL地址,而Location是它实际被加载的路径。例如,如果程序集从互联网下载,它的CodeBase可能以“http://”开头,但其Location可能以“C:\”开头。如果该文件被影像复制了,则Location将是影像复制目录中该文件的路径。

还需注意的是,对于GAC中的程序集,CodeBase未必会被设置。然而,从磁盘加载的程序集始终会设置Location。

因此,如果需要获取执行文件所在的真实目录,则最好使用Location属性。


2
实际上,使用影子复制单元测试和一些文件复制到输出时,您可能希望出于完全相同的原因使用CodeBase(通过Uri类解析)。 - Wernight
1
以防其他人也有疑问:GAC 意味着全局程序集缓存(Global Assembly Cache) :) - Marcus Mangelsdorf
博客文章链接不再指向博客文章。 - Daniel Williams

8
很遗憾,如果你使用像XenoCode postbuild这样的虚拟化技术,上述所有方法都可能失败。我测试了很多方法并在这里找到了另一种解决方案。
System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe"

返回可执行文件的正确文件名。 因此,将文件名与Assembly.GetEntryAssembly().Location中的路径组合,您将获得可执行文件的正确路径。


1
这将返回ASP.NET开发服务器文件名 ;) - Stefan Steiger
2
当然,这是ASP.NET中的主要执行程序集。 - Alexander Zwitbaum
1
在 .NET 中,使用 System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; 获取当前进程的 exe 路径,而在 AppDomain 中则不同。 - Olivier de Rivoyre
1
这似乎是真正的答案,因为在我的情况下,Assembly.GetEntryAssembly() 可能为空。 - dudeNumber4

0

来自:http://msdn.microsoft.com/zh-cn/library/system.reflection.assembly.codebase.aspx

Assembly.CodeBase

要获取已加载包含清单的文件的绝对路径,请改用Assembly.Location属性。如果使用Load方法的一个重载,该重载接受字节数组来加载程序集,则此属性返回调用该方法的位置,而不是已加载程序集的位置。至于AppDomain.CurrentDomain.BaseDirectory,从实际角度来看,我真的不知道它与其他方法有什么区别。

我实际上需要知道exe文件的预期执行位置,而不是它的实际位置,所以我想我会坚持使用CodeBase :) 虽然谢谢你的指引。 - tsemer

-4

使用 System.Linq

string MainExecutablePath= (System.Diagnostics.Process.GetProcesses().First(P =>P.MainWindowTitle == "MainExecutable'sWindowName")).MainModule.FileName.ToString();

5
谢谢你的回答。改进:Linq可以被GetCurrentProcess()替换(正如@Shurup所提到的),而FileName已经是一个字符串,因此不需要使用ToString() :) 因此代码为:System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName - tsemer

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