Consolen Outpot gleichzeitig in .txt speichern

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von Plexian.

    Consolen Outpot gleichzeitig in .txt speichern

    Hallo,

    Ich wollte fragen wie ich den Consolen Output gleichzeitig in eine Textdatei speichern kann, aber so, dass ich den Consolen Output noch sehe!
    Habe bis das dato immer gelöst mit Console.SetOut und dann in nen Filestream rein, aber dann sieht man ja den Output nich mehr.

    Frage also: Geht das überhaupt? Wenn ja.. wie ?

    MfG,
    Logan.
    Im folgenden Thread auf Stackoverflow werden für das Problem einige erfolgsversprechende Lösungen vorgestellt:

    stackoverflow.com/questions/42…-console-output-to-a-file

    Für VB.Net dann den Converter benutzen: codeconverter.sharpdevelop.net/SnippetConverter.aspx

    Bzw. Fragen falls der Converter nicht alles richtig übersetzt.

    Edit: Folgender Code funktioniert bei mir (eigenes Programm) - wenn ich aber richtig gelesen habe, werden damit keine Consolen-Eingaben in das Log gespeichert.

    VB.NET-Quellcode

    1. Private Sub MirroringOutput()
    2. Trace.Listeners.Clear()
    3. Dim twtl = New TextWriterTraceListener(AppDomain.CurrentDomain.BaseDirectory & "log.txt")
    4. twtl.Name = "TextLogger"
    5. twtl.TraceOutputOptions = TraceOptions.ThreadId Or TraceOptions.DateTime
    6. Dim ctl = New ConsoleTraceListener(False)
    7. ctl.TraceOutputOptions = TraceOptions.DateTime
    8. Trace.Listeners.Add(twtl)
    9. Trace.Listeners.Add(ctl)
    10. Trace.AutoFlush = True
    11. Trace.WriteLine("The first line to be in the logfile and on the console.")
    12. End Sub

    Oder im folgenden kann man weiterhin "Console.WriteLine" benutzen und auch die Konsolen-Eingabe in Log speichern - durch erneute Ausgabe der eingelesen Daten von der Konsole.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Text
    2. Imports System.IO
    3. Module Module1
    4. Sub Main()
    5. Using cc = New ConsoleCopy(AppDomain.CurrentDomain.BaseDirectory & "mylogfile.txt")
    6. Console.WriteLine("testing 1-2-3")
    7. Console.WriteLine("testing 4-5-6")
    8. Dim ReadStr As String = Console.ReadLine()
    9. Console.WriteLine(ReadStr)
    10. End Using
    11. End Sub
    12. End Module
    13. Public Class ConsoleCopy
    14. Implements IDisposable
    15. Private fileStream As FileStream
    16. Private fileWriter As StreamWriter
    17. Private doubleWriter As TextWriter
    18. Private oldOut As TextWriter
    19. Private Class DoubleWrite
    20. Inherits TextWriter
    21. Private one As TextWriter
    22. Private two As TextWriter
    23. Public Sub New(one As TextWriter, two As TextWriter)
    24. Me.one = one
    25. Me.two = two
    26. End Sub
    27. Public Overrides ReadOnly Property Encoding() As Encoding
    28. Get
    29. Return one.Encoding
    30. End Get
    31. End Property
    32. Public Overrides Sub Flush()
    33. one.Flush()
    34. two.Flush()
    35. End Sub
    36. Public Overrides Sub Write(value As Char)
    37. one.Write(value)
    38. two.Write(value)
    39. End Sub
    40. End Class
    41. Public Sub New(path As String)
    42. oldOut = Console.Out
    43. Try
    44. fileStream = File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read)
    45. fileWriter = New StreamWriter(fileStream)
    46. fileWriter.AutoFlush = True
    47. doubleWriter = New DoubleWrite(fileWriter, oldOut)
    48. Catch e As Exception
    49. Console.WriteLine("Cannot open file for writing")
    50. Console.WriteLine(e.Message)
    51. Return
    52. End Try
    53. Console.SetOut(doubleWriter)
    54. End Sub
    55. Public Overloads Sub Dispose() Implements IDisposable.Dispose
    56. Console.SetOut(oldOut)
    57. If fileWriter IsNot Nothing Then
    58. fileWriter.Flush()
    59. fileWriter.Close()
    60. fileWriter = Nothing
    61. End If
    62. If fileStream IsNot Nothing Then
    63. fileStream.Close()
    64. fileStream = Nothing
    65. End If
    66. End Sub
    67. End Class

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „Thias“ ()

    Hi
    besonders elegant fände ich folgende Lösung:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Text
    3. Public Class WriteToManyTextWriter
    4. Inherits TextWriter
    5. Private ReadOnly _writers As IEnumerable(Of TextWriter)
    6. Public Sub New(ParamArray writerArray As TextWriter())
    7. Me.New(writers:=writerArray)
    8. End Sub
    9. Public Sub New(writers As IEnumerable(Of TextWriter))
    10. If writers Is Nothing Then Throw New ArgumentNullException(NameOf(writers))
    11. _writers = writers
    12. End Sub
    13. Public Overrides ReadOnly Property Encoding As Encoding
    14. Get
    15. Return Encoding.Default
    16. End Get
    17. End Property
    18. Public Overrides Sub Write(buffer() As Char, index As Integer, count As Integer)
    19. For Each w As TextWriter In _writers
    20. If w IsNot Nothing Then w.Write(buffer, index, count)
    21. Next
    22. End Sub
    23. Public Overrides Sub Flush()
    24. MyBase.Flush()
    25. For Each w As TextWriter In _writers
    26. If w IsNot Nothing Then w.Flush()
    27. Next
    28. End Sub
    29. End Class


    Du erzeugst einfach eine Instanz obigen TextWriters mit den Ausgabetextwritern (StreamWriter zum Filestream und Console.Out) als Parameter, in die geschrieben werden soll und setzt diesen als neuen Konsolen-Output.

    Viele Grüße
    ~blaze~

    Fortender schrieb:

    Geht es um den Console-Output deines eigenen oder eines externen Programms?

    Für ein externes Programm (gestartet über Process.Start) - wie z.B. cmd.exe - habe ich mir aus meinem editierten Post 5 und aus folgenden Post's mal schnell ein Konsolenprogramm zusammengebastelt - da mag dann einiges (strukturell) zu verbessern sein, aber als primitives Beispiel sollte es taugen.

    Problem bei Kommandozeile Auslesen
    stackoverflow.com/questions/16…nd-with-visual-basic-code

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Text
    2. Imports System.IO
    3. Module Module1
    4. Public cc As ConsoleCopy = New ConsoleCopy(AppDomain.CurrentDomain.BaseDirectory & "mylogfile.txt")
    5. Sub Main()
    6. Run_Process("CMD.exe", "/C Dir /B", True)
    7. Run_Process("CMD.exe", "/C Dir2 /B", True)
    8. Run_Process("CMD.exe", "/C @Echo OFF & For /L %X in (0,1,10) Do (Echo %X)", True, False)
    9. MsgBox("Stop")
    10. End Sub
    11. Private Function Run_Process(ByVal Process_Name As String, Optional Process_Arguments As String = Nothing, Optional Read_Output As Boolean = False, Optional Process_Hide As Boolean = False, Optional Process_TimeOut As Integer = -1) As String
    12. ' Returns True if "Read_Output" argument is False and Process was finished OK
    13. ' Returns False if ExitCode is not "0"
    14. ' Returns Nothing if process can't be found or can't be started
    15. ' Returns "ErrorOutput" or "StandardOutput" (In that priority) if Read_Output argument is set to True.
    16. Dim My_Process As New Process()
    17. Try
    18. If Read_Output Then
    19. AddHandler My_Process.ErrorDataReceived, AddressOf ProcessOutputDataReceived
    20. AddHandler My_Process.OutputDataReceived, AddressOf ProcessOutputDataReceived
    21. End If
    22. Dim My_Process_Info As New ProcessStartInfo()
    23. My_Process_Info.FileName = Process_Name ' Process filename
    24. My_Process_Info.Arguments = Process_Arguments ' Process arguments
    25. My_Process_Info.CreateNoWindow = Process_Hide ' Show or hide the process Window
    26. My_Process_Info.UseShellExecute = False ' Don't use system shell to execute the process
    27. My_Process_Info.RedirectStandardOutput = Read_Output ' Redirect (1) Output
    28. My_Process_Info.RedirectStandardError = Read_Output ' Redirect non (1) Output
    29. My_Process.EnableRaisingEvents = True ' Raise events
    30. My_Process.StartInfo = My_Process_Info
    31. My_Process.Start() ' Run the process NOW
    32. If Read_Output Then
    33. My_Process.BeginErrorReadLine()
    34. My_Process.BeginOutputReadLine()
    35. End If
    36. My_Process.WaitForExit(Process_TimeOut)
    37. Threading.Thread.Sleep(1000)
    38. Catch ex As Exception
    39. MsgBox(ex.Message)
    40. Return Nothing ' Returns nothing if the process can't be found or started.
    41. Finally
    42. If Read_Output Then
    43. RemoveHandler My_Process.ErrorDataReceived, AddressOf ProcessOutputDataReceived
    44. RemoveHandler My_Process.OutputDataReceived, AddressOf ProcessOutputDataReceived
    45. End If
    46. End Try
    47. Return CStr(True) ' Returns True if Read_Output argument is set to False and the process finished without errors.
    48. End Function
    49. Private Sub ProcessOutputDataReceived(sender As Object, e As DataReceivedEventArgs)
    50. If Not String.IsNullOrEmpty(e.Data) Then
    51. Console.WriteLine(e.Data)
    52. End If
    53. End Sub
    54. End Module
    55. Public Class ConsoleCopy
    56. Implements IDisposable
    57. Private fileStream As FileStream
    58. Private fileWriter As StreamWriter
    59. Private doubleWriter As TextWriter
    60. Private oldOut As TextWriter
    61. Private Class DoubleWrite
    62. Inherits TextWriter
    63. Private one As TextWriter
    64. Private two As TextWriter
    65. Public Sub New(one As TextWriter, two As TextWriter)
    66. Me.one = one
    67. Me.two = two
    68. End Sub
    69. Public Overrides ReadOnly Property Encoding() As Encoding
    70. Get
    71. Return one.Encoding
    72. End Get
    73. End Property
    74. Public Overrides Sub Flush()
    75. one.Flush()
    76. two.Flush()
    77. End Sub
    78. Public Overrides Sub Write(value As Char)
    79. one.Write(value)
    80. two.Write(value)
    81. End Sub
    82. End Class
    83. Public Sub New(path As String)
    84. oldOut = Console.Out
    85. Try
    86. fileStream = File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read)
    87. fileWriter = New StreamWriter(fileStream)
    88. fileWriter.AutoFlush = True
    89. doubleWriter = New DoubleWrite(fileWriter, oldOut)
    90. Catch e As Exception
    91. Console.WriteLine("Cannot open file for writing")
    92. Console.WriteLine(e.Message)
    93. Return
    94. End Try
    95. Console.SetOut(doubleWriter)
    96. End Sub
    97. Public Overloads Sub Dispose() Implements IDisposable.Dispose
    98. Console.SetOut(oldOut)
    99. If fileWriter IsNot Nothing Then
    100. fileWriter.Flush()
    101. fileWriter.Close()
    102. fileWriter = Nothing
    103. End If
    104. If fileStream IsNot Nothing Then
    105. fileStream.Close()
    106. fileStream = Nothing
    107. End If
    108. End Sub
    109. End Class


    Edit: "My_Process.BeginErrorReadLine()" ergänzt!

    Dieser Beitrag wurde bereits 9 mal editiert, zuletzt von „Thias“ ()

    Habe noch ein wenig gebastelt - auch wenn sich von der Struktur her immer noch vieles verbessern ließe - aber dazu wäre ein realer Anwendungsfall hilfreicher.

    Jedenfalls habe ich noch das Process Excited Event reingebaut.

    Da gibt es dann nur ein Problem, obwohl dieses Exit-Event gefeuert wird, liest OutputDataReceived immer noch Daten ein - bei mir noch bis zu einer Sekunde nach dem Exit-Event - je nach DOS-Befehl.

    Deswegen setzt die Sub ProcessOutputDataReceived dann DataReceived = True

    Und erst wenn in der Sub My_Process_Exited für mindestens 100ms DataReceived = False ist, erst dann wird WaitExitEvent.Set() gesetzt.

    Worauf hin dann in der Sub Run_Process es bei WaitExitEvent.WaitOne weitergeht.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Text
    2. Imports System.IO
    3. Imports System.Threading
    4. Module Module1
    5. Public cc As ConsoleCopy = New ConsoleCopy(AppDomain.CurrentDomain.BaseDirectory & "mylogfile.txt")
    6. Public WaitExitEvent As New ManualResetEvent(False)
    7. Public DataReceived As Boolean = False
    8. Sub Main()
    9. Run_Process("CMD.exe", "/C DIR C:\Windows\Microsoft.NET /S", True)
    10. Run_Process("CMD.exe", "/C Dir /B", True)
    11. Run_Process("CMD.exe", "/C Dir2 /B", True)
    12. Run_Process("CMD.exe", "/C @Echo OFF & For /L %X in (0,1,10) Do (Echo %X)", True, False)
    13. MsgBox("Stop")
    14. End Sub
    15. Private Function Run_Process(ByVal Process_Name As String, Optional Process_Arguments As String = Nothing, Optional Read_Output As Boolean = False, Optional Process_Hide As Boolean = False, Optional Process_TimeOut As Integer = -1) As String
    16. ' Returns True if "Read_Output" argument is False and Process was finished OK
    17. ' Returns False if ExitCode is not "0"
    18. ' Returns Nothing if process can't be found or can't be started
    19. ' Returns "ErrorOutput" or "StandardOutput" (In that priority) if Read_Output argument is set to True.
    20. Dim My_Process As New Process()
    21. WaitExitEvent.Reset()
    22. Try
    23. If Read_Output Then
    24. AddHandler My_Process.Exited, AddressOf My_Process_Exited
    25. AddHandler My_Process.ErrorDataReceived, AddressOf ProcessOutputDataReceived
    26. AddHandler My_Process.OutputDataReceived, AddressOf ProcessOutputDataReceived
    27. End If
    28. Dim My_Process_Info As New ProcessStartInfo()
    29. My_Process_Info.FileName = Process_Name ' Process filename
    30. My_Process_Info.Arguments = Process_Arguments ' Process arguments
    31. My_Process_Info.CreateNoWindow = Process_Hide ' Show or hide the process Window
    32. My_Process_Info.UseShellExecute = False ' Don't use system shell to execute the process
    33. My_Process_Info.RedirectStandardOutput = Read_Output ' Redirect (1) Output
    34. My_Process_Info.RedirectStandardError = Read_Output ' Redirect non (1) Output
    35. My_Process.EnableRaisingEvents = True ' Raise events
    36. My_Process.StartInfo = My_Process_Info
    37. My_Process.Start() ' Run the process NOW
    38. If Read_Output Then
    39. My_Process.BeginErrorReadLine()
    40. My_Process.BeginOutputReadLine()
    41. End If
    42. My_Process.WaitForExit(Process_TimeOut)
    43. If Read_Output Then
    44. WaitExitEvent.WaitOne(10000)
    45. End If
    46. Catch ex As Exception
    47. MsgBox(ex.Message)
    48. Return Nothing ' Returns nothing if the process can't be found or started.
    49. Finally
    50. If Read_Output Then
    51. RemoveHandler My_Process.Exited, AddressOf My_Process_Exited
    52. RemoveHandler My_Process.ErrorDataReceived, AddressOf ProcessOutputDataReceived
    53. RemoveHandler My_Process.OutputDataReceived, AddressOf ProcessOutputDataReceived
    54. End If
    55. End Try
    56. Return CStr(True) ' Returns True if Read_Output argument is set to False and the process finished without errors.
    57. End Function
    58. Private Sub ProcessOutputDataReceived(sender As Object, e As DataReceivedEventArgs)
    59. If Not String.IsNullOrEmpty(e.Data) Then
    60. Console.WriteLine(e.Data)
    61. DataReceived = True
    62. End If
    63. End Sub
    64. ' Handle Exited event and display process information.
    65. Private Sub My_Process_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
    66. ' https://msdn.microsoft.com/de-de/library/system.diagnostics.process.exited(v=vs.110).aspx
    67. Dim myProcess As Process = DirectCast(sender, Process)
    68. Console.WriteLine("Exit time: {0}" & vbCrLf & "Exit code: {1}", myProcess.ExitTime, myProcess.ExitCode)
    69. Do While DataReceived
    70. DataReceived = False
    71. Threading.Thread.Sleep(100)
    72. Loop
    73. WaitExitEvent.Set()
    74. End Sub
    75. End Module
    76. Public Class ConsoleCopy
    77. Implements IDisposable
    78. Private fileStream As FileStream
    79. Private fileWriter As StreamWriter
    80. Private doubleWriter As TextWriter
    81. Private oldOut As TextWriter
    82. Private Class DoubleWrite
    83. Inherits TextWriter
    84. Private one As TextWriter
    85. Private two As TextWriter
    86. Public Sub New(one As TextWriter, two As TextWriter)
    87. Me.one = one
    88. Me.two = two
    89. End Sub
    90. Public Overrides ReadOnly Property Encoding() As Encoding
    91. Get
    92. Return one.Encoding
    93. End Get
    94. End Property
    95. Public Overrides Sub Flush()
    96. one.Flush()
    97. two.Flush()
    98. End Sub
    99. Public Overrides Sub Write(value As Char)
    100. one.Write(value)
    101. two.Write(value)
    102. End Sub
    103. End Class
    104. Public Sub New(path As String)
    105. oldOut = Console.Out
    106. Try
    107. fileStream = File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read)
    108. fileWriter = New StreamWriter(fileStream)
    109. fileWriter.AutoFlush = True
    110. doubleWriter = New DoubleWrite(fileWriter, oldOut)
    111. Catch e As Exception
    112. Console.WriteLine("Cannot open file for writing")
    113. Console.WriteLine(e.Message)
    114. Return
    115. End Try
    116. Console.SetOut(doubleWriter)
    117. End Sub
    118. Public Overloads Sub Dispose() Implements IDisposable.Dispose
    119. Console.SetOut(oldOut)
    120. If fileWriter IsNot Nothing Then
    121. fileWriter.Flush()
    122. fileWriter.Close()
    123. fileWriter = Nothing
    124. End If
    125. If fileStream IsNot Nothing Then
    126. fileStream.Close()
    127. fileStream = Nothing
    128. End If
    129. End Sub
    130. End Class

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „Thias“ ()

    finde auch die Implementierung von @~blaze~ am schönsten
    @Thias
    Process_TimeOut sollte -1 sein für endloses warten.
    Außerdem dürfte ein Console Redirect meines wissens auch die Consolenausgabe verhindern, was der TE ja anscheinend gar nicht will

    Was man dafür versuchen könnte wäre(anstelle von Redirect von Process) mittels WinAPI AttachConsole auf das Process handle anzuwenden(evtl muss auch CreateRemoteThread o.ä. verwendet werden),
    anschließend mittels GetStdHandle das Handle dieser Konsole zu holen(und zu merken), mittels SetStdHandle die Konsolenausgabe zu einer von uns erstelltem Stream umzuleiten und mittels eines solchen Writers beim Empfang von Konsolenoutput
    wieder an das Ursprüngliche Handle von GetStdHandle zurückschreiben.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    Thias schrieb:

    Wobei es ihm wohl sogar mehr um sein eigenes Programm ging (Lösungen in Post 5) - wenn ich es recht verstanden habe - als über Process.Start (kam da selber nur auf die Idee wg. der Nachfrage von @Fortender)
    Aber letztendlich wäre es an @Fachkraftx3 mitzuteilen, ob das Problem für ihn gelöst ist oder ob noch Klärungsbedarf ist.

    Ja für so eine Art Logger lohnt es sich ein Singleton zu erstellen, welches das Reporting sowohl in dem Fenster, als auch in einem File bereitstellt
    Man kann dann von überall ​Log.Instance.Report(report) aufrufen.
    Danke @jvbsl für den Hinweis mit Process_TimOut -1 - ist angepasst und lässt sich ja bei Bedarf als Parameter dennoch weiterhin anders setzen.

    Denke @Fachkraftx3 ging es hauptsächlich darum, den Output in eine Datei weggeschrieben zu bekommen und gleichzeitig am Bildschirm in irgendeiner Konsole zu sehen - was bei allen Lösungen der Fall ist.
    Wobei es ihm wohl sogar mehr um sein eigenes Programm ging (Lösungen in Post 5) - wenn ich es recht verstanden habe - als über Process.Start (kam da selber nur auf die Idee wg. der Nachfrage von @Fortender)
    Aber letztendlich wäre es an @Fachkraftx3 mitzuteilen, ob das Problem für ihn gelöst ist oder ob noch Klärungsbedarf ist.

    Ob bei der Lösung von @~blaze~ weiterhin die Möglichkeit besteht, wie bei der Lösung mit ConsoleCopy, mit Console.WriteLine zu arbeiten (was ich schön finde) oder ob Programme für die Ausgaben angepasst werden müssen auf WriteToManyTextWriter.Write, ist mir leider noch nicht so klar, da ein Beispiel für den Aufruf fehlt und ich sichlerlich einige Zeit basteln müsste bis ich den hinbekäme - wg. der List(Of TextWriter) beim Konstruktor.
    Wobei das entscheidende wohl ist, dass Console.Out ja auch ein TextWriter ist - eventuell wäre der Aufruf doch nicht so schwert, aber ich hab es noch nicht ausprobiert.

    Edit: Für Eingaben bei cmd.exe fehlt noch My_Process_Info.RedirectStandardInput
    Falls ich Zeit habe, werde ich dafür andermal auch noch etwas basteln.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Thias“ ()

    Hilfe - warum hilft man hier nur, wenn es doch nicht gelesen wird?

    Der von mir in Post 5 verlinkte Ursprung ist alles in C#

    stackoverflow.com/questions/42…-console-output-to-a-file

    Die Lösung entweder mit den 90 Bewertungen oder den 25 Bewertugnen - wobei auch die anderen C#-Lösungen dort wohl großteils funktionieren!

    Und dann gib doch bitte wenigstens in Deinem Profil an, dass Du mit C# programmierst.

    Edit: @Plexian - stimmt, den letztn Satz hätte ich mir sparen können - insbesonere da ich damals extra den C#-Post bei stackoverflow.com rausgesucht hatte - nur heute habe ich nicht mehr zum Threadtitel hochgesrcollt - dies würde im Profil schneller ersichtlich sein

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Thias“ ()

    Thias schrieb:

    Und dann gib doch bitte wenigstens in Deinem Profil an, dass Du mit C# programmierst.

    OT: Steht neben dem Threadtitle, als Threadtag.
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais