How To Prevent Caching After Grabbing A Picture's HDC Handle

Does anybody have experience of using HandleTypeHDC with Direct2D versions of Xojo?

We have a custom control that draws its content via the WM_PAINT message of a custom WndProc. Part of the content is a Xojo picture which is drawn by grabbing its HDC Handle and calling BitBlt.

This works perfectly in Xojo 2014 but doesn’t work correctly in Xojo 2019r1.1

As soon as we grab the HDC, the picture content seems to get cached. This means that if we make further changes to the picture and draw it again we still get the original content.

I’ve managed to distill the problem down to a few lines of code which I have included below.
When the code hits the first break, ‘p’ will consist of a red oval on a black background. However, when the second break is hit, both ‘p’ and ‘p2’ will just contain the black background.

So far, the only solution we have found is to create a temporary copy of the picture, grab its HDC, call BitBlt and then throw the temporary copy away. Although this appears to solve the problem we would prefer to find a solution that is more efficient.

[code]Dim p As New Picture(100, 100, 32)
Dim hdc As Integer

p.Graphics.FillRect(0, 0, 100, 100)

hdc = p.Graphics.Handle(Graphics.HandleTypeHDC)

p.Graphics.ForeColor = RGB(255, 0, 0)
p.Graphics.FillOval(0, 0, 100, 100)

'check the contents of p - it should be a red circle on a black background
Break

Dim p2 As New Picture(p.Width, p.Height, p.Depth)
p2.Graphics.DrawPicture(p, 0, 0)

'check the contents of p2 - it should be the same as p but it is p at the point we retrieved the HDC
'check the contents of p - it has actually reverted back
Break[/code]

Probably… but I don’t :slight_smile:

First… I really think you should report this as a bug in Feedback…!

This reminds me of this thread.
Xojo’s agressive caching leads to other issues such as <https://xojo.com/issue/54421>. So that’s why I think you should report your situation, too!

Maybe the workaround described in the other thread might work here, too?
The “cache” is created when drawing a Picture or according to your situation when getting the HDC.

If you don’t want the “cache”, you first store the Picture in a new Object-Instance: bufferPicture = New Picture(sourcePicture.Width, sourcePicture.Height, Array(sourcePicture)). When you do actions that will create the “cache”, get it out from there again as a new Picture instance: tempPicture = bufferPicture.IndexedImage(0). Throw away that object instance as soon as possible, so that the “cache” is gone.
This might be more efficient (since I assume this will not create a new Picture and invoke “drawing”). It’s just working around the fact that the “cache” gets added to a Xojo-Picture-Object-instance (and not to it’s underlying Picture-Data). This allows to code in a way where you can control which instances will have a “cache” (e.g. once being used to draw), but allow you to keep Picture-objects without the “cache”.

You’d certainly have to investigate in more detail at what’s going to happen with such an approach…
I just hope that the info is related and relevant, and might help you to find another way that is more efficient by not having to “draw into temporary copies” too often (if you can use this other kind of “Picture store/retrieve”, avoiding the “cache” by getting the temporary instances out of it without having to perform expensive drawings).

<https://xojo.com/issue/54028> November 8, 2018

I’ve not looked at this problem since I put the ticket in so I can’t really offer any help but here’s some old code for drawing directly on a Direct2DRenderTarget, it might be of some use.

A bug (<https://xojo.com/issue/59656>) was found when writing that, I asked for the ticket to be made public a few days ago.

Hi Jürg / Julian

Thanks for your replies.

Feedback #54028 is the problem I am hitting.

This is what I think is happening…
• Prior to the D2D versions, asking for Graphics.HandleTypeHDC would give you a HDC which pointed directly to the picture’s pixels.
• With the D2D versions, the picture is something else (D2D bitmap?) so asking for a HDC requires the framework to clone the picture as a HDC.
• The HDC created by the framework is cached as part of the picture so that it doesn’t have to re-create it again if a HDC is asked for.
• Unfortunately, there is no way to tell Xojo to clear the cached HDC which means every time you ask for a HDC you just get the original back again.

I found another forum posting from 3 years ago that mentions the SDK functio REALGraphicsReleaseDC. I also found this in the 2016r4 release notes:

so… It appears that Graphics.HandleTypeHDC will only work correctly with the aid of a plugin since there is no way to trigger REALGraphicsReleaseDC directly from code. This means my workaround of cloning the picture and accessing its HDC might be the only solution for now.

I also have to deal with a more complex situation with a custom popup menu WndProc where I am using Win32 API calls to draw a menu’s background theme into a picture (via its HDC) so that I can then draw on top of it in Xojo code before BitBlt’ing it to the screen.

I think the solution is possibly to interact with D2D directly like your example project. Unfortunately, Graphics.HandleTypes.Direct2DRenderTarget is not available in 2019r1.1 and we currently cannot use newer versions because the IDE now functions at a totally unusable speed.

If needed, I could export REALGraphicsReleaseDC via plugin.

We have a C++ class, which automatically releases, so maybe best I make a class, which could release it in destructor.

That is potentially a solution.

I’m wondering if REALGraphicsReleaseDC calls ID2D1GdiInteropRenderTarget::ReleaseDC as part of its clean-up as that allows you to provide a rectangle which I assume copies the DC content back into the source.

At this present time, i’m only investigating the issues with moving up to a newer version so I won’t be implementing any changes in the near future.

WindowsGraphicsDeviceContextMBS class added for next release.

Not sure how it works internally, but I think this copying back is the main reason we have the release function there.