CreateDCFromHandle выберите верхнюю координату объекта в Python

#python #winapi #coordinates #screenshot #bitblt

Вопрос:

Мне нужно захватить конкретное окно приложения в Windows, даже если оно не сфокусировано или находится на переднем плане, БЕЗ верхней панели заголовка окна, высоту которой я указываю вручную в переменной application_window_topbar_size, чтобы упростить ее.

 monitor_info = GetMonitorInfo(MonitorFromPoint((0,0))) # get screen information
work_area = monitor_info.get("Work")
screen_width = work_area[2] # if taskbar is on the left
screen_height = work_area[3] # if taskbar is on the left

target_window_hwnd = win32gui.FindWindow(None, ("Math Analysis - Google Chrome"))
application_window_topbar_size = 200 # just roughly for testing

    hwndDC = win32gui.GetWindowDC(target_window_hwnd) 
    mfcDC  = win32ui.CreateDCFromHandle(hwndDC)    
    saveDC = mfcDC.CreateCompatibleDC()
    saveBitMap = win32ui.CreateBitmap()
    saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
    saveDC.SelectObject(saveBitMap)

#    saveDC.BitBlt((0, 0), (screen_width, screen_height), mfcDC, (0, application_window_topbar_size),win32con.SRCCOPY)
    
    result = windll.user32.PrintWindow(target_window_hwnd, saveDC.GetSafeHdc(), 3)
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(target_window_hwnd, hwndDC)
 

Процедура захвата должна работать для обработки Windows «WM_PRINT» (спасибо за комментарий IInspectable), НО я не могу понять, как установить ВЕРХНЮЮ координату области захвата. Другими словами, я хотел бы захватить целевое окно без верхней панели. Изменение высоты-это одно, так как логично, что если координаты прямоугольника захвата начинаются с 0,0, то он вырезает только нижнюю область окна, а не верхнюю. Я пытался использовать BitBlt() , но у меня ничего не вышло.

—————— Альтернативная версия ниже на основе ожидаемого комментария

 target_window_hwnd = win32gui.FindWindow(None, ("Math Analysis - Google Chrome"))
application_window_topbar_size = 200 # just roughly for testing

    hwndDC = win32gui.GetWindowDC(target_window_hwnd) 
    mfcDC  = win32ui.CreateDCFromHandle(hwndDC)    
    saveDC = mfcDC.CreateCompatibleDC()
    saveBitMap = win32ui.CreateBitmap()

    l, t, r, b = win32gui.GetClientRect(target_window_hwnd)
    screen_width = r - l
    screen_height = b -t

    saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
    saveDC.SelectObject(saveBitMap)   
    result = windll.user32.PrintWindow(target_window_hwnd, saveDC.GetSafeHdc(), 3)
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(target_window_hwnd, hwndDC)
 

Тем не менее, изменение верхней координаты влияет только на высоту, а не на захваченное содержимое (чтобы иметь возможность захватывать только «область документа» окна, исключая адресную строку/панель инструментов).

Комментарии:

1. GetClientRect возвращает вам координаты клиентской области. «Процедура захвата работает идеально» — Для некоторого определения «идеально» , я думаю. Вы увидите, что это значит, как только столкнетесь с окном, которое не обрабатывает WM_PRINT сообщение или обрабатывает его иначе, чем вы ожидаете. Вместо этого используйте API миниатюр DWM .

2. @IInspectable : Я отредактировал свой оригинальный пост и перефразировал слово «идеальный» на основе вашего комментария, чтобы избежать любого введения в заблуждение, что не входило в мои намерения. Моя целевая платформа-Python (также обновленный заголовок), и я не смог найти пример миниатюры DWM в этом контексте. Могу я спросить, не могли бы вы помочь с руководством по одному из них, пожалуйста ? Что касается GetClientRect(), я (ошибочно) подумал, что для этого требуется, чтобы окно было на переднем плане.Я добавил новый вариант кода с помощью GetClientRect() в своем первоначальном сообщении, но я все еще пытаюсь установить верхнюю координату таким образом, чтобы избавиться от области панели инструментов окна.

3. Окно печати не позволяет указать регион. Это либо полное окно, либо клиентская область. Предположительно, вы уже запрашиваете визуализацию только клиентской области. Проблема в том, что приложения обычно отображают все окно (клиентское и неклиентское, т. Е. Границу, заголовок и т. Д.). Это делается путем создания окна, в котором вообще нет неклиентской области. В этой ситуации у системы больше нет возможности определить «логическую» клиентскую область.

4. @IInspectable : извините, но сейчас я в полном замешательстве. Я имею в виду: 1) как насчет вопроса о миниатюре DWM, если я могу спросить? 2) Как окно печати приходит к уравнению? Поскольку моя цель не состоит в том, чтобы «официально» захватить область клиента/документа, я просто нацеливаюсь на снимок экрана, на котором я устанавливаю прямоугольник захвата с координатами относительно координат экрана, используя wiun32, как указано выше, чтобы убедиться, что окно не должно быть в фокусе и не должно быть на переднем плане. Извините, если я упустил суть, но именно поэтому я спрашиваю.

5. API миниатюр DWM представлен в виде набора COM -объектов. Я уверен, что существуют привязки Python, которые позволяют вам получать доступ к COM-объектам. PrintWindow это системный API, который вы вызываете, и мой предыдущий комментарий объясняет, как он ведет себя в ситуациях, когда разработчики класса window предпочитают не использовать реализацию, предоставляемую системой, не относящуюся к клиентской области. Поскольку вы передаете значение 3 nFlags аргумента, эта часть имеет значение.

Ответ №1:

После долгих поисков и испытаний я понял, что наиболее очевидным решением является ОБРЕЗКА скриншота после его создания. Таким образом, ненужная часть (в моем случае верхняя панель) может быть легко удалена.

Обрезка легко выполняется с помощью изображения PIL:

 box = (left, top, right, bottom)
screen_image.crop(box)