在 MAC 系统下,如果在运行过程中,应用程序的文件被删除了,那么此时如果应用程序执行了 Process.Start 方法时,将会抛出 Win32Exception 异常

我写了一个工具 dotnetCampus.UpdateAllDotNetTools 用来更新所有的 dotnet tool 工具。因为 dotnetCampus.UpdateAllDotNetTools 也是一个 dotnet tool 工具,因此也会更新自身

但是有小伙伴告诉我,在使用 dotnetCampus.UpdateAllDotNetTools 更新 dotnetCampus.UpdateAllDotNetTools 到最新版本之后,其他的 dotnet tool 就都不能更新了,将会抛出 Win32Exception 异常,如下图

在更新 dotnetCampus.UpdateAllDotNetTools 将会删除当前运行的 dotnetCampus.UpdateAllDotNetTools 进程的文件,在 MAC 下是可以删除正在运行的程序的文件,但是在 .NET Core 的 Process.Start 方法里面的逻辑是需要先获取当前进程所在的文件,获取对应的文件夹,用于找到命令

例如我输入了 dotnet 命令,通过 Process.Start("dotnet") 那么 .NET 将先尝试在程序所在的文件夹寻找是否存在 “dotnet” 这个程序,如果存在,那么执行。因此第一步就是获取当前进程所在的文件

在 .NET 开源代码里面,可以在 src\libraries\System.Diagnostics.Process\src\System\Diagnostics\Process.Unix.cs 找到实际的 Process Start 逻辑

        /// <summary>
        /// Starts the process using the supplied start info.
        /// With UseShellExecute option, we'll try the shell tools to launch it(e.g. "open fileName")
        /// </summary>
        /// <param name="startInfo">The start info with which to start the process.</param>
        private bool StartCore(ProcessStartInfo startInfo)
        {
                // 忽略代码
                filename = ResolvePath(startInfo.FileName);
                argv = ParseArgv(startInfo);
                if (Directory.Exists(filename))
                {
                    throw new Win32Exception(SR.DirectoryNotValidAsInput);
                }

                ForkAndExecProcess(filename, argv, envp, cwd,
                    startInfo.RedirectStandardInput, startInfo.RedirectStandardOutput, startInfo.RedirectStandardError,
                    setCredentials, userId, groupId, groups,
                    out stdinFd, out stdoutFd, out stderrFd, usesTerminal);

                // 忽略代码
        }

在 ResolvePath 方法将会先尝试获取当前的文件夹,具体的实现将会在 src\libraries\System.Diagnostics.Process\src\System\Diagnostics\Process.OSX.cs 文件里

在 MAC 系统的代号里面,上古版本就是 OSX 也就是 OSX 就是 MAC 系统,上面这个代码文件就是特别给 MAC 系统使用的

那么获取当前文件用的是什么方法?调用一个和 Windows 的 P/Invoke 方法差不多的方法

        /// <summary>Gets the path to the current executable, or null if it could not be retrieved.</summary>
        private static string GetExePath()
        {
            return Interop.libproc.proc_pidpath(Environment.ProcessId);
        }

这个 proc_pidpath 是什么方法?这是一个获取传入的进程号拿到对应的文件路径的方法,如果传入的进程号对应的文件被删除了,那么将会抛出 Win32Exception 异常

System.ComponentModel.Win32Exception (2): No such file or Directory
at Interop.libproc.proc_pidpath(Int32 pid)
at System.Diagnostics.Process.ResolvePath(string filename)
at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()

这就是在 Unhandled exception on Mac · Issue #4 · dotnet-campus/dotnetCampus.UpdateAllDotNetTools 的问题

我尝试在 dotnet runtime 修复这个坑 Ignore the executable file be deleted in Process Start by lindexi · Pull Request #40748 · dotnet/runtime


本文会经常更新,请阅读原文: https://dotnet-campus.github.io//post/dotnet-core-%E5%9C%A8-MAC-%E7%B3%BB%E7%BB%9F%E4%B8%8B%E5%88%A0%E9%99%A4%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E8%87%AA%E5%B7%B1%E5%90%8E%E8%B0%83-Process-Start-%E6%96%B9%E6%B3%95%E5%B0%86%E4%BC%9A%E6%8A%9B%E5%87%BA-Win32-%E5%BC%82%E5%B8%B8.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

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