public static bool RelaunchSelfElevated()

in src/Cli/func/ConsoleApp.cs [128:201]


        public static bool RelaunchSelfElevated(IAction action, out string errors)
        {
            // A command is:
            //     func <context: optional> \
            //          <subcontext: valid only if there is a context, optional> \
            //          <action: not optional> --options
            errors = string.Empty;
            var attribute = action.GetType().GetCustomAttribute<ActionAttribute>();
            if (attribute != null)
            {
                // First extract the contexts for the given action
                Func<Context, string> getContext = c => c == Context.None ? string.Empty : c.ToString();
                var context = getContext(attribute.Context);
                var subContext = getContext(attribute.Context);

                // Get the actual action name to use on the command line.
                var name = attribute.Name;

                // Every action is expected to return a ICommandLineParserResult that contains
                // a collection UnMatchedOptions that the action accepts.
                // That means this method doesn't support actions that have untyped ordered options.
                // This however can be updated to support them easily just like help does now.
                var args = action
                    .ParseArgs(Array.Empty<string>())
                    .UnMatchedOptions

                    // Description is expected to contain the name of the POCO's property holding the value.
                    .Select(o => new { Name = o.Description, ParamName = o.HasLongName ? $"--{o.LongName}" : $"-{o.ShortName}" })
                    .Select(n =>
                    {
                        var property = action.GetType().GetProperty(n.Name);
                        if (property.PropertyType.IsGenericEnumerable())
                        {
                            var genericCollection = property.GetValue(action) as IEnumerable;
                            var collection = genericCollection.Cast<object>().Select(o => o.ToString());
                            return $"{n.ParamName} {string.Join(" ", collection)}";
                        }
                        else
                        {
                            return $"{n.ParamName} {property.GetValue(action).ToString()}";
                        }
                    })
                    .Aggregate((a, b) => string.Join(" ", a, b));

                var command = $"{context} {subContext} {name} {args}";

                // Since the process will be elevated, we won't be able to redirect stdout\stdin to
                // our process if we are not elevated too, which is most probably the case.
                // Therefore I use shell redirection > to a temp file, then read the content after
                // the process exists.
                var logFile = Path.GetTempFileName();
                var exeName = Process.GetCurrentProcess().MainModule.FileName;

                // '2>&1' redirects stderr to stdout.
                command = $"/c \"\"{exeName}\" {command} > \"{logFile}\" 2>&1\"";
                var startInfo = new ProcessStartInfo("cmd")
                {
                    Verb = "runas",
                    Arguments = command,
                    WorkingDirectory = Environment.CurrentDirectory,
                    CreateNoWindow = false,
                    UseShellExecute = true
                };

                var process = Process.Start(startInfo);
                process.WaitForExit();
                errors = File.ReadAllText(logFile);
                return process.ExitCode == ExitCodes.Success;
            }
            else
            {
                throw new ArgumentException($"{nameof(IAction)} type doesn't have {nameof(ActionAttribute)}");
            }
        }