Form uygulamasında bir konsol çıktısını / penceresini nasıl gösterebilirim?


131

Hemen takılıp kalmak için çok basit bir örnek:

using System;
using System.Windows.Forms;

class test
{ 
    static void Main()
    { 
        Console.WriteLine("test");
        MessageBox.Show("test");
    }
}

Bunu varsayılan seçeneklerle (komut satırında csc kullanarak) derlersem, beklendiği gibi, bir konsol uygulamasına derlenir. Ayrıca, içe System.Windows.Formsaktardığım için bir mesaj kutusu da gösterecek.

Şimdi, proje seçenekleri içinden /target:winexeseçim yapmakla aynı olduğunu düşündüğüm seçeneği kullanırsam, Windows Applicationbeklendiği gibi sadece Mesaj Kutusunu göreceğim ve konsol çıktısı görmeyeceğim.

(Aslında, komut satırından başlatıldığı anda, uygulama tamamlanmadan bir sonraki komutu verebilirim).

Öyleyse, sorum şu - bir konsol uygulamasından "windows" / form çıkışı alabileceğinizi biliyorum, ancak konsolu bir Windows uygulamasından göstermek için yine de var mı?


2
ikisi arasındaki farkı ne olarak görüyorsunuz? Neden sadece konsol olarak derleyip bir form göstermiyorsunuz?
Doggett

7
@Doggett, basit - Öğreniyorum ve onu gerçek bir uygulamada hiç kullanmasam bile neden / nasıl yapılacağını anlamak istiyorum .... Şu anda, ekstra komutlar veren bir seçenek düşünüyorum / VLC'deki gibi çıktı, ancak TBH, buna ihtiyacım yok - yine, sadece öğreniyorum ve anlamak istiyorum!
Wil

Bunu bu öğreticiyi kullanarak başardım
vivanov

Yanıtlar:


153

bu çalışmalı.

using System.Runtime.InteropServices;

private void Form1_Load(object sender, EventArgs e)
{
    AllocConsole();
}

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();

8
Harika, bu soru çok sorulmuş gibi görünüyor, bulabildiğim sorunun tek gerçek yanıtı bu, +1
RobJohnson

5
Ana sorun: Kapattığınızda tüm uygulamalar kapanır.
Mark

4
Windows 8 ve Windows 10'da test ettim: - AttachConsole bir cmd kutusundan çalışıyor - AllocConsole, Visual Studio'dan çalışıyor. Tahsis gerektiğinde, AttachConsole yanlış döndürür. Uygulamayı konsol modunda sonlandırmadan önce FreeConsole () 'u da çağırmalısınız. Programımda Matthew Strawbridge'in kodunu (aşağıya bakın), AttachConsole () satırı şu şekilde değiştirilmiş olarak kullandım: if (! AttachConsole (-1)) AllocConsole ();
Berend Engelbrecht

Bu bir kullanıcı kontrolünde çalışacak mı? Granados (örneğin) kullanarak bir winforms bileşeni olarak bir SSH kontrolü yapmaya çalışıyorum ve bu yalnızca arka plan bileşenidir. Konsolu bir bileşende de görüntülemesi ve kullanması için güzel bir sarmalayıcı eklemek istiyorum.
Kraang Prime

2
Bu harika değil, komut satırından çalıştırıldığında ayrı bir konsol penceresi açıyor ve komut satırından çalıştırıp >çıktıyı yeniden yönlendirmeye çalıştığımda ayrı bir konsol penceresi ve dosyamda sıfır çıktı alıyorum.
uglycoyote

140

Belki de bu aşırı basittir ...

Bir Windows Form projesi oluşturun ...

Ardından: Proje Özellikleri -> Uygulama -> Çıktı Türü -> Konsol Uygulaması

O zaman Konsol ve Formların birlikte çalışmasını sağlayabilir, benim için çalışır


2
En basit görünüyor, sorunum da çözüldü.
dadude999

2
Bu kesinlikle en iyi çözüm! Diğerleri akıllı ama çok karmaşık
LM.Croisez

3
Basit ve iyi çalıştı. Kabul edilen cevap bu olmalıdır.
madu

7
Evet, teknik olarak bu, posterin istediği şeye izin vermek için kullanılabilir - bu harika bir çözüm değildir. Bunu yaparak, winforms uygulamanızı GUI ile başlatırsanız - ayrıca bir konsol penceresi açacaksınız. Bu durumda, daha çok Mike de Klerk'in cevabına benzer bir şeye ihtiyacınız olacaktır.
Justin Greywolf

2
Winforms uygulamamın komut satırından çalıştırıldığında konsola çıktı yazmasını veya komut satırında ile yeniden yönlendirildiğinde dosyaya yazmasını sağlayabildiğim tek çözüm budur >. Bununla birlikte, bir "Konsol Uygulaması" olarak nasıl çalıştırılacağını sadece bir süre açıklayacak bir çözüm umuyordum (yani, bu gizemli Visual Studio ayarını değiştirmenin yaptığı her şeyi programlı olarak etkinleştirmek için). Bunun kaputun altında nasıl çalıştığını bilen var mı?
uglycoyote

63

Komutta bir konsol açma konusunda endişelenmiyorsanız, projenizin özelliklerine gidebilir ve bunu Konsol Uygulaması olarak değiştirebilirsiniz.

proje türünü değiştirmenin ekran görüntüsü.

Bu, formunuzu göstermenin yanı sıra bir konsol penceresi açmaya devam edecektir. Konsol penceresini kapatamazsınız, ancak hata ayıklama için mükemmel bir geçici kaydedici olarak çalışır.

Programı dağıtmadan önce tekrar kapatmayı unutmayın.


1
Güzel. Bu, form uygulamamda çıktının bir dosyaya yeniden yönlendirilmesini desteklerken çıktıyı bir konsol penceresine verebilmem gereken sorunu çözüyor. Ve manuel olarak herhangi bir konsol bağlamama gerek yok ...
Kai Hartmann

2
@JasonHarrison Konsol penceresini kapatırsanız program kapanır. Ayrıca program çalışırken pencere her zaman açıktır.
gunr2171

2
@ gun2171: Teşekkürler. Bu yaklaşımın olumsuz yanları cevapta belirtilmiştir: uygulama çift tıklama, Başlat menüsü vb.
Jason Harrison

17

AttachConsoleWinForms projesine eklenmiş bir konsol penceresi almak için pinvoke kullanarak arayabilirsiniz : http://www.csharp411.com/console-output-from-winforms-application/

Farklı konfigürasyonlarda günlük çıktısını yapılandırmak için Log4net'i ( http://logging.apache.org/log4net/index.html ) de düşünebilirsiniz .


+1 - Vay canına, bir console.show veya benzeri bir şey bekliyordum! düşündüğümden çok daha karmaşık! Daha iyi / daha kolay bir cevap olması durumunda şimdilik açık bırakacağım.
Wil

Bu benim için çalıştı, AllocConsole () yeni bir konsol penceresi oluşturduğu için olmadı (AllocConsole'a daha fazla girmedi, belki de orada bir şeyi kaçırdım).
derFunk

14

Bu benim için çıktıyı bir dosyaya yönlendirmek için çalıştı. Konsolu arayın

cmd / c "C: \ yol \ \ uygulamanız.exe \ yolunuz"> dosyam.txt

Bu kodu uygulamanıza ekleyin.

    [DllImport("kernel32.dll")]
    static extern bool AttachConsole(UInt32 dwProcessId);
    [DllImport("kernel32.dll")]
    private static extern bool GetFileInformationByHandle(
        SafeFileHandle hFile,
        out BY_HANDLE_FILE_INFORMATION lpFileInformation
        );
    [DllImport("kernel32.dll")]
    private static extern SafeFileHandle GetStdHandle(UInt32 nStdHandle);
    [DllImport("kernel32.dll")]
    private static extern bool SetStdHandle(UInt32 nStdHandle, SafeFileHandle hHandle);
    [DllImport("kernel32.dll")]
    private static extern bool DuplicateHandle(
        IntPtr hSourceProcessHandle,
        SafeFileHandle hSourceHandle,
        IntPtr hTargetProcessHandle,
        out SafeFileHandle lpTargetHandle,
        UInt32 dwDesiredAccess,
        Boolean bInheritHandle,
        UInt32 dwOptions
        );
    private const UInt32 ATTACH_PARENT_PROCESS = 0xFFFFFFFF;
    private const UInt32 STD_OUTPUT_HANDLE = 0xFFFFFFF5;
    private const UInt32 STD_ERROR_HANDLE = 0xFFFFFFF4;
    private const UInt32 DUPLICATE_SAME_ACCESS = 2;
    struct BY_HANDLE_FILE_INFORMATION
    {
        public UInt32 FileAttributes;
        public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
        public UInt32 VolumeSerialNumber;
        public UInt32 FileSizeHigh;
        public UInt32 FileSizeLow;
        public UInt32 NumberOfLinks;
        public UInt32 FileIndexHigh;
        public UInt32 FileIndexLow;
    }
    static void InitConsoleHandles()
    {
        SafeFileHandle hStdOut, hStdErr, hStdOutDup, hStdErrDup;
        BY_HANDLE_FILE_INFORMATION bhfi;
        hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
        hStdErr = GetStdHandle(STD_ERROR_HANDLE);
        // Get current process handle
        IntPtr hProcess = Process.GetCurrentProcess().Handle;
        // Duplicate Stdout handle to save initial value
        DuplicateHandle(hProcess, hStdOut, hProcess, out hStdOutDup,
        0, true, DUPLICATE_SAME_ACCESS);
        // Duplicate Stderr handle to save initial value
        DuplicateHandle(hProcess, hStdErr, hProcess, out hStdErrDup,
        0, true, DUPLICATE_SAME_ACCESS);
        // Attach to console window – this may modify the standard handles
        AttachConsole(ATTACH_PARENT_PROCESS);
        // Adjust the standard handles
        if (GetFileInformationByHandle(GetStdHandle(STD_OUTPUT_HANDLE), out bhfi))
        {
            SetStdHandle(STD_OUTPUT_HANDLE, hStdOutDup);
        }
        else
        {
            SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
        }
        if (GetFileInformationByHandle(GetStdHandle(STD_ERROR_HANDLE), out bhfi))
        {
            SetStdHandle(STD_ERROR_HANDLE, hStdErrDup);
        }
        else
        {
            SetStdHandle(STD_ERROR_HANDLE, hStdErr);
        }
    }

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // initialize console handles
        InitConsoleHandles();

        if (args.Length != 0)
        {

            if (args[0].Equals("waitfordebugger"))
            {
                MessageBox.Show("Attach the debugger now");
            }
            if (args[0].Equals("version"))
            {
#if DEBUG
                String typeOfBuild = "d";
#else
                String typeOfBuild = "r";
#endif
                String output = typeOfBuild + Assembly.GetExecutingAssembly()
                    .GetName().Version.ToString();
                //Just for the fun of it
                Console.Write(output);
                Console.Beep(4000, 100);
                Console.Beep(2000, 100);
                Console.Beep(1000, 100);
                Console.Beep(8000, 100);
                return;
            }
        }
    }

Bu kodu burada buldum: http://www.csharp411.com/console-output-from-winforms-application/ Burada da göndermeye değer olduğunu düşündüm.


5
Bu, Windows 8 ve Windows 10'da artık başarısız olduğu HARİÇ harika çalışıyor. Başarısız derken, fazladan bilgi isteminden başka bir çıktı olmadığını kastediyorum (bu bir ipucu ise). Birisi AllocConsole'u önerdi, ancak bu sadece bir cmd penceresini gösterdi.
Simon Heffer

Ayrıca Chaz'ın yukarıdaki cevabını denedim, ancak bu Windows 7'de yeni bir konsol veriyor (8 veya 10'da değil). Sadece komut satırında yeniden yönlendirme ile çalıştırma veya bağımsız değişken yoksa bir gui olarak çalıştırma seçeneğine ihtiyacım var.
Simon Heffer

Bunu denedim ama işe yaramadı. Sadece AttachConsole(ATTACH_PARENT_PROCESS)konsol çıktısını alıyorum ancak komut satırına yönlendirmek >işe yaramıyor. Bu cevabı denediğimde, ne konsolda ne de bir dosyada herhangi bir çıktı alamıyorum.
uglycoyote

12

Burada olabilecek temelde iki şey var.

Konsol çıkışı Bir winforms programının kendisini yaratan konsol penceresine (veya istenirse farklı bir konsol penceresine veya gerçekten yeni bir konsol penceresine) eklemesi mümkündür. Konsol penceresine eklendiğinde Console.WriteLine () vb. Beklendiği gibi çalışır. Bu yaklaşımın bir yolu, programın kontrolü hemen konsol penceresine döndürmesi ve ardından ona yazmaya devam etmesidir, böylece kullanıcı konsol penceresine de yazabilir. Bunu halletmek için / wait parametresiyle start kullanabilirsiniz.

Komut sözdizimini başlatmak için bağlantı

Yeniden yönlendirilen konsol çıktısı Bu, birisinin programınızın çıktısını başka bir yere yönlendirmesidir, örn.

uygulamanız> dosya.txt

Bu durumda bir konsol penceresine takmak, boruları etkin bir şekilde yok sayar. Bunun çalışması için, çıktının borulanması gereken akışa bir tutamaç almak için Console.OpenStandardOutput () 'u çağırabilirsiniz. Bu yalnızca çıktı borulu ise çalışır, bu nedenle her iki senaryoyu da işlemek istiyorsanız, standart çıktıyı açmanız ve ona yazmanız ve konsol penceresine eklemeniz gerekir. Bu, çıktının konsol penceresine ve boruya gönderildiği anlamına gelir, ancak bulabildiğim en iyi çözüm budur. Bunu yapmak için kullandığım kodun altında.

// This always writes to the parent console window and also to a redirected stdout if there is one.
// It would be better to do the relevant thing (eg write to the redirected file if there is one, otherwise
// write to the console) but it doesn't seem possible.
public class GUIConsoleWriter : IConsoleWriter
{
    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    private static extern bool AttachConsole(int dwProcessId);

    private const int ATTACH_PARENT_PROCESS = -1;

    StreamWriter _stdOutWriter;

    // this must be called early in the program
    public GUIConsoleWriter()
    {
        // this needs to happen before attachconsole.
        // If the output is not redirected we still get a valid stream but it doesn't appear to write anywhere
        // I guess it probably does write somewhere, but nowhere I can find out about
        var stdout = Console.OpenStandardOutput();
        _stdOutWriter = new StreamWriter(stdout);
        _stdOutWriter.AutoFlush = true;

        AttachConsole(ATTACH_PARENT_PROCESS);
    }

    public void WriteLine(string line)
    {
        _stdOutWriter.WriteLine(line);
        Console.WriteLine(line);
    }
}

Konsola yazamadım; önce ana süreci eklemek hile yaptı. Teşekkür ederim.
Pupper

Görünüşe göre bu cevap, tüm çağrıları yukarıda tanımlanan Console.WriteLineyeniyi aramak için yeniden yazmanızı gerektiriyor WriteLine. Uygulamayı komut satırında çalıştırırken ve bir dosyaya yeniden yönlendirirken bu kodla bir dosyaya yönlendirilen herhangi bir şey alamadığımı denememe rağmen >.
uglycoyote

@uglycoyote, GUIConsoleWriter'ı uygulamanızda olabildiğince erken oluşturduğunuzdan emin olun, aksi takdirde gizemli Windows türü nedenlerle çalışmayacaktır. Console.WriteLineTest etmenize ve oturum açtığınız yerleri kolayca değiştirmenize izin verdiği için çağrıları kapsüllemenin sadece iyi bir uygulama olduğunu iddia ediyorum (örneğin, PaperTrail gibi bulut tabanlı bir günlük kaydı hizmetinde oturum açmaya başlamak isteyebilirsiniz veya her neyse) )
cedd

bu benim için Win10'da bile iyi çalıştıStreamWriter _stdOutWriter;
TS

Cevap borulamaktır, ancak bir dosya yerine DAHA FAZLA kullanın, örneğin: yourapp | Daha ; lütfen stackoverflow.com/a/13010823/1845672
Roland

9

Bir Windows Forms Uygulaması oluşturun ve çıktı türünü Konsol olarak değiştirin.

Hem konsol hem de formun açılmasıyla sonuçlanacaktır .

görüntü açıklamasını buraya girin


Bu tam olarak aradığım şey. Basit ve WINAPI kullanmıyor.
Michael Coxon

Birçok örnek denedim, ancak hiçbiri beklentilerimi karşılayan sonuçlar vermedi. Ancak bu çözüm tam olarak istediğim şey ve açık ara en kolay çözüm.
inexcitus

4
//From your application set the Console to write to your RichTextkBox 
//object:
Console.SetOut(new RichTextBoxWriter(yourRichTextBox));

//To ensure that your RichTextBox object is scrolled down when its text is 
//changed add this event:
private void yourRichTextBox_TextChanged(object sender, EventArgs e)
{
    yourRichTextBox.SelectionStart = yourRichTextBox.Text.Length;
    yourRichTextBox.ScrollToCaret();
}

public delegate void StringArgReturningVoidDelegate(string text);
public class RichTextBoxWriter : TextWriter
{
    private readonly RichTextBox _richTextBox;
    public RichTextBoxWriter(RichTextBox richTexttbox)
    {
        _richTextBox = richTexttbox;
    }

    public override void Write(char value)
    {
        SetText(value.ToString());
    }

    public override void Write(string value)
    {
        SetText(value);
    }

    public override void WriteLine(char value)
    {
        SetText(value + Environment.NewLine);
    }

    public override void WriteLine(string value)
    {
        SetText(value + Environment.NewLine);
    }

    public override Encoding Encoding => Encoding.ASCII;

    //Write to your UI object in thread safe way:
    private void SetText(string text)
    {
        // InvokeRequired required compares the thread ID of the  
        // calling thread to the thread ID of the creating thread.  
        // If these threads are different, it returns true.  
        if (_richTextBox.InvokeRequired)
        {
            var d = new StringArgReturningVoidDelegate(SetText);
            _richTextBox.Invoke(d, text);
        }
        else
        {
            _richTextBox.Text += text;
        }
    }
}

3
using System;
using System.Runtime.InteropServices;

namespace SomeProject
{
    class GuiRedirect
    {
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool AttachConsole(int dwProcessId);
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetStdHandle(StandardHandle nStdHandle);
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetStdHandle(StandardHandle nStdHandle, IntPtr handle);
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern FileType GetFileType(IntPtr handle);

    private enum StandardHandle : uint
    {
        Input = unchecked((uint)-10),
        Output = unchecked((uint)-11),
        Error = unchecked((uint)-12)
    }

    private enum FileType : uint
    {
        Unknown = 0x0000,
        Disk = 0x0001,
        Char = 0x0002,
        Pipe = 0x0003
    }

    private static bool IsRedirected(IntPtr handle)
    {
        FileType fileType = GetFileType(handle);

        return (fileType == FileType.Disk) || (fileType == FileType.Pipe);
    }

    public static void Redirect()
    {
        if (IsRedirected(GetStdHandle(StandardHandle.Output)))
        {
            var initialiseOut = Console.Out;
        }

        bool errorRedirected = IsRedirected(GetStdHandle(StandardHandle.Error));
        if (errorRedirected)
        {
            var initialiseError = Console.Error;
        }

        AttachConsole(-1);

        if (!errorRedirected)
            SetStdHandle(StandardHandle.Error, GetStdHandle(StandardHandle.Output));
    }
}

1
Komut isteminden sorunsuz çalışır, ancak Başlat> Çalıştır veya Visual Studio'da çalışmaz. Her durumda çalışmasını sağlamak için, AttachConsole satırını şu şekilde değiştirin: if (! AttachConsole (-1)) AllocConsole (); AllocConsole () çağrılırsa, FreeConsole () da çağrılmalıdır, aksi takdirde konsol ana bilgisayarı programı sonlandırdıktan sonra çalışmaya devam eder.
Berend Engelbrecht

2
İnitialiseOut ve initialiseError'ın amaçlanan kullanımı nedir, çünkü kullanılmazlar?
Edwin

StandardHandle : uintburada yanlış ... hem x86 hem de x64 üzerinde çalışmak için IntPtr olmalı
Dmitry Gusarov

1

İstediğiniz zaman uygulama türleri, konsol veya pencereler arasında geçiş yapabilirsiniz. Yani, stdout'u görmek için özel bir mantık yazmayacaksınız. Ayrıca, uygulamayı hata ayıklayıcıda çalıştırırken, tüm stdout'u çıktı penceresinde göreceksiniz. Ayrıca bir kesme noktası da ekleyebilirsiniz ve kesme noktası özelliklerinde "Vurulduğunda ..." değiştirilir, herhangi bir mesaj ve değişken çıktısını alabilirsiniz. Ayrıca "Yürütmeye devam et" seçeneğini işaretleyebilir / işaretini kaldırabilirsiniz ve kesme noktanız kare şeklinde olacaktır. Böylece, hata ayıklama çıktı penceresinde uygulamadaki hiçbir şeyi değiştirmeden kesme noktası mesajları.


0

Neden onu bir Pencere Formları uygulaması olarak bırakıp Konsolu taklit etmek için basit bir form oluşturmuyorsunuz? Form, tıpkı siyah ekranlı Konsol gibi görünecek şekilde yapılabilir ve doğrudan tuşa basılmasına yanıt vermesi sağlanabilir. Ardından program.cs dosyasında, ana formu veya ConsoleForm'u çalıştırmanız gerekip gerekmediğine karar verirsiniz. Örneğin, program.cs dosyasındaki komut satırı argümanlarını yakalamak için bu yaklaşımı kullanıyorum. ConsoleForm'u oluşturuyorum, önce gizliyorum, sonra komut satırı dizelerini içindeki bir AddCommand işlevine geçiriyorum, bu da izin verilen komutları gösteriyor. Son olarak, kullanıcı -h veya -? komutunu, ConsoleForm'da .Show olarak adlandırıyorum ve kullanıcı herhangi bir tuşa bastığında programı kapatıyorum. Kullanıcı -? komutu, gizli ConsoleForm'u kapatıyorum ve ana formu çalıştırıyorum.


2
Merhaba ve StackOverflow'a hoş geldiniz, soruları yanıt olarak göndermekten kaçının, yorum bölümünü kullanın.
Pedro Rodrigues
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.