如何找到当前可执行文件的文件名?

114
7个回答

149

编辑:

从.NET 6开始,推荐使用CA1839中指定的方法:System.Environment.ProcessPath


原始回答:

System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName

7
如果使用System.Reflection.Assembly.GetEntryAssembly().Location,就不会出现这个问题,因为该问题会在文件名中添加“.vhost.”(请参见备选答案)。 - Contango
5
由于在VS中使用“Visual Studio Hosting Process”,导致@Contango问题发生。 - LuddyPants
用于VS 2012的单元测试。ProcessName: vstest.executionengine.x86 ConfigurationFile: C:\TFS\Tests\MyData.Tests.v4.0\bin\Debug\MyData.Tests.v4.0.dll.config MainModule.FileName: C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 11.0\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\TESTWINDOW\vstest.executionengine.x86.exe MainModule.ModuleName: vstest.executionengine.x86.exe FriendlyName: UnitTestAdapter: 运行测试 - Kiquenet
3
@Kiquenet,是的,在单元测试中,当前可执行文件就是测试运行程序;你有什么观点? - Thomas Levesque
4
这将给出可执行文件的完整路径,如果你只需要可执行文件的名称,请使用System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName。 - Igor Levicki
显示剩余3条评论

90

如果您想获取可执行文件:

System.Reflection.Assembly.GetEntryAssembly().Location

如果你想要获取使用你的库的程序集(如果你的代码直接从可执行文件中的类中调用,则可能是与上述相同的程序集):

System.Reflection.Assembly.GetCallingAssembly().Location

如果你只想获取文件的名称而不是路径,使用以下代码:

Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location)

6
如果从非托管代码中调用GetEntryAssembly,它可能返回null。 - Stephen Drew
我不相信如果可执行文件不是.NET应用程序,它就不能工作。例如,IIS会启动Worker进程(w3wp.exe),这些进程是不受管理的可执行文件,内部会启动CLR实例来执行托管代码。如果您在托管代码中使用此功能,我不相信您将获得w3wp.exe路径。 - Micah Zoltu
@MicahZoltu:在 Web 应用程序中,GetEntryAssembly 返回 null,请参见 GetEntryAssembly for web applications - IvanH
Assembly.GetEntryAssembly().LocationAssembly.GetCallingAssembly().Location 返回应用程序的 .dll,而不是 .exe - vee

42

除了上面的答案之外。

我编写了以下的test.exe作为控制台应用程序。

static void Main(string[] args) {
  Console.WriteLine(
    System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
  Console.WriteLine(
    System.Reflection.Assembly.GetEntryAssembly().Location);
  Console.WriteLine(
    System.Reflection.Assembly.GetExecutingAssembly().Location);
  Console.WriteLine(
    System.Reflection.Assembly.GetCallingAssembly().Location);
}
然后我编译了该项目并将其输出重命名为test2.exe文件。输出行是正确的,也相同。
但是,如果我在Visual Studio中启动它,结果是:
d:\test2.vhost.exe d:\test2.exe d:\test2.exe C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
Visual Studio的ReSharper插件已经标记出了

System.Diagnostics.Process.GetCurrentProcess().MainModule
尽可能抛出System.NullReferenceException。如果查看MainModule的文档,您将发现该属性还可能引发NotSupportedException、PlatformNotSupportedException和InvalidOperationException异常。
GetEntryAssembly方法也并非完全“安全”。 MSDN: GetEntryAssembly方法在从未托管应用程序中加载托管程序集时可能返回null。例如,如果一个未托管的应用程序创建了一个由C#编写的COM组件实例,则从C#组件调用GetEntryAssembly方法会返回null,因为进程的入口点是未托管代码而不是托管程序集。
对于我的解决方案,我更喜欢使用 Assembly.GetEntryAssembly().Location。
更有趣的是,如果需要解决虚拟化问题。例如,我们有一个项目,在其中使用Xenocode Postbuild将.NET代码链接到一个可执行文件中。这个可执行文件必须重命名。因此,所有上面的方法都不起作用,因为它们只获取原始程序集或内部进程的信息。
我找到的唯一解决方案是:
var location = System.Reflection.Assembly.GetEntryAssembly().Location;
var directory = System.IO.Path.GetDirectoryName(location);
var file = System.IO.Path.Combine(directory, 
  System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe");

1
如果您在IIS下以调试模式运行WCF服务应用程序,则GetEntryAssembly方法也会返回null。这是一个很少见的情况,只有开发人员才会遇到。 - Contango
当我使用Addin VS 2008时,Assembly.GetEntryAssembly()对我来说是空的。System.Diagnostics.Process.GetCurrentProcess().MainModule是devenv.exe,而不是我的Addin DLL。我不知道如何从另一个使用Addin的程序集中获取Addin的Assembly。 - Kiquenet
  1. 如果A.exe使用B.dll,而B.dll使用C.dll,如果该代码在C.dll中,会发生什么?
  2. AppDomain可以是EXE应用程序、Web应用程序、单元测试应用程序、Visual Studio插件和“Silverlight App”(?)。这可能是适用于所有情况的完整解决方案,非常有趣。
- Kiquenet

13

Environment.GetCommandLineArgs()[0]


static void Main(string[] args) 内应该使用这个。很奇怪,因为我记得在 C++ 中,第一个参数总是可执行文件。 - javon27

7
我认为这应该是您想要的内容:
System.Reflection.Assembly.GetEntryAssembly().Location

这将返回进程启动时首次加载的程序集,这似乎是您想要的。
在一般情况下,GetCallingAssembly 不一定会返回您想要的程序集,因为它返回包含调用堆栈中紧接在上面的方法的程序集(即它可能在同一个 DLL 中)。

我对GetCallingAssembly()不太确定 - 它返回包含调用方法的程序集(在调用堆栈上方)吗?这很有可能是库而不是可执行文件。 - Martin Harris
@Martin:是的,你说得对。GetCallingAssembly在某些情况下可能有效/适用,但在这里他似乎想要GetEntryAssembly。 - Noldorin

3

Assembly.GetEntryAssembly()


2
还有其他的内容:
System.Windows.Forms.Application.ExecutablePath;

1
我一直觉得Forms命名空间有Application类很蠢...它与Forms毫不相关。 - Nyerguds
LOL - 是啊,我也不用它。在这里找到它有点奇怪。 - user153923
4
实际上,Application类与Forms密切相关,因为Application.Run方法是启动主窗口消息循环的方式,处理所有窗口(也称为“表单”)事件。它还包含DoEvents(),允许窗口在处理程序内部处理待处理的事件。这些应该对于在.NET发明之前就编写Win32 API程序的任何人来说都很熟悉。 - Triynko

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