Brak danych wyjściowych do konsoli z aplikacji WPF?


113

Używam Console.WriteLine () z bardzo prostej aplikacji testowej WPF, ale kiedy wykonuję aplikację z wiersza poleceń, nic nie jest zapisywane w konsoli. Czy ktoś wie, co może się tutaj dziać?

Mogę to odtworzyć, tworząc aplikację WPF w VS 2008 i po prostu dodając Console.WriteLine („tekst”) w dowolnym miejscu, które zostanie wykonane. Jakieś pomysły?

Teraz potrzebuję tylko czegoś tak prostego jak Console.WriteLine (). Zdaję sobie sprawę, że mógłbym użyć log4net lub innego rozwiązania do logowania, ale naprawdę nie potrzebuję tak dużej funkcjonalności dla tej aplikacji.

Edycja: Powinienem był pamiętać, że Console.WriteLine () jest przeznaczona dla aplikacji konsolowych. No cóż, żadnych głupich pytań, prawda? :-) Na razie użyję tylko System.Diagnostics.Trace.WriteLine () i DebugView.


Możliwe duplikaty tutaj i tutaj (nowsze, ale z kilkoma interesującymi odpowiedziami przy użyciu AttachConsole z Kernel32.dll )
maks.

1
@Max, te pytania są możliwymi duplikatami tego pytania. To pytanie zostało zadane 2-4 lata przed którymkolwiek z zadanych przez Ciebie pytań.
Rob

Odpowiedzi:


91

Będziesz musiał ręcznie utworzyć okno konsoli, zanim faktycznie wywołasz jakiekolwiek metody Console.Write. Spowoduje to zainicjowanie konsoli do prawidłowego działania bez zmiany typu projektu (co w przypadku aplikacji WPF nie będzie działać).

Oto kompletny przykład kodu źródłowego, jak może wyglądać klasa ConsoleManager i jak można jej używać do włączania / wyłączania konsoli, niezależnie od typu projektu.

W przypadku następującej klasy wystarczy ConsoleManager.Show()gdzieś napisać, zanim zadzwonisz do Console.Write...

[SuppressUnmanagedCodeSecurity]
public static class ConsoleManager
{
    private const string Kernel32_DllName = "kernel32.dll";

    [DllImport(Kernel32_DllName)]
    private static extern bool AllocConsole();

    [DllImport(Kernel32_DllName)]
    private static extern bool FreeConsole();

    [DllImport(Kernel32_DllName)]
    private static extern IntPtr GetConsoleWindow();

    [DllImport(Kernel32_DllName)]
    private static extern int GetConsoleOutputCP();

    public static bool HasConsole
    {
        get { return GetConsoleWindow() != IntPtr.Zero; }
    }

    /// <summary>
    /// Creates a new console instance if the process is not attached to a console already.
    /// </summary>
    public static void Show()
    {
        //#if DEBUG
        if (!HasConsole)
        {
            AllocConsole();
            InvalidateOutAndError();
        }
        //#endif
    }

    /// <summary>
    /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.
    /// </summary>
    public static void Hide()
    {
        //#if DEBUG
        if (HasConsole)
        {
            SetOutAndErrorNull();
            FreeConsole();
        }
        //#endif
    }

    public static void Toggle()
    {
        if (HasConsole)
        {
            Hide();
        }
        else
        {
            Show();
        }
    }

    static void InvalidateOutAndError()
    {
        Type type = typeof(System.Console);

        System.Reflection.FieldInfo _out = type.GetField("_out",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.FieldInfo _error = type.GetField("_error",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        Debug.Assert(_out != null);
        Debug.Assert(_error != null);

        Debug.Assert(_InitializeStdOutError != null);

        _out.SetValue(null, null);
        _error.SetValue(null, null);

        _InitializeStdOutError.Invoke(null, new object[] { true });
    }

    static void SetOutAndErrorNull()
    {
        Console.SetOut(TextWriter.Null);
        Console.SetError(TextWriter.Null);
    }
} 

5
Można najpierw spróbować wywołać AttachConsole (-1) i sprawdzić jego wartość zwracaną, aby dołączyć do konsoli procesu nadrzędnego; jeśli zwróci false, wywołaj AllocConsole. Jednak aplikacja nadal najpierw „zwraca”, a dopiero potem wyprowadza na konsolę. Napiszę więcej, jeśli znajdę rozwiązanie. Ponadto, jeśli ustawisz typ aplikacji WPF na Aplikacja konsolowa, problem znika, ale nie możesz odłączyć konsoli bez jej wyświetlania na ekranie przez chwilę po uruchomieniu programu, więc wygląda to trochę niezręcznie (ale jeśli możesz z tym żyć , działa świetnie).
Alex Paven,

2
Ech, właściwie nie, nie sądzę, aby można było mieć to w obie strony; aplikacja konsoli jest oznaczona jako CUI w nagłówku PE i dzięki temu dobrze współpracuje z CMD automatycznie. Z drugiej strony aplikacja GUI natychmiast zwraca sterowanie do CMD, a nawet jeśli może ponownie podłączyć się do konsoli, odczyt i zapis będą wymieszane z następnymi wyjściami w potoku, co jest oczywiście bardzo złe. Jeśli z drugiej strony oznaczysz aplikację jako aplikację konsolową, musisz tylko żyć z CMD wyświetlającym się przez chwilę podczas uruchamiania aplikacji; następnie możesz użyć FreeConsole do odłączenia i dołączenia / alokacji później itp.
Alex Paven,

1
Po co to robić, skoro odpowiedź Briana działa równie dobrze i jest znacznie łatwiejsza.
Wouter Janssens

2
Może to być oczywiste, ale stwierdziłem, że Console.WriteLine nadal nie działa przy użyciu tej techniki, gdy dołączony był debugger Visual Studio. Kiedy uruchomiłem aplikację poza VS, zadziałało to świetnie. Dzięki.
aboy021

2
@Mark Tak, ale to nie działa ... Jest SetConsoleCtrlHandlerfunkcja, która pozwala otrzymywać powiadomienia o CTRL_CLOSE_EVENTzdarzeniu, ale nie możesz nic z nią zrobić, nic nie pozwala na kontynuowanie aplikacji. Zostaniesz zamknięty. Jeśli masz ochotę włamać się, prawdopodobnie możesz zamienić program obsługi komunikatów systemu Windows na proces konsoli i po prostu porzucić komunikat WM_CLOSE. Nigdy tego nie próbowałem, ale może zadziałać. To tylko kolejne okno, ale powiedziawszy to, chyba że chcesz zająć się tym pomysłem, prawdopodobnie lepiej poświęć wysiłek na zrobienie czegoś innego.
John Leidegren

129

Kliknij prawym przyciskiem myszy projekt, „Właściwości”, zakładkę „Aplikacja”, zmień „Typ wyjścia” na „Aplikacja konsolowa”, a wtedy również będzie miała konsolę.


2
Jedynym problemem jest to, że w tle będzie otwarte polecenie cmd, ale działa :).
ykatchou

5
Świetnie, ale okno wiersza poleceń zostanie utworzone, gdy aplikacja nie zostanie uruchomiona z cmd.exe (dwa okna utworzone dla jednej aplikacji). Ale do tego jest też rozwiązanie: możesz ukryć okno cmd przez ShowWindow (hWnd, 0). stackoverflow.com/a/10416180/1457197 . Korzystając z tego rozwiązania, zobaczysz tekst w konsoli tylko wtedy, gdy aplikacja WPF jest wykonywana z wiersza polecenia.
CoperNick

Zauważ, że będziesz musiał przełączyć go z powrotem na „Aplikacja okna” podczas pracy w Blend, ponieważ pokazuje on tylko XAML (bez dostępu do widoku projektu) dla typów „Aplikacja konsolowa”. (stan na Blend 2013)

2
Nieprawidłowe odpowiedzi. Ukrywa główne okna. Po prostu pojawia się konsola.
Yash

nie działa z wpf, brak głównego okna
alp

129

Możesz użyć

Trace.WriteLine("text");

Spowoduje to wyświetlenie okna „Dane wyjściowe” w programie Visual Studio (podczas debugowania).

upewnij się, że zestaw diagnostyczny zawiera:

using System.Diagnostics;

9
to najlepsza odpowiedź, ale nie ma najwyższej oceny
kiltek

Zgadzam się - dokładnie o to prosi op. Świetna alternatywa dla Console.WriteLine () - rozwiązanie oznaczone jako odpowiedź to zgrabne ćwiczenie, ale nieuzasadnione umieszczanie go w aplikacji produkcyjnej.
nocarrier

4
PS dla aplikacji ze Sklepu Windows (Windows Runtime) odpowiednikiem Trace.WriteLine jest Debug.WriteLine ()
nocarrier

To proste, czyste rozwiązanie, ale nie zadziałało. Nie działał w metodzie inicjującej struktury jednostki podczas aktualizacji bazy danych. W przeciwnym razie działa wszędzie!
Charles W

To najlepsze rozwiązanie. Byłoby lepiej, gdyby odpowiedź wyjaśniła również, że w Console.WriteLineogóle nie jest przeznaczona dla aplikacji WPF i jest przeznaczona tylko dla aplikacji wiersza polecenia.
Andrew Koster

12

Chociaż John Leidegren wciąż odrzuca ten pomysł, Brian ma rację. Właśnie uruchomiłem go w programie Visual Studio.

Aby być wyczyszczonym, aplikacja WPF nie tworzy domyślnie okna konsoli.

Musisz utworzyć aplikację WPF, a następnie zmienić OutputType na „Console Application”. Po uruchomieniu projektu zobaczysz okno konsoli z oknem WPF przed nim.

Nie wygląda to zbyt ładnie, ale uznałem to za pomocne, ponieważ chciałem, aby moja aplikacja była uruchamiana z wiersza poleceń z informacją zwrotną, a następnie dla niektórych opcji poleceń wyświetliłbym okno WPF.


1
Idealny. Wykonuje pracę.
mroźny cud

10

Możliwe jest wyświetlenie danych wyjściowych przeznaczonych dla konsoli przy użyciu przekierowania wiersza poleceń .

Na przykład:

C:\src\bin\Debug\Example.exe > output.txt

zapisze całą zawartość do output.txtpliku.


Najlepsza odpowiedź, ponieważ jest prosta i nie wymaga zmiany źródła
Buckley

9

Stary post, ale natknąłem się na to, więc jeśli próbujesz wyprowadzić coś do wyjścia w projekcie WPF w programie Visual Studio, współczesna metoda to:

Uwzględnij to:

using System.Diagnostics;

I wtedy:

Debug.WriteLine("something");

4

Używam Console.WriteLine () do użycia w oknie Output ...


4
To pytanie sprzed 4 lat, które było mocno edytowane, odkąd je pierwszy raz zobaczyłem. Teraz oczywiście pytanie zostało lepiej sformułowane, a moja odpowiedź stała się nieistotna.
erodewald

1

Stworzyłem rozwiązanie, pomieszałem informacje z różnych postów.

Jest to formularz, który zawiera etykietę i jedno pole tekstowe. Dane wyjściowe konsoli są przekierowywane do pola tekstowego.

Istnieje również klasa o nazwie ConsoleView, która implementuje trzy metody publics: Show (), Close () i Release (). Ostatni służy do pozostawienia otwartej konsoli i aktywacji przycisku Zamknij, aby wyświetlić wyniki.

Formularze nazywa się FrmConsole. Oto kod XAML i C #.

Użycie jest bardzo proste:

ConsoleView.Show("Title of the Console");

Aby otworzyć konsolę. Posługiwać się:

System.Console.WriteLine("The debug message");

Do wyprowadzania tekstu do konsoli.

Posługiwać się:

ConsoleView.Close();

Aby zamknąć konsolę.

ConsoleView.Release();

Pozostawia otwartą konsolę i włącza przycisk Zamknij

XAML

<Window x:Class="CustomControls.FrmConsole"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:CustomControls"
    mc:Ignorable="d"
    Height="500" Width="600" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Topmost="True" Icon="Images/icoConsole.png">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="40"/>
    </Grid.RowDefinitions>
    <Label Grid.Row="0" Name="lblTitulo" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" FontFamily="Arial" FontSize="14" FontWeight="Bold" Content="Titulo"/>
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="10"/>
        </Grid.ColumnDefinitions>
        <TextBox Grid.Column="1" Name="txtInner" FontFamily="Arial" FontSize="10" ScrollViewer.CanContentScroll="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible" TextWrapping="Wrap"/>
    </Grid>
    <Button Name="btnCerrar" Grid.Row="2" Content="Cerrar" Width="100" Height="30" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center"/>
</Grid>

Kod okna:

partial class FrmConsole : Window
{
    private class ControlWriter : TextWriter
    {
        private TextBox textbox;
        public ControlWriter(TextBox textbox)
        {
            this.textbox = textbox;
        }

        public override void WriteLine(char value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value.ToString());
                textbox.AppendText(Environment.NewLine);
                textbox.ScrollToEnd();
            }));
        }

        public override void WriteLine(string value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value);
                textbox.AppendText(Environment.NewLine);
                textbox.ScrollToEnd();
            }));
        }

        public override void Write(char value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value.ToString());
                textbox.ScrollToEnd();
            }));
        }

        public override void Write(string value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value);
                textbox.ScrollToEnd();
            }));
        }

        public override Encoding Encoding
        {
            get { return Encoding.UTF8; }

        }
    }

    //DEFINICIONES DE LA CLASE
    #region DEFINICIONES DE LA CLASE

    #endregion


    //CONSTRUCTORES DE LA CLASE
    #region CONSTRUCTORES DE LA CLASE

    public FrmConsole(string titulo)
    {
        InitializeComponent();
        lblTitulo.Content = titulo;
        Clear();
        btnCerrar.Click += new RoutedEventHandler(BtnCerrar_Click);
        Console.SetOut(new ControlWriter(txtInner));
        DesactivarCerrar();
    }

    #endregion


    //PROPIEDADES
    #region PROPIEDADES

    #endregion


    //DELEGADOS
    #region DELEGADOS

    private void BtnCerrar_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }

    #endregion


    //METODOS Y FUNCIONES
    #region METODOS Y FUNCIONES

    public void ActivarCerrar()
    {
        btnCerrar.IsEnabled = true;
    }

    public void Clear()
    {
        txtInner.Clear();
    }

    public void DesactivarCerrar()
    {
        btnCerrar.IsEnabled = false;
    }

    #endregion  
}

kod klasy ConsoleView

static public class ConsoleView
{
    //DEFINICIONES DE LA CLASE
    #region DEFINICIONES DE LA CLASE
    static FrmConsole console;
    static Thread StatusThread;
    static bool isActive = false;
    #endregion

    //CONSTRUCTORES DE LA CLASE
    #region CONSTRUCTORES DE LA CLASE

    #endregion

    //PROPIEDADES
    #region PROPIEDADES

    #endregion

    //DELEGADOS
    #region DELEGADOS

    #endregion

    //METODOS Y FUNCIONES
    #region METODOS Y FUNCIONES

    public static void Show(string label)
    {
        if (isActive)
        {
            return;
        }

        isActive = true;
        //create the thread with its ThreadStart method
        StatusThread = new Thread(() =>
        {
            try
            {
                console = new FrmConsole(label);
                console.ShowDialog();
                //this call is needed so the thread remains open until the dispatcher is closed
                Dispatcher.Run();
            }
            catch (Exception)
            {
            }
        });

        //run the thread in STA mode to make it work correctly
        StatusThread.SetApartmentState(ApartmentState.STA);
        StatusThread.Priority = ThreadPriority.Normal;
        StatusThread.Start();

    }

    public static void Close()
    {
        isActive = false;
        if (console != null)
        {
            //need to use the dispatcher to call the Close method, because the window is created in another thread, and this method is called by the main thread
            console.Dispatcher.InvokeShutdown();
            console = null;
            StatusThread = null;
        }

        console = null;
    }

    public static void Release()
    {
        isActive = false;
        if (console != null)
        {
            console.Dispatcher.Invoke(console.ActivarCerrar);
        }

    }
    #endregion
}

Mam nadzieję, że ten wynik będzie przydatny.



-17

O ile wiem, Console.WriteLine () jest tylko dla aplikacji konsolowych. Myślę, że to twój problem.


1
Nie wiem o WPF, ale z pewnością tak nie jest w przypadku WinForms. Console.WriteLine działa tam dobrze, ale oczywiście nie zobaczysz konsoli, zobaczysz ją w oknie wyjściowym Debuggera i jeśli posłuchasz standardowego wyjścia.
Jeff Yates

2
możesz ustawić projekt na aplikację konsolową i nadal będzie działał jako aplikacja Windows, ale będzie miał również widoczną konsolę
Mark Cidade

To niepoprawne, proces kompilacji aplikacji innej niż konsolowa nie dołącza konsoli przez błąd. Możesz to zrobić ręcznie, wywołując funkcję AllocConsole () Win32 API przed jakimkolwiek wywołaniem Console.Write, klasa Console zostanie wówczas zainicjowana do pracy z tym oknem konsoli.
John Leidegren,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.