How do I create a C# app that decides itself whether to show as a console or windowed app?

前端 未结 11 786
广开言路
广开言路 2020-11-28 21:10

Is there a way to launch a C# application with the following features?

  1. It determines by command-line parameters whether it is a windowed or console app
相关标签:
11条回答
  • 2020-11-28 21:10

    NOTE: I haven't tested this, but I believe it would work...

    You could do this:

    Make your app a windows forms application. If you get a request for console, don't show your main form. Instead, use platform invoke to call into the Console Functions in the Windows API and allocate a console on the fly.

    (Alternatively, use the API to hide the console in a console app, but you'd probably see the console "flicker" as it was created in this case...)

    0 讨论(0)
  • 2020-11-28 21:12

    The important thing to remember to do after AttachConsole() or AllocConsole() calls to get it to work in all cases is:

    if (AttachConsole(ATTACH_PARENT_PROCESS))
      {
        System.IO.StreamWriter sw =
          new System.IO.StreamWriter(System.Console.OpenStandardOutput());
        sw.AutoFlush = true;
        System.Console.SetOut(sw);
        System.Console.SetError(sw);
      }
    

    I have found that works with or without VS hosting process. With output being sent with System.Console.WriteLine or System.Console.out.WriteLine before call To AttachConsole or AllocConsole. I have included my method below:

    public static bool DoConsoleSetep(bool ClearLineIfParentConsole)
    {
      if (GetConsoleWindow() != System.IntPtr.Zero)
      {
        return true;
      }
      if (AttachConsole(ATTACH_PARENT_PROCESS))
      {
        System.IO.StreamWriter sw = new System.IO.StreamWriter(System.Console.OpenStandardOutput());
        sw.AutoFlush = true;
        System.Console.SetOut(sw);
        System.Console.SetError(sw);
        ConsoleSetupWasParentConsole = true;
        if (ClearLineIfParentConsole)
        {
          // Clear command prompt since windows thinks we are a windowing app
          System.Console.CursorLeft = 0;
          char[] bl = System.Linq.Enumerable.ToArray<char>(System.Linq.Enumerable.Repeat<char>(' ', System.Console.WindowWidth - 1));
          System.Console.Write(bl);
          System.Console.CursorLeft = 0;
        }
        return true;
      }
      int Error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
      if (Error == ERROR_ACCESS_DENIED)
      {
        if (log.IsDebugEnabled) log.Debug("AttachConsole(ATTACH_PARENT_PROCESS) returned ERROR_ACCESS_DENIED");
        return true;
      }
      if (Error == ERROR_INVALID_HANDLE)
      {
        if (AllocConsole())
        {
          System.IO.StreamWriter sw = new System.IO.StreamWriter(System.Console.OpenStandardOutput());
          sw.AutoFlush = true;
          System.Console.SetOut(sw);
          System.Console.SetError(sw);
          return true;
        }
      }
      return false;
    }
    

    I also called this when I was done in case I needed command prompt to redisplay when I was done doing output.

    public static void SendConsoleInputCR(bool UseConsoleSetupWasParentConsole)
    {
      if (UseConsoleSetupWasParentConsole && !ConsoleSetupWasParentConsole)
      {
        return;
      }
      long LongNegOne = -1;
      System.IntPtr NegOne = new System.IntPtr(LongNegOne);
      System.IntPtr StdIn = GetStdHandle(STD_INPUT_HANDLE);
      if (StdIn == NegOne)
      {
        return;
      }
      INPUT_RECORD[] ira = new INPUT_RECORD[2];
      ira[0].EventType = KEY_EVENT;
      ira[0].KeyEvent.bKeyDown = true;
      ira[0].KeyEvent.wRepeatCount = 1;
      ira[0].KeyEvent.wVirtualKeyCode = 0;
      ira[0].KeyEvent.wVirtualScanCode = 0;
      ira[0].KeyEvent.UnicodeChar = '\r';
      ira[0].KeyEvent.dwControlKeyState = 0;
      ira[1].EventType = KEY_EVENT;
      ira[1].KeyEvent.bKeyDown = false;
      ira[1].KeyEvent.wRepeatCount = 1;
      ira[1].KeyEvent.wVirtualKeyCode = 0;
      ira[1].KeyEvent.wVirtualScanCode = 0;
      ira[1].KeyEvent.UnicodeChar = '\r';
      ira[1].KeyEvent.dwControlKeyState = 0;
      uint recs = 2;
      uint zero = 0;
      WriteConsoleInput(StdIn, ira, recs, out zero);
    }
    

    Hope this helps...

    0 讨论(0)
  • 2020-11-28 21:22

    No 1 is easy.

    No 2 can't be done, I don't think.

    The docs say:

    Calls to methods such as Write and WriteLine have no effect in Windows applications.

    The System.Console class is initialized differently in console and GUI applications. You can verify this by looking at the Console class in the debugger in each application type. Not sure if there's any way to re-initialize it.

    Demo: Create a new Windows Forms app, then replace the Main method with this:

        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
            else
            {
                Console.WriteLine("Console!\r\n");
            }
        }
    

    The idea is that any command line parameters will print to the console and exit. When you run it with no arguments, you get the window. But when you run it with a command line argument, nothing happens.

    Then select the project properties, change the project type to "Console Application", and recompile. Now when you run it with an argument, you get "Console!" like you want. And when you run it (from the command line) with no arguments, you get the window. But the command prompt won't return until you exit the program. And if you run the program from Explorer, a command window will open and then you get a window.

    0 讨论(0)
  • 2020-11-28 21:26

    I've done this by creating two separate apps.

    Create the WPF app with this name: MyApp.exe. And create the console app with this name: MyApp.com. When you type your app name in the command line like this MyApp or MyApp /help (without .exe extension) the console app with the .com extension will take precedence. You can have your console application invoke the MyApp.exe according to the parameters.

    This is exactly how devenv behaves. Typing devenv at the command line will launch Visual Studio's IDE. If you pass parameters like /build, it will remain in the command line.

    0 讨论(0)
  • 2020-11-28 21:27

    I would create a solution that is a Windows Form App since there are two functions you can call that will hook into the current console. So you can treat the program like a console program. or by default you can launch the GUI.

    The AttachConsole function will not create a new console. For more information about AttachConsole, check out PInvoke: AttachConsole

    Below a sample program of how to use it.

    using System.Runtime.InteropServices;
    
    namespace Test
    {
        /// <summary>
        /// This function will attach to the console given a specific ProcessID for that Console, or
        /// the program will attach to the console it was launched if -1 is passed in.
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool AttachConsole(int dwProcessId);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool FreeConsole();
    
    
        [STAThread]
        public static void Main() 
        {   
            Application.ApplicationExit +=new EventHandler(Application_ApplicationExit);
            string[] commandLineArgs = System.Environment.GetCommandLineArgs();
    
            if(commandLineArgs[0] == "-cmd")
            {
                //attaches the program to the running console to map the output
                AttachConsole(-1);
            }
            else
            {
                //Open new form and do UI stuff
                Form f = new Form();
                f.ShowDialog();
            }
    
        }
    
        /// <summary>
        /// Handles the cleaning up of resources after the application has been closed
        /// </summary>
        /// <param name="sender"></param>
        public static void Application_ApplicationExit(object sender, System.EventArgs e)
        {
            FreeConsole();
        }
    }
    
    0 讨论(0)
  • 2020-11-28 21:28

    One way to do this is to write a Window app that doesn't show a window if the command line arguments indicate it shouldn't.

    You can always get the command line arguments and check them before showing the first window.

    0 讨论(0)
提交回复
热议问题