0

I have a WPF windowed application. There is a new request to enable application's command line arguments.

  1. Starting the app from command line (invoking app.exe) with no arguments, should start the GUI (as before)

  2. Starting the app from a command line with GUI switch (e.g., app.exe -gui) should start the GUI (as before)

  3. Starting the app from a command line with other switching (e.g., app.exe -f a -b e) should run the application in console mode (no window)

I have explored multiple posts such as Start WPF Application in Console Application (and similar), No output to console from a WPF application and reviewed command line parsers utilities such as Commandline Parser.

When implementing all the above (see code below), in option 3, the application is running in a new console process (new cmd window), so wrong arguments or --help switch is opening a new window and immediatly it is being closed.

How can I make use of the console that i invoked the app from? So when the user opens a new cmd window and type app.exe --help, no new console process/window will be opened.

[SuppressUnmanagedCodeSecurity]
public static class ConsoleManager
{
    private const string Kernel32_DllName = "kernel32.dll";

    [DllImport(Kernel32_DllName)]
    private static extern bool AllocConsole();

    [DllImport(Kernel32_DllName)]
    private static extern bool FreeConsole();

    [DllImport(Kernel32_DllName)]
    private static extern IntPtr GetConsoleWindow();

    [DllImport(Kernel32_DllName)]
    private static extern int GetConsoleOutputCP();

    public static bool HasConsole
    {
        get { return GetConsoleWindow() != IntPtr.Zero; }
    }

    /// <summary>
    /// Creates a new console instance if the process is not attached to a console already.
    /// </summary>
    public static void Show()
    {
        //#if DEBUG
        if (!HasConsole)
        {
            AllocConsole();
            InvalidateOutAndError();
        }
        //#endif
    }

    /// <summary>
    /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.
    /// </summary>
    public static void Hide()
    {
        if (HasConsole)
        {
            SetOutAndErrorNull();
            FreeConsole();
        }
    }

    static void InvalidateOutAndError()
    {
        Type type = typeof(System.Console);

        System.Reflection.FieldInfo _out = type.GetField("_out",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.FieldInfo _error = type.GetField("_error",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        Debug.Assert(_out != null);
        Debug.Assert(_error != null);

        Debug.Assert(_InitializeStdOutError != null);

        _out.SetValue(null, null);
        _error.SetValue(null, null);

        _InitializeStdOutError.Invoke(null, new object[] { true });
    }

    static void SetOutAndErrorNull()
    {
        Console.SetOut(TextWriter.Null);
        Console.SetError(TextWriter.Null);
    }
}

Code in my Starter class

 [STAThread]
 public static void Main(string[] args)
 {
        // enable the console
        ConsoleManager.Show();

        parser.ExtractArgumentAttributes(CommandLineArguments);

        try
        {
            parser.ParseCommandLine(args);                

            if (parser.ParsingSucceeded)
            {
                CommandLineArguments.AnalyzeOptions();
                if (CommandLineArguments.OpenMode == CmdLineArguments.UI_TYPE.GUI)
                {
                    // GUI usage model                        
                    ConsoleManager.Hide();  // shutdown the console                        
                    App.Main();             // start up the UI
                }
                else // command line usage
                {
                    // do stuff without a GUI
                }
            }                
        }
        catch (CommandLineException parser_exception)
        {
            Console.WriteLine(parser_exception.Message);
            parser.ShowUsage();
        }
  }
NirMH
  • 4,769
  • 3
  • 44
  • 69
  • 1
    I guess you should tear your app apart: 1 (or more) Core Libraries with all the Business, 1 WPF GUI App using the former, 1 Console App that either makes use of the Core Lib for operation without GUI or simply starts an instance of the GUI. – Fildor May 06 '19 at 11:39
  • @Fildor: what is the difference between my above architecture and your proposal? It seems to me it is the same, isn't it? – NirMH May 06 '19 at 12:32
  • You seem to cram all it into one Application. I am talking about 2 separate Applications and one (or several, depends on your architecture) Libs. You can have all of those in one Solution, but they would be different projects: Library, Console App and WPF App. That way you spare yourself from p/invoke and things will more likely actually work. – Fildor May 06 '19 at 12:38
  • Then, if you want to start the GUI from Console, you can just use Process.Start from within your console App. – Fildor May 06 '19 at 12:41
  • When it's in 'console' mode. What will your application do? It could be your logic should be in a windows service. – Andy May 06 '19 at 15:51

0 Answers0