DataGrid, Generic GDI+ errors, and a Big Red X
-
Hi, I'm populating a DataTable in a DataGrid via asynchronous callbacks. Right now my program is single-threaded and I add each "event" (actually a MSMQ msg) to a Queue using lock in the PeekCompletedHandler. I process MSMQ peeks by firing a timer every 3 seconds and, if the queue has events, dequeue the msgs with lock. Sometimes the program will run for hours with no problems. Sometimes I get an exception saying a Generic GDI+ error has occurred and the DataGrid's screen real-estate is blanked-out and has a big red "X" throught it. I can't figure out what it causing this. Does anybody what is wrong and how I fix this? TIA, Matt
-
Hi, I'm populating a DataTable in a DataGrid via asynchronous callbacks. Right now my program is single-threaded and I add each "event" (actually a MSMQ msg) to a Queue using lock in the PeekCompletedHandler. I process MSMQ peeks by firing a timer every 3 seconds and, if the queue has events, dequeue the msgs with lock. Sometimes the program will run for hours with no problems. Sometimes I get an exception saying a Generic GDI+ error has occurred and the DataGrid's screen real-estate is blanked-out and has a big red "X" throught it. I can't figure out what it causing this. Does anybody what is wrong and how I fix this? TIA, Matt
If you're setting (and even getting sometimes) properties or calling methods on controls from your asynchronous callbacks, are you using
Control.Invoke
? It's important that when modifying a control from another thread (which async calls are on) that you make the changes in the thread on which the control was created. See theControl.Invoke
method andControl.InvokeRequired
property documentation in the .NET Framework SDK for more information. Why does it works sometimes? It's not absolute behavior to fail or succeed (well, not in all cases) if you do modify a control from a different thread than what it was created on. It basically comes down to the Windows messaging system that is used for Windows Forms and the threads that the message pumps are running on. More information can be found in the .NET Framework SDK. Another possibility is that you're not disposing disposable objects. While you'll typically run into memory problems, other strange things can happen as well (like depleating available handles). Telling us exact what the error was may help.Microsoft MVP, Visual C# My Articles
-
If you're setting (and even getting sometimes) properties or calling methods on controls from your asynchronous callbacks, are you using
Control.Invoke
? It's important that when modifying a control from another thread (which async calls are on) that you make the changes in the thread on which the control was created. See theControl.Invoke
method andControl.InvokeRequired
property documentation in the .NET Framework SDK for more information. Why does it works sometimes? It's not absolute behavior to fail or succeed (well, not in all cases) if you do modify a control from a different thread than what it was created on. It basically comes down to the Windows messaging system that is used for Windows Forms and the threads that the message pumps are running on. More information can be found in the .NET Framework SDK. Another possibility is that you're not disposing disposable objects. While you'll typically run into memory problems, other strange things can happen as well (like depleating available handles). Telling us exact what the error was may help.Microsoft MVP, Visual C# My Articles
Heath, Thanks for your reply. Re: InvokeRequired; my program does not start any threads. My PeekCompletedHandler function is appended to the end of my post. Also, when I get the "Generic GDI+ exception" I don't see any specific info about the error in the MessageBox. How do I get that info so I can post it here? -Matt ============================================================= private static void MQPeekCompletedHandler(Object source, PeekCompletedEventArgs peekArgs) { MessageQueue mq = null; try { mq = (MessageQueue) source; if(mq == null) { MessageBox.Show("Source was null", "MQPeekCompletedHandler()"); return; } if(peekArgs.AsyncResult.IsCompleted == false) { MessageBox.Show("IsCompleted=false", "MQPeekCompletedHandler()"); return; } System.Messaging.Message msg = mq.EndPeek(peekArgs.AsyncResult); IceEvent oEvent = new IceEvent(mq, msg); if((oEvent.IsEventValid) && (s_nvcMsgsSeen[oEvent.Identifier] == null)) { s_nvcMsgsSeen[oEvent.Identifier] = "Y"; lock(s_MQEventQueue.SyncRoot) { s_MQEventQueue.Enqueue(oEvent); } } } catch(System.Messaging.MessageQueueException smmqe) { // The IOTimeouts occur because, as per the MS MSMQ Best Practices, we timeout our // Asnychronous BeginPeek() methods. if(smmqe.MessageQueueErrorCode != System.Messaging.MessageQueueErrorCode.IOTimeout) { throw new System.Exception("Unexpected MessageQueue exception", smmqe); } } catch(System.Exception se) { MessageBox.Show(se.GetType().ToString() + ": " + se.Message, "Exception occurred in MQPeekCompletedCallback()"); } finally { if(mq != null) { mq.BeginPeek(new TimeSpan(0,1,0)); } } } ==============================================================
-
Hi, I'm populating a DataTable in a DataGrid via asynchronous callbacks. Right now my program is single-threaded and I add each "event" (actually a MSMQ msg) to a Queue using lock in the PeekCompletedHandler. I process MSMQ peeks by firing a timer every 3 seconds and, if the queue has events, dequeue the msgs with lock. Sometimes the program will run for hours with no problems. Sometimes I get an exception saying a Generic GDI+ error has occurred and the DataGrid's screen real-estate is blanked-out and has a big red "X" throught it. I can't figure out what it causing this. Does anybody what is wrong and how I fix this? TIA, Matt
I ran into a similar problem before. I understood that I had to update the control on the thread that created it and I wrote a queue mechanism to do this. However, I was surprised to see that the asynchronous callback from the event ran on different thread. (Now its obvious but it was unclear to me then.) Try setting the thread name property on the form where you create your datagrid. Then do a simple debug assert against this thread name property in the asynchronous callback to ensure that you are updating your datagrid on the correct thread. This will at least tell you whether or not your async callback is on the same thread. (My guess is that it isn’t but I’m still new to this .Net stuff) //Place this in the form init Thread.CurrentThread.Name ="UI thread"; //Place this in the async callback Debug.Assert(Thread.CurrentThread.Name =="UI thread"); Heath’s recommendation about Invoke is sound advice.
-
Heath, Thanks for your reply. Re: InvokeRequired; my program does not start any threads. My PeekCompletedHandler function is appended to the end of my post. Also, when I get the "Generic GDI+ exception" I don't see any specific info about the error in the MessageBox. How do I get that info so I can post it here? -Matt ============================================================= private static void MQPeekCompletedHandler(Object source, PeekCompletedEventArgs peekArgs) { MessageQueue mq = null; try { mq = (MessageQueue) source; if(mq == null) { MessageBox.Show("Source was null", "MQPeekCompletedHandler()"); return; } if(peekArgs.AsyncResult.IsCompleted == false) { MessageBox.Show("IsCompleted=false", "MQPeekCompletedHandler()"); return; } System.Messaging.Message msg = mq.EndPeek(peekArgs.AsyncResult); IceEvent oEvent = new IceEvent(mq, msg); if((oEvent.IsEventValid) && (s_nvcMsgsSeen[oEvent.Identifier] == null)) { s_nvcMsgsSeen[oEvent.Identifier] = "Y"; lock(s_MQEventQueue.SyncRoot) { s_MQEventQueue.Enqueue(oEvent); } } } catch(System.Messaging.MessageQueueException smmqe) { // The IOTimeouts occur because, as per the MS MSMQ Best Practices, we timeout our // Asnychronous BeginPeek() methods. if(smmqe.MessageQueueErrorCode != System.Messaging.MessageQueueErrorCode.IOTimeout) { throw new System.Exception("Unexpected MessageQueue exception", smmqe); } } catch(System.Exception se) { MessageBox.Show(se.GetType().ToString() + ": " + se.Message, "Exception occurred in MQPeekCompletedCallback()"); } finally { if(mq != null) { mq.BeginPeek(new TimeSpan(0,1,0)); } } } ==============================================================
Again, asynchronous calls - which you said you used - run in a different thread. How else do you think they are not blocking calls? You invoke the call on a different thread, whether you know it or not (and you really should). What I said about
Control.Invoke
still applies. You should read Including Asynchronous Calls[^] in the .NET Framework SDK for more information. As far as the GDI+ error, when and where in your code do you see it? You should step through and determine where it's being thrown. If it's not, it's probably thrown from the message pump I mentioned before. Certain errors like that are difficult to trace.Microsoft MVP, Visual C# My Articles
-
Again, asynchronous calls - which you said you used - run in a different thread. How else do you think they are not blocking calls? You invoke the call on a different thread, whether you know it or not (and you really should). What I said about
Control.Invoke
still applies. You should read Including Asynchronous Calls[^] in the .NET Framework SDK for more information. As far as the GDI+ error, when and where in your code do you see it? You should step through and determine where it's being thrown. If it's not, it's probably thrown from the message pump I mentioned before. Certain errors like that are difficult to trace.Microsoft MVP, Visual C# My Articles
Heath, Thank you for your assistance. I was aware (from a previous problem that you helped me with on CodeProject) the need for Control.Invoke and multiple threads. I was not aware that the asynchronous callback was implicitly its own thread althought this makes sense now. I do need to read "Including Asnych Calls" in .NET SDK... thanks for the link. @@@@ 1 More question @@@@@ I guess my job as a programmer is not to know at compile time which thread a given method is executing in and, hence, the need for InvokeRequired and Control.Invoke. Finally, my question. Why can't .NET make the "InvokeREquired" check for? Granted programmer's would get "generic" processing but then they could override the default .NET processing. Or am I missing something here? TIA, Matt
-
Heath, Thank you for your assistance. I was aware (from a previous problem that you helped me with on CodeProject) the need for Control.Invoke and multiple threads. I was not aware that the asynchronous callback was implicitly its own thread althought this makes sense now. I do need to read "Including Asnych Calls" in .NET SDK... thanks for the link. @@@@ 1 More question @@@@@ I guess my job as a programmer is not to know at compile time which thread a given method is executing in and, hence, the need for InvokeRequired and Control.Invoke. Finally, my question. Why can't .NET make the "InvokeREquired" check for? Granted programmer's would get "generic" processing but then they could override the default .NET processing. Or am I missing something here? TIA, Matt
For synchronous execution in a single-threaded application, that would be a complete waste of time. You could something like I did, however. Have a method that accepts some information about what method you want to call or what property you want to get/set. Also accept a delegate (
Delegate
- nothing specific). That method would checkInvokeRequired
and conditionally execute thatDelegate
either usingControl.Invoke
(to execute it on the creation thread) orDelegate.Invoke
(to execute it on the calling thread).Microsoft MVP, Visual C# My Articles