Groups | Blog | Home
all groups > dotnet drawing api > june 2005 >

dotnet drawing api : I think I know why setstyle double buffering cases invalidOperationExceptions!


Bob Powell [MVP]
6/20/2005 12:00:00 AM
This is interesting because I've suspected for some time that double
buffering is broken and I've seen exceptions due to the use of automatic
double buffering on several occasions. It would be great to nail this down
and post it as a real bug report with MS.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.





[quoted text, click to view]

Josh
6/20/2005 12:00:00 AM
Hi Garry,

Just thought I'd let you know that I downloaded your screensaver and swapped
out the insects class and put in a version of Pong. It was quite easy to do
( as you suggested in the Code project article ).

Works really well. Slows up considerable on dual monitors, but I'm guessing
thats my problem not yours.

Thanks for sharing your code!

Garry Freemyer
6/20/2005 12:22:45 AM
Here is some of the points and observations about my screensaver, where I am
being tortured by the InvalidOperationException raised from
System.Graphics.EndContainer(). Here is what I think is happening. Does the
stack trace below bear this out? Is there a method to tell the control when
to copy it's backbuffer to the screen, and when NOT to, but to wait?

1. The manual double buffering never crashes. In this kind of buffering
there is no double buffer between the graphics object, and the display, and
so there is no guess work about when the drawn lines and Images that are
loaded and displayed because they are loaded when the paint event argument
is told to load the image, it displays it right away.

2. I noticed that even tho I specify a particular swarm to appear on screen,
instead of appearing within seconds of each other because the drawing is
fast. So, things drawn are not displayed immediately when using the SetStyle
DoubleBuffer, true);

3. The contol's built in double buffering is automatic in that the control
has a backbuffer, that you have little control over! In this scenario the
control has to guess when it's time to copy the backbuffer to the display
and also guess when it's time to clear it's backbuffer and the only metric
it has, is a couple of hints in the flush method, and the next slot of idle
time.

So, that's it!! The control under double-buffering via the setstyle is
deciding to flush the buffer out to the screen just before my program tries
to start drawing to it! The control tries to end container on the bitmap,
but the bitmap is still being drawn to!

This would explain the delaytime where I have seven swarms on the screen,
but one only appears every 4 seconds, till they are all on-screen.

If this is raising an event, that eventually turns into a paint event, then,
the thread could be blocking itself by invoking threads.

So, the event is being caused by the bad guesswork about when a buffer is to
be drawn and disposed of! In every error there is this message about
something trying to access the buffer while it's being disposed!

Since this drawing is so fast, it also explains why it can take hours for
the exception to show its ugly face because the program is like a slightly
drunken person tripping over it's own feet!

I'm a bit surprised that there aren't or that I've not found in any
double-buffering articles, a command to tell the graphic to stop guessing
around and copy and dispose of the backbuffer when I tell it that it's time
to.

Here is the trace ...

See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.

************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere.
at System.Drawing.Graphics.EndContainer(GraphicsContainer container)
at
System.Windows.Forms.DibGraphicsBufferManager.ReleaseBuffer(GraphicsBuffer
buffer)
at System.Windows.Forms.GraphicsBuffer.Dispose()
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg,
IntPtr wparam, IntPtr lparam)



--
To reply to this message take off the .NoJunkSpam off of the end of
garryfre@pacbell.net.NoJunkSpam

Garry Freemyer
6/20/2005 7:11:38 AM
I am glad you were able to use it so easily! Thanks, you made my day! :)

[quoted text, click to view]

Garry Freemyer
6/20/2005 7:57:18 AM
Well, I suppose the way to nail it down would be to create a small version
of this that just does a whole lot of grapics, I mean really stress the
double-buffering, and show that it fails, or feel free to pack up my code to
the screensaver and send it to them, there is a config option, where I chose
auto-double buffering or manual buffering, and the first works, the second
breaks horribly.

By the way, InvalidOperationException, is NOT caught by a catch
InvalidOperationException at all and those catches that do catch it have the
exception stolen from them by the runtime. It's snatched away from their
program by the runtime, which to me is a terrible thing to do to a
programmer. I can only PARTIALLY catch it with a regular empty catch that I
know I know it caught the error because the showcursor is executed, but the
rest fails to execute because after the first line in the catch statement is
execute and before the next line, the runtime kidnaps my error handling by
somehow taking control away and putting up that cursed dialog box. Really, I
see no excuse for a runtime kidnapping my code execution and fingering my
code as if I did not write an exception handler at all - a cardinal sin of
programming.

It would only take a program that really stresses the system and
demonstrates it in three modes ...

1. Double-buffering showing the breakage, the kidnapping of the error
handler execution.
2. Auto Double Buffering showing that the program works. These two my
program can do.
3. Showing how using flat 32 unmanaged code that gives ME control over when
the backbuffer is drawn to the screen and when it's released solves the
problem.

The fact that GDI+ take away my ability to control when the backbuffer is
applied, ought to be proof enough that the GDI isn't so much as broken, but
is merely missing a vital piece of functionality. There is also the proof in
the documentation itself that either the documentation is wrong or GDI+
stands for blank blank irritating and I bet you can guess what the first two
words are.

The closest to controlling when the backbuffer is written appears to be the
flush command, but it contains all EXCEPT what I need because of the missing
feature ...

1. Flush which strangely tells the system to start to write the backbuffer,
BUT does so assynchroneously and yet says the command syncs the graphics
objects?!!?? What is syncroneous about an assync operation? What This sets
the program up for a blunder right then and there because then all I need is
three with lines with auto buffering on...

pev.Graphics.DrawSomethingbig(); // Draw something that will take a long
time to draw.
pev.Graphics.Flush(FlushIntention.Flush); // Start the drawing from the
backbuffer to the screen...
pev.Graphics.DrawAnything(); // Cause the InvalidOperationException error by
trying to draw to the backbuffer while it is in an innaccessible state
because it is being read or closing.

2. The Sync option, which the documentation says causes the program to wait
till the flush is done before returning and then the docs turn around and
says it "Syncs all graphics objects". Why would I be having more than ONE
graphics object ultimately referring to the same buffer?? That would be like
having a car where there are two drivers going to the same destination, but
trying to take different routes? The docs are confusing at best, vague in
the very least. My guess on this was that it is a write-now operation
despite that it say to write as soon as possible implying that the command
is assync!

3. What is missing is a method to tell the double-buffering NOT to write
till I issue a flush command, otherwise, its guess work, on the part of GDI+
that is going to end up tripping over it's own feet.

I suppose the GDI+ Auto-Double buffer could be considered broken and broken
badly if it was intended that it would check first before trying to access
the backbuffer for writing or reading while it's busy being read, or written
or worse yet, being disposed, but then that begs the question about why
would Migrosoft presume to imagine that all the diverse graphic needs could
all be shoehorned into a narrow minded double buffering model that gives the
programmer NO control over when the backbuffer is drawn. It's like a car
with a huge accelleration pedel, but no brakes - It's going to crash!!

I've been working on this saver, for weeks, I've seen this error about 80
times, despite the fact that it can take up to 10 hours for it to show! It
is exasperating in the extreme. I feel like I've spent half a million
dollars putting together a custom built Citrine Mazaradi, and I am sitting
in the garage all gassed up, polished up, in the driver seat ready to go the
door is open, but I can't exit the garage because a piece of sand is in the
way! Doh!



1. Running auto-double buffering, and get the error, which
[quoted text, click to view]
Frank Hileman
6/20/2005 8:00:38 AM
We use explicit double-buffering in VG.net and have never had this problem.
I cannot comment on the built-in double buffering. EndContainer may be buggy
as well -- we don't use this in VG.net, but I don't recall any specific bugs
with it.


Regards,
Frank Hileman

check out VG.net: http://www.vgdotnet.com
Animated vector graphics system
Integrated Visual Studio .NET graphics editor


[quoted text, click to view]

Garry Freemyer
6/20/2005 2:53:59 PM
I believe I've taken care of the issue. I added a
pev.Flush(FlushIntention.Flush); to my code for calling my GDI drawing
routine, and this thing has been running for a very long time without an
error.

The documention says this flush with the flush enum as an argument
"Terminates or flushes" the command stack, but it appears that this doen't
terminate in the sense of canceliing the commands for the results of the
call to my drawing routine are shown in all the colorful glory.

It seems to "Terminate" in that it causes the commands in the stack to
execute at once. Since all my auto-double buffering drawing stuff is in this
one call from OnPaint, the ending result is that when the next call to my
routine occurs, the backbuffer has already been drawn and it's ready to do
more.

I would have preferred that I had a way to tell it to wait for a command,
but this terminating each call with a flush as described above fixes the
issue.

As for reproducing the error, I would say if they were patient enough, you
could just comment out the flush command and send them my screensaver code.
It will create the error, albiet, they might have to run it up to 9 hours
before the bug shows itself, or it might show immediately.

[quoted text, click to view]
Bob Powell [MVP]
6/20/2005 8:05:50 PM
I have written code to stress the system and have not found any indication
that lots of use causes the error. Some applications just refuse to run
using built-in double buffering, others never show an exception after
literally weeks of soak-testing.

In order to post a bug I need to make it reproducable otherwise MS will not
pay it much attention.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.





[quoted text, click to view]
Garry Freemyer
6/24/2005 7:38:05 PM
I found out what the issue was with my program as stated here ...

Seems that the problem is all my fault, tho I still think its a sickening
thing to throw up a dialog box fingering my code for not handling an error
before control is returned to my program. This makes those using MS tools
look bad.

I wrongly assumed that the graphic object in a form's paint event would have
the same clipping region as the form itself, but this is not so.

I was able to solve the issue by re-inserting code into my program to see to
it that no drawing is even attempted outside the intended size of the
drawing area.

Since I've inserted the code to make sure I don't draw out of the screen
area, I no longer need any flushing, nor do I seem to get any more of those
cursed InvalidOperationException errors.
[quoted text, click to view]
AddThis Social Bookmark Button