'python win32ui.error: BitBlt failed & CreateCompatibleBitmap failed in multithreading
I'm here to ask for help on a recent problem I encountered with my program....
I am getting this error when I try to use BitBlt
and CreateCompatibleBitmap
: win32ui.error: BitBlt failed & CreateCompatibleBitmap
, It's worth mentioning that this happens only when my program is calling these functions from different threads, probably at the same time.
Perhaps the methods of win32gui
are not multithreaded compatible?
I am currently wrapping the calls in a try-except
block to prevent the program from crashing, but apparently the threads are competing to use the methods BitBlt
y CreateCompatibleBitmap
I thought about ignoring the error messages and letting the threads compete this race, but it really scares me that this could result in a poor program becoming slow or breaking something by avoiding millions of these errors.
I am calling BitBlt and CreateCompatibleBitmap constantly every 50 to 100 milliseconds.
Here's an example of the function I'm calling from multiple threads:
When I do it this way I get win32gui.error CreateCompatibleBitmap failed
def get_capture_icons():
hwndDC_icons = win32gui.GetWindowDC(game_client_hwnd)
mfcDC_icons = win32ui.CreateDCFromHandle(hwndDC_icons)
saveDC_icons = mfcDC_icons.CreateCompatibleDC()
saveBitMap_icons = win32ui.CreateBitmap()
saveBitMap_icons.CreateCompatibleBitmap(mfcDC_icons, 106, 11)
saveDC_icons.SelectObject(saveBitMap_icons)
saveDC_icons.BitBlt((-16, -39), (1366, 768), mfcDC_icons, (1191, 256), win32con.SRCCOPY)
img = Image.frombuffer('RGB', (106, 11), saveBitMap_icons.GetBitmapBits(True), 'raw', 'BGRX', 0, 1)
mfcDC_icons.DeleteDC()
saveDC_icons.DeleteDC()
win32gui.ReleaseDC(game_client_hwnd, hwndDC_icons)
win32gui.DeleteObject(saveBitMap_icons.GetHandle())
return img
But when I save the objects in global variables I get win32gui.error BitBlt failed
def load_capturer_icons():
global hwndDC_icons, mfcDC_icons, saveDC_icons, saveBitMap_icons
hwndDC_icons = win32gui.GetWindowDC(game_client_hwnd)
mfcDC_icons = win32ui.CreateDCFromHandle(hwndDC_icons)
saveDC_icons = mfcDC_icons.CreateCompatibleDC()
saveBitMap_icons = win32ui.CreateBitmap()
saveBitMap_icons.CreateCompatibleBitmap(mfcDC_icons, 106, 11)
saveDC_icons.SelectObject(saveBitMap_icons)
def get_capture_icons():
saveDC_icons.BitBlt((-16, -39), (1366, 768), mfcDC_icons, (1191, 256), win32con.SRCCOPY)
return Image.frombuffer('RGB', (106, 11), saveBitMap_icons.GetBitmapBits(True), 'raw', 'BGRX', 0, 1)
Extra Info:
I have 2 threads processing scripts
scripting.Thread(target=scripting.Thread.check_queue_scripts, name="Ekko").start()
scripting.Thread(target=scripting.Thread.check_queue_scripts, name="Zoe").start()
example of two scripts that make a call to get_capture_icons
and generate the error explained at the beginning of this page
def callback(player, sleep):
get_capture_icons()#call example
script = Script("callback-1", 100)
script.add_callback(callback)
script.register(__name__)
script = Script("callback-2", 100)
script.add_callback(callback)
script.register(__name__)
If I delete one of these scripts so that only 1 remains, then the error no longer appears
Solution 1:[1]
Thanks to @Tim Roberts I was able to orient myself a little more and I started an investigation in this regard, and although his answer was not accurate, he was very clear that simultaneous access to GDI objects produces race conditions, so the solution was to use threading.Lock
so now each thread executes the scripts with lock
lock.acquire()
script.execute()
lock.release()
from threading import Lock
class Thread(threading.Thread):
def __init__(self, target: 'Callable', name: str = "", lock: threading.Lock = None):
threading.Thread.__init__(self, target=target, args=(self, lock))
self.running = True
self.name = name
def check_queue_scripts(self, lock):
while self.running:
script_name = Scripts.queue.get()
script = Scripts.get_script(script_name)
if script:
if script.to_reload:
Scripts.queue.task_done()
Scripts.reload_script(script)
continue
lock.acquire()
script.execute()
lock.release()
Scripts.queue.task_done()
Scripts.queue.put(script_name)
sleep(1/20)
scripts_lock = Lock()
scripting.Thread(target=scripting.Thread.check_queue_scripts, name="Ekko", lock=scripts_lock).start()
scripting.Thread(target=scripting.Thread.check_queue_scripts, name="Zoe", lock=scripts_lock).start()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |