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モードで実行すると、ウィンドウと厄介なコンソールの両方がバックグラウンドに潜んでしまい、それを非表示にする方法がありません。

人々が望んでいるように見えるのは、どちらのモードでもスムーズに実行できる真の両生類アプリケーションです。

分解すると、実際には次の4つのユースケースがあります。

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.

これを行うためのコードを投稿していますが、注意が必要です。

私は実際、この種のアプローチは、それが価値があるよりもはるかに多くの問題に直面するだろうと思います。たとえば、2つの異なるUIが必要になります。1つはGUI用で、もう1つはコマンド/シェル用です。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. 2011

3
フォアグラウンドウィンドウであるコンソールに依存するのではなく、winapiを使用して現在のプロセスの親プロセスIDを取得できます:stackoverflow.com/a/3346055/855432
ghord 2012

2
執筆時点で、記事のバックアップコピーはこちらから入手できます。web.archive.org
web / 20111227234507 / http://www.rootsilver.com/…– Andrew Savinykh 2013

2
こんにちは!Far、ncのようなシェルからこのソリューションを実行すると、新しいコンソールが作成されることがわかりました。cmdのようにFarConsoleに接続すると、正しく機能しません。ConsoleApplicationを作成し、GUIが必要な場合は、FreeConsole();を実行することをお勧めします。素晴らしい記事です!ありがとう!
マキシムヴァシリエフ2013年

6
フォアグラウンドウィンドウが適切なコマンドウィンドウであると期待するのではなく、(API定数の値)を使用して呼び出すことAttachConsoleをお勧めします。-1ATTACH_PARENT_PROCESS
Jon Hanna

70

これは少し古いです(OK、非常に古いです)が、私は今まったく同じことをしています。これが私のために働いている非常に簡単な解決策です:

[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 2015年

私の側では、提供されたコードを使用しましたが、ハンドルがintptr.zeroの場合、AllocConsole()部分を実行するためにInitConsole()共有関数を追加しました。ShowConsoleWindowを使用してすぐに印刷したところ、機能しませんでした。アプリケーションの起動時にコンソールを割り当ててから、ShowConsoleWindowを使用すると機能しました。それ以外は、これは私にとって完璧です。..あなたに感謝
セージPourpre

Console.WriteLinecmdからargsを使用して開始した場合、ログが表示されない
MohammadAli19年

忘れないでくださいusing System.Runtime.InteropServices;
ダレングリフィス

19

最も簡単な方法は、WinFormsアプリケーションを起動し、設定に移動して、タイプをコンソールアプリケーションに変更することです。


1
アプリケーション->出力タイプ:コンソールアプリケーション。私のためにやった!
Lodewijk 2014年

それはうまくいきました。うまくいけば、それは他に何も台無しにしないでください。ありがとうと+1。
deathismyfriend 2015年

1
アプリケーションをダブルクリックして起動すると、cmdウィンドウが開きます
MohammadAli19年

13

免責事項

これを実現する方法は非常に簡単ですが、他の人に見てもらうための良いアプローチだとは思いません。ただし、開発者がコンソールフォームとWindowsフォームを同時に表示する必要がある場合は、非常に簡単に実行できます。

この方法では、コンソールウィンドウのみの表示もサポートされていますが、Windowsフォームのみの表示はサポートされていません。つまり、コンソールは常に表示されます。ウィンドウフォームを表示しない場合にのみ、コンソールウィンドウと対話できます(つまり、データを受信します- Console.ReadLine()Console.Read())。コンソールへの出力----Console.WriteLine()両方のモードで動作します。

これはそのまま提供されます。これが後で恐ろしいことをしないという保証はありませんが、機能します。

プロジェクトのステップ

標準のコンソールアプリケーションから開始します

Mainメソッドを次のようにマークします[STAThread]

プロジェクト内の参照をSystem.Windows.Formsに追加します

プロジェクトにWindowsフォームを追加します。

標準のWindows開始コードをMainメソッドに追加します。

最終結果

コンソールとオプションで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 2009

@ r4ccoon-できません。ただし、すべてのコードを通常のWindowsアプリに簡単に移動できます。
Sam Meldrum 2010

同じ効果を実現する簡単な方法は、通常どおりWindowsフォームプロジェクトを作成し、ソリューションエクスプローラー->プロパティで右クリックして、[出力の種類]を[コンソールアプリケーション]に変更することです。(編集:基本的にICRの答えだと気づきました)
kamilk 2013年

素敵なステップバイステップの回答
AminM 2013年

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 2017年

注意の言葉をありがとうケビン-私はこのSOの投稿で提案されたアプローチに問題があり、以前にコンソール出力が発生していなくても「隠されたコンソール」を取得しているようです...それが判明しましたアプリがデバッガーを接続して実行している場合、VisualStudioの[出力]ウィンドウは非表示のコンソールです。言及する価値があります...(したがって、私のプログラムは、デバッガーなしで実行するように切り替えるときに機能しました。つまり、Ctrl-F5)
Lundbergによる2018

3

私にとってうまくいったのは、私がやりたいことを実行するコンソールアプリを個別に作成し、それをexeにコンパイルしてから、実行することでした。 Process.Start("MyConsoleapp.exe","Arguments")


1
それがオッカムの剃刀バージョンです。十分に挑戦的ではありません:P
Michael Hoffmann

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のように、コンソールプログラムでメッセージループを作成できることに注意してください。
user346 6019年

0

上記のJeffreyKnightの引用によると、何かを実行するためにAPI呼び出しが必要な状況に遭遇するとすぐに、私は立ち止まって「物事を複雑にしすぎているのか」と自問する傾向があります。

コードを用意してWindowsGUIモードまたはコンソールモードで実行する必要がある場合は、両方のモードで使用されるコードをコードライブラリDLLに移動し、そのDLLを使用するWindowsフォームアプリケーションとコンソールを用意することを検討してください。そのDLLを使用するアプリケーション(つまり、Visual Studioに、コードの大部分を含むライブラリ、Winフォームコードのみを含むGUI、およびコンソールコードのみを含むコンソールの3つのプロジェクトソリューションがある場合)。


0

そして、さらに別の遅れた答え。AllocConsole以前の提案に従って作成されたコンソールへの出力を取得できなかったため、代わりにコンソールアプリケーションから始めています。次に、コンソールが必要ない場合:

        [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.