Захватывать пиксели окна независимо от его z-порядка - PullRequest
3 голосов
/ 03 января 2012

Я на самом деле пытаюсь прочитать определенный пиксель в окне, которое скрыто другими.Я хочу использовать функцию GetPixel из библиотеки GDI, но, похоже, она работает только с глобальным контекстом устройства.Я не могу прочитать пиксель из определенного окна, и я не понимаю, почему .. Я нашел эту статью , которая использует функцию PrintWindow для копирования определенного содержимого окна во временный контекст устройства, который можно прочитать.Но я не могу воспроизвести его.

РЕДАКТИРОВАТЬ

Спасибо, что все мои проблемы решены:)
Этот скрипт даст вам RGB цвет указателя навыбранное окно, даже если оно скрыто.Напомним, что эта программа должна запускаться с правами администратора, чтобы получить пиксели процессов, запущенных с правами администратора.

#define STRICT
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
// 0x0501 for PrintWindow function
// You must be at least running Windows XP
// See http://msdn.microsoft.com/en-us/library/6sehtctf.aspx

#include <stdio.h>
#include <string.h>
#include <windows.h>

#define WINDOW_LIST_LIMIT 32
#define WINDOW_NAME_LIMIT 1024

void FatalError(char* error)
{
    printf("%s", error);
    exit(-1);
}

HWND window_list[WINDOW_LIST_LIMIT];
unsigned int window_list_index = 0;

BOOL EnumWindowsProc(HWND window_handle, LPARAM param)
{
    char window_title[WINDOW_NAME_LIMIT];

    if(!IsWindowVisible(window_handle)) return TRUE;

    RECT rectangle = {0};
    GetWindowRect(window_handle, &rectangle);
    if (IsRectEmpty(&rectangle)) return TRUE;

    GetWindowText(window_handle, window_title, sizeof(window_title));
    if(strlen(window_title) == 0) return TRUE;
    if(!strcmp(window_title, "Program Manager")) return TRUE;

    window_list[window_list_index] = window_handle;
    window_list_index++;

    printf("%u - %s\n", window_list_index, window_title);

    if(window_list_index == WINDOW_LIST_LIMIT) return FALSE;
    return TRUE;
}

int main(int argc, char** argv)
{
    unsigned int i, input;

    EnumWindows((WNDENUMPROC) EnumWindowsProc, (LPARAM) NULL);

    printf("\nChoose a window: ");
    scanf("%u", &input);
    printf("\n");
    if(input > window_list_index) FatalError("Bad choice..\n");

    HDC window_dc = GetWindowDC(window_list[input - 1]), global_dc = GetDC(0), temp_dc;
    if(!window_dc && !global_dc) FatalError("Fatal Error - Cannot get device context.\n");

    POINT cursor, previous_cursor;

    while(1)
    {
        temp_dc = CreateCompatibleDC(window_dc);
        if(!temp_dc) FatalError("Fatal Error - Cannot create compatible device context.\n");

        RECT window_rectangle;
        GetWindowRect(window_list[input - 1], &window_rectangle);

        HBITMAP bitmap = CreateCompatibleBitmap(window_dc,
            window_rectangle.right - window_rectangle.left,
            window_rectangle.bottom - window_rectangle.top);

        if (bitmap)
        {
            SelectObject(temp_dc, bitmap);
            PrintWindow(window_list[input - 1], temp_dc, 0);
            DeleteObject(bitmap);
        }

        GetCursorPos(&cursor);
        if(cursor.x != previous_cursor.x && cursor.y != previous_cursor.y)
        {
            COLORREF color = GetPixel(temp_dc, cursor.x - window_rectangle.left, cursor.y - window_rectangle.top);
            int red = GetRValue(color);
            int green = GetGValue(color);
            int blue = GetBValue(color);

            printf("\rRGB %02X%02X%02X", red, green, blue);

            cursor = previous_cursor;
        }

        DeleteDC(temp_dc);
        Sleep(50); // for lags
    }

    ReleaseDC(window_list[input - 1], window_dc);
    return 0;
}

Я кое-что изменил, теперь User32 не загружается динамически.
Компилируетсяс

gcc main.c -o main.exe -lGid32 -lUser32

Хорошего дня!

Ответы [ 4 ]

4 голосов
/ 03 января 2012
 HDC process_dc = GetDC(process_handle)

Ну, это все виды неправильно. GetDC принимает дескриптор окна, а не дескриптор процесса.

Чтобы найти такие ошибки, перекомпилируйте с

#define STRICT

размещено перед включением.

4 голосов
/ 03 января 2012

Вы передаете дескриптор процесса на GetDC.Это не правильно.Процессы не имеют контекста устройства, окна имеют.Помните, что у процесса может быть много окон, или даже вообще нет.

Вам нужно взять дескриптор окна, HWND, для рассматриваемого окна и передать его в GetDC.Я бы хотел использовать FindWindow или EnumWindows, чтобы найти целевое окно верхнего уровня.

Конечно, могут быть другие проблемы с вашим кодом, но это та, которая выскакивает у меня.

2 голосов
/ 03 января 2012

Это немного запутанная тема, поэтому давайте посмотрим, смогу ли я кое-что прояснить.

Сначала обо всем: как Дэвид, так и Бен уже ответили, вы передаете дескриптор процесса в функцию GetDC, что неправильно. GetDC принимает дескриптор окна (тип HWND) и возвращает контекст устройства (DC, тип HDC), соответствующий этому окну. Вам нужно исправить это, прежде чем что-нибудь еще сработает.

Теперь, как показывает прочитанная статья, окна (при условии, что они были правильно запрограммированы) реагируют на сообщения WM_PRINT или WM_PRINTCLIENT, визуализируя свое изображение в указанном контексте устройства (HDC ). Это простой и эффективный способ захвата «изображения» окна, будь то перекрывающееся окно или окно отдельного элемента управления.

Проблема возникает, как упоминал Ганс в комментарии, потому что имеет отношение к контекстам устройства и имеет сходство процессов , что означает, что HDC вы передаете окну в отдельном процессе, в который он входит. Предполагается, что будет отображаться сам, не будет действительным от этого другого процесса. Дескрипторы к контексту устройства не могут быть переданы через границы процесса. Это основная причина, по которой ваш код не работает (или собирается потерпеть неудачу, как только вы исправите проблемы с типом дескриптора). Запись MSDN для объектов GDI ясно дает понять:

Дескрипторы объектов GDI являются частными для процесса. То есть только процесс, создавший объект GDI, может использовать дескриптор объекта.

Исправить или обойти это будет тяжелым сражением. Единственное известное мне решение состоит в том, чтобы ввести код в процесс другого приложения, который сначала создает DC в памяти, а затем отправляет сообщение WM_PRINT или WM_PRINTCLIENT в окно, принадлежащее этому процессу, для рисования в это устройство в памяти контекста, а затем передает полученное растровое изображение обратно в ваше собственное приложение. Это потребует от вас реализации какого-либо механизма межпроцессного взаимодействия.

Я видел некоторые неподтвержденные доказательства того, что передача контекста устройства между процессами с помощью сообщений WM_PRINT и WM_PRINTCLIENT «работает», но мне неясно, является ли это артефактом текущей реализации (и поэтому подвержен в будущих версиях Windows), или, если это так, потому что Windows фактически обрабатывает маршалинг между процессами. Я не видел никакой документации, так или иначе. Если это разовый проект, который вы делаете для развлечения или для ограниченного использования, вы можете попробовать его и сойти с рук. Для других целей вы, вероятно, захотите исследовать использование IPC, чтобы действительно сделать это правильно.

1 голос
/ 03 января 2012

Не используйте GetDC для DC, чтобы передать PrintWindow. Вам нужно создать совместимый DC, как вы делаете (хотя вы можете передать его NULL, чтобы получить универсальный экран DC), затем создать совместимое растровое изображение размером окна, которое вы пытаетесь захватить, и выбрать его в DC. Затем передайте этот дескриптор DC в PrintWindow.

Windows не обязана правильно отвечать на WM_PRINT или WM_PRINTCLIENT, поэтому могут быть некоторые сбои, даже если вы заставите это работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...