I have a C#.Net console app code that transfers files between linux and windows ftp servers.
The code's behavior is that I automatically open a console to display status of the file transfer. While transferring files to and from the windows server, I need to display an indicator that the file transfer is on-going (using symbols that look like they are moving or turning) . These symbols are as follows : "|" --> "/" --> "-" --> "\" --> "|"
There had been no problem displaying the said "indicator/symbols" in C#.Net using the following codes :
ProcessStartInfo PSI = new ProcessStartInfo("CMD.exe", "/C [here is the call to psftp / ftp scripts]");
String SymbolChar = "|";
Process proc = Process.Start(PSI);
while (true)
{
Thread.sleep(20);
if (proc.HasExited)
{
Console.WriteLine("\b-> Done\n");
break;
}
else
{
SymbolChar = NextChar(SymbolChar);
Console.Write("\b{0}", SymbolChar);
}
}
StreamReader srError = proc.StandardError;
StreamReader srOutput = proc.StandardOutput;
//method used to animate status during process
private static String NextChar(String sCurrentChar)
{
String sTempChar = "";
if (sCurrentChar.equals("|")) {
sTempChar = "/";
}
else if (sCurrentChar.equals("/")) {
sTempChar = "-";
}
else if (sCurrentChar.equals("-")) {
sTempChar = "\\";
}
else if (sCurrentChar.equals("-")) {
sTempChar = "\\";
}
else if (sCurrentChar.equals("\\")) {
sTempChar = "|";
}
return sTempChar;
}
My problem started when I converted the said codes into java using the following :
String sParam = "bash " + [here is the call to psftp / ftp scripts];
String cmd[] = {"/bin/bash","-c",sParam};
Process proc = Runtime.getRuntime().exec(cmd);
Above code is perfectly working.
Problem is I don't know how to display the "animated" characters anymore.
I suddenly don't know (and I badly need help on) how to convert this :
Process proc = Process.Start(PSI);
while (true)
{
Thread.sleep(20);
if (proc.HasExited)
{
Console.WriteLine("\b-> Done\n");
break;
}
else
{
SymbolChar = NextChar(SymbolChar);
Console.Write("\b{0}", SymbolChar);
}
}
Do you have any idea as to what I can use for the line "if (proc.HasExited)" in java so that I could move forward?
Sorry for the long explanation.
I really, really need help.
Thank you very much in advance!
You could use 'Process.exitValue()' and catch the Exception (it will throw an Exception if the process is not done yet). If the value is 0 the process exited normally. However this is rather ugly IMHO.
The only 'proper' way I know in Java to wait for a process to finish is to have the current Thread block until the Process is done with 'Process.waitFor()'.
This would however require a different thread to display your progress animation since your current thread is blocked and waiting for the Process to finish.
Related
For educational purposes at the university, I am developing a modular WCF C# application. The client app sends a source, the server takes care of compiling, testing and returning a result to the client.
One of the modules of the server does the compiling job. It consumes the source and produces an EXE to be used by the other module(s).
My problem is: when invoking cl.exe for the case when the given source is written in C++, I manage to compile it, but the compiling module cannot receive the error messages properly from the child process, that runs cmd.exe, which then starts cl.exe. The source code and an example of what actually happens say more than a million words, so here they are:
public static string clPath = #"E:\path_to_project\Client\clTo4kaEXE\";
string sourceCode = "//given source";
using (StreamWriter sw = new StreamWriter(clPath + exeName + ".cpp"))
{
sw.Write(sourceCode);
sw.Flush();
}
Process clTo4kaEXE = new Process();
clTo4kaEXE.StartInfo.FileName = clPath + "cmd.exe";
clTo4kaEXE.StartInfo.WorkingDirectory = clPath;
clTo4kaEXE.StartInfo.UseShellExecute = false;
clTo4kaEXE.StartInfo.RedirectStandardOutput = true;
clTo4kaEXE.StartInfo.RedirectStandardError = true;
clTo4kaEXE.StartInfo.RedirectStandardInput = true;
clTo4kaEXE.StartInfo.Arguments = "%comspec% /k \"\"e:\\vs2010\\VC\\vcvarsall.bat\"\" x86";
clTo4kaEXE.Start();
clTo4kaEXE.StandardInput.WriteLine("cl /EHsc " + exeName + ".cpp");
StreamReader clStandardOutput = clTo4kaEXE.StandardOutput;
StreamReader clErrorOutput = clTo4kaEXE.StandardError;
string clStdOutput = "";
string temp = "";
while(true)
{
//Debugger.Launch(); // breakpoint
temp = clStandardOutput.ReadLine();
Console.WriteLine("STD TEMP = {0}", temp);
clStdOutput += temp;
//if (temp == null /*|| temp == "" */|| clStandardOutput.EndOfStream)
//{
// break;
//}
if (clStandardOutput.Peek() == -1 && temp == "")
{
break;
}
}
string clErrOutput = "";
temp = "";
while (true)
{
temp = clErrorOutput.ReadLine();
Console.WriteLine("ERROR TEMP = {0}", temp);
clErrOutput += temp;
//if (temp == null || temp == "" || clErrorOutput.EndOfStream)
//{
// break;
//}
if (clErrorOutput.Peek() == -1 && temp == "")
{
break;
}
}
clTo4kaEXE.Close();
Console.WriteLine("[Modul_Compile] cl.exe returned on its standard output: {0}\n", clStdOutput);
Console.WriteLine("[Modul_Compile] cl.exe returned on its error output: {0}\n", clErrOutput);
When there is an error in the source, for example missing ';' somewhere, then here is what i see in the console:
Then I decided to run Visual Studio Command Prompt giving it the same source code and here is what i get:
Remarks:
clStdOutput= clStandardOutput.ReadToEnd(); used instead of that while(true){} causes the client app's window to "freeze" and the compiling module doesn't receive anything from its child process.
I am pretty surprised that cl.exe prints the messages "Microsoft (R) 32-bit C/C++ Optimizing..." and "Copyright (C)... All rights reserved." to its Error output - the stream, from which I expect to receive the compiling error.
I searched the net for any info, found some clues, which helped me get anything from the child process. Now the next step is to get what i need to.
I couldn't find a way to start the VS Command Prompt, because it's actually a shortcut, not pointing to an exe, so I couldn't benefit from it.
Any help would be appreciated! Thanks! :)
I am starting a process (actually phantomjs) using c#, and am trying to pass information (a base64image string) via the standard output.
Should the process succeed, everything goes well.
Should the process fail (in this case because there is a javascript error in a page phantomjs is opening), it hangs indefinitely.
My code looks like this:
var path = Server.MapPath("phantomjs.exe");
var args = string.Join(" ", new[] { Server.MapPath(#"screenshot.js"), url });
var info = new ProcessStartInfo(path, args);
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
var p = Process.Start(info);
// it hangs on the following line:
var base64image = p.StandardOutput.ReadToEnd();
bytes = Convert.FromBase64CharArray(base64image.ToCharArray(), 0, base64image.Length);
I am assuming that running any external process could lead to this problem.
If that process does not finish properly (for whatever reason) then there will never be an output end to read to (maybe?).
What I would like to know is, how can I introduce a maximum timeout? Should the process succeed and exit in less than the timeout, great. If not, kill the process and do something else.
I have tried the following:
if (p.WaitForExit(30000))
{
var base64image = p.StandardOutput.ReadToEnd();
bytes = Convert.FromBase64CharArray(base64image.ToCharArray(), 0, base64image.Length);
// do stuff with bytes
}
else
{
p.Kill();
// do something else
}
This worked when I was running a much simpler application (that simply wrote a number to the console every second for 60 seconds).
When I tried with phantomjs, it fails (waiting 30 seconds) for cases that should work (and take much less than 30 seconds when I revert to my original code, or run it from the console).
Maybe phantomjs (or the js script I wrote) does not exit properly, but can the c# deal with all scenarios?
You have to add a global error handler in your script.
phantomjs won't exit by itself when there is a JavaScript execution error (parsing, exception, ...).Try by yourself in a simple script.
A very basic example is available here.
phantom.onError = function(msg, trace) {
var msgStack = ['PHANTOM ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : ''));
});
}
console.error(msgStack.join('\n'));
phantom.exit(1);
};
In addition you can also add a page error handler.
page.onError = function(msg, trace) {
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function + '")' : ''));
});
}
console.error(msgStack.join('\n'));
};
Just note that phantom.error is invoked when there is a JavaScript execution error not caught by a WebPage#onError handler.
Brief Summary
I am creating a lightweight IDE for NASM development in C# (I know kind of an irony). Kinda of like Notepad++ but simpler but with features that make it more than source editor. Since Notepad++ is really just a fancy source editor. I have already implemented features like Project creation (using a project format similar to how Visual Studio organizes projects). Project extension .nasmproj. I am also in the works of hosting it in an open-source place (Codeplex). Although the program is far from finish, and definitely cannot be used in a production environment without proper protection and equipment. In addition, I am working alone with it at this moment, more like a spare time project since I just finished my last Summer final taking Calculus I.
Problem
Right now I am facing a problem, I can build the project but no output from NASM is being fed into the IDE. I have succesfully built a project, and I was able to produce object files. I even tried producing a syntax error to see if I finally see something come up but none and I check the bin folder of the test project I created and I see no object file creating. So definitely NASM is doing its magic. Is it because NASM doesn't want me to see its output. Is there a solution? Any advice would be great. Here is the code which I think is giving Trouble.
Things to Note
I have already checked if events have been invoked. An yes they have but they return empty strings
I have also checked error data and same effect.
Code
public static bool Build(string arguments, out Process nasmP)
{
try
{
ProcessStartInfo nasm = new ProcessStartInfo("nasm", arguments);
nasm.CreateNoWindow = true;
nasm.RedirectStandardError = true;
nasm.RedirectStandardInput = true;
nasm.RedirectStandardOutput = true;
nasm.UseShellExecute = false;
nasmP = new Process();
nasmP.EnableRaisingEvents = true;
nasmP.StartInfo = nasm;
bool predicate = nasmP.Start();
nasmP.BeginOutputReadLine();
return true;
}
catch
{
nasmP = null;
return false;
}
}
//Hasn't been tested nor used
public static bool Clean(string binPath)
{
if (binPath == null || !Directory.Exists(binPath))
{
throw new ArgumentException("Either path is null or it does not exist!");
}
else
{
try
{
DirectoryInfo binInfo = new DirectoryInfo(binPath);
FileInfo[] filesInfo = binInfo.GetFiles();
for (int index = 0; index < filesInfo.Length; index++)
{
try
{
filesInfo[index].Delete();
filesInfo[index] = null;
}
catch
{
break;
}
}
GC.Collect();
return true;
}
catch
{
return false;
}
}
}
}
using (BuildDialog dlg = new BuildDialog(currentSolution))
{
DialogResult result = dlg.ShowDialog();
dlg.onOutputRecieved += new BuildDialog.OnOutputRecievedHandler(delegate(Process _sender, string output)
{
if (result == System.Windows.Forms.DialogResult.OK)
{
outputWindow.Invoke(new InvokeDelegate(delegate(string o)
{
Console.WriteLine("Data:" + o);
outputWindow.Text = o;
}), output);
}
});
}
Edits
I have tried doing synchronously instead of asynchronously but still the same result (and empty string "" is returned) actually by debugging the stream is already at the end. So looks like nothing has been written into the stream.
This is what I tried:
string readToEnd = nasmP.StandardOutput.ReadToEnd();
nasmP.WaitForExit();
Console.WriteLine(readToEnd);
And another interesting thing I have tried was I copied the arguments from the debugger and pasted it in the command line shell and I can see NASM compiling and giving the error that I wanted to see all along. So definitely not a NASM problem. Could it be a problem with my code or the .Net framework.
Here is a nice snapshot of the shell window (although not technically proof; this is what the output should look like in my IDE):
Alan made a very good point, check the sub processes or threads. Is sub process and thread synonymous? But here is the problem. Almost all the properties except a select few and output/error streams are throwing an invalid operation. Here is the debugger information as an image (I wish Visual Studio would allow you to copy the entire information in click):
Okay I finally was able to do it. I just found this control that redirect output from a process and I just looked at the source code of it and got what I needed to do. Here is the the modified code:
public static bool Build(string command, out StringBuilder buildOutput)
{
try
{
buildOutput = new StringBuilder();
ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe");
startInfo.Arguments = "/C " + " nasm " + command;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
Process p = Process.Start(startInfo);
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
if (output.Length != 0)
buildOutput.Append(output);
else if (error.Length != 0)
buildOutput.Append(error);
else
buildOutput.Append("\n");
return true;
}
catch
{
buildOutput = null;
return false;
}
}
Here is how the output is formatted like:
I also wanted to thank Alan for helping me debug my code, although he didn't physically had my code. But he really was helpful and I thank him for it.
How can I have a button in my desktop application that causes the user's default browser to launch and display a URL supplied by the application's logic.
Process.Start("http://www.google.com");
Process.Start([your url]) is indeed the answer, in all but extremely niche cases. For completeness, however, I will mention that we ran into such a niche case a while back: if you're trying to open a "file:\" url (in our case, to show the local installed copy of our webhelp), in launching from the shell, the parameters to the url were thrown out.
Our rather hackish solution, which I don't recommend unless you encounter a problem with the "correct" solution, looked something like this:
In the click handler for the button:
string browserPath = GetBrowserPath();
if (browserPath == string.Empty)
browserPath = "iexplore";
Process process = new Process();
process.StartInfo = new ProcessStartInfo(browserPath);
process.StartInfo.Arguments = "\"" + [whatever url you're trying to open] + "\"";
process.Start();
The ugly function that you shouldn't use unless Process.Start([your url]) doesn't do what you expect it's going to:
private static string GetBrowserPath()
{
string browser = string.Empty;
RegistryKey key = null;
try
{
// try location of default browser path in XP
key = Registry.ClassesRoot.OpenSubKey(#"HTTP\shell\open\command", false);
// try location of default browser path in Vista
if (key == null)
{
key = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http", false); ;
}
if (key != null)
{
//trim off quotes
browser = key.GetValue(null).ToString().ToLower().Replace("\"", "");
if (!browser.EndsWith("exe"))
{
//get rid of everything after the ".exe"
browser = browser.Substring(0, browser.LastIndexOf(".exe") + 4);
}
key.Close();
}
}
catch
{
return string.Empty;
}
return browser;
}
I am trying to start a process and capture the output, have come a far way, but am not quite at the solution I'd want.
Specifically, I am trying to reset the IIS on my development machine from a small utility application that I am writing. I have come to the conclusion, by experimenting, that the safe way to do this is by running iisreset.exe in a child process.
If you run iisreset.exe on a command prompt, you get feedback during the process. Running iisreset takes several seconds, and several lines of feedback is generated, with pauses in between.
I'd like to capture this feedback and present it in my Windows Forms application (in a ListBox), and I have succeeded with that. My remaining concern is that I dont get it until the child process finishes. I'd like to get the output from the child process, line by line, immediately when the lines are created.
I have tried to do my homework, reading/testing things from e.g. these:
How to spawn a process and capture its STDOUT in .NET?
Capturing console output from a .NET application (C#)
http://www.aspcode.net/ProcessStart-and-redirect-standard-output.aspx
and several more with similar content. Most (all?) get the output asynchronously (e.g. with Process.ReadToEnd()). I want the output synchonously, which acording to the MSDN documentation involves establishing an event handler etc and I've tried that. It works, but the event handler does not get called until the process exits. I get the output from iisreset.exe, but not until it has finished.
To rule out the possibility that this has something to do with iisreset.exe in particular, I wrote a small console application that generates some output, pausing in between:
namespace OutputGenerator
{
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("OutputGenerator starting and pausing for 10 seconds..");
System.Threading.Thread.Sleep(10000);
System.Console.WriteLine("Pausing for another 10 seconds..");
System.Threading.Thread.Sleep(10000);
System.Console.WriteLine("Exiting!");
}
}
}
Testing with this it turns out that I get captured data diretly when I want. So, to some extent it seems that the way iisreset.exe outputs the data come into play here.
Here is the code of the program (a Windows Forms application) that does the capture:
using System;
using System.Windows.Forms;
using System.Diagnostics;
namespace OutputCapturer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnRun_Click(object sender, EventArgs e)
{
// Running this will show all output after the process has exited
//String path = #"C:\Windows\system32\iisreset.exe";
// Running this will show all output "when it happens"
String path = #"C:\OutputGenerator.exe";
var p = new Process();
p.StartInfo.FileName = path;
p.StartInfo.UseShellExecute = false; // ShellExecute = true not allowed when output is redirected..
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.CreateNoWindow = true;
p.OutputDataReceived += OutputDataReceived;
p.Start();
p.BeginOutputReadLine();
}
private delegate void OutputDataToTextboxDelegate(String s);
void OutputDataToTextbox(String s)
{
tbxOutput.Text += s + Environment.NewLine;
tbxOutput.Refresh();
}
private void OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null && e.Data.ToString() != "")
{
// Must run the update of the textbox in the same thread that created it..
tbxOutput.Invoke(
new OutputDataToTextboxDelegate(OutputDataToTextbox),
DateTime.Now.ToString() + ": " + e.Data.ToString()
);
}
}
}
}
Thinking it was an EOL-encoding problem (the output of iisreset.exe apearing as one line to my app)), I ran a debug session. Nope. The event handler for StandardOutput gets called several times (one time for each output line from iisreset.exe), buth these calls come in one burst after the process exits.
I would LOVE if I could get the output from iisreset.exe "when it happens" so that I can show it as a progress indication.
I've seen one other thread with the same/similar problem, Asynchronous capture from a process output not working properly , but w/o a solution.
I'm sort of stumped.
To do autoflushing of printfs / stdouts
C equivalent of autoflush (flush stdout after each write)?
This saved my ass...
It seems that sixlettervariables is correct, and that this has something to do with iisreset.exe isn't flushing it's buffers for each line. (I still wonder what makes it work on a plain command line - i.e. what does cmd.exe do?)
Anyhow.. I tried what apacay suggested, and wrote this:
private void btnRun_Click(object sender, EventArgs e)
{
// Running this will show the output after the process has finished
//String path = #"C:\Windows\system32\iisreset.exe";
// Running this will show all output "when it happens"
String path = #"C:\OutputGenerator.exe";
var p = new Process();
p.StartInfo.FileName = path;
p.StartInfo.UseShellExecute = false; // ShellExecute = true not allowed when output is redirected..
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
StreamReader sr = p.StandardOutput;
while (!sr.EndOfStream)
{
String s = sr.ReadLine();
if (s != "")
{
tbxOutput.Text += DateTime.Now.ToString() + ": " + s + Environment.NewLine;
}
tbxOutput.Refresh();
}
}
Notice that I am timestamping when I get each line. For my OutputGenerator I get this:
2011-07-06 17:49:11: OutputGenerator starting and pausing for 10 seconds..
2011-07-06 17:49:21: Pausing for another 10 seconds..
2011-07-06 17:49:31: Exiting!
And for iisreset.exe I get this:
2011-07-06 17:57:11: Attempting stop...
2011-07-06 17:57:11: Internet services successfully stopped
2011-07-06 17:57:11: Attempting start...
2011-07-06 17:57:11: Internet services successfully restarted
Running iisreset.exe on the command line, those lines come with pauses in between, over a span of perhaps 10 seconds.
The case seems more or less closed now. Not that I am all that satisfied, but I'm at roads end it seems. I'll reluctantly live with it..
To summarise: In the general case, it is quite possible to capture output synchronously with when it is generated. This thread presents code for two ways to do that - by establishing an event handler, and by "polling" the stream. In my specific case there is something with how iisreset.exe generates output that prevents this.
Thanks to those who participated and contributed!
Well.... you could kick it old-school. Output can be redirected to the input of another program using old-school DOS commands (foo.exe | bar.exe). Write a program that reads from standard in, and you'll get it every time the stream flushes.
Edit
You could also redirect the ouput to a named pipe and read from that. That would also be "as it happens".
Well, I tried a helper class that I know works: http://csharptest.net/browse/src/Library/Processes/ProcessRunner.cs
ProcessRunner runner = new ProcessRunner("iisreset.exe");
runner.OutputReceived += OutputDataReceived;
runner.Start("/RESTART", "/STATUS");
However, this still doesn't solve the problem with this specific executable. It seems that iisreset was written in such a way that this is not possible. Even running the following from the command line:
iisreset.exe /RESTART /STATUS > temp.txt
Still nothing is written to the text file 'temp.txt' until after all services have been restarted.
As for your example code, I would recommend reading a post I wrote some time ago: How to use System.Diagnostics.Process correctly. Specifically you are not reading the std::err stream or redirecting and closing the std::in stream. This can cause very undesirable results in your program. You can look at the example wrapper class linked above for how to do it with the output events, or if you want to directly read the streams you need to use two of your own threads.
static void Main()
{
ProcessStartInfo psi = new ProcessStartInfo(#"C:\Windows\system32\iisreset.exe", "/RESTART /STATUS");
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
ManualResetEvent output_complete = new ManualResetEvent(false);
ManualResetEvent error_complete = new ManualResetEvent(false);
Process p = Process.Start(psi);
new ReadOutput(p.StandardOutput, output_complete);
new ReadOutput(p.StandardError, error_complete);
p.StandardInput.Close();
p.WaitForExit();
output_complete.WaitOne();
error_complete.WaitOne();
}
private class ReadOutput
{
private StreamReader _reader;
private ManualResetEvent _complete;
public ReadOutput(StreamReader reader, ManualResetEvent complete)
{
_reader = reader;
_complete = complete;
Thread t = new Thread(new ThreadStart(ReadAll));
t.Start();
}
void ReadAll()
{
int ch;
while(-1 != (ch = _reader.Read()))
{
Console.Write((char) ch);
}
_complete.Set();
}
}
I wrote this just to see if anything was coming through. Still got nothing until the end, so I think your just SOL on getting asynchronous output from iisreset.
I've had that problem and had to solve it when my logs where too long to read in a single readtoend.
This is what I've done to solve it. It's been doing Ok so far.
myProcess.StartInfo.FileName = path;
myProcess.StartInfo.Arguments = args;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.ErrorDialog = false;
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.RedirectStandardInput = (stdIn != null);
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.Start();
int index;
OpenLogFile(myLog); //LOGGGGGGGGGGGGG
if (myProcess.StartInfo.RedirectStandardInput)
{
StreamWriter sw = myProcess.StandardInput;
sw.Write(stdIn + Convert.ToChar(26));
}
StreamReader sr = myProcess.StandardOutput;
/*stdOut = new ArrayLi
*/
while (!sr.EndOfStream)
{ //LOGGGGGGGGGGGGG
Log(sr.ReadLine(), true);
}
Here's OpenLogFile
private void OpenLogFile(string fileName)
{
if (file == StreamWriter.Null)
{
file = new StreamWriter(fileName, true);
file.AutoFlush = true;
}
}
Of course that Log is a function that does something elsewhere. But the solution to you question lies here:
while (!sr.EndOfStream)
{ //LOGGGGGGGGGGGGG
Log(sr.ReadLine(), true);
}
while stream reader is still reading, you can be writing it down as the log comes out.
For my specific situation, the solution is what Mr Moses suggested in a comment above, i.e. run iisreset /stop followed by iisreset /start.
I need a proper answer, rather than a comment, in order to mark it as my "accepted answer", so this answer is more of administrativa than a new contribution. The cred should go to Mr Moses.. :-)