어떤 플래그가 전달되는지에 따라 CLI 또는 GUI 응용 프로그램으로 실행할 수 있는 C # 프로그램 을 만들고 싶습니다 . 할 수 있습니까?
이러한 관련 질문을 찾았지만 내 상황을 정확히 다루지는 않습니다.
어떤 플래그가 전달되는지에 따라 CLI 또는 GUI 응용 프로그램으로 실행할 수 있는 C # 프로그램 을 만들고 싶습니다 . 할 수 있습니까?
이러한 관련 질문을 찾았지만 내 상황을 정확히 다루지는 않습니다.
답변:
Jdigital의 답변 은 Raymond Chen의 블로그 를 가리키며 , 이는 콘솔 프로그램과 비 콘솔 프로그램 모두 인 애플리케이션을 가질 수없는 이유를 설명 합니다. 프로그램이 사용할 하위 시스템을 실행하기 전에*
OS가 알아야 합니다. 프로그램이 실행되기 시작하면 돌아가서 다른 모드를 요청하기에는 너무 늦습니다.
Cade의 대답 은 콘솔로 .Net WinForms 응용 프로그램을 실행하는 방법 에 대한 기사를 가리 킵니다 . AttachConsole
프로그램 실행이 시작된 후 호출하는 기술을 사용합니다 . 이것은 프로그램이 프로그램을 시작한 명령 프롬프트의 콘솔 창에 다시 쓸 수 있도록하는 효과가 있습니다. 그러나 그 기사의 댓글은 내가 치명적인 결함이라고 생각하는 것을 지적합니다 . 자식 프로세스는 실제로 콘솔을 제어하지 않습니다. 콘솔은 부모 프로세스를 대신하여 계속 입력을 받아들이고 부모 프로세스는 다른 작업을 위해 콘솔을 사용하기 전에 자식이 실행을 마칠 때까지 기다려야한다는 것을 인식하지 못합니다.
Chen의 기사 는 몇 가지 다른 기술을 설명하는 Junfeng Zhang 의 기사를 가리 킵니다 .
첫 번째는 devenv가 사용하는 것입니다. 실제로 두 개의 프로그램을 가지고 작동합니다. 하나는 기본 GUI 프로그램 인 devenv.exe 이고 다른 하나는 콘솔 모드 작업을 처리하는 devenv.com 이지만 콘솔과 같은 방식이 아닌 방식으로 사용되는 경우 해당 작업을 devenv.exe로 전달 하고 출구. 이 기술은 파일 확장자없이 명령을 입력 할 때 com 파일이 exe 파일 보다 먼저 선택 된다는 Win32 규칙에 의존 합니다.
Windows 스크립트 호스트가하는 것보다 더 간단한 변형이 있습니다. 완전히 분리 된 두 바이너리, wscript.exe 및 cscript.exe를 제공 합니다. 마찬가지로 Java는 콘솔 프로그램 에는 java.exe 를 , 비 콘솔 프로그램에는 javaw.exe 를 제공합니다.
Junfeng의 두 번째 기술은 ildasm이 사용하는 것입니다. 그는 ildasm 의 저자가 두 모드에서 실행될 때 겪은 과정을 인용합니다 . 궁극적으로 수행하는 작업은 다음과 같습니다.
FreeConsole
첫 번째 인스턴스가 콘솔 프로그램 이되지 않도록 호출 하는 것만으로는 충분하지 않습니다 . 프로그램을 시작한 프로세스 cmd.exe 는 콘솔 모드 프로그램을 시작한 것을 "인식"하고 프로그램 실행이 중지되기를 기다리고 있기 때문입니다. 호출 FreeConsole
하면 ildasm 이 콘솔 사용을 중지하지만 부모 프로세스 가 콘솔 사용을 시작 하지 않습니다 .
따라서 첫 번째 인스턴스는 자체적으로 다시 시작됩니다 (추가 명령 줄 매개 변수 사용). 를 호출 할 때 CreateProcess
시도 할 두 가지 플래그가 DETACHED_PROCESS
있으며CREATE_NEW_CONSOLE
, 둘 중 하나는 두 번째 인스턴스가 상위 콘솔에 연결되지 않도록합니다. 그 후 첫 번째 인스턴스가 종료되고 명령 프롬프트가 명령 처리를 재개하도록 허용 할 수 있습니다.
이 기술의 부작용은 GUI 인터페이스에서 프로그램을 시작할 때 여전히 콘솔이 있다는 것입니다. 화면에서 잠시 깜박이다가 사라집니다.
프로그램의 콘솔 모드 플래그를 변경하기 위해 editbin 을 사용하는 것에 대한 Junfeng의 기사에서 부분은 red herring이라고 생각합니다. 컴파일러 또는 개발 환경은 생성되는 바이너리 유형을 제어하는 설정 또는 옵션을 제공해야합니다. 나중에 수정할 필요가 없습니다.
결론 은 두 개의 바이너리를 가질 수 있거나 콘솔 창의 일시적인 깜박임이있을 수 있다는 것 입니다. 어느 것이 덜 악한 지 결정하면 구현을 선택할 수 있습니다.
*
GUI 대신 콘솔이 아니라고 말합니다. 그렇지 않으면 잘못된 이분법이기 때문입니다. 프로그램에 콘솔이 없다고해서 GUI가있는 것은 아닙니다. 서비스 응용 프로그램이 대표적인 예입니다. 또한 프로그램은 콘솔 과 창을 가질 수 있습니다 .
WinMain
가 적절한 매개 변수와 함께 함수 를 연결하고 (그래서 컴파일 /SUBSYSTEM:WINDOWS
) 사후에 모드를 변경하는 것이라고 생각합니다. 로더는 콘솔 호스트를 시작합니다. 더 많은 피드백을 위해 CREATE_NO_WINDOW
CreateProcess에서 GetConsoleWindow() == NULL
다시 실행 여부를 확인하면서 이것을 시도했습니다 . 이것은 콘솔 깜박임을 수정하지는 않지만 특별한 cmd 인수가 없음을 의미합니다.
이 주제에 대한 Raymond의 블로그를 확인하십시오.
https://devblogs.microsoft.com/oldnewthing/20090101-00/?p=19643
그의 첫 번째 문장 : "당신은 할 수 없지만 그것을 속일 수 있습니다."
http://www.csharp411.com/console-output-from-winforms-application/
WinForms 항목 전에 명령 줄 인수를 확인하십시오 Application.
.
.NET에서는 main을 제외한 모든 어셈블리를 공유하는 동일한 솔루션에서 콘솔 및 GUI 프로젝트를 간단하게 만드는 것이 매우 쉽습니다. 이 경우 매개 변수없이 실행되는 경우 명령 행 버전이 GUI 버전을 실행하도록 만들 수 있습니다. 당신은 깜박이는 콘솔을 얻을 것입니다.
원하는 것을 쉽게 할 수있는 방법이 있습니다. CLI와 GUI가 모두 있어야하는 앱을 작성할 때 항상 사용하고 있습니다. 이 작업을 수행하려면 "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
});
}
}
}
선호하는 기술은 Rob이 두 개의 실행 파일을 사용 하는 devenv 기술 이라고 부르는 것입니다. 실행 프로그램 ".com"과 원래 ".exe"입니다. 작업 할 상용구 코드가있는 경우 사용하기가 그리 까다 롭지 않습니다 (아래 링크 참조).
이 기술은 ".com"이 stdin / stdout / stderr에 대한 프록시가되도록하고 동일한 이름의 .exe 파일을 실행하는 트릭을 사용합니다. 이것은 프로그램이 콘솔에서 호출 될 때 (잠재적으로 특정 명령 줄 인수가 감지 된 경우에만) 명령 줄 모드에서 수행 할 수있는 동작을 제공하는 동시에 콘솔없이 GUI 응용 프로그램으로 시작할 수 있습니다.
저는 이 기술의 오래된 codeguru 솔루션을 업데이트하고 소스 코드와 작업 예제 바이너리를 제공하는 Google 코드에서 dualsubsystem 이라는 프로젝트를 호스팅했습니다 .
다음은 문제에 대한 간단한 .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
(부모) 콘솔의 텍스트 커서를 진행시키지 않는다는 것입니다 . 따라서 앱을 종료 할 때 커서 위치가 잘못된 위치에 있으며 Enter 키를 몇 번 눌러 "깨끗한"프롬프트로 돌아 가야합니다.
/*
** 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);
}
콘솔 플래시를 피하는 대체 접근 방식을 작성했습니다. GUI 및 콘솔 응용 프로그램으로 작동하는 Windows 프로그램을 만드는 방법을 참조하십시오 .