I have this method:
private delegate void watcherReader(StreamReader sr);
private void watchProc(StreamReader sr) {
while (true) {
string line = sr.ReadLine();
while (line != null) {
if (stop) {
return;
}
//Console.WriteLine(line);
line = stripColors(line);
txtOut.Text += line + "\n";
line = sr.ReadLine();
}
}
}
And it reads the streams from a Process (cmd.exe). When the user closes the cmd.exe window, it causes the CPU usage to jump to 100%. When playing with the debugger I see that it stops on the sr.ReadLine() and never returns. Because this is watching both the StandardErrorStream and the StandardOutputStream it uses 100% on both cores.
Here's some more code of the project if you need it.
[DllImport("User32")]
private static extern int ShowWindow(int hwnd, int nCmdShow); //this will allow me to hide a window
public ConsoleForm(Process p) {
this.p = p;
p.Start();
ShowWindow((int)p.MainWindowHandle, 0); //0 means to hide the window.
this.inStream = p.StandardInput;
this.outStream = p.StandardOutput;
this.errorStream = p.StandardError;
InitializeComponent();
wr = new watcherReader(watchProc);
wr.BeginInvoke(this.outStream, null, null);
wr.BeginInvoke(this.errorStream, null, null);
}
public void start(string[] folders, string serverPath) {
this.inStream.WriteLine("chdir C:\\cygwin\\bin");
this.inStream.WriteLine("bash --login -i");
this.inStream.WriteLine("");
}
//code example from http://geekswithblogs.net/Waynerds/archive/2006/01/29/67506.aspx it is
//to make the textbox autoscroll I don't understand what it does, but it works.
#region autoscroll
[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const int WM_VSCROLL = 277;
const int SB_BOTTOM = 7;
private void txtOut_TextChanged(object sender, EventArgs e) {
IntPtr ptrWparam = new IntPtr(SB_BOTTOM);
IntPtr ptrLparam = new IntPtr(0);
SendMessage(((RichTextBox)sender).Handle, WM_VSCROLL, ptrWparam, ptrLparam);
}
#endregion
private void ConsoleForm_FormClosed(object sender, FormClosedEventArgs e) {
this.stop = true;
try {
this.p.Kill();
} catch (InvalidOperationException) {
return;
}
}
Another interesting this is that it doesn't always hide the cmd window like it's supposed to. It hides it the first time, and then the second (or after) it won't hide it. This is when the user can close the cmd.exe window and cause the readline to act funny. It also never reads the last line outputted to cmd unless it exits.
Any suggestions on how to fix this?
I would change:
while(true)
to:
while(!sr.EOS) {
}
It is a better way to check to end the loop.
Whenever you have a while(true) loop in your code you're going to peg your cpu (or at least one core) at 100%, unless you also have a way to break out of the loop. In your case, you do have a return statement, but at no point in the loop do you ever do anything to the stop variable guarding it.
This seems like an interesting issue here. At first glance, it would appear that ReadLine has an issue with the handle being closed from under it while it's trying to read data, and thus would seem to be a bug in the Framework. However, I'm not convinced that easily that it's a bug in the .Net framework...
However, there are a couple low level issues here.
The other answers you have got so far all suggest you modify the while loop. I would do this as well, but I don't think this is the root of your problem. You do not need a sleep in there, because you will get your waitstate from the ReadLine(), unless there is no data to read, and it just returns a failue, THEN you will 'tight-loop'. So, make sure you are checking any and all error states during this loop.
If you do not, I can see issues.
If everything else is working as it should, then if I were you, I would start by trying to identify if you can duplicate it outside of your program with a small demo program. I'm sure there is plenty of error checking in the Framework's Stream handling. However, it looks like you are running some stuff from Cygwin, and that's the output you are reading from the cmd shell.
Try making a simple app that just spits out data to stdout, and stderr, and then make sure the app closes while you are still reading..
Also use the debugger to see what line== after the failure occurs.
Larry
Having while(true) with no sleep in the loop will cause 100% CPU usage.
You need to sleep for some amount of time or break out of the loop at some point so the CPU can do something else.
At the very least you should be doing something along the lines of:
while (sr.Peek() >= 0)
{
Console.WriteLine(sr.ReadLine());
Thread.Sleep(0);
}
Related
I have this chunk of code in my Notepad clone program, it is to track my carat location in my RichTextBox and has been working fine for me.
private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
{
Curpos();
}
private static int EM_LINEINDEX = 0xbb;
[DllImport("user32.dll")]
extern static int SendMessage(IntPtr hwnd, int message, int wparam, int lparam);
private void Curpos()
{
{
int line, col, index;
index = richTextBox1.SelectionStart;
line = richTextBox1.GetLineFromCharIndex(index);
col = index - SendMessage(richTextBox1.Handle, EM_LINEINDEX, -1, 0);
Lblcurpos.Text = "Line: " + (++line).ToString() + ", Column:" + (++col).ToString();
}
}
It has been working perfectly for me, until i added some code so that i could close my program with the Esc Key.
Here is the code for my Esc Key:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}
EDIT: This is where it has the problem:
index = richTextBox1.SelectionStart;
When i am doing this, i can run my program fine, type text in my RichTextBox and everything but when i press the Esc Key it says:
Cannot Access disposed object
Object name: 'RichTextBox'
Then is says:
Trouble Shooting Tips:
Make sure you have not released a resource before attempting to use this.
Get general help for this exception
Any ideas on what i can do? I tried to use the shortcut keys but it doesn't have a Esc key you can use for the shortcut. Any help with either doing a different shortcut or a fix to this problem, i would like it!!
Try:
Application.Exit();
instead of
this.Close();
From MSDN:
Application.Exit
Informs all message pumps that they must terminate, and then closes all application windows after the messages have been processed. This is the code to use if you are have called Application.Run (WinForms applications), this method stops all running message loops on all threads and closes all windows of the application.
Do note that Application.Exit() is not a substitute for this.Close(). Exit() terminates the entire application, Close() just close the form.
There are better ways to avoid accidents like this. When you handle shortcut keystrokes like you did, you should always set e.Handled and e.SuppressKeyPress to true so the keystroke will be completely dismissed and not generate any additional events. Like the one that bombed your code.
By far the best way is to use the dedicated method for this in Winforms, ProcessCmdKey(). It implements true shortcut keystroke behavior, the KeyPreview property is a VB6 compatibility feature which is close but not equivalent. Make it look like this:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == Keys.Escape) {
this.Close();
return true; // Used, don't process any further
}
return base.ProcessCmdKey(ref msg, keyData);
}
I need to show cursor in RichTextBox control in WinForms application even when it's not in focus. How can I do this? I found only the way for WPF ( How to keep WPF TextBox selection when not focused?)
You can use WinAPI ..
[DllImport("user32.dll", EntryPoint = "ShowCaret")]
public static extern long ShowCaret(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "HideCaret")]
public static extern long HideCaret(IntPtr hwnd);
and call ShowCaret whenever you want
You can't set focus to the two or more UI at same time however you can preserve the selection by setting HideSelection=false.
I don't know what you are trying to achieve and how much is it really useful. But if it is just for visual purpose, write some thing like '|' in it. Its a bad, weird, awkward way or what ever you call it, for visual purpose it may work.
public void blink()
{
while (true)
{
textBox1.Text = "|";
Thread.Sleep(200);
textBox1.Text = "";
Thread.Sleep(200);
}
}
private void Form1_Load(object sender, EventArgs e)
{
Thread t1 = new Thread(new ThreadStart(blink));
t1.Start();
}
I am not sure if I am giving is what you are asking, but to get accurate answer, you have to expose your need of this requirement.
Hope it helps.
I have a method that gets called into a new thread like so:
if (!_isPlaying)
{
_playBackThread = new Thread(PlayMacroEvents);
_playBackThread.Start();
...
}
The method looks like:
Process proc = Process.GetProcessesByName("notepad").FirstOrDefault();
if (proc != null)
{
SetForegroundWindow(proc.MainWindowHandle);
}
int loopCount = this.dsUserInput.Tables[0].Rows.Count;
for (int i = 0; i < loopCount; i++)
{
foreach(MacroEvent macroEvent in _events)
{
Thread.Sleep(macroEvent.TimeSinceLastEvent);
switch (macroEvent.MacroEventType)
{
...
The problem I'm having is that if notepad is not already up (not minimized) there is enough delay between setting the foreground window and the macro output that often the first series of commands is not shown. How can I put enough of a pause to make sure that the window is up before the the macros start kicking in? A Thread.Sleep() between SetForegroundWindow() and the for loop does not seem to do the trick. Ideas?
The reason the first series of inputs were being dropped is because I also had to include the command to ShowWindow like so..
in the class header:
private const int SW_RESTORE = 9;
[DllImport("user32")]
private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
...
In the macro thread method I changed
if (proc != null)
{
SetForegroundWindow(proc.MainWindowHandle);
}
to look like:
if (proc != null)
{
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
SetForegroundWindow(proc.MainWindowHandle);
}
Use some api to get the active window, and wait until the window belonging to notepad is the active one
I'm creating an app that is threaded. I started GUI (Announce : Form) as separate thread.
That window will be very minimalistic with one input box and maybe a button. On another thread there will be Tcp Client running and when it gets info from TcpServer it should pass what it gets into that inputbox and show the gui (and topmost windows). After couple of seconds the gui should hide itself and wait for another tcp msg and so on.
public void setTextBox(string varText) {
if (InvokeRequired) {
textBox.BeginInvoke(new textBoxCallBack(setTextBox), new object[] {varText});
} else {
textBox.Text = varText;
}
}
This code is used to fill the textBox from the Tcp Thread. The only problem now is getting the window show and hide properly. Been trying many solutions and always something went wrong. Like:
private void windowStateChange(string varState) {
if (InvokeRequired) {
Invoke(new WindowStateChangeCallBack(windowStateChange), new object[] {varState});
} else {
if (varState == "Hide") {
//Hide();
// TopMost = false;
//TopMost = varState != FormWindowState.Minimized;
} else {
//Show();
//MessageBox.Show("TEST1");
}
}
}
public void windowStateChangeDiffrent(FormWindowState varState) {
if (InvokeRequired) {
Invoke(new WindowStateChangeCallBack(windowStateChange), new object[] {varState});
} else {
WindowState = varState;
// Hide();
TopMost = varState != FormWindowState.Minimized;
}
}
What would be best aproach to do this (and fastest, as time matters)?
Answer 1 which seems to work:
private static void windowStateChange(string varState) {
if (mainAnnounceWindow.InvokeRequired) {
mainAnnounceWindow.BeginInvoke(new StateCallBack(windowStateChange), new object[] {varState});
} else {
if (varState == "Hide") {
mainAnnounceWindow.Hide();
mainAnnounceWindow.TopMost = false;
} else {
mainAnnounceWindow.Show();
mainAnnounceWindow.TopMost = true;
}
}
}
Anything bad about it?
Making the form hide with form.Hide() should pose no problems.
However I've experienced making the form show again does not always work.
So if you're encountering the same problem, you could use something like this:
string RunningProcess = Process.GetCurrentProcess().ProcessName;
Process[] processes = Process.GetProcessesByName(RunningProcess);
int SW_SHOW = 5, SW_HIDE = 0, SW_RESTORE = 9, SW_SHOWNORMAL = 1;
for (int a = 0; a < processes.Length; a++)
{
IntPtr hWnd = processes[a].MainWindowHandle;
ShowWindowAsync(hWnd, SW_RESTORE);
ShowWindowAsync(hWnd, SW_SHOWNORMAL);
ShowWindowAsync(hWnd, SW_SHOW);
SetForegroundWindow((int)hWnd);
}
//Required Win32 API imports
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool ShowWindowAsync(IntPtr windowHandle, int cmd);
[System.Runtime.InteropServices.DllImportAttribute("User32.dll")]
private static extern IntPtr SetForegroundWindow(int hWnd);
you might try
form.Hide();
but make sure that you show/hide the form from the same thread that created them
this.Invoke(new MethodInvoker(this.hide()));
Another approach would be to expose events on your TCP thread object. It could define an event such as RecievedData(...) then the GUI could subscribe to that event and update itself without having to do any InvokeRequired checks etc.
Update: link to C# events tutorial
http://msdn.microsoft.com/en-us/library/aa645739%28VS.71%29.aspx
Form.Hide() is the right method to hide the form. I remember having issues with Form.Show(), I have a vague memory of having to use Form.Activate() as well to get the form to restore correctly.
You're already dealing with thread marshalling correctly (InvokeRequired and Invoke). You could also use Form.BeginInvoke(), which is an async version of Form.Invoke. That might be faster.
I've written a c# application to run an external program and i've redirectet it's output to a richtextbox in my form. I've created the process using the following settings
p1.StartInfo.RedirectStandardOutput = true;
p1.OutputDataReceived += new DataReceivedEventHandler(outputreceived);
and in the outputreceived event
void outputreceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
richTextBox1.Invoke(new UpdateOutputCallback(this.updateoutput),
new object[] { e.Data });
}
}
void updateoutput(string text)
{
int len = text.Length;
int start = richTextBox1.Text.Length;
richTextBox1.Text += text + Environment.NewLine;
richTextBox1.Select(start, len);
richTextBox1.SelectionColor = System.Drawing.Color.White;
richTextBox1.Select(richTextBox1.Text.Length, 0);
richTextBox1.ScrollToCaret();
}
Now the thing is though it is working, but my main form which contains the textbox, hangs if the output is huge from the application. I think each time the invoke call leads to repainting of the form, which happens very frequently. Is there any alternative so that i can see the updates to the textbox as they happen and also keep the form completely active?
Update:
I think I got my answer, I used BeginInvoke when I should have used Invoke.
Update 1:
I tried both BeginInvoke and Suspendlayout but it is not giving me the desired functionality, what happens is that the process has returened all the standardoutput to the string, but the thread which is responsible for updating the text is taking it's own time to print the data. Can i do any thing to it?
Since you've already solved your problem, I'll just note that it will be faster if you use rtb.AppendText (instead of Text += ...) and use pinvoke to scroll to the bottom:
private const int WM_VSCROLL = 0x115;
private const int SB_BOTTOM = 7;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam,
IntPtr lParam);
// ...
// Scroll to the bottom, but don't move the caret position.
SendMessage(rtb.Handle, WM_VSCROLL, (IntPtr) SB_BOTTOM, IntPtr.Zero);
You might want to try
richTextBox1.BeginInvoke()
rather than
richTextBox1.Invoke()
That will at least make the call Asynchronous. Still not sure if that will cause the UI thread to lock while the updates are being painted.
Try to suspend and resume layout of richTextBox1
void updateoutput(string text)
{
try
{
richTextBox1.SuspendLayout();
int len = text.Length;
int start = richTextBox1.Text.Length;
richTextBox1.Text += text + Environment.NewLine;
richTextBox1.Select(start, len);
richTextBox1.SelectionColor = Color.White;
richTextBox1.Select(richTextBox1.Text.Length, 0);
richTextBox1.ScrollToCaret();
}
finally
{
richTextBox1.ResumeLayout();
}
}
Is there any alternative so that i can see the updates to the textbox as they happen and also keep the form completely active?
I think you should use Debug.Print to view what's going on instead.
It is an old post, but maybe somebody still looking for it like I did.
You also can do, like "for(writeToTextbox % 10 == 0)" then invoke.
In this case it will be updated only every 10 times.
UPDATE: sorry for the misspelling! (wirte -> write) and thanks for "HaveNoDisplayName" to show it to me!