I have a program that uses pywin32 to take a screengrab of a game & display it using pygame. I am experiencing intermittent crashing/hanging where the program will give the usual windows "not responding" error, saying python.exe is not responding after about 5-10 seconds of the program running sometimes.
I have narrowed it down to the following function:
def screengrab(self):
hwnd = self.aoe_hwnd
left, top, right, bot = win32gui.GetClientRect(hwnd)
w = right - left
h = bot - top
#returns the device context (DC) for the entire window, including title bar, menus, and scroll bars.
hwndDC = win32gui.GetWindowDC(hwnd)
#Creates a DC object from an integer handle.
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
#Creates a memory device context (DC) compatible with the specified device.
saveDC = mfcDC.CreateCompatibleDC()
saveDC.SetWindowOrg((w - self.map_w,h - self.map_h))
#Creates bitmap Object
saveBitMap = win32ui.CreateBitmap()
#Creates a bitmap object from a HBITMAP.
saveBitMap.CreateCompatibleBitmap(mfcDC, self.map_w, self.map_h)
saveDC.SelectObject(saveBitMap)
# Change the line below depending on whether you want the whole window
# or just the client area.
#result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 1)
result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 1)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer(
'RGB',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
if result == 1:
tmp = cStringIO.StringIO()
im = im.resize(self.window_size)
im.save(tmp, "bmp")
tmp.seek(0)
return tmp
I'm new to win32's api and I really am not entirely sure what could be causing it to hang like that. The odd thing is that print statements placed in the main loop of the program (where screengrab() is also called), will still execute while the program is hung/not responding.
The entire program on gist: https://gist.github.com/Andygmb/f8ae761e689788136fc0
We talked on IRC earlier.
I think that your issue is related to the use of windll.user32.PrintWindow. Looking at the Microsoft documentation for this function, this line caught my attention:
"Note This is a blocking or synchronous function and might not return immediately. How quickly this function returns depends on run-time factors such as network status, print server configuration, and printer driver implementation—factors that are difficult to predict when writing an application. Calling this function from a thread that manages interaction with the user interface could make the application appear to be unresponsive."
So that is probably related.
Adding time.sleep(1) (after importing time) at the bottom of the while running loop seems to prevent the crashes on my system, so if that works for you and a one-second delay is acceptable, that's an option.
Really what you want to do is be able to call PrintWindow in the background and update the screen when PrintWindow returns, but you may have to do some silly business with file read/write locks and/or threading. So it really depends on what this is for and what's more important to you.