取消

.NET 命令行参数包含应用程序路径吗?

如果你关注过命令行参数,也许发现有时你会在命令行参数的第一个参数中中看到应用程序的路径,有时又不会。那么什么情况下有路径呢?


其实是否有路径只是取决于获取命令行参数的时候用的是什么方法。而这是 Windows 操作系统的机制,与具体的运行环境无关。

测试程序

考虑下面这样的测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Globalization;

namespace Walterlv.Demo.CommandLines
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"参数总数:{args.Length}");
            OutputArgsInfo(args);

            Console.WriteLine($"按任意键继续……");
            Console.ReadKey();
        }

        private static void OutputArgsInfo(string[] args)
        {
            var digitCount = (args.Length - 1).ToString(CultureInfo.InvariantCulture).Length;

            for (var i = 0; i < args.Length; i++)
            {
                Console.WriteLine($"[{i.ToString().PadLeft(digitCount, ' ')}] {args[i]}");
            }
        }
    }
}

当我们向命令行中传入参数的时候,我们可以得到所有的命令行。

Main 函数中的命令行参数
▲ Main 函数中的命令行参数

这种行为与具体的 .NET SDK 无关。看我们的项目文件,可以发现,无论是老旧的 .NET Framework 4.5 还是新的 .NET Framework 4.7.2 还是更加主流的 .NET Core 2.1,命令行参数中都是没有应用程序路径的。

1
2
3
4
5
6
7
8
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net45;net472;netcoreapp2.1</TargetFrameworks>
  </PropertyGroup>

</Project>

那为什么有时候会看到应用程序路径呢?

解释

在《Windows 核心编程》一书中有说到:

可以获得一个指向进程的完整命令行的指针,方法是调用 GetCommandLine 函数:

1
PTSTR GetCommandLine();

该函数返回一个指向包含完整命令行的缓存的指针,该命令行包括执行文件的完整路径名。

也就是说,调用 GetCommandLine 函数时,我们将得到包含执行文件的完整路径名的命令行参数。这个方法对应到 .NET 中,是 System.Environment.GetCommandLineArgs()

于是修改我们刚刚的函数,加上 Environment.GetCommandLineArgs() 的调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Globalization;

namespace Walterlv.Demo.CommandLines
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"Main 函数参数列表中参数总数:{args.Length}");
            OutputArgsInfo(args);

            args = Environment.GetCommandLineArgs();
            Console.WriteLine($"GetCommandLineArgs 参数总数:{args.Length}");
            OutputArgsInfo(args);

            Console.WriteLine($"按任意键继续……");
            Console.ReadKey();
        }

        private static void OutputArgsInfo(string[] args)
        {
            var digitCount = (args.Length - 1).ToString(CultureInfo.InvariantCulture).Length;

            for (var i = 0; i < args.Length; i++)
            {
                Console.WriteLine($"[{i.ToString().PadLeft(digitCount, ' ')}] {args[i]}");
            }
        }
    }
}

现在,我们能看到参数列表中多了应用程序的完整路径:

GetCommandLineArgs 中的命令行参数
▲ GetCommandLineArgs 中的命令行参数

事实上这样的差异不止在 .NET 中有体现,整个 Windows 上的程序都是这样的特性。这在《Windows 核心编程》一书中是有说明的。

总结

  1. Main 函数的参数中不包含应用程序执行路径;
  2. System.Environment.GetCommandLineArgs() 得到的命令行参数中包含应用程序的执行路径;
  3. Windows 上的所有程序其命令行参数的行为表现都是如此,这不是 .NET 的专属特性。

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/when-will-the-command-line-args-contain-the-executable-path.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

登录 GitHub 账号进行评论