Windows 응용 프로그램에 콘솔을 표시 하시겠습니까?


85

Windows 애플리케이션에서 콘솔을 표시하는 방법이 있습니까?

다음과 같이하고 싶습니다.

static class Program
{
    [STAThread]
    static void Main(string[] args) {
        bool consoleMode = Boolean.Parse(args[0]);

        if (consoleMode) {
            Console.WriteLine("consolemode started");
            // ...
        } else {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

답변:


77

당신이하고 싶은 것은 건전한 방식으로 불가능합니다. 비슷한 질문이 있었으니 답을보세요 .

그런 다음 거기에 또한 정신 접근 방식 (사이트 다운 - . 여기에 사용 가능한 백업 )가 쓴 제프리 기사 :

질문 : GUI (Windows) 모드 또는 명령 줄 / 콘솔 모드에서 실행할 수있는 응용 프로그램을 어떻게 생성합니까?

표면적으로는 간단 해 보일 것입니다. 콘솔 애플리케이션을 만들고 여기에 Windows 양식을 추가하면 실행 중입니다. 그러나 문제가 있습니다.

문제 : GUI 모드에서 실행하면 창과 배경에 숨어있는 성가신 콘솔이 표시되고 숨길 방법이 없습니다.

사람들이 원하는 것은 어느 모드에서든 원활하게 실행될 수있는 진정한 양서류 응용 프로그램입니다.

세분화하면 실제로 네 가지 사용 사례가 있습니다.

User starts application from existing cmd window, and runs in GUI mode
User double clicks to start application, and runs in GUI mode
User starts application from existing cmd window, and runs in command mode
User double clicks to start application, and runs in command mode.

이 작업을 수행하는 코드를 게시하고 있지만주의해야합니다.

저는 실제로 이런 접근 방식이 가치가있는 것보다 훨씬 더 많은 문제를 겪게 될 것이라고 생각합니다. 예를 들어, 두 개의 서로 다른 UI가 있어야합니다. 하나는 GUI 용이고 다른 하나는 명령 / 셸용입니다. GUI 대 명령 줄에서 추상화하는 이상한 중앙 논리 엔진을 구축해야하는데 이상하게 될 것입니다. 저라면 물러서서 이것이 실제로 어떻게 사용 될지, 이런 종류의 모드 전환이 작업 할 가치가 있는지 생각해 보겠습니다. 따라서 어떤 특별한 경우가 필요하지 않으면이 코드를 직접 사용하지 않을 것입니다. 작업을 수행하기 위해 API 호출이 필요한 상황이 발생하자마자 멈추고 스스로에게 "내가 과도하게 복잡하게 만드는가? ".

출력 유형 = Windows 응용 프로그램

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Win32;

namespace WindowsApplication
{
    static class Program
    {
        /*
    DEMO CODE ONLY: In general, this approach calls for re-thinking 
    your architecture!
    There are 4 possible ways this can run:
    1) User starts application from existing cmd window, and runs in GUI mode
    2) User double clicks to start application, and runs in GUI mode
    3) User starts applicaiton from existing cmd window, and runs in command mode
    4) User double clicks to start application, and runs in command mode.

    To run in console mode, start a cmd shell and enter:
        c:\path\to\Debug\dir\WindowsApplication.exe console
        To run in gui mode,  EITHER just double click the exe, OR start it from the cmd prompt with:
        c:\path\to\Debug\dir\WindowsApplication.exe (or pass the "gui" argument).
        To start in command mode from a double click, change the default below to "console".
    In practice, I'm not even sure how the console vs gui mode distinction would be made from a
    double click...
        string mode = args.Length > 0 ? args[0] : "console"; //default to console
    */

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AllocConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FreeConsole();

        [DllImport("kernel32", SetLastError = true)]
        static extern bool AttachConsole(int dwProcessId);

        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [STAThread]
        static void Main(string[] args)
        {
            //TODO: better handling of command args, (handle help (--help /?) etc.)
            string mode = args.Length > 0 ? args[0] : "gui"; //default to gui

            if (mode == "gui")
            {
                MessageBox.Show("Welcome to GUI mode");

                Application.EnableVisualStyles();

                Application.SetCompatibleTextRenderingDefault(false);

                Application.Run(new Form1());
            }
            else if (mode == "console")
            {

                //Get a pointer to the forground window.  The idea here is that
                //IF the user is starting our application from an existing console
                //shell, that shell will be the uppermost window.  We'll get it
                //and attach to it
                IntPtr ptr = GetForegroundWindow();

                int  u;

                GetWindowThreadProcessId(ptr, out u);

                Process process = Process.GetProcessById(u);

                if (process.ProcessName == "cmd" )    //Is the uppermost window a cmd process?
                {
                    AttachConsole(process.Id);

                    //we have a console to attach to ..
                    Console.WriteLine("hello. It looks like you started me from an existing console.");
                }
                else
                {
                    //no console AND we're in console mode ... create a new console.

                    AllocConsole();

                    Console.WriteLine(@"hello. It looks like you double clicked me to start
                   AND you want console mode.  Here's a new console.");
                    Console.WriteLine("press any key to continue ...");
                    Console.ReadLine();       
                }

                FreeConsole();
            }
        }
    }
}

13
Microsoft와 모든 API에 대해 C # 인터페이스를 생성하려는 방법은 아이러니하지만 이러한 간단한 작업을 수행하는 C # 방법은 없습니다.
Ramon Zarazua B.

3
콘솔이 전경
창인

2
쓰기의 시간으로, 문서의 백업 복사본은 여기를 web.archive.org/web/20111227234507/http://www.rootsilver.com/...
앤드류 Savinykh

2
안녕하세요! 이 솔루션을 Far와 같은 쉘에서 실행하면 새 콘솔이 생성된다는 것을 알았습니다. cmd와 같이 Far 콘솔에 연결하면 잘못 작동합니다. ConsoleApplication을 만들고 GUI가 필요한 경우 FreeConsole (); 훌륭한 기사! 감사!
Maxim Vasiliev 2013 년

6
포 그라운드 창이 쓰기에 적합한 명령 창이되기를 바라기보다는 ( API 상수 값)으로 호출 AttachConsole하는 것이 좋습니다 . -1ATTACH_PARENT_PROCESS
Jon Hanna 2013

70

이것은 약간 오래되었지만 (좋아, 아주 오래되었습니다) 지금 똑같은 일을하고 있습니다. 나를 위해 일하는 매우 간단한 솔루션은 다음과 같습니다.

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();

[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

const int SW_HIDE = 0;
const int SW_SHOW = 5;

public static void ShowConsoleWindow()
{
    var handle = GetConsoleWindow();

    if (handle == IntPtr.Zero)
    {
        AllocConsole();
    }
    else
    {
        ShowWindow(handle, SW_SHOW);
    }
}

public static void HideConsoleWindow()
{
    var handle = GetConsoleWindow();
    ShowWindow(handle, SW_HIDE);
}

5
cmd 창에서 실행하는 경우 콘솔 출력을 캡처해야하는 자동화 된 프로세스에서 바람직하지 않은 다른 콘솔 창이 열립니다.
AaA

필자는 제공된 코드를 사용했지만 핸들이 intptr.zero 인 경우 AllocConsole () 부분을 수행하기 위해 InitConsole () 공유 함수를 추가했습니다. ShowConsoleWindow를 사용한 다음 즉시 인쇄하면 작동하지 않았습니다. 응용 프로그램이 시작될 때 콘솔을 할당 한 다음 ShowConsoleWindow를 사용하면 작동합니다. 그 외에는 이것이 나에게 완벽합니다. 주셔서 감사합니다 ..
세이지 Pourpre에게

Console.WriteLine로그는 인수에 cmd를에서 시작하는 경우 표시되지 않습니다
모하마드 알리

잊지 마세요using System.Runtime.InteropServices;
Darren Griffith 19 년

19

가장 쉬운 방법은 WinForms 응용 프로그램을 시작하고 설정으로 이동하여 유형을 콘솔 응용 프로그램으로 변경하는 것입니다.


1
애플리케이션-> 출력 유형 : 콘솔 애플리케이션. 나를 위해 했어요!
Lodewijk

잘 작동했습니다. 바라건대 그것은 다른 것을 엉망으로 만들지 않습니다. 감사합니다.
deathismyfriend 2015 년

1
더블 클릭하여 응용 프로그램을 시작하면 cmd를 창을 엽니 다
모하마드 알리

13

부인 성명

이를 달성하는 방법은 매우 간단하지만 다른 사람들에게 보여줄 앱에 대한 좋은 접근 방식은 아닙니다. 그러나 일부 개발자가 콘솔과 Windows 양식을 동시에 표시해야하는 경우 매우 쉽게 수행 할 수 있습니다.

이 방법은 콘솔 창만 표시하는 것도 지원하지만 Windows Form 만 표시하는 것은 지원하지 않습니다. 즉, 콘솔이 항상 표시됩니다. Windows 양식을 표시하지 않으면 콘솔 창과 만 상호 작용 (즉 Console.ReadLine(), 데이터 수신- , Console.Read()) 할 수 있습니다 . 콘솔로 출력-- Console.WriteLine()두 모드 모두에서 작동합니다.

이것은있는 그대로 제공됩니다. 이것이 나중에 끔찍한 일을하지 않을 것이라는 보장은 없지만 작동합니다.

프로젝트 단계

표준 콘솔 응용 프로그램 에서 시작합니다 .

Main방법을 다음과 같이 표시하십시오.[STAThread]

프로젝트의 참조를 System.Windows.Forms 에 추가합니다.

프로젝트에 Windows Form 을 추가합니다 .

Main방법에 표준 Windows 시작 코드를 추가합니다 .

최종 결과

콘솔 및 선택적으로 Windows 양식을 표시하는 애플리케이션이 있습니다.

샘플 코드

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ConsoleApplication9 {
    class Program {

        [STAThread]
        static void Main(string[] args) {

            if (args.Length > 0 && args[0] == "console") {
                Console.WriteLine("Hello world!");
                Console.ReadLine();
            }
            else {
                Application.EnableVisualStyles(); 
                Application.SetCompatibleTextRenderingDefault(false); 
                Application.Run(new Form1());
            }
        }
    }
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ConsoleApplication9 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Click(object sender, EventArgs e) {
            Console.WriteLine("Clicked");
        }
    }
}

여기에 좋은 코드. 그러나 판매 또는 배포 또는 다른 것을 원한다면 콘솔 표시를 어떻게 비활성화합니까 ???
r4ccoon

@ r4ccoon-할 수 없습니다. 그러나 모든 코드를 일반 Windows 앱으로 쉽게 이동할 수 있습니다.
Sam Meldrum

음, IMHO는 동일한 효과를 얻는 더 간단한 방법은 평소와 같이 Windows Forms 프로젝트를 만든 다음 솔루션 탐색기-> 속성에서 마우스 오른쪽 단추로 클릭하고 출력 유형을 콘솔 응용 프로그램으로 변경하는 것입니다. (편집 : 이제 기본적으로 ICR의 답변임을 깨달았습니다.)
kamilk

스텝 응답에 의해 좋은 단계
AminM

9

여기에있는 답변 중 어느 것도 나를 위해 잘 작동하지 않았기 때문에 아주 오래된 스레드를 다시 부활시킵니다.

꽤 견고하고 간단 해 보이는 간단한 방법을 찾았습니다. 그것은 나를 위해 일했습니다. 아이디어:

  • 프로젝트를 Windows 애플리케이션으로 컴파일하십시오. 실행 파일이 시작될 때 부모 콘솔이있을 수 있지만 아닐 수도 있습니다. 목표는 존재하는 경우 기존 콘솔을 재사용하거나 그렇지 않은 경우 새 콘솔을 만드는 것입니다.
  • AttachConsole (-1)은 상위 프로세스의 콘솔을 찾습니다. 하나가 있으면 첨부하고 완료됩니다. (나는 이것을 시도했고 cmd에서 내 응용 프로그램을 호출 할 때 제대로 작동했습니다)
  • AttachConsole이 false를 반환하면 부모 콘솔이없는 것입니다. AllocConsole로 하나를 만듭니다.

예:

static class Program
{
    [DllImport( "kernel32.dll", SetLastError = true )]
    static extern bool AllocConsole();

    [DllImport( "kernel32", SetLastError = true )]
    static extern bool AttachConsole( int dwProcessId );

    static void Main(string[] args)
    {
        bool consoleMode = Boolean.Parse(args[0]);
        if (consoleMode)
        {
           if (!AttachConsole(-1))
              AllocConsole();
           Console.WriteLine("consolemode started");
           // ...
        } 
        else
        {
           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
        }
    }
}

주의 사항 : 콘솔을 연결하거나 할당하기 전에 콘솔에 쓰기를 시도하면이 접근 방식이 작동하지 않는 것 같습니다. 내 생각 엔 Console.Write / WriteLine을 처음 호출하는 것 같습니다. 아직 콘솔이 없으면 Windows가 자동으로 어딘가에 숨겨진 콘솔을 만듭니다. (따라서 Anthony의 ShowConsoleWindow 답변은 이미 콘솔에 작성한 후에 더 좋으며 아직 콘솔에 작성하지 않은 경우 내 대답이 더 좋습니다). 주목해야 할 중요한 것은 이것이 작동하지 않는다는 것입니다.

static void Main(string[] args)
    {
        Console.WriteLine("Welcome to the program");   //< this ruins everything
        bool consoleMode = Boolean.Parse(args[0]);
        if (consoleMode)
        {
           if (!AttachConsole(-1))
              AllocConsole();
           Console.WriteLine("consolemode started");   //< this doesn't get displayed on the parent console
           // ...
        } 
        else
        {
           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
        }
    }

샘플 코드를 공유해 주셔서 감사합니다. 나는 그것을 시도하고 그것이 작동하지만 한계가 있음을 발견했습니다. 콘솔 출력 리디렉션이 없습니다 (추가 소스 코드로 수정할 수 있음). 그러나 가장 큰 단점은 콘솔에 즉시 제어권을 반환한다는 것입니다. 예를 들어 내가 입력 \bin\Debug>shareCheck.exe /once 하고 Enter를 누르면 명령 프롬프트가 표시되고 콘솔이 출력을 시작합니다. \bin\Debug>hello. It looks like you started me from an existing console.프로그램이 종료되면 명령 프롬프트가 없으므로 마지막 출력 라인과 약간 미친 빈 화면
oleksa

Kevin에게주의 해 ​​주셔서 감사합니다.이 SO 게시물에서 제안한 접근 방식에 문제가 있으며 이전에 콘솔 출력이 발생 하지 않았 음에도 "숨겨진 콘솔"이 나타나는 것 같습니다. 앱이 Debugger가 연결된 상태에서 실행중인 경우 Visual Studio의 "출력"창은 숨겨진 콘솔입니다. 언급 할만큼 가치 ... (그래서, 내 프로그램은 일 디버거없이 실행 전환 할 때, 즉 Ctrl 키 F5)
당 룬드

3

나를 위해 일한 것은 내가 원하는 것을 수행하는 콘솔 앱을 별도로 작성하고 exe로 컴파일 한 다음 수행하는 것이 었습니다. Process.Start("MyConsoleapp.exe","Arguments")


1
이것이 Occam의 면도기 버전입니다. P : 충분히 도전하지
마이클 호프만

3

이 소스 코드를 확인하십시오. 주석 처리 된 모든 코드 – Windows 앱에서 콘솔을 만드는 데 사용됩니다. 주석 없음 – 콘솔 앱에서 콘솔을 숨 깁니다. 에서 여기 . (이전에는 여기에 있습니다 .) 프로젝트 reg2run.

// Copyright (C) 2005-2015 Alexander Batishchev (abatishchev at gmail.com)

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Reg2Run
{
    static class ManualConsole
    {
        #region DllImport
        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool AllocConsole();
        */

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)]string fileName, [MarshalAs(UnmanagedType.I4)]int desiredAccess, [MarshalAs(UnmanagedType.I4)]int shareMode, IntPtr securityAttributes, [MarshalAs(UnmanagedType.I4)]int creationDisposition, [MarshalAs(UnmanagedType.I4)]int flagsAndAttributes, IntPtr templateFile);
        */

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool FreeConsole();

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern IntPtr GetStdHandle([MarshalAs(UnmanagedType.I4)]int nStdHandle);

        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetStdHandle(int nStdHandle, IntPtr handle);
        */
        #endregion

        #region Methods
        /*
        public static void Create()
        {
            var ptr = GetStdHandle(-11);
            if (!AllocConsole())
            {
                throw new Win32Exception("AllocConsole");
            }
            ptr = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
            if (!SetStdHandle(-11, ptr))
            {
                throw new Win32Exception("SetStdHandle");
            }
            var newOut = new StreamWriter(Console.OpenStandardOutput());
            newOut.AutoFlush = true;
            Console.SetOut(newOut);
            Console.SetError(newOut);
        }
        */

        public static void Hide()
        {
            var ptr = GetStdHandle(-11);
            if (!CloseHandle(ptr))
            {
                throw new Win32Exception();
            }
            ptr = IntPtr.Zero;
            if (!FreeConsole())
            {
                throw new Win32Exception();
            }
        }
        #endregion
    }
}

1

실제로 GUI 응용 프로그램에서 SetStdHandle을 사용하는 AllocConsole이 더 안전한 방법 일 수 있습니다. 이미 언급 한 "콘솔 하이재킹"의 문제점은 무엇보다도 콘솔이 전경 창이 아닐 수도 있다는 것입니다 (특히 Vista / Windows 7의 새 창 관리자 유입을 고려할 때).


0

wind32에서 콘솔 모드 응용 프로그램은 일반적인 메시지 대기열 수신 응용 프로그램과 완전히 다릅니다. 그들은 선언되고 다르게 컴파일됩니다. 콘솔 부분과 일반 창을 모두 포함하고 둘 중 하나를 숨기는 응용 프로그램을 만들 수 있습니다. 그러나 당신이 생각했던 것보다 조금 더 많은 일을 할 것이라고 생각하십시오.


Wind32는 기상학 포럼에 속하는 것처럼 들리지만 Win32를 의미한다면 PostThreadMessage to Console Application 에서처럼 콘솔 프로그램에서 메시지 루프를 만들 수 있습니다 .
user34660

0

위의 Jeffrey Knight 인용문에 따르면 작업을 수행하기 위해 API 호출이 필요한 상황에 직면하자마자 멈추고 "내가 과도하게 복잡해 졌는가?"라고 자문하는 경향이 있습니다.

원하는 것이 일부 코드를 가지고 Windows GUI 모드 또는 콘솔 모드에서 실행하는 것이라면 두 모드에서 사용되는 코드를 코드 라이브러리 DLL로 이동 한 다음 해당 DLL을 사용하는 Windows Forms 응용 프로그램과 콘솔을 사용하는 것이 좋습니다. 해당 DLL을 사용하는 응용 프로그램 (즉, Visual Studio에 이제 코드가 많은 라이브러리, Win Forms 코드 만있는 GUI, 콘솔 코드 만있는 콘솔 등 3 개 프로젝트 솔루션이있는 경우)


0

그리고 또 다른 뒤늦은 대답. AllocConsole이전 제안에 따라 만든 콘솔에 출력을 얻을 수 없었 으므로 대신 Console application으로 시작 합니다 . 그런 다음 콘솔이 필요하지 않은 경우 :

        [DllImport("kernel32.dll")]
        private static extern IntPtr GetConsoleWindow();

        [DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 5;

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        public static bool HideConsole()
        {
            var hwnd = GetConsoleWindow();
            GetWindowThreadProcessId(hwnd, out var pid);

            if (pid != Process.GetCurrentProcess().Id) // It's not our console - don't mess with it.
                return false;

            ShowWindow(hwnd, SW_HIDE);

            return true;
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.