web-dev-qa-db-ja.com

Windowsアプリケーションにコンソールを表示しますか?

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());
        }
    }
}
79
Ase

あなたがやりたいことは、まともな方法では不可能です。同様の質問がありました だから答えを見てください

また、 非常識なアプローチ (サイトダウン- バックアップはこちらJeffrey Knight によって書かれています:

質問: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.

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

私は実際、この種のアプローチは価値があるよりもずっと多くのトラブルにあなたを駆り立てると思います。たとえば、GUI用とコマンド/シェル用の2つの異なるUIが必要です。 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();
            }
        }
    }
}
76
Igal Serban

これは少し古い(非常に古い)が、今はまったく同じことをしている。これは私のために働いている非常に簡単なソリューションです:

[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);
}
65
Anthony

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

17
ICR

免責事項

これを達成する方法は非常に簡単ですが、他の人に見せようとしているアプリに適した方法であることはお勧めしません。しかし、コンソールフォームとWindowsフォームを同時に表示する必要がある開発者がいれば、非常に簡単に行うことができます。

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

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

プロジェクトステップ

標準のConsole Applicationから開始します。

Mainメソッドを[STAThread]としてマークします

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

Windows Formをプロジェクトに追加します。

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

最終結果

コンソールとオプションのウィンドウフォームを表示するアプリケーションができます。

サンプルコード

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");
        }
    }
}
13
Sam Meldrum

ここでの答えはどれも私にとってはうまくいきませんでしたので、非常に古いスレッドを再び復活させます。

私は非常に堅牢でシンプルに思える簡単な方法を見つけました。それは私のために働いた。アイデア:

  • プロジェクトを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());
        }
    }
7
Kevin Holt

私のために働いたのは、私がやりたいことをしたコンソールアプリを別々に書き、exeにコンパイルしてからProcess.Start("MyConsoleapp.exe","Arguments")を実行することでした

3
Ian

このソースコードをチェックしてください。コメントされたすべてのコード– Windowsアプリでコンソールを作成するために使用されます。コメントなし-コンソールアプリでコンソールを非表示にします。 here から。 (以前は ここ。) 事業 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
    }
}
2
abatishchev

実際には、GUIアプリケーションでSetStdHandleを使用したAllocConsoleの方が安全なアプローチかもしれません。既に述べた「コンソールハイジャック」の問題は、特にコンソールがフォアグラウンドウィンドウではない可能性があることです(特に、Vista/Windows 7での新しいウィンドウマネージャーの流入を考慮して)。

1
EFraim

Wind32では、コンソールモードアプリケーションは、通常のメッセージキュー受信アプリケーションとはまったく異なるものです。それらは異なる方法で宣言され、コンパイルされます。コンソール部分と通常のウィンドウの両方を持ち、どちらか一方を非表示にするアプリケーションを作成できます。しかし、あなたが思ったよりも全体が少しだけ多くの仕事を見つけると思う。

0