Как создать приложение на C #, которое решит, показывать ли его как консольное или оконное приложение?

Есть ли способ запустить приложение C # со следующими функциями?

  1. Он определяет по параметрам командной строки, является ли это оконным или консольным приложением
  2. Он не показывает консоль, когда ее просят оконть и не отображает окно GUI при запуске с консоли.

Например,

  myapp.exe / help 

будет выводиться на stdout на консоли, которую вы использовали, но

  myapp.exe 

сам по себе запустит мой Winforms или пользовательский интерфейс WPF.

Лучшие ответы, которые я знаю до сих пор, связаны с наличием двух отдельных exe и использованием IPC, но это кажется действительно взломанным.

Какие у меня есть варианты и компромиссы, чтобы получить поведение, описанное в примере выше? Я открыт для идей, специфичных для Winform или WPF.

Сделайте приложение обычным окном и при необходимости создайте консоль на лету.

Подробнее об этой ссылке (код ниже оттуда)

using System; using System.Windows.Forms; namespace WindowsApplication1 { static class Program { [STAThread] static void Main(string[] args) { if (args.Length > 0) { // Command line given, display console if ( !AttachConsole(-1) ) { // Attach to an parent process console AllocConsole(); // Alloc a new console } ConsoleMain(args); } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } private static void ConsoleMain(string[] args) { Console.WriteLine("Command line = {0}", Environment.CommandLine); for (int ix = 0; ix < args.Length; ++ix) Console.WriteLine("Argument{0} = {1}", ix + 1, args[ix]); Console.ReadLine(); } [System.Runtime.InteropServices.DllImport("kernel32.dll")] private static extern bool AllocConsole(); [System.Runtime.InteropServices.DllImport("kernel32.dll")] private static extern bool AttachConsole(int pid); } } 

Я в основном делаю так, как показано в ответе Эрика, кроме того, я отсоединяю консоль от FreeConsole и использую команду SendKeys, чтобы вернуть командную строку.

  [DllImport("kernel32.dll")] private static extern bool AllocConsole(); [DllImport("kernel32.dll")] private static extern bool AttachConsole(int pid); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeConsole(); [STAThread] static void Main(string[] args) { if (args.Length > 0 && (args[0].Equals("/?") || args[0].Equals("/help", StringComparison.OrdinalIgnoreCase))) { // get console output if (!AttachConsole(-1)) AllocConsole(); ShowHelp(); // show help output with Console.WriteLine FreeConsole(); // detach console // get command prompt back System.Windows.Forms.SendKeys.SendWait("{ENTER}"); return; } // normal winforms code Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } 

Напишите два приложения (одну консоль, одно окно), а затем напишите еще одно меньшее приложение, которое на основе указанных параметров откроет одно из других приложений (а затем, предположительно, закроется, так как оно больше не понадобится)?

Я сделал это, создав два отдельных приложения.

Создайте приложение WPF с таким именем: MyApp.exe . Создайте консольное приложение с таким именем: MyApp.com . Когда вы вводите имя приложения в командной строке, например MyApp или MyApp /help (без расширения .exe ), консольное приложение с расширением .com будет иметь приоритет. Вы можете MyApp.exe консольное приложение MyApp.exe соответствии с параметрами.

Это именно то, как ведет себя devenv. Набрав devenv в командной строке, вы запустите среду разработки Visual Studio. Если вы передадите такие параметры, как /build , он останется в командной строке.

ПРИМЕЧАНИЕ. Я не тестировал это, но я считаю, что это сработает …

Вы можете сделать это:

Сделайте приложение приложение форм Windows. Если вы получите запрос на консоль, не показывайте свою основную форму. Вместо этого используйте платформу invoke для вызова функций консоли в Windows API и распределения консоли «на лету».

(В качестве альтернативы, используйте API, чтобы скрыть консоль в консольном приложении, но вы, вероятно, увидите консоль «мерцание», поскольку она была создана в этом случае …)

Насколько мне известно, в exe есть флаг, в котором говорится, следует ли запускать консольное или оконное приложение. Вы можете щелкнуть флагом инструментами, которые поставляются с Visual Studio, но вы не можете сделать это во время выполнения.

Если exe скомпилирован как консоль, он всегда будет открывать новую консоль, если ее не запускать с одной. Если exe является приложением, он не может выводиться на консоль. Вы можете создать отдельную консоль – но она не будет вести себя как консольное приложение.

В прошлом мы использовали 2 отдельных exe. Консоль – это тонкая shell над формами (вы можете ссылаться на exe, поскольку вы ссылаетесь на dll, и вы можете использовать атрибут [assembly: InternalsVisibleTo («cs_friend_assemblies_2»)], чтобы доверять консольному, t должен выставить больше, чем вам нужно).

Я бы создал решение, которое является приложением Windows Form, поскольку есть две функции, которые вы можете вызвать, которые будут подключаться к текущей консоли. Таким образом, вы можете рассматривать программу как консольную программу. или по умолчанию вы можете запустить графический интерфейс.

Функция AttachConsole не создаст новую консоль. Для получения дополнительной информации о AttachConsole, проверьте PInvoke: AttachConsole

Ниже пример программы использования.

 using System.Runtime.InteropServices; namespace Test { ///  /// 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. ///  [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(); } } ///  /// Handles the cleaning up of resources after the application has been closed ///  ///  public static void Application_ApplicationExit(object sender, System.EventArgs e) { FreeConsole(); } } 

Возможно, эта ссылка даст некоторое представление о том, что вы хотите сделать.

Один из способов сделать это – написать приложение Window, которое не отображает окно, если аргументы командной строки указывают, что это не должно.

Вы всегда можете получить аргументы командной строки и проверить их перед отображением первого windows.

Важная вещь, которую следует помнить после вызовов AttachConsole() или AllocConsole() чтобы заставить ее работать во всех случаях:

 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); } 

Я обнаружил, что работает с хостингом VS или без него. При отправке вывода с помощью System.Console.WriteLine или System.Console.out.WriteLine перед вызовом AttachConsole или AllocConsole . Я включил свой метод ниже:

 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(System.Linq.Enumerable.Repeat(' ', 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; } 

Я также назвал это, когда я закончил, если мне понадобится командная строка для повторного отображения, когда я закончил делать вывод.

 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); } 

Надеюсь это поможет…

Нет 1 легко.

Нет, я не могу сделать, я не думаю.

Документы говорят:

Вызовы на такие методы, как Write и WriteLine, не имеют эффекта в приложениях Windows.

Класс System.Console инициализируется по-разному в консольных и графических приложениях. Вы можете проверить это, посмотрев class Console в отладчике в каждом типе приложения. Не уверен, есть ли способ повторно инициализировать его.

Демонстрация: создайте новое приложение Windows Forms, затем замените метод Main следующим образом:

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

Идея заключается в том, что любые параметры командной строки будут выводиться на консоль и выходить из нее. Когда вы запускаете его без аргументов, вы получаете окно. Но когда вы запускаете его с аргументом командной строки, ничего не происходит.

Затем выберите свойства проекта, измените тип проекта на «Консольное приложение» и перекомпилируйте. Теперь, когда вы запускаете его с аргументом, вы получаете «Консоль!». Как ты хочешь. И когда вы запустите его (из командной строки) без аргументов, вы получите окно. Но командная строка не вернется, пока вы не выйдете из программы. И если вы запустите программу из Проводника, откроется окно команд, а затем вы получите окно.

Я разработал способ сделать это, включая использование stdin, но я должен предупредить вас, что это некрасиво.

Проблема с использованием stdin из подключенной консоли заключается в том, что shell также будет читать из нее. Это приводит к тому, что входные данные иногда идут в ваше приложение, но иногда в оболочку.

Решение состоит в том, чтобы заблокировать оболочку в течение всего срока службы приложений (хотя технически вы можете попытаться заблокировать ее только тогда, когда вам потребуется ввод). Способ, которым я решил сделать это, – это отправить нажатия клавиш в оболочку, чтобы запустить команду powershell, которая ждет завершения приложения.

Кстати, это также устраняет проблему, когда запрос не возвращается после завершения приложения.

Я кратко попытался заставить его работать с консоли powershell. Те же принципы применяются, но я не получил его для выполнения моей команды. Возможно, у powershell есть некоторые проверки безопасности, чтобы предотвратить запуск команд из других приложений. Поскольку я не использую powershell много, я не смотрел в нее.

  [DllImport("kernel32.dll", SetLastError = true)] private static extern bool AllocConsole(); [DllImport("kernel32", SetLastError = true)] private static extern bool AttachConsole(int dwProcessId); private const uint STD_INPUT_HANDLE = 0xfffffff6; private const uint STD_OUTPUT_HANDLE = 0xfffffff5; private const uint STD_ERROR_HANDLE = 0xfffffff4; [DllImport("kernel32.dll")] private static extern IntPtr GetStdHandle(uint nStdHandle); [DllImport("Kernel32.dll", SetLastError = true)] public static extern int SetStdHandle(uint nStdHandle, IntPtr handle); [DllImport("kernel32.dll", SetLastError = true)] private static extern int GetConsoleProcessList(int[] ProcessList, int ProcessCount); [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); ///  /// Attach to existing console or create new. Must be called before using System.Console. ///  /// Return true if console exists or is created. public static bool InitConsole(bool createConsole = false, bool suspendHost = true) { // first try to attach to an existing console if (AttachConsole(-1)) { if (suspendHost) { // to suspend the host first try to find the parent var processes = GetConsoleProcessList(); Process host = null; string blockingCommand = null; foreach (var proc in processes) { var netproc = Process.GetProcessById(proc); var processName = netproc.ProcessName; Console.WriteLine(processName); if (processName.Equals("cmd", StringComparison.OrdinalIgnoreCase)) { host = netproc; blockingCommand = $"powershell \"& wait-process -id {Process.GetCurrentProcess().Id}\""; } else if (processName.Equals("powershell", StringComparison.OrdinalIgnoreCase)) { host = netproc; blockingCommand = $"wait-process -id {Process.GetCurrentProcess().Id}"; } } if (host != null) { // if a parent is found send keystrokes to simulate a command var cmdWindow = host.MainWindowHandle; if (cmdWindow == IntPtr.Zero) Console.WriteLine("Main Window null"); foreach (char key in blockingCommand) { SendChar(cmdWindow, key); System.Threading.Thread.Sleep(1); // required for powershell } SendKeyDown(cmdWindow, Keys.Enter); // i haven't worked out how to get powershell to accept a command, it might be that this is a security feature of powershell if (host.ProcessName == "powershell") Console.WriteLine("\r\n *** PRESS ENTER ***"); } } return true; } else if (createConsole) { return AllocConsole(); } else { return false; } } private static void SendChar(IntPtr cmdWindow, char k) { const uint WM_CHAR = 0x0102; IntPtr result = PostMessage(cmdWindow, WM_CHAR, ((IntPtr)k), IntPtr.Zero); } private static void SendKeyDown(IntPtr cmdWindow, Keys k) { const uint WM_KEYDOWN = 0x100; const uint WM_KEYUP = 0x101; IntPtr result = SendMessage(cmdWindow, WM_KEYDOWN, ((IntPtr)k), IntPtr.Zero); System.Threading.Thread.Sleep(1); IntPtr result2 = SendMessage(cmdWindow, WM_KEYUP, ((IntPtr)k), IntPtr.Zero); } public static int[] GetConsoleProcessList() { int processCount = 16; int[] processList = new int[processCount]; // supposedly calling it with null/zero should return the count but it didn't work for me at the time // limiting it to a fixed number if fine for now processCount = GetConsoleProcessList(processList, processCount); if (processCount <= 0 || processCount >= processList.Length) return null; // some sanity checks return processList.Take(processCount).ToArray(); } 
  • Убедитесь, что приложение не работает в течение определенного периода времени и блокирует его
  • Как повысить свойства Измененные события в зависимости от свойства?
  • Наследование с помощью UserControl в WPF
  • Обеспечение того, чтобы все выполнялось в streamе пользовательского интерфейса в WPF
  • Использование управления изображением в WPF для отображения System.Drawing.Bitmap
  • Как изменить источник изображения динамически из кода в WPF с изображением в свойствах. Ресурсы?
  • Ленивая загрузка содержимого вкладки WPF
  • Проблемы с перезаписью (повторным сохранением) изображения, когда он был установлен как источник изображения
  • Как связать команду в WPF с обработчиком события двойного щелчка элемента управления?
  • Доступ к переменной codebehind в XAML
  • Лучший способ сделать сортировку WPF ListView / GridView при нажатии на заголовок столбца?
  • Давайте будем гением компьютера.