Updating Textbox Text

G

Guest

Hi

A while ago I got some help in writing a mock console. The idea was to write a VB app that contained a textbox, then run a process from within the app and redirect the stdout to the textbox. My problem was that textboxes have maximum length limits, so I had to write a function to erase some of the text when it started getting full. That appears to work, but I am experiencing odd behaviour when the textbox text gets long. When it got to around 22000 characters, it suddenly stopped updating, and then randomly it started updating again after a minute or so. So, if the numbers coming from the console were numbered, it would have looked like this
2223
2223
2229
2229
...
I was missing those lines that had gone by in the minute where the textbox "froze"

It did this again around 33000 - the max is around 100000. I am quite baffled and if anyone has an idea as to what is going wrong I would appreciate his/her help

Thanks
Tim.
 
D

Derek Harmon

Tim said:
I am experiencing odd behaviour when the textbox text gets long. When it got to
around 22000 characters, it suddenly stopped updating, and then randomly it
started updating again after a minute or so. : :
It did this again around 33000 [...] I am quite baffled and if anyone has an idea
as to what is going wrong I would appreciate his/her help.

I speculate it might be the garbage collector. It sounds like your application is
performing a lot of string processing, and as strings are immutable the number
of discarded string objects can grow quickly.

As an experiment, I'd suggest calling GC.Collect( ) more frequently in the app
when the garbage that is laying around is small and manageable.

There are two reasons this isn't a good idea. Each time the garbage collector
runs to pick up the trash, it suspends all threads, so just running a collection may
cause you to miss a few numbers if its not done frequently enough. Another
reason you wouldn't ordinarily want to call for a collection is that the GC optimizes
itself, and forcing premature collections is generally more wasteful of processor
resources than productive.

However, if these minute-long lapses disappear when you run the application with
more frequent garbage collections, then that would suggest it is garbage collection
that is the problem.

At that point (if it is the GC), you'll want to look at why the application is creating so
much garbage, and construct a plan for disposing of it.


Derek Harmon
 
S

Scott M.

I would use a StringBuilder and test it before I started calling GC.collect.

The StringBuilder is designed for managing strings that will be manipulated
often because of the immutability of the String class.

When you call GC.Collect, you bring the garbage collector into the same
thread as your application, rather than letting it work in its own thread.
In some cases, this can actually slow your app. down.


Derek Harmon said:
I am experiencing odd behaviour when the textbox text gets long. When it got to
around 22000 characters, it suddenly stopped updating, and then randomly it
started updating again after a minute or so. : :
It did this again around 33000 [...] I am quite baffled and if anyone has an idea
as to what is going wrong I would appreciate his/her help.

I speculate it might be the garbage collector. It sounds like your application is
performing a lot of string processing, and as strings are immutable the number
of discarded string objects can grow quickly.

As an experiment, I'd suggest calling GC.Collect( ) more frequently in the app
when the garbage that is laying around is small and manageable.

There are two reasons this isn't a good idea. Each time the garbage collector
runs to pick up the trash, it suspends all threads, so just running a collection may
cause you to miss a few numbers if its not done frequently enough. Another
reason you wouldn't ordinarily want to call for a collection is that the GC optimizes
itself, and forcing premature collections is generally more wasteful of processor
resources than productive.

However, if these minute-long lapses disappear when you run the application with
more frequent garbage collections, then that would suggest it is garbage collection
that is the problem.

At that point (if it is the GC), you'll want to look at why the application is creating so
much garbage, and construct a plan for disposing of it.


Derek Harmon
 
C

Cor

Hi Tim

Some pseude code
\\\\
Dim sb as new system.text.stringbuilder
myreader procedure or whatever
sb.append(mytextfromreader)
end of my reader procedure
mytextbox.text sb.tostring
///

I hope this helps,

Cor
A while ago I got some help in writing a mock console. The idea was to
write a VB app that contained a textbox, then run a process from within the
app and redirect the stdout to the textbox. My problem was that textboxes
have maximum length limits, so I had to write a function to erase some of
the text when it started getting full. That appears to work, but I am
experiencing odd behaviour when the textbox text gets long. When it got to
around 22000 characters, it suddenly stopped updating, and then randomly it
started updating again after a minute or so. So, if the numbers coming from
the console were numbered, it would have looked like this:
22234
22235
22298
22299
...
I was missing those lines that had gone by in the minute where the textbox "froze".

It did this again around 33000 - the max is around 100000. I am quite
baffled and if anyone has an idea as to what is going wrong I would
appreciate his/her help.
 
S

Scott M.

Well, you didn't provide us your code, so I can't really say for sure.

If you are making changes to a string, use a stringbuilder instead since
strings are immutable and manipulation of them eats up space on the heap.

Dim sb as New StringBuilder("Initial String")
sb.append("more stuff")
sb.append("even more stuff")
 
G

Guest

Ok, here is some basic code with what I am trying to do

Dim letter As Integer = m_Process.StandardOutput.Read(
Dim strLine As Strin
Dim output As StringBuilder = New StringBuilder(""
Tr
If letter = 10 The
strLine = ControlChars.NewLin
Els
strLine = Convert.ToChar(letter
End I

Do While strLine.Length >=
If strLine.Length <> 0 The
output.Append(strLine
If strLine = ControlChars.NewLine Or strLine.Equals(".") The
Console.AppendText(output.ToString
output.Remove(0, output.Length
End I
End I

letter = m_Process.StandardOutput.Read(
If letter = 10 The
strLine = ControlChars.NewLin
Els
strLine = Convert.ToChar(letter
End I
Loo
Catc
'process close
End Tr

I am reading each character from the output stream (converting to ControlChars.Newline if it is a Newline). I previously wrote them to the textbox directly - but this caused an annoying flickering from constant redraws. So, I write the letters to a StringBuilder until I hit a newline or "." (the "." exists at times where I want to make sure the output is shown and there is no newline). I believe my problem is how the Textbox.Text property works - it is a String. So I think that is where the main memory "leak" is coming from that takes the GC so long to handle it. If only I could just connect a stream with a textbox or other control directly

Thank you for your help
Tim
 
G

Guest

That is the gist of what I am trying to do, I added the StringBuilder in to replace one of the Strings I was working with. My problem is that the Stream does not end until the process I am running ends (see my code in the 7:27 post - sorry for not posting it earlier).

Thank you,
Tim
 
C

Cor

Hi Tim,

Is this not a very slow and giving a mem overflow
Dim letter As Integer = m_Process.StandardOutput.Read()
Dim strLine As String
Dim output As StringBuilder = New StringBuilder("")
Try .........................................................................
If letter = 10 Then
strLine = ControlChars.NewLine
Else
strLine = Convert.ToChar(letter)
End If
..........................................................................
chr 10 is the linefeed character, therefore there is no need to do that
above however I saw you want to give a linefeed after the dot so this can be
If letter = ASC(".") Then
strLine = chr(letter) & chr(10)
Else
strLine = chr(letter)
End If
Do While strLine.Length >= 0
If strLine.Length <> 0 Then
output.Append(strLine) ...............................................................
If strLine = ControlChars.NewLine Or strLine.Equals(".") Then
Console.AppendText(output.ToString)
output.Remove(0, output.Length)
End If
End If
..........................................................
This has to be at the end of the complete routine in my opinion, you create
a linefeed so that cannot be the problem.
letter = m_Process.StandardOutput.Read() ------------------------------------
If letter = 10 Then
strLine = ControlChars.NewLine
Else
strLine = Convert.ToChar(letter)
End If
--------------------------------------
Here again that code to get after a dot a linefeed
If letter = ASC(".") Then
strLine = chr(letter) & chr(10)
Else
strLine = chr(letter)
End If
Loop
Catch
'process closed
End Try

Console.appendtext(output.Tostring)

If I did not make typos and understand you well, this should do it I think?

Cor
 
G

Guest

Hi
I know this is not an ideal algorithm, but there does not seem to be any other way
If letter = ASC(".") The
strLine = chr(letter) & chr(10
Els
strLine = chr(letter
End I

I do not really want to add a newline after a ".", I just want to refresh the textbox
"Doing something...." (add this to the textbox) "SUCCESS\n" (add this to the textbox

Unfortunately, performing a chr(letter) when letter is a linefeed does not result in a newlin
being shown in the textbox - the VB TextBox control seems to ignore linefeeds. The only wa
I could get a newline to show up was to use ControlChars.Newline instead
This has to be at the end of the complete routine in my opinion, you creat
a linefeed so that cannot be the problem

If I do not keep this code in the loop(ie, put it at the end of the routine after all data has been read from the stream), the textbox(acting as the mock console) will not be updated until the process finishes. The plan is to wait for data to come in on the OutputStream (m_Process.StandardOutput.Read()) and when it does, buffer it until a newline or "." is received, then append that to the textbox for the user to receive, and wait for more data. If the text is not buffered, the textbox redraws after every character is appended to it and flickers

I am not sure if there is a way around this problem because of the way the TextBox(and RichTextBox) control is set up. It does not seem that they are efficient enough to process constant updates like that

Thanks
Ti
 
C

Cor

Hi Tim,

The 10 is not linefeed, I did tell it wrong it has to be the carriage return
13 that does it, so you can keep that code you had. (Although I think that
the vbcrlf better is to test instead of that 10 or 13)

I changed that, because it did seems to me useless, but that was only
cosmetic, however the big problem for your procedure is in my opinion in
this part.

If strLine = ControlChars.NewLine Or strLine.Equals(".") Then
Console.AppendText(output.ToString)
output.Remove(0, output.Length)
End If

That you have to remove. With this your stringbuilder is absolutly useless.

Try it with deleting it and put the append from the sb.string on the end of
the procedure.


Cor
 
G

Guest

Hi

I agree with you, with how it works right now, the StringBuilder is useless. The problem is that if I move that statement to the end of the procedure, what I am trying to achieve will be useless. The Output Stream will remain open until the process closes. The process I am redirecting is a server, and is meant to run for days - outputting information to the console when people join and exit. If I do not update the textbox until the OutputStream is done, I will not get the information from the server until the server closes

The loop: Do While strLine.Length >= 0, keeps running while the OutputStream is open (while the server process is running). I have to update the textbox at some point in this loop or I will not get the Output until the server is closed days, even weeks later

Do you know of any other control that could handle constant text updates like the Windows Console

Thanks
Tim
 
T

Tom Leylan

Tim... I've glanced through the messages and I'll admit I don't quite know
what you are trying to do... but, from what I've understood it sounds like
the approach is wrong.

Bear with me but what's the point of a textbox with a very long text string
again? You want to monitor activity, you want to report that activity
neither of those actions depends upon a textbox.

Again, given my limited understanding... it sounds like you need to separate
the actions. Something (an object of some sort) should be storing the
values it is receiving from the server. They will be displayed of course
but regardless if they were or weren't at least you are gathering them up.
My suggestion would be not to gather them as one continous string. They're
messages of some sort right?

Add an Id and a timestamp and keep them as separate message objects. You
can then display them, archive them to disk, transmit them somewhere else or
destroy them on a case-by-case basis.

Then you need a way to view them. You can view them in some sort of
listbox. You can view "all" or limit the view to the last 20 message
objects or whatever else strikes your fancy. Listbox doesn't work? View
them in something else... you've got message objects now, use them.

Is this helpful at all or have I completely misunderstood the problem? [In
which case] "never mind" :)

Tom Leylan

Tim said:
Hi,

I agree with you, with how it works right now, the StringBuilder is
useless. The problem is that if I move that statement to the end of the
procedure, what I am trying to achieve will be useless. The Output Stream
will remain open until the process closes. The process I am redirecting is
a server, and is meant to run for days - outputting information to the
console when people join and exit. If I do not update the textbox until the
OutputStream is done, I will not get the information from the server until
the server closes.
The loop: Do While strLine.Length >= 0, keeps running while the
OutputStream is open (while the server process is running). I have to
update the textbox at some point in this loop or I will not get the Output
until the server is closed days, even weeks later.
 
G

Guest

Hi Tom

I haven't thought about that approach - it may work

The server runs and prints messages out to the Windows Console (Console.WriteLine()) such as "client joined", "client did this", "client did that". The purpose of creating this VB mock console was to provide a box to type in server commands that was separate from the output window. If the Windows Console is constantly being spammed with server messages - it is quite difficult to type out a command since it gets broken up by Console updates. I had thought I could just redirect this output to another control that would just mirror the Windows Console output - but as I have found, there are no VB controls that can act like the Windows Console does

To store them in a data structure and them provide viewing of them when requested could work. And I haven't thought about the ListBox. That could also provide for filtering (view all Error messages, view all Connection Requests). I will have to think about this some more, thank you for your comments

-Tim
 
D

Derek Harmon

Scott M. said:
Well, you didn't provide us your code, so I can't really say for sure.

Hmm, so maybe he should run a test with a GC.Collect( ) call in there
to verify that is the problem before going off and re-writing his string
handling? :)


Derek Harmon
 
S

Scott M.

....Or, maybe he should write the string handling in an efficient way and see
if it works without GC.Collect? IMHO (and in the humble opinion of a great
many developers including those at MS), calling GC.Collect should be a last
resort and not a normal part of the programming process.

:)
 
G

Guest

How would you recommend writing a more efficient algorithm? (I tried calling GC.Collect() and it did not help the problem - it still would freeze after the length of the Textbox.Text got long

Thank you
Tim
 
S

Scott M.

Well, I guess my last comment was a more philosophical response. My point
was that calling GC.collect should only be done as a last resort and not
part of the normal programming process because it interferes with the normal
operations of the GC as well as pulling the workload of the GC into the same
thread as the application that called it. Normally, the GC works in its own
thread.


Tim said:
How would you recommend writing a more efficient algorithm? (I tried
calling GC.Collect() and it did not help the problem - it still would freeze
after the length of the Textbox.Text got long)
 
G

Guest

Oh, ok. Yes, I agree with you. That is part of the reason I believe I am going to switch to the implementation that Tom Leylan recommended

Thanks
Tim
 
C

Cor

Hi Tim,

My answer would not have much different from Tom,

But one thing I want to add.

It seems if you are saving your data in the textbox of your client.
When it is powered of, it is gone.

I think you need to made a message system, where your store your messages
and read it from there.

My approach would be a database from what kind, and send it as hourly text
in that (after a certain string break).

In my opinion will this approach only lead you to every time more
difficulties.

I hope this helps?

Cor
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top