捕获窗口像素,无论其z顺序如何

时间:2012-01-03 01:31:59

标签: c windows winapi

我实际上是在尝试读取其他人隐藏的窗口上的特定像素。我想使用GDI库中的GetPixel函数,但它似乎只适用于全局设备上下文。我无法从特定窗口读取像素,我不明白为什么.. 我发现this article使用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 个答案:

答案 0 :(得分:4)

您正在将处理句柄传递给GetDC。那是不对的。进程没有设备上下文,Windows也没有。记住一个进程可以有很多窗口,甚至根本没有窗口。

您需要抓住相关窗口的窗口句柄HWND,然后将其传递给GetDC。我希望使用FindWindowEnumWindows来找到您的目标顶级窗口。

当然,您的代码可能还有其他问题,但那是跳出来的问题。

答案 1 :(得分:4)

 HDC process_dc = GetDC(process_handle)

那是各种各样的错误。 GetDC接受窗口句柄,而不是进程句柄。

要查找此类错误,请使用

重新编译
#define STRICT

放在您的包含之前。

答案 2 :(得分:2)

这是一个令人困惑的主题,所以让我们看看我是否可以澄清一些事情。

首先要做的事情是:正如大卫和本已经回答的那样,你正在向GetDC函数传递一个进程句柄,这是错误的。 GetDC接受窗口HWND类型)的句柄,并返回与该窗口对应的设备上下文(DC,HDC类型)。你需要在其他任何工作之前解决问题。

现在,正如您所阅读的文章所指出的那样,Windows(假设它们已被正确编程)通过将自己的图像渲染到指定的设备上下文来响应WM_PRINTWM_PRINTCLIENT消息(HDC)。这是一种捕获窗口“图像”的简单有效方法,无论是重叠窗口还是单个控件的窗口。

正如Hans在评论中提到的那样,因为设备上下文的句柄具有进程关联性,这意味着您在单独的进程中传递给窗口的HDC,应该将其自身呈现的内容将不会从其他进程中生效。设备上下文的句柄不能跨进程边界传递。这是您的代码失败的主要原因(或者一旦您修复了句柄类型问题就会失败)。 MSDN entry on GDI Objects明确说明了这一点:

  

GDI对象的句柄对进程是私有的。也就是说,只有创建GDI对象的进程才能使用对象句柄。

修复或绕过这将是一场艰苦的战斗。我所知道的唯一解决方案是将代码注入到其他应用程序的进程中,该进程首先在内存中创建DC,然后将WM_PRINTWM_PRINTCLIENT消息发送到该进程拥有的窗口以绘制到该进程中内存设备上下文,然后将生成的位图传输回您自己的应用程序。这将要求您实现某种类型的进程间通信机制。

我已经看到一些轶事证据表明通过WM_PRINTWM_PRINTCLIENT消息“工作”在进程之间传递设备上下文句柄,但我不清楚这是否是当前实现的工件(因此在Windows的未来版本中会受到破坏),或者这是因为Windows实际上正在处理进程之间的编组。我没有看到任何文件的方式或方式。如果这是一个一次性的项目,你是为了娱乐或有限的使用,你可以尝试它并逃脱它。出于其他目的,您可能希望使用IPC进行调查,以正确的方式执行此操作。

答案 3 :(得分:1)

不要使用GetDC将DC传递给PrintWindow。您需要创建兼容的DC(尽管您可以将其传递给NULL以获得通用屏幕DC),然后创建与您尝试捕获的窗口大小相同的兼容位图并将其选择到DC中。然后将该DC句柄传递给PrintWindow。

Windows不需要对WM_PRINT或WM_PRINTCLIENT做出正确响应,因此即使让它工作也可能会出现一些故障。