Я хочу создать программу на C #, которую можно будет запускать как приложение CLI или GUI, в зависимости от того, какие флаги в него переданы. Это можно сделать?
Я нашел эти связанные вопросы, но они не совсем подходят для моей ситуации:
Я хочу создать программу на C #, которую можно будет запускать как приложение CLI или GUI, в зависимости от того, какие флаги в него переданы. Это можно сделать?
Я нашел эти связанные вопросы, но они не совсем подходят для моей ситуации:
Ответы:
Ответ Jdigital указывает на блог Рэймонда Чена , в котором объясняется, почему у вас не может быть приложения, которое одновременно является консольной и неконсольной
*
программой: ОС должна знать перед запуском программы, какую подсистему использовать. После того, как программа запустилась, уже слишком поздно возвращаться и запрашивать другой режим.Ответ Кейда указывает на статью о запуске приложения .Net WinForms с помощью консоли . Он использует технику вызова
AttachConsole
после запуска программы. Это позволяет программе выполнять обратную запись в окно консоли командной строки, запустившей программу. Но комментарии в этой статье указывают на то, что я считаю фатальной ошибкой: дочерний процесс на самом деле не управляет консолью. Консоль продолжает принимать ввод от имени родительского процесса, и родительский процесс не знает, что ему следует дождаться завершения дочернего процесса, прежде чем использовать консоль для других целей.В статье Чена есть ссылка на статью Цзюньфэна Чжана, в которой объясняются еще несколько техник .
Первое - это то, что использует devenv . Он работает, фактически имея две программы. Один из них - devenv.exe , основная программа с графическим интерфейсом, а другой - devenv.com , который обрабатывает задачи в консольном режиме, но если он используется не консольным образом, он перенаправляет свои задачи на devenv.exe и выходы. Этот метод основан на правиле Win32, согласно которому com- файлы выбираются перед exe- файлами, когда вы вводите команду без расширения файла.
Есть более простой вариант, который делает Windows Script Host. Он предоставляет две совершенно отдельные исполняемые файлы, wscript.exe и cscript.exe . Точно так же Java предоставляет java.exe для консольных программ и javaw.exe для неконсольных программ.
Илдазм использует вторую технику Джунфэна . Он цитирует процесс, через который прошел автор ildasm, заставляя его работать в обоих режимах. В конечном итоге вот что он делает:
Недостаточно просто вызвать,
FreeConsole
чтобы первый экземпляр перестал быть консольной программой. Это связано с тем, что процесс, запустивший программу, cmd.exe , «знает», что он запустил программу в режиме консоли, и ожидает, когда программа остановится. ВызовFreeConsole
заставит ildasm перестать использовать консоль, но не заставит родительский процесс начать использовать консоль.Итак, первый экземпляр перезагружается (полагаю, с дополнительным параметром командной строки). Когда вы вызываете
CreateProcess
, есть два разных флага, которые нужно попробовать,DETACHED_PROCESS
иCREATE_NEW_CONSOLE
любой из них гарантирует, что второй экземпляр не будет прикреплен к родительской консоли. После этого первый экземпляр может завершить работу и разрешить командной строке возобновить обработку команд.Побочный эффект этого метода заключается в том, что когда вы запускаете программу из графического интерфейса пользователя, консоль все равно будет. Он на мгновение мигнет на экране, а затем исчезнет.
Я думаю, что часть статьи Джунфэна об использовании editbin для изменения флага консольного режима программы - отвлекающий маневр. Ваш компилятор или среда разработки должны предоставлять параметр или параметр для управления типом создаваемого двоичного файла. После этого не нужно ничего изменять.
Суть в том, что у вас может быть либо два двоичных файла, либо у вас может быть мгновенное мерцание окна консоли . Как только вы решите, какое из зол меньшее, у вас будет выбор вариантов реализации.
*
Я говорю « неконсольный» вместо GUI, потому что в противном случае это ложная дихотомия. То, что у программы нет консоли, не означает, что у нее есть графический интерфейс. Приложение-служба - яркий тому пример. Также программа может иметь консоль и окна.источник
WinMain
функцию с соответствующими параметрами (так что скомпилировать с/SUBSYSTEM:WINDOWS
), а затем изменить режим постфактум, чтобы загрузчик запускает консольный хост. Для получения дополнительной обратной связи я пробовал этоCREATE_NO_WINDOW
в CreateProcess и вGetConsoleWindow() == NULL
качестве проверки, был ли перезапущен или нет. Это не устраняет мерцание консоли, но означает отсутствие специального аргумента cmd.Посмотрите блог Раймонда по этой теме:
https://devblogs.microsoft.com/oldnewthing/20090101-00/?p=19643
Его первое предложение: «Вы не можете, но вы можете попытаться подделать это».
источник
http://www.csharp411.com/console-output-from-winforms-application/
Просто проверьте аргументы командной строки перед
Application.
материалом WinForms .Я должен добавить, что в .NET НЕДОСТАТОЧНО легко просто создать консоль и проекты GUI в одном решении, которые разделяют все свои сборки, кроме main. И в этом случае вы можете заставить версию командной строки просто запускать версию с графическим интерфейсом, если она запускается без параметров. Вы получите мигающую консоль.
источник
Есть простой способ делать то, что вы хотите. Я всегда использую его при написании приложений, которые должны иметь как интерфейс командной строки, так и графический интерфейс. Вы должны установить свой "OutputType" на "ConsoleApplication", чтобы это работало.
class Program { [DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")] private static extern IntPtr _GetConsoleWindow(); /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); /* * This works as following: * First we look for command line parameters and if there are any of them present, we run the CLI version. * If there are no parameters, we try to find out if we are run inside a console and if so, we spawn a new copy of ourselves without a console. * If there is no console at all, we show the GUI. * We make an exception if we find out, that we're running inside visual studio to allow for easier debugging the GUI part. * This way we're both a CLI and a GUI. */ if (args != null && args.Length > 0) { // execute CLI - at least this is what I call, passing the given args. // Change this call to match your program. CLI.ParseCommandLineArguments(args); } else { var consoleHandle = _GetConsoleWindow(); // run GUI if (consoleHandle == IntPtr.Zero || AppDomain.CurrentDomain.FriendlyName.Contains(".vshost")) // we either have no console window or we're started from within visual studio // This is the form I usually run. Change it to match your code. Application.Run(new MainForm()); else { // we found a console attached to us, so restart ourselves without one Process.Start(new ProcessStartInfo(Assembly.GetEntryAssembly().Location) { CreateNoWindow = true, UseShellExecute = false }); } } }
источник
Я думаю, что предпочтительнее то, что Роб назвал devenv , с использованием двух исполняемых файлов: программы запуска «.com» и оригинального «.exe». Это не так сложно использовать, если у вас есть шаблонный код для работы (см. Ссылку ниже).
В этом методе используются уловки, позволяющие сделать этот .com прокси-сервером для stdin / stdout / stderr и запустить одноименный файл .exe. Это дает возможность программе преформироваться в режиме командной строки при вызове из консоли (возможно, только при обнаружении определенных аргументов командной строки), в то же время имея возможность запускаться как приложение с графическим интерфейсом без консоли.
Я разместил в Google Code проект под названием dualsubsystem, который обновляет старое решение codeguru для этой техники и предоставляет исходный код и рабочие примеры двоичных файлов.
источник
Вот то, что я считаю простым решением проблемы .NET C #. Чтобы повторить проблему, когда вы запускаете консольную «версию» приложения из командной строки с переключателем, консоль продолжает ждать (она не возвращается в командную строку, и процесс продолжает работать), даже если у вас есть
Environment.Exit(0)
в конце вашего кода. Чтобы исправить это, перед вызовомEnvironment.Exit(0)
вызовите это:SendKeys.SendWait("{ENTER}");
Затем консоль получает последний ключ Enter, необходимый для возврата в командную строку, и процесс завершается. Примечание: не звоните
SendKeys.Send()
, иначе приложение выйдет из строя.По-прежнему необходимо вызывать,
AttachConsole()
как упоминалось во многих сообщениях, но при этом я не получаю мерцания окна команд при запуске версии приложения WinForm.Вот весь код созданного мной образца приложения (без кода WinForms):
using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace ConsoleWriter { static class Program { [DllImport("kernel32.dll")] private static extern bool AttachConsole(int dwProcessId); private const int ATTACH_PARENT_PROCESS = -1; [STAThread] static void Main(string[] args) { if(args.Length > 0 && args[0].ToUpperInvariant() == "/NOGUI") { AttachConsole(ATTACH_PARENT_PROCESS); Console.WriteLine(Environment.NewLine + "This line prints on console."); Console.WriteLine("Exiting..."); SendKeys.SendWait("{ENTER}"); Environment.Exit(0); } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } } }
Надеюсь, это поможет кому-то также потратить дни на эту проблему. Спасибо за подсказку @dantill.
источник
Console.WriteLine
, не перемещает текстовый курсор (родительской) консоли. Поэтому при выходе из приложения курсор находится не в том месте, и вам нужно несколько раз нажать клавишу ввода, чтобы вернуть его к «чистой» подсказке./* ** dual.c Runs as both CONSOLE and GUI app in Windows. ** ** This solution is based on the "Momentary Flicker" solution that Robert Kennedy ** discusses in the highest-rated answer (as of Jan 2013), i.e. the one drawback ** is that the console window will briefly flash up when run as a GUI. If you ** want to avoid this, you can create a shortcut to the executable and tell the ** short cut to run minimized. That will minimize the console window (which then ** immediately quits), but not the GUI window. If you want the GUI window to ** also run minimized, you have to also put -minimized on the command line. ** ** Tested under MinGW: gcc -o dual.exe dual.c -lgdi32 ** */ #include <windows.h> #include <stdio.h> static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow); static LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam); static int win_started_from_console(void); static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp); int main(int argc,char *argv[]) { HINSTANCE hinst; int i,gui,relaunch,minimized,started_from_console; /* ** If not run from command-line, or if run with "-gui" option, then GUI mode ** Otherwise, CONSOLE app. */ started_from_console = win_started_from_console(); gui = !started_from_console; relaunch=0; minimized=0; /* ** Check command options for forced GUI and/or re-launch */ for (i=1;i<argc;i++) { if (!strcmp(argv[i],"-minimized")) minimized=1; if (!strcmp(argv[i],"-gui")) gui=1; if (!strcmp(argv[i],"-gui-")) gui=0; if (!strcmp(argv[i],"-relaunch")) relaunch=1; } if (!gui && !relaunch) { /* RUN AS CONSOLE APP */ printf("Console app only.\n"); printf("Usage: dual [-gui[-]] [-minimized].\n\n"); if (!started_from_console) { char buf[16]; printf("Press <Enter> to exit.\n"); fgets(buf,15,stdin); } return(0); } /* GUI mode */ /* ** If started from CONSOLE, but want to run in GUI mode, need to re-launch ** application to completely separate it from the console that started it. ** ** Technically, we don't have to re-launch if we are not started from ** a console to begin with, but by re-launching we can avoid the flicker of ** the console window when we start if we start from a shortcut which tells ** us to run minimized. ** ** If the user puts "-minimized" on the command-line, then there's ** no point to re-launching when double-clicked. */ if (!relaunch && (started_from_console || !minimized)) { char exename[256]; char buf[512]; STARTUPINFO si; PROCESS_INFORMATION pi; GetStartupInfo(&si); GetModuleFileNameA(NULL,exename,255); sprintf(buf,"\"%s\" -relaunch",exename); for (i=1;i<argc;i++) { if (strlen(argv[i])+3+strlen(buf) > 511) break; sprintf(&buf[strlen(buf)]," \"%s\"",argv[i]); } memset(&pi,0,sizeof(PROCESS_INFORMATION)); memset(&si,0,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwX = 0; /* Ignored unless si.dwFlags |= STARTF_USEPOSITION */ si.dwY = 0; si.dwXSize = 0; /* Ignored unless si.dwFlags |= STARTF_USESIZE */ si.dwYSize = 0; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWNORMAL; /* ** Note that launching ourselves from a console will NOT create new console. */ CreateProcess(exename,buf,0,0,1,DETACHED_PROCESS,0,NULL,&si,&pi); return(10); /* Re-launched return code */ } /* ** GUI code starts here */ hinst=GetModuleHandle(NULL); /* Free the console that we started with */ FreeConsole(); /* GUI call with functionality of WinMain */ return(my_win_main(hinst,argc,argv,minimized ? SW_MINIMIZE : SW_SHOWNORMAL)); } static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow) { HWND hwnd; MSG msg; WNDCLASSEX wndclass; static char *wintitle="GUI Window"; wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance; wndclass.hIcon = NULL; wndclass.hCursor = NULL; wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = wintitle; wndclass.hIconSm = NULL; RegisterClassEx (&wndclass) ; hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wintitle,0, WS_VISIBLE|WS_OVERLAPPEDWINDOW, 100,100,400,200,NULL,NULL,hInstance,NULL); SetWindowText(hwnd,wintitle); ShowWindow(hwnd,iCmdShow); while (GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return(msg.wParam); } static LRESULT CALLBACK WndProc (HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam) { if (iMsg==WM_DESTROY) { PostQuitMessage(0); return(0); } return(DefWindowProc(hwnd,iMsg,wParam,lParam)); } static int fwbp_pid; static int fwbp_count; static int win_started_from_console(void) { fwbp_pid=GetCurrentProcessId(); if (fwbp_pid==0) return(0); fwbp_count=0; EnumWindows((WNDENUMPROC)find_win_by_procid,0L); return(fwbp_count==0); } static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp) { int pid; GetWindowThreadProcessId(hwnd,(LPDWORD)&pid); if (pid==fwbp_pid) fwbp_count++; return(TRUE); }
источник
Я написал альтернативный подход, который позволяет избежать вспышки консоли. См. Раздел Как создать программу для Windows, которая работает как в графическом интерфейсе, так и в консольном приложении .
источник
Запустить AllocConsole () в статическом конструкторе у меня работает
источник