C# WinForms ScrollToCaret Buggy? - c#

Is the ScrollToCaret in WinForms buggy?
I have a server that processes messages, a server that allows irc style communication.
I'm working on a WinForms gui that interacts with the server.
The WinForms gui has a component that does reads/writes to the newtork. This component launches a thread to do continuous reading (receive) on the socket. This thread takes a delegate that allows the read message to be processed right away instead of being placed on an internal read queue that is built into the component. Then I have the main form that shows chat messages received and sent, among other things. Chat messages are stored in a RichTextBox. The chat log is update via method of the main form:
private void UpdateChatWindow(string text)
{
lock (rtxt_msgLog)
{
rtxt_msgLog.Text += Environment.NewLine + text;
rtxt_msgLog.SelectionStart = rtxt_msgLog.Text.Length;
rtxt_msgLog.ScrollToCaret();
}
}
The above method is called from the main form as well as the thread that does the continuous reading of a socket.
If I have one GUI window open, the msgLog updates fine, scrolls to bottom. Focus() is not an issue, as the RichTextBox is never in focus as indicated by Focused property which always returns 'false'.
Weirdness stars when I launch a second GUI and log into the server. I start receiving
on the first GUI launched when I do a send from it, while the second GUI works fine - receives the message sent from the first GUI, scrolls correctly to the bottom of the msgLog. After I send a few messages on the Error GUI, the error stops occurring on the first GUI and moves on to the second GUI. Now I receive the Error when trying to do a msg send on the second GUI. While all this is happening. When I send a msg on the first (now a non Error GUI) both GUIs scroll as they should. But when I send a msg on the Error GUI, none of the GUIs scroll, but they do receive the msg.
Interestingly enough, the Error never occurs when writing a received msg to the msgLog, which also makes use of the UpdateChatWindow method kicked off from the component thread I mentioned at the beginning. The send is initiated on the main form, so a separate thread from the one started by the network read/write component.
After typing this post up, I went back to my open GUIs and for the next few tries the Error was jumping between the GUIs, once shows up on the first GUI, next on the second, then back on the first, finally it's back on the second GUI and stays there. While the error was jumping between the GUIs, one GUI scrolled while the other did not, which also changed based on where the error was occurring.
I did see something on this site that might explain this. Is the reason behind my weirdness related to:
"you are not able to directly do UI operations from another thread than the main UI thread"
Or is there more to it?
UPDATE 1
Apparently there are some conflicts on my system, WinXP SP2 64bit. May even have to do with my numerous .Net runtimes installed. GUI works without a hitch on my laptop WinXP SP3 32bit. Have 3 windows open and all work flawlessly. As soon as I open a GUI on my development comp, join the same server as the 3 laptop GUIs are on, the new GUI gets this Error, while the GUIs on the laptop are still fine. Typed in my problem into google and it seemed from the results like it may be a system configuration issue and it looks to be just that :(

Related

Windows Message Queue: How to inspect the nbr of 'posted messages to a process' message queue' from C#

(We are developing an application in C#, WinForms + WPF, VS 2017, .NET Framework 4.7.1+)
We have run into issue regarding this error:
Win32Exception (0x80004005): Not enough quota is available to process this command.
I have read numerous resources about this since:
Mysterious "Not enough quota is available to process this command" in WinRT port of DataGrid
https://social.msdn.microsoft.com/Forums/en-US/d778c6e0-c248-4a1a-9391-28d038247578/mysterious-8220not-enough-quota-is-available-to-process-this-command8221-in-winrt-port-of
https://github.com/dotnet/wpf/issues/137
https://social.msdn.microsoft.com/Forums/en-US/e28dafb1-3ef6-484e-924a-89aff714b3e9/begininvoke-messages-not-getting-stuck?forum=winforms
However, its hard to reproduce the problem so we can figure out what parts of the application is responsible for the posting too much, and I am not clear on how we can detect/inspect/count the message queue, as indicated in the resources linked to above.
We have many UserControls, WinForms, as well as some WPF controls, that all update automatically from the remote server. All of the UCs contribute to posting messages to the queue, so its not about just inspecting one UC, but its the sum of all UCs that creates this problem.
Here are my questions:
There is one message queue per Window, so if we are developing a desktop C# WinForms application, there is one and only one message queue for the entire app, correct?
Is there a way, in code (C#), to look at the Message Queue and see if we are approaching the limit, 10 000 / second? We'd need some base counter, that can keep counting/checking regardless of where we are in the application, and preferably, see what part of the code is mostly responsible for this.
Is there a difference between UI elements being "rendered in an async manner" vs sync? In the second link, its said that "There are too many UIElements being rendered in an async manner here". I have code that does this.Dispatcher.Invoke, so that's sync, right? I don't see why that would matter, its still a message posted to the queue?
In one UC, we use Dispatcher.Invoke and set the System.Windows.Threading.DispatcherPriority to SystemIdle. What does that actually do?
I also wanted to share one method in one of the many UserControls. This is an WPF control, and this method is called to update GUI. Its called many times, for every element or update. What we did, however, is that this method will not return until done is true. Does this mean that the message queue has processed and handled the message, and is no longer on the queue, since we are using this.Dispatcher.Invoke, so its a sync message?
void RunOnGUIThread(Action callback)
{
bool done = false;
if (this.Dispatcher.Thread != System.Threading.Thread.CurrentThread)
{
this.Dispatcher.Invoke(delegate
{
try
{
callback.DynamicInvoke(null);
done = true;
}
catch (Exception e) { }
}, System.Windows.Threading.DispatcherPriority.SystemIdle);
}
else
{
callback.DynamicInvoke(null);
done = true;
}
while (!done)
System.Threading.Thread.Sleep(1);
}
Generally, we have a lot of information in the desktop app, some in fixed menus that is always visible, other controls are loaded as they navigate the application etc. A lot of data gets automatically updated from the remote server if something happens to the data, so its worth to again point out that the message queue probably is affected by a lot of different UCs and updated on-the-fly, in real-time.
Any other tips how to debug or handle this in Visual Studio 2017 / C#?

Why an infinite loop makes a program non-responsive?

In a windows desktop application (windows forms), I've created a while loop in (c#):
bool stop = false;
while (!stop){
...
// code to update a label that displays the number of times the loop executed.
...
}
When a button is clicked, "stop" is made true and the loop will stop.
However, I don't get the chance to click the button after the application runs because it becomes unresponsive.
that's one thing.
The other thing that puzzles me more is why the label is not showing the number of loops. It's like the program is just looping and doing nothing.
If you are wondering, I'm trying to write code for a game loop.
Windows applications work by responding to Windows messages.
All WinForms events actually come from Windows messages like WM_PAINT or WM_MOSUEMOVE.
The .Net Framework's Application class runs a message loop which asks Windows for the next message, then processes the message as appropriate.
For example, if the user clicks a button, Windows sends your program a WM_CLICK message. .Net's message loop converts this into a Click event and runs any event handlers that you registered.
While your code is running, the application is busy responding to whatever message it received, and it cannot respond to messages.
Therefore, it appears frozen.
You should replace your loop with a Timer component.
Move the loop body into the Tick handler and set the Interval to something like 20 or 50.

Sending WM_COPYDATA but getting WM_ACTIVATEAPP on the receiving side

I have the following scenario:
My app gets some data from the command line.
After getting executed by the first time, my app runs always one instance and that instance will be in memory until the use explicitly tells it to shutdown instead of just hiding the form when not needed.
When the user tries to run the app a second time, the process starts, checks if there is another one in memory and if that is true, it sends a WM_COPYDATA message to the process in memory with the data it got from the command line and exits.
That all works well when the it«s the user who runs the app.
I needed to ran it from the Microsoft Word 2003 toolbar so i used a "Add-in" for that. The problem is that when my app is started from that Add-In (using Process class), it seems that the process already in memory gets a WM_ACTIVATEAPP message instead of a WM_COPYDATA one, so i can't get the needed data sent from the process started by the Add-In.
I have no idea on why is that happening and how to fix it. I've googled for hours and nothing helped...
Can anyone help?
Getting a WM_ACTIVATEAPP message is quite normal, part of the usual notifications that Windows sends. Don't assume that the first message you'll get is WM_COPYDATA, keep looking. If you don't get it at all then the window handle that you used to send the message was wrong. Which is a very common problem, it is not that easy to accurately find a window back.
The .NET framework already has very good support for single-instance apps that can retrieve the command line from a second instance. Consider using it instead. Check this blog post.

C# on .net Mobile Framework - Windows form property won't change

I'm writing a c# application that requires user authentication.
When the user hits the log in button, quite a bit of stuff is done in the background, but I'm having trouble informing the user that something is happening, and that the program isn't just frozen.
I have some hidden text fields that I would like to have show up after they log in, while this stuff is running, but I can't seem to get it to show up.
Basically, when the user hits the log in button, it checks to see if their credentials are correct, then the messages should show up, and then either some other functions might run, followed by a different form being shown.
After the credentials are checked, and I know that the user is valid, I tried running this:
please_wait.Visible = true;
But it doesn't change when it gets to that point in the code.
I've tried threading it, to see if that would help. Instead of calling the above line, I just start a thread that does it.
That doesn't work either. The field still doesn't show up.
If I return out of the function right after I either start the thread or change the Visible property, it works just fine.
How do I get it to work fine and have more code run after the change?
The problem isn't that you need to update the UI from a background thread. Rather, it's that you should be performing your long-running task in a background thread and marshalling updates to the foreground. This is frequently done via a BackgroundWorker with progress notification (on a progressbar, for example).
Basically, your UI foreground thread is either loaded or blocked doing work, so it isn't handling messages in its message queue to update your user interface.
Along with what Greg recommends (which is certainly the first step if you're not already doing it) you may also need to give up some quantum.
If you are taxing the scheduler, it's possible that the UI updates (which are pretty low priority) may be getting preempted until your worker is complete. Adding an Application.DoEvents (or maybe Thread.Sleep(1) in the background thread) could give the UI a little scheduler time to paint.
Have you tried adding a call to Application.DoEvents()? It's a hack, but it's all you sometimes need.

C# cross thread dialogue co-operation

K I am looking at a primarily single thread windows forms application in 3.0. Recently my boss had a progress dialogue added on a separate thread so the user would see some activity when the main thread went away and did some heavy duty work and locked out the GUI.
The above works fine unless the user switches applications or minimizes as the progress form sits top most and will not disappear with the main application. This is not so bad if there are lots of little operations as the event structure of the main form catches up with its events when it gets time so minimized and active flags can be checked and thus the dialog thread can hide or show itself accordingly.
But if a long running sql operation kicks off then no events fire. I have tried intercepting the WndProc command but this also appears queued when a long running sql operation is executing. I have also tried picking up the processes, finding the current app and checking various memory values isiconic and the like inside the progress thread but until the sql operation finishes none of these get updated. Removing the topmost causes the dialog to disappear when another app activates but if the main app is then brought back it does not appear again.
So I need a way to find out if the other thread is minimized or no longer active that does not involve querying the actual thread as that locks until the sql operation finishes.
Now I know that this is not the best way to write this and it would be better to have all the heavy processing on separate threads leaving the GUI free but as this is a huge ancient legacy app the time to re-write in that fashion will not be provided so I have to work with what I have got.
Any help is appreciated
It sounds as if the long running operation is bound to the progress dialog? That's usually a bad idea and I wonder whether the progress can be showed at all.
However you should consider using a BackgroundWorker for your long running operations. So your GUI (the main form as well as the progress dialog stays alive).
This way you should be able to send the minimize event of the main form to the progress dialog which can react to it instantly.
Btw. the BackgroundWorker supports progress reports on its own.

Categories

Resources