How can I use the ID2D1DeviceContext interface in an MFC Direct2D application?

simonx 126 Reputation points
2024-11-02T11:31:51.59+00:00

I am using Direct2D in a Visual C++ (Visual Studio 2022) MFC application that uses many of the Direct2D MFC classes. I need to use the ID2D1DeviceContext interface for various purposes. The MFC classes force you to use either ID2D1HwndRenderTarget or the ID2D1DCRenderTarget to render to windows. All 3 of these interfaces inherit directly from ID2D1RenderTarget. None of them inherit from each other. So how can I use the ID2D1DeviceContext interface to output to a window that I am rendering to with either ID2D1HwndRenderTarget or ID2D1RenderTarget?

I can imagine that someone might suggest rewriting my application to remove all use of MFC Direct2D classes. It might be argued that I should just do all the Direct2D stuff myself. Unfortunately that option is not open to me. There is just too much legacy MFC Direct2D code for it to be feasible to remove it. It is a very big application and there is a ton of legacy code.

I did a search on the Internet for answers and found this very interesting post on StackOverflow: "An interesting undocumented secret is that any ID2D1RenderTarget you get from ID2D1Factory will also be an ID2D1DeviceContext (it seems to be intentional from what I've gathered, just accidentally undocumented?). Just call IUnknown::QueryInterface() to snag it. From there you can toy around with methods like GetDevice() and GetTarget()" (See https://stackoverflow.com/questions/28584566/get-direct3d-device-from-direct2d-render-target).

I tried checking this with Github Copilot and was told that it would not work. Copilor said that if I tried calling QueryInterface to request an ID2D1DeviceContext from either an ID2D1HwndRenderTarget or an ID2D1DCRenderTarget, the call would fail with E_NOINTERFACE. So I tried it and, surprise surprise, it turns out that Copilot was wrong. The call succeeds. And I can use the ID2D1DeviceContext to paint to the window with, seemingly, no problems.

So it looks like this is the solution to my problem: When my MFC Direct2D draw function is called (via registered message AFX_WM_DRAW2D), the lParam will be either an ID2D1HwndRenderTarget or an ID2D1DCRenderTarget, depending on how I enabled Direct2D for the window. Either way, it appears that I can call QueryInterface to request an ID2D1DeviceContext interface and I should be good to go. Can someone confirm this for me please? Will this always work - given that my application requres Windows 10 or later? I don't like using undocumented features if I can avoid it, but it's looking like I may have to in this case.

All help with this very much appreciated. Thank you.

C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,776 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.