2

I'm trying to get an image of an RTL window using the following python code using pywin32:

def get_window_image(hwnd: int, window_region: Region) -> np.ndarray:
    PW_RENDERFULLCONTENT = 2

    dc_handle = None
    dc_obj = None
    save_bitmap = None
    memory_dc_obj = None

    try:
        # Creating device context for retrieving the screenshot. Based on the following:
        # https://stackoverflow.com/questions/19695214/python-screenshot-of-inactive-window-printwindow-win32gui
        # Documentation about device context:
        # https://learn.microsoft.com/en-us/cpp/mfc/device-contexts?view=vs-2019

        dc_handle = win32gui.GetWindowDC(hwnd)
        dc_obj = win32ui.CreateDCFromHandle(dc_handle)
        memory_dc_obj = dc_obj.CreateCompatibleDC()

        # Create a bitmap from the device context
        save_bitmap = win32ui.CreateBitmap()
        save_bitmap.CreateCompatibleBitmap(dc_obj, window_region.get_width(), window_region.get_height())
        memory_dc_obj.SelectObject(save_bitmap)

        if sys.getwindowsversion().major >= 8:
            render_flags = PW_RENDERFULLCONTENT
        else:
            render_flags = 0

        # Take a screenshot of the window
        windll.user32.PrintWindow(hwnd, memory_dc_obj.GetSafeHdc(), render_flags)

        # Retrieve the bitmap content from the taken screenshot
        bmp_info = save_bitmap.GetInfo()
        bmp_str = save_bitmap.GetBitmapBits(True)

        bmp_height, bmp_width = bmp_info['bmHeight'], bmp_info['bmWidth']
        window_image = np.fromstring(string=bmp_str, dtype=np.uint8)

        window_image = window_image.reshape((bmp_height, bmp_width, 4))
        window_image[:, :, 3] = 255  # remove alpha channel

        return window_image

    finally:
        if save_bitmap is not None:
            win32gui.DeleteObject(save_bitmap.GetHandle())

        if memory_dc_obj is not None:
            memory_dc_obj.DeleteDC()

        if dc_obj is not None:
            dc_obj.DeleteDC()

        if dc_handle is not None:
            win32gui.ReleaseDC(hwnd, dc_handle)

The code to generate the RTL window:

win32gui.MessageBox(None, 'משהו בעברית', 'test-rtl', win32con.MB_ICONWARNING | win32con.MB_RTLREADING)

I'm passing PrintWindow the flag - PW_RENDERFULLCONTENT = 2, which should help render the window with it's correct frame and not with the classic frame (also see https://stackoverflow.com/a/40042587/4313487)

I have 2 PCs with the same specs. On the first PC the image is printed as mirrored and on the second it is printed as it's originally displayed.

Both PCs have windows 10 with the same region and locale settings. On both PCs the messagebox window has the same style flags including WS_EX_LAYOUTRTL.

Image from PC1 - gets mirrored:

enter image description here

Image from PC2 (same code):

enter image description here

When the flag passed to PrintWindow is 0 (instead of PW_RENDERFULLCONTENT) the image is mirrored in both PCs:

enter image description here

Why does it happen?

How can I make sure that the image gets printed in the same direction on different PCs?

Etan Grundstein
  • 395
  • 1
  • 11
  • `Both PCs have windows 10 with the same region and locale settings.` What are the versions of the two computers? 2004? 1903? – Strive Sun Sep 11 '20 at 08:32
  • 1
    After my test, there will be mirroring on the 2004 version, but no mirroring on windows 8.1 and windows 10 1903. (render_flags = PW_RENDERFULLCONTENT). I will discuss this question with relevant engineers. – Strive Sun Sep 11 '20 at 08:34
  • `PW_RENDERFULLCONTENT` is not supported by [doc](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-printwindow). It is not recommended to use this, because it may be modified or discontinued sometime in the future. – Strive Sun Sep 14 '20 at 08:16
  • @StriveSun-MSFT, thanks for the answer. I see that there are numerous libraries that do use this flag, for example: https://github.com/chromium/chromium/blob/177a25e5f6d54520f8407b2c730d6509eeb19448/ui/snapshot/snapshot_win.cc#L53 If it's not recommended for use. What would be the best way to print window apps like chromium, where, without this flag the app is rendered as a black screen? See: https://stackoverflow.com/questions/30965343/printwindow-could-not-print-google-chrome-window-chrome-widgetwin-1/40042587#40042587 – Etan Grundstein Sep 15 '20 at 10:53

1 Answers1

1

If it's not recommended for use. What would be the best way to print window apps like chromium, where, without this flag the app is rendered as a black screen?

I use PrintWindow(without PW_RENDERFULLCONTENT) to print the chrome window, and there is no black screen problem, provided that the chrome window was not minimized. That being said, there have been many problems with Chrome returning to a black screen for a long time in the past.

But PrintWindow() doesn't work very well. You can try GetWindowDC() and BitBlt() from that DC into your bitmap. Or GetClientRect()+ClientToScreen() and BitBlt() that rectangle from the screen DC to your bitmap.

Or look at the Screen Capture API added in Win10. Or the Desktop Duplication API added in Win8. Or Direct3D. See Ways to capture the screen.

Refer: Getting Black Screenshots with Windows API

Since you need to implement it in python, I suggest you use the first method(relatively simple), GetClientRect()+ClientToScreen() and BitBlt()...

Code sample: Capturing screenshots with win32api python returns black image

Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • My version is 2004, and when calling `PrintWindow` with 0 in the flags, it renders as a black screen. I wonder what is the root cause. Anyway, the last 2 links are great but the solution there proposes using the desktop window for getting the image. Is there a solid way to get a screenshot of chrome without having it as the foreground window? – Etan Grundstein Sep 16 '20 at 10:37
  • @EtanGrundstein From the [PrintWindow](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-printwindow), `nFlags` has only one optional parameter, which is `PW_CLIENTONLY(1)`. `nflags = 0` is undocumented behavior, so I don't recommend you to try it. – Strive Sun Sep 17 '20 at 01:48
  • @EtanGrundstein `Is there a solid way to get a screenshot of chrome without having it as the foreground window?` Using `winapi(GetClientRect()+ClientToScreen() and BitBlt())` in python to capture the chrome screen is the easiest way I know. If you need to research other methods, you may need to study [Screen capture (windows 10)](https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/screen-capture) , I have no experience with this either. – Strive Sun Sep 17 '20 at 01:57
  • Sun correct me if I'm wrong `nflags` = 0 is a parameter which indicates that I didn't pass any flag to `PrintWindow`. From the links you sent me I saw that the usage of BitBlt is for cropping the window from the desktop window's device context -> the window needs to be foreground to be captured. My question to you is if there's a way to do it when the window is not foreground (and without the `PW_RENDERFULLCONTENT` flag) – Etan Grundstein Sep 17 '20 at 12:06
  • @EtanGrundstein If it is only for apps like Chrome, in my experience, the answer is no. – Strive Sun Sep 18 '20 at 05:06