-
Notifications
You must be signed in to change notification settings - Fork 306
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
backends/winrt: raise exception when trying to scan with STA
The pywin32-related packages will implicitly set the threading model to STA when imported. Since Bleak is using WinRT for async methods and we don't have a Windows event loop running, we need to let users know that Bleak is not going to work in this case. Also add a utility function and troubleshooting docs to provide a workaround for this issue. Fixes: #1132
- Loading branch information
Showing
4 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import ctypes | ||
from enum import IntEnum | ||
from typing import Tuple | ||
|
||
from ...exc import BleakError | ||
|
||
|
||
def _check_hresult(result, func, args): | ||
if result: | ||
raise ctypes.WinError(result) | ||
|
||
return args | ||
|
||
|
||
# https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cogetapartmenttype | ||
_CoGetApartmentType = ctypes.windll.ole32.CoGetApartmentType | ||
_CoGetApartmentType.restype = ctypes.c_int | ||
_CoGetApartmentType.argtypes = [ | ||
ctypes.POINTER(ctypes.c_int), | ||
ctypes.POINTER(ctypes.c_int), | ||
] | ||
_CoGetApartmentType.errcheck = _check_hresult | ||
|
||
_CO_E_NOTINITIALIZED = -2147221008 | ||
|
||
# https://learn.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-apttype | ||
class _AptType(IntEnum): | ||
CURRENT = -1 | ||
STA = 0 | ||
MTA = 1 | ||
NA = 2 | ||
MAIN_STA = 3 | ||
|
||
|
||
# https://learn.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-apttypequalifier | ||
class _AptQualifierType(IntEnum): | ||
NONE = 0 | ||
IMPLICIT_MTA = 1 | ||
NA_ON_MTA = 2 | ||
NA_ON_STA = 3 | ||
NA_ON_IMPLICIT_STA = 4 | ||
NA_ON_MAIN_STA = 5 | ||
APPLICATION_STA = 6 | ||
RESERVED_1 = 7 | ||
|
||
|
||
def _get_apartment_type() -> Tuple[_AptType, _AptQualifierType]: | ||
""" | ||
Calls CoGetApartmentType to get the current apartment type and qualifier. | ||
Returns: | ||
The current apartment type and qualifier. | ||
Raises: | ||
OSError: If the call to CoGetApartmentType fails. | ||
""" | ||
api_type = ctypes.c_int() | ||
api_type_qualifier = ctypes.c_int() | ||
_CoGetApartmentType(ctypes.byref(api_type), ctypes.byref(api_type_qualifier)) | ||
return _AptType(api_type.value), _AptQualifierType(api_type_qualifier.value) | ||
|
||
|
||
def assert_mta() -> None: | ||
""" | ||
Asserts that the current apartment type is MTA. | ||
Raises: | ||
BleakError: If the current apartment type is not MTA. | ||
""" | ||
try: | ||
apt_type, _ = _get_apartment_type() | ||
if apt_type != _AptType.MTA: | ||
raise BleakError(f"The current thread apartment type is not MTA: {apt_type.name}. Beware of packages like pywin32 that may change the apartment type implicitly.") | ||
except OSError as e: | ||
# All is OK if not initialized yet. WinRT will initialize it. | ||
if e.winerror != _CO_E_NOTINITIALIZED: | ||
raise | ||
|
||
def uninitialize_sta(): | ||
""" | ||
Uninitialize the COM library on the current thread if it was not initialized | ||
as MTA. | ||
This is intended to undo the implicit initialization of the COM library as STA | ||
by packages like pywin32. | ||
It should be called as early as possible in your application after the | ||
offending package has been imported. | ||
""" | ||
try: | ||
assert_mta() | ||
except BleakError: | ||
ctypes.windll.ole32.CoUninitialize() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters