dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.41k stars 382 forks source link

Support with WPF (WinUI) apps #733

Open shaggygi opened 4 years ago

shaggygi commented 4 years ago

It would be nice to use this API with WPF (and future WinUI) apps to parse out arguments. I have created basic apps that do this, within the App.xaml.cs file, but would probably be better if there was support from here.

jonsequitur commented 4 years ago

This is right in @Keboo's wheelhouse.

Keboo commented 4 years ago

@shaggygi what kind of support or integration were you wanting to see? To some degree there is already support (simple example here).

There are a few key details to be aware of:

  1. The "DragonFruit" project setup doesn't work. This is because both WPF and DragonFruit are going to try and generate your Main method for you.
  2. With the new SDK project (WPF on .NET Core) you have to change your OutputType (as indicated in the sample project above) if you want to see console output. You can still use this library to parse the arguments but it will silently hide all console output from you.
shaggygi commented 4 years ago

@Keboo thanks for response. I believe your example link will work for me. I'll try it out soon.

While I have not thought it out completely, I was mainly wanting to have the ability to 1) call the WPF EXE and run normally (displaying UI and such) and 2) interact with the WPF EXE in a console to see parameters/help/results like you would with this API and Console-type app. Thx

commonsensesoftware commented 4 years ago

@Keboo (and others), you might be interested in this solution that I finally put on out SO. @jonsequitur and I have talked about it offline before. While it's currently Windows-specific, I don't see why it has to be. This approach allows a winexe to operate as normal (e.g. with a hidden window), which is usually important; especially for apps that typically shouldn't have a console (ex: Windows service). However, if any command-line arguments are specified then you're in Console Mode. For a single command, you just run and exit. In an existing console, you take over and then relinquish it back when you're done. I didn't have a need for an interactive CLI, but I think it could be adapted to support that. Ultimately, this takes the reverse approach so that you only have the console window when it's necessary instead of all the time.

It'd be interesting to genericize this behavior and make it something that could easily be turned on or off. Nicer still would be if we actually had a compile target to handle this bootstrapping automatically (e.g. <OutputType>dualexe</OutputType> or something).

MovGP0 commented 4 years ago

I currently use it like this:

App.xaml.cs

    protected override void OnStartup(StartupEventArgs e)
    {
               // initalization here

               // at bottom of method
               var command = new RootCommand("SmartCardSolution")
                {
                    new Option<StartPage>(
                        new []{"--page", "-p"},
                        () => StartPage.Admin,
                        "The page that should be visible on startup.")
                };

                command.Handler = CommandHandler.Create((StartPage page) =>
                {
                    switch (page)
                    {
                        case StartPage.Admin:
                            ExecuteAdmin();
                            return 0;
                        default:
                            return 1;
                    }
                });

                var exitCode = command.InvokeAsync(e.Args).GetAwaiter().GetResult();
                Shutdown(exitCode);
    }

However, when executing the application with the --help or --version parameters, I don't get any console output.

cerebrate commented 3 years ago

I, too, wish to use this with WPF and WinForms apps.

However, the functionality I need is just displaying the help (and to a lesser extent version) information when those switches are used from the command line; in my roll-your-own apps in the past, I got this to work simply by calling AttachConsole (AttachParentProcess) before outputting the help text.

Is there a convenient hook or place I could intercept --help and --version usage in order to make that call?

jonsequitur commented 3 years ago

@cerebrate I don't have enough context. Right now those are implemented in middleware so when the parser or command is invoked, and those are present, they short circuit the command handler and write to the current IConsole. You could certainly provide an IConsole and call the invocation yourself, which is what our tests do using the TestConsole class. I could provide a more useful answer I think if I understood more about what you're currently doing.

Keboo commented 3 years ago

The primary issue I see with using the library with a WPF app, is with how Windows does output redirection. To avoid showing terminal windows every time a WPF app is launched, the Console.Out gets redirected. This is why things like --help and --version do not appear to work. Within your application this library is simply outputting that information to the default IConsole implementation (which just uses Console.Write). The hard part here is separating the difference between the applications being started normally, and the application being started from a command line invocation. Given that, I don't think there is much this library can do to address the WPF limitation..

With all of that said I did update my example app to use the Kernel32 AttachConsole method. This now properly shows help and version when they are passed.

cerebrate commented 3 years ago

@jonsequitur

The immediate context is that I have a utility (Mouse Jiggler), which is a Windows application, but which takes a variety of command-line parameters to allow manual override of various settings on startup (which would usually be used in the launching shortcut). However, since it can be launched from the command-line as well, I would like to let parameter errors (specifying an invalid jiggle period, for example), requests for help, etc., to show when this is the case - so that people can try out command-line parameters, refresh their memory of them, etc., easily.

In the old version of Mouse Jiggler, when I was rolling my own command-line parsing, I just called AttachConsole at the top of my PrintHelpText method, thus ensuring that if it was launched from a console session it would re-redirect output to that session's console. That's basically what I want to achieve here.

cerebrate commented 3 years ago

I can get what I want in the present just by wrapping the entire invoking of the command line in an AttachConsole / FreeConsole pair, since it does not appear to have harmful side effects when the parent is not a console app (e.g., it's started from a shortcut), but that seems inelegant. It would seem better to me if there were some hook that could be used to call them only when actually needed.