pywinrt/python-winsdk

`is_border_required = False` hard crashes

Avasam opened this issue · 4 comments

This is the error from the Event Viewer:

Faulting application name: python.exe, version: 3.9.6150.1013, time stamp: 0x60d9eb23
Faulting module name: _winrt.pyd, version: 0.0.0.0, time stamp: 0x62a021ac
Exception code: 0xc0000005
Fault offset: 0x00000000005a667b
Faulting process ID: 0x6764
Faulting application start time: 0x01d881b77d70cee9
Faulting application path: C:\Program Files\Python39\python.exe
Faulting module path: C:\Program Files\Python39\lib\site-packages\winsdk\_winrt.pyd
Report ID: d2add601-2895-4e52-a8b7-074c7b3ae78c
Faulting package full name: 
Faulting package-relative application ID: 

Minimal repro:

import asyncio

import win32gui
from winsdk.windows.graphics.capture import (Direct3D11CaptureFramePool,
                                             GraphicsCaptureItem)
from winsdk.windows.graphics.capture.interop import create_for_window
from winsdk.windows.graphics.directx import DirectXPixelFormat
from winsdk.windows.media.capture import MediaCapture


def create_windows_graphics_capture(item: GraphicsCaptureItem):
    media_capture = MediaCapture()

    async def coroutine():
        await (media_capture.initialize_async() or asyncio.sleep(0))
    asyncio.run(coroutine())

    if not media_capture.media_capture_settings:
        raise OSError("Unable to initialize a Direct3D Device.")
    frame_pool = Direct3D11CaptureFramePool.create_free_threaded(
        media_capture.media_capture_settings.direct3_d11_device,
        DirectXPixelFormat.B8_G8_R8_A8_UINT_NORMALIZED,
        1,
        item.size)
    if not frame_pool:
        raise OSError("Unable to create a frame pool for a capture session.")
    session = frame_pool.create_capture_session(item)
    if not session:
        raise OSError("Unable to create a capture session.")
    session.is_cursor_capture_enabled = False
    try:
        # This "crashes" (force closes the program, no python error)
        session.is_border_required = False
    except:
        print("caught")


def print_hwnd(hwnd: int, _):
    window_text = win32gui.GetWindowText(hwnd)
    if (
        window_text
        and window_text not in ("Default IME", "MSCTFIME UI")
        and "Window" not in window_text
    ):
        print(hwnd, window_text)


win32gui.EnumWindows(print_hwnd, '')


while True:
    try:
        hwnd = int(input("^ Enter an HWND from above ^ :"))
    except:
        print("not an int")
        continue

    try:
        print("Selected window:", win32gui.GetWindowText(hwnd))
    except:
        print("not a valid HWND")
        continue

    item = create_for_window(hwnd)
    create_windows_graphics_capture(item)

Microsoft's docs does mentions needing to call the following first GraphicsCaptureAccess.request_access_async(GraphicsCaptureAccessKind.BORDERLESS): https://docs.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscapturesession.isborderrequired#remarks

If possible, I would ask for python-winsdk to raise an appropriate exception rather than letting the entire application crash silently.

Sidenote, GraphicsCaptureAccess.request_access_async also throws OSError: [WinError -2147221164] Class not registered, but this may or may not be due to the lack of an application manifest declaring the graphicsCaptureWithoutBorder. Or because it's Win11 only. I'll need to try both. The former I can do when I bundle my app with PyInstaller.

Edit: see coment

Update: It does all work on a Windows 11 machine (I didn't even need to call GraphicsCaptureAccess.request_access_async(GraphicsCaptureAccessKind.BORDERLESS) when running from whithin VSCode) I did try with and without border to make sure I was actually capturing the window.

Still I think that raising a proper error in Python will be preferable over letting the whole thing hard crash

dlech commented

So the crash only happens on older versions of Windows? Which version specifically?

Version 10.0.19044 Build 19044 (latest Windows 10)
Python 3.9.6
winsdk 1.0.0b5

For now I've resorted to checking the version using platform.version since I can't try-catch

dlech commented

This should be fixed in v1.0.0b7.