def __get_window_class_name(hwnd, max_nof_bytes_class_name=32): """ :param hwnd: window handle :param max_nof_bytes_class_name: [description], defaults to 32 :param max_nof_bytes_class_name: int, optional :return: Name of the window class that the specified window belongs to. """ windows_class_name_buffer = ctypes.create_unicode_buffer( max_nof_bytes_class_name) assert_win( ctypes.windll.user32.GetClassNameW(hwnd, windows_class_name_buffer, max_nof_bytes_class_name)) return windows_class_name_buffer.value
def __get_window_handle(window_name=None, window_class_name=None): """Searches for the window handle matching the window name or window class name. Uses https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindoww :param window_name: [description], defaults to None :param window_name: [type], optional :param window_class_name: [description], defaults to None :param window_class_name: [type], optional :return: matched window handle """ window_handle = ctypes.windll.user32.FindWindowW( ctypes.wintypes.LPCWSTR(window_class_name), ctypes.wintypes.LPCWSTR(window_name)) assert_win(window_handle) return window_handle
def __get_window_handle_information(): """Retrieves information such as window_handle, process_id and thread_id. :return: a named tuple with window_handle, process_id and thread_id """ information = list() def EnumWindowsProc(hwnd, lParam): information.append(__get_window_handle_information_for(hwnd)) #To continue enumeration, the callback function must return TRUE; #to stop enumeration, it must return FALSE. return True assert_win( ctypes.windll.user32.EnumWindows( ctypes.WINFUNCTYPE(ctypes.wintypes.BOOL, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)(EnumWindowsProc), 0)) return information
def get_windowshot(window_handle): """Get an RGB screen capture of a window :param window_handle: Handle to the window that will be screenshot. :type window_handle: ctypes.wintypes.HWND :return: RGB-screenshot :rtype: numpy.array """ with Context( window_handle, constructor=ctypes.windll.user32.GetWindowDC, destructor=lambda hDC, hWnd=window_handle: ctypes.windll.user32.ReleaseDC(hWnd, hDC)) as ctx_window_device_context: with Context( ctx_window_device_context(), constructor=ctypes.windll.gdi32.CreateCompatibleDC, destructor=ctypes.windll.gdi32.DeleteDC) as ctx_memory_device_context_handle: window_rect = ctypes.wintypes.RECT() assert_win(ctypes.windll.user32.GetWindowRect(window_handle, ctypes.byref(window_rect))) width = window_rect.right - window_rect.left height = window_rect.bottom - window_rect.top with Context( ctx_window_device_context(), width, height, constructor=ctypes.windll.gdi32.CreateCompatibleBitmap, destructor=ctypes.windll.gdi32.DeleteObject) as ctx_graphics_device_interface_bitmap_handle: logging.debug('Contexts created.') graphics_device_interface_previously_selected_bitmap_handle = ctypes.windll.gdi32.SelectObject( ctx_memory_device_context_handle(), ctx_graphics_device_interface_bitmap_handle()) assert_win(graphics_device_interface_previously_selected_bitmap_handle) assert graphics_device_interface_previously_selected_bitmap_handle != ctypes.wintypes.HANDLE(0xFFFFFFFF) assert_win(ctypes.windll.user32.PrintWindow(window_handle, ctx_memory_device_context_handle(), 0)) #[https://msdn.microsoft.com/sv-se/02f8ed65-8fed-4dda-9b94-7343a0cfa8c1, # https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx] bitmap_info = BITMAPINFO() bitmap_info.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) bitmap_info.bmiHeader.biWidth = width #Top-down image [https://msdn.microsoft.com/sv-se/library/ms787796.aspx, # https://docs.microsoft.com/sv-se/windows/desktop/api/wingdi/nf-wingdi-getdibits] bitmap_info.bmiHeader.biHeight = -height bitmap_info.bmiHeader.biPlanes = 1 #"The bitmap has a maximum of 2^32 colors. If the biCompression member of the BITMAPINFOHEADER is BI_RGB, the bmiColors # member of BITMAPINFO is NULL. Each DWORD in the bitmap array represents the relative intensities of blue, green, and # red for a pixel. The value for blue is in the least significant 8 bits, followed by 8 bits each for green and red. # The high byte in each DWORD is not used." # => Do not set bmiColors in ctypes? bitmap_info.bmiHeader.biBitCount = 32 #"BI_RGB An uncompressed format." [wingdi.h] bitmap_info.bmiHeader.biCompression = 0 #"This may be set to zero for BI_RGB bitmaps." bitmap_info.bmiHeader.biSizeImage = 0 #0? 10 pixels/millimeter = 10000 pixels/m? -- no idea if there is a "correct" value bitmap_info.bmiHeader.biXPelsPerMeter = 0 bitmap_info.bmiHeader.biYPelsPerMeter = 0 #"If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount # member for the compression mode specified by biCompression." bitmap_info.bmiHeader.biClrUsed = 0 #"If this value is zero, all colors are required." bitmap_info.bmiHeader.biClrImportant = 0 data = ctypes.create_string_buffer(width * height * 4) #[https://docs.microsoft.com/sv-se/windows/desktop/api/wingdi/nf-wingdi-getdibits] #DIB_RGB_COLORS = 0 [wingdi.h] nof_scanlines = ctypes.windll.gdi32.GetDIBits(ctx_memory_device_context_handle(), ctx_graphics_device_interface_bitmap_handle(), 0, height, data, bitmap_info, 0) assert_win(nof_scanlines == height) #Convert BGRX to RGB #https://stackoverflow.com/a/37421379 return np.frombuffer(data, dtype='uint8').reshape(height, width, 4)[..., :3][..., ::-1]
def __exit__(self, *exc_args): if self.destructor: self.destruct = self.destructor(self.construct) assert_win(self.destruct_ok(self.destruct)) return False
def __enter__(self): if self.constructor: self.construct = self.constructor(*self.args) assert_win(self.construct_ok(self.construct)) return self
def __get_cursor_pos(): function = ctypes.windll.user32.GetCursorPos function.restype = ctypes.wintypes.BOOL point = ctypes.wintypes.POINT() assert_win(function(ctypes.byref(point))) return point
def __window_from_point(point): function = ctypes.windll.user32.WindowFromPoint function.restype = ctypes.wintypes.HANDLE return assert_win(function(point))