Search code examples
bitmapmshtmliwebbrowser2

Get bitmap from hidden WebBrowser control (IWebBrowser2 interface aka MSHTML )


I'm interested in the render output from a WebBrowser ActiveX control. That WebBrowser is running in-process. The idea is to obtain a "screenshot" of what the control is showing. In other words: get the WebBrowser pixels.

What is currently the best practice to do so?

Because after some small research I found there are at least 5 ways to achieve this:

  1. The IHTMLElementRenderer::DrawToDC method.
  2. The IViewObject2::Draw route.
  3. The OleDraw route. To me, this just seems a short hand for method 2.
  4. The PrintWindow Windows API function. Delivers the correct result, but fails when the Window with the WebBrowser control is invisible (WS_VISIBLE style missing)
  5. Capture the desktop and crop to the area where the webbrowser control is showing

The first method looks most promising. However, according to the documentation, it is deprecated, so I guess I can't rely on it anymore?

Notes:

  • If the page is bigger than the window/viewport, then it's not a problem that those pixels are missing (the pages I'd like to capture are of a fixed size)
  • I have seen the IE feature FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI, but this seem to matter only for programs that want Windows Metafile (WMF) output. A bitmap is fine for me.
  • Other windows should not be visible on the end result
  • When I first called DrawToDC I ended up with a completely black bitmap. Later it turns out this happened because my bitmap was 1-bit monochrome. That's the default for a bitmap apparently: I fell into an old GDI trap!

Solution

  • Ended up grabbing the pixels from the window using Windows GDI function BitBlt. This works okay, but expect to also get the window border + minimize and maximize boxes in your image in case its the main application window.

    Solution for that is to create a child window of the main window, run the browser in that window and grab the pixels from that window as well.

    Also found that using the browser's "render to a DC" method from multiple isolated processes doesn't scale in a linear way: the per process CPU usage figure increases significantly the more processes you start. With the BitBlt method this doesn't happen.

    And in both cases problems occur when the browser runs in the main window and that gets minimized. In that case the system decides nothing needs to be drawn and the resulting screenshot bitmap will remain empty.