Windows Uygulamasında Konsol Gösterilsin mi?


85

Konsolu bir Windows uygulamasında göstermenin bir yolu var mı?

Bunun gibi bir şey yapmak istiyorum:

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

Yanıtlar:


77

Yapmak istediğin şey mantıklı bir şekilde mümkün değil. Benzer bir soru vardı, cevaplara bakın .

Ayrıca Jeffrey Knight tarafından yazılmış çılgınca bir yaklaşım da var (site aşağı - yedekleme burada mevcuttur ) :

Soru: GUI (Windows) modunda veya komut satırı / konsol modunda çalışabilen bir uygulamayı nasıl oluşturabilirim?

Görünüşe bakılırsa, bu kolay görünebilir: Bir Konsol uygulaması oluşturursunuz, ona bir pencere formu eklersiniz ve çalışmaya başlarsınız. Ancak bir sorun var:

Sorun: GUI modunda çalıştırırsanız, hem bir pencere hem de arka planda gizlenen sinir bozucu bir konsolla karşılaşırsınız ve bunu gizlemenin herhangi bir yolu yoktur.

İnsanların istediği şey, her iki modda da sorunsuzca çalışabilen gerçek bir amfibi uygulamasıdır.

Parçalarsanız, burada aslında dört kullanım durumu vardır:

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.

Bunu yapmak için kodu gönderiyorum, ancak bir uyarı ile.

Aslında bu tür bir yaklaşımın sizi yolda değdiğinden çok daha fazla belaya sürükleyeceğini düşünüyorum. Örneğin, biri GUI için diğeri komut / kabuk için olmak üzere iki farklı UI'ye sahip olmanız gerekir. GUI'den komut satırına göre özetleyen garip bir merkezi mantık motoru oluşturmanız gerekecek ve bu garipleşecek. Ben olsaydım, geri adım atar ve bunun pratikte nasıl kullanılacağını ve bu tür bir mod değiştirmenin işe değer olup olmadığını düşünürdüm. Bu nedenle, özel bir durum gerektirmedikçe, bu kodu kendim kullanmam, çünkü bir şeyi halletmek için API çağrılarına ihtiyaç duyduğum durumlarla karşılaştığım anda durup kendime "İşleri aşırı mı karmaşık hale getiriyorum? ".

Çıktı türü = Windows Uygulaması

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
Bunu Microsoft için ironik buluyorum ve tüm API'leri için C # arayüzleri oluşturmak istiyor, ancak bu kadar basit bir görevi gerçekleştirmenin C # yolu yok.
Ramon Zarazua B.17

3
Konsolun ön plan penceresi olmasına bağlı olmak yerine,
winapi'yi

2
Yazım tarihi itibariyle, makalenin yedek kopyası burada mevcuttur web.archive.org/web/20111227234507/http://www.rootsilver.com/…
Andrew Savinykh

2
Selam! Bu çözümü Far gibi bir kabuktan çalıştırırsam yeni konsol oluşturduğunu buldum. Uzak Konsol'a cmd gibi bağlarsam yanlış çalışıyor. ConsoleApplication oluşturmanızı ve GUI gerekiyorsa FreeConsole () yapmanızı öneririm; Harika makale! Teşekkürler!
Maxim Vasiliev

6
Ön plan penceresinin yazılacak doğru komut penceresi olmasını ummak yerine ( API sabitinin değeri) AttachConsoleile çağırmanızı öneririm . -1ATTACH_PARENT_PROCESS
Jon Hanna

70

Bu biraz eski (Tamam, ÇOK eski), ama şu anda aynı şeyi yapıyorum. İşte benim için çalışan çok basit bir çözüm:

[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
bunu bir cmd penceresinde çalıştırıyorsanız, bu, konsol çıktısını yakalamaya ihtiyaç duyacak otomatikleştirilmiş işlemde arzu edilmeyen başka bir konsol penceresi açacaktır.
AaA

Benim tarafımda, sağlanan kodu kullandım ancak tutamacı intptr.zero ise AllocConsole () bölümünü yapmak için bir InitConsole () paylaşılan işlevi ekledim. ShowConsoleWindow'u kullandığımda ve hemen ardından üzerine yazdırdığımda çalışmadı. Uygulama başladığında konsolu ayırmak ve ardından ShowConsoleWindow'u kullanmak işe yaradı. Onun dışında bu benim için mükemmel. Teşekkür ederim ..
Sage Pourpre

Console.WriteLinecmd'den bağımsız değişkenlerle başlatılan durumda günlükler gösterilmiyor
Mohammad Ali

Unutmausing System.Runtime.InteropServices;
Darren Griffith

19

En kolay yol bir WinForms uygulamasını başlatmak, ayarlara gitmek ve türü bir konsol uygulaması olarak değiştirmektir.


1
Uygulama -> çıktı türü: Konsol Uygulaması. Benim için yaptım!
Lodewijk

Bu harika çalıştı. Umarım başka hiçbir şeyi bozmaz. Teşekkürler ve +1.
deathismyfriend

1
Uygulamayı çift tıklayarak başlatmak bir cmd penceresi açar
Mohammad Ali

13

Feragatname

Bunu başarmanın oldukça basit bir yolu var, ancak başkalarının görmesine izin vereceğiniz bir uygulama için iyi bir yaklaşım olduğunu önermem. Ancak bazı geliştiricilerin konsol ve Windows formlarını aynı anda göstermesi gerekiyorsa, oldukça kolay bir şekilde yapılabilir.

Bu yöntem aynı zamanda yalnızca Konsol penceresinin gösterilmesini destekler, ancak yalnızca Windows Formunun gösterilmesini desteklemez - yani Konsol her zaman gösterilecektir. Windows formlarını göstermezseniz, yalnızca konsol penceresiyle etkileşim (yani veri - Console.ReadLine(), Console.Read()) yapabilirsiniz; Konsola çıktı - Console.WriteLine()- her iki modda da çalışır.

Bu olduğu gibi sağlanır; bunun daha sonra korkunç bir şey yapmayacağını garanti eden yok, ama işe yarıyor.

Proje adımları

Standart bir Konsol Uygulamasından başlayın .

İşaretle Maingibi metodu[STAThread]

System.Windows.Forms'a projenize bir başvuru ekleyin

Projenize bir Windows Formu ekleyin.

Standart Windows başlangıç ​​kodunu Mainyönteminize ekleyin :

Sonuç

Konsolu ve isteğe bağlı olarak Windows formlarını gösteren bir uygulamaya sahip olacaksınız.

Basit kod

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

burada güzel kod. ancak satmak veya dağıtmak veya başka bir şey yapmak istiyorsanız konsolu göstermeyi nasıl devre dışı bırakırsınız?
r4ccoon

@ r4ccoon - yapamazsınız. Ancak tüm kodunuzu kolayca normal bir Windows uygulamasına taşıyabilirsiniz.
Sam Meldrum

Eh, IMHO aynı etkiyi elde etmenin daha basit bir yolu, her zamanki gibi bir Windows Forms Projesi oluşturmak, ardından Çözüm Gezgini -> Özellikler'de sağ tıklayın ve Çıktı Türünü Konsol Uygulaması olarak değiştirmektir. (Düzenleme: Şimdi temelde
ICR'nin

9

Buradaki cevapların hiçbiri benim için pek işe yaramadığından, çok eski bir konuyu yeniden diriltmek.

Oldukça sağlam ve basit görünen basit bir yol buldum. Benim için çalıştı. Fikir:

  • Projenizi bir Windows Uygulaması olarak derleyin. Çalıştırılabilir programınız başladığında bir üst konsol olabilir, ancak olmayabilir. Amaç, varsa mevcut konsolu yeniden kullanmak veya yoksa yenisini oluşturmaktır.
  • AttachConsole (-1), üst sürecin konsolunu arayacaktır. Biri varsa ona bağlanır ve işin biter. (Bunu denedim ve cmd'den uygulamamı ararken düzgün çalıştı)
  • AttachConsole yanlış döndürdüyse, üst konsol yoktur. AllocConsole ile bir tane oluşturun.

Misal:

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

Bir uyarı: Bir konsol eklemeden veya tahsis etmeden önce konsola yazmayı denerseniz, bu yaklaşım işe yaramaz. Tahminimce Console.Write / WriteLine'ı ilk aradığınızda, zaten bir konsol yoksa Windows otomatik olarak sizin için bir yerde gizli bir konsol oluşturur. (Belki de Anthony'nin ShowConsoleWindow cevabı konsola yazdıktan sonra daha iyidir ve henüz konsola yazmadıysanız cevabım daha iyidir). Unutulmaması gereken önemli nokta, bunun işe yaramadığıdır:

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

örnek kodu paylaştığınız için teşekkür ederiz. Denedim ve işe yaradığını buldum, ancak sınırlamalarla. Konsol çıkışı yeniden yönlendirmesi yoktur (ek kaynak koduyla düzeltilebilir). Ancak ana dezavantajı, kontrolü hemen konsola döndürmesidir. Örneğin, yazdığımda \bin\Debug>shareCheck.exe /once ve Enter tuşuna bastığımda komut istemi gösteriliyor ve ardından konsol çıktı vermeye başlıyor: \bin\Debug>hello. It looks like you started me from an existing console.ve program bittiğinde komut istemi yok, bu nedenle son çıktı satırı ve biraz çılgın olan boş ekran
oleksa

Tedbir kelimesi için teşekkürler Kevin - Bu SO gönderisinde önerilen yaklaşımlarla ilgili sorun yaşıyorum ve daha önce herhangi bir Konsol çıkışı olmamasına rağmen "gizli konsolu" alıyorum gibi görünüyor ... Visual Studio'daki "Çıktı" penceresi, uygulamanız Hata Ayıklayıcı takılı olarak çalışıyorsa gizli konsoldur! Bahsetmeye değer ... (yani, programım hata ayıklayıcı olmadan çalışmaya geçerken çalıştı, yani Ctrl-F5)
Her Lundberg

3

Benim için işe yarayan şey, yapmasını istediğim şeyi yapan bir konsol uygulaması yazmak, onu bir exe'ye derlemek ve sonra yapmaktı. Process.Start("MyConsoleapp.exe","Arguments")


1
Bu Occam'ın ustura versiyonu. Yeterince zorlayıcı değil: P
Michael Hoffmann

3

Bu kaynak kodunu kontrol edin. Tüm yorumlanmış kod - bir Windows uygulamasında bir konsol oluşturmak için kullanılır. Yorumlanmamış - konsolu bir konsol uygulamasında gizlemek için. Gönderen burada . (Daha önce burada .) Project 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

Aslında bir GUI uygulamasında SetStdHandle ile AllocConsole daha güvenli bir yaklaşım olabilir. Daha önce bahsedilen "konsol kaçırma" ile ilgili sorun, konsolun diğer şeylerin yanı sıra (özellikle Vista / Windows 7'deki yeni pencere yöneticilerinin akını dikkate alındığında) bir ön plan penceresi olmamasıdır.


0

Wind32'de, konsol modu uygulamaları normal mesaj kuyruğu alma uygulamalarından tamamen farklı bir canavardır. Bildirilirler ve farklı şekilde derlenirler. Hem konsol kısmı hem de normal penceresi olan ve birini veya diğerini gizleyen bir uygulama oluşturabilirsiniz. Ama şüpheleniyorum, her şeyi düşündüğünüzden biraz daha fazla iş bulacaksınız.


Wind32, bir meteoroloji forumuna ait bir şeye benziyor, ancak Win32'yi kastediyorsanız, PostThreadMessage to Console Uygulamasında olduğu gibi konsol programlarında mesaj döngüleri oluşturabileceğimizi unutmayın .
kullanıcı34660

0

Jeffrey Knight'ın yukarıdaki sözüne göre, bir şeyi halletmek için API çağrılarına ihtiyaç duyduğum durumlarla karşılaştığım anda durup kendime "işleri aşırı mı karmaşık hale getiriyorum?" Diye sorma eğilimindeyim.

İstenen şey bir koda sahip olmak ve bunu Windows GUI modunda veya Konsol modunda çalıştırmaksa, her iki modda da kullanılan kodu bir kod kitaplığı DLL'sine taşımayı ve ardından bu DLL'yi kullanan bir Windows Forms uygulamasına ve bir Konsol'a sahip olmayı düşünün. bu DLL'yi kullanan uygulama (yani, Visual Studio'da artık üç proje çözümünüz varsa: kodun büyük bir kısmına sahip kitaplık, yalnızca Win Forms koduyla GUI ve yalnızca konsol kodunuzla Konsol.)


0

Ve yine başka bir geç cevap. AllocConsoleÖnceki önerilere göre oluşturulan konsola herhangi bir çıktı alamadım , bunun yerine Konsol uygulamasıyla başlıyorum . Ardından, konsol gerekli değilse:

        [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;
        }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.