获取一个窗口的截图
我想要通过窗口句柄获取一个窗口的截图,这个窗口是一个外部程序的,只知道进程号,然后回显到当前正在运行的程序窗口上。这些代码是我从https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image复制下来的,在147行GetDC(NULL)获取桌面窗口句柄,这样可以成功显示。
但是当我把GetDC()参数换成上面这种通过进程ID获取的窗口句柄时,显示都是黑屏的。我不知道是什么原因,求大佬解答{:10_266:}BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
EnumWindowsArg* pArg = (EnumWindowsArg*)lParam;
DWORDdwProcessID = 0;
// 通过窗口句柄取得进程ID
::GetWindowThreadProcessId(hwnd, &dwProcessID);
if (dwProcessID == pArg->dwProcessID)
{
pArg->hwndWindow = hwnd;
// 找到了返回FALSE
return FALSE;
}
// 没找到,继续找,返回TRUE
return TRUE;
}
///< 通过进程ID获取窗口句柄
HWND GetWindowHwndByPID(DWORD dwProcessID)
{
HWND hwndRet = NULL;
EnumWindowsArg ewa;
ewa.dwProcessID = dwProcessID;
ewa.hwndWindow = NULL;
EnumWindows(EnumWindowsProc, (LPARAM)&ewa);
if (ewa.hwndWindow)
{
hwndRet = ewa.hwndWindow;
}
return hwndRet;
}
// GDI_CapturingAnImage.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "GDI_CapturingAnImage.h"
#include "GetHWND.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle; // The title bar text
WCHAR szWindowClass; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_GDICAPTURINGANIMAGE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GDICAPTURINGANIMAGE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
//
//FUNCTION: MyRegisterClass()
//
//PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GDICAPTURINGANIMAGE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_GDICAPTURINGANIMAGE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// FUNCTION: CaptureAnImage(HWND hWnd)
//
// PURPOSE: Captures a screenshot into a window ,and then saves it in a .bmp file.
//
// COMMENTS:
//
// Note: This function attempts to create a file called captureqwsx.bmp
//
int CaptureAnImage(HWND hWnd)
{
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
DWORD dwBytesWritten = 0;
DWORD dwSizeofDIB = 0;
HANDLE hFile = NULL;
char* lpbitmap = NULL;
HANDLE hDIB = NULL;
DWORD dwBmpSize = 0;
// Retrieve the handle to a display device context for the client
// area of the window.
// HWND nhdcScreen = GetWindowHwndByPID(27380);
hdcScreen = GetDC(NULL); // source
hdcWindow = GetDC(hWnd);// destination
// Create a compatible DC, which is used in a BitBlt from the window DC.
hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
goto done;
}
// Get the client area for size calculation.
RECT rcClient;
GetClientRect(hWnd, &rcClient);
// This is the best stretch mode.
SetStretchBltMode(hdcWindow, HALFTONE);
// The source DC is the entire screen, and the destination DC is the current window (HWND).
if (!StretchBlt(hdcWindow,
0, 0,
rcClient.right, rcClient.bottom,
hdcScreen,
0, 0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SRCCOPY))
{
MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
goto done;
}
// Create a compatible bitmap from the Window DC.
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
// Get the BITMAP from the HBITMAP.
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
// Gets the "bits" from the bitmap, and copies them into a buffer
// that's pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
hFile = CreateFile(L"captureqwsx.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size.
dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
// Size of the file.
bmfHeader.bfSize = dwSizeofDIB;
// bfType must always be BM for Bitmaps.
bmfHeader.bfType = 0x4D42; // BM.
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
// Unlock and Free the DIB from the heap.
GlobalUnlock(hDIB);
GlobalFree(hDIB);
// Close the handle for the file that was created.
CloseHandle(hFile);
// Clean up.
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
return 0;
}
//
//FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//PURPOSE: Processes messages for the main window.
//
//WM_COMMAND- process the application menu
//WM_PAINT - Paint the main window
//WM_DESTROY- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
CaptureAnImage(hWnd);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
你遇到的问题是在将代码中的获取窗口句柄的函数`GetWindowHwndByPID`替换进程号获取窗口句柄代码时出现黑屏的情况。这是因为你使用的是外部程序的进程ID,而不是窗口句柄。
想要通过进程ID获取窗口句柄,你可以使用以下函数来实现:
HWND GetWindowHwndByPID(DWORD dwProcessID)
{
HWND hwndRet = NULL;
EnumWindows([&](HWND hWnd, LPARAM lParam) {
DWORD pid = 0;
GetWindowThreadProcessId(hWnd, &pid);
if (pid == dwProcessID) {
hwndRet = hWnd;
return FALSE; // 找到了就返回FALSE停止枚举
}
return TRUE; // 没找到继续
}, 0);
return hwndRet;
}
请注意这里我们使用了C++11中的lambda表达式来实现回调函数,以便在回调函数内部使用局部变量。
然后,你需要在`WndProc`中进行截图并回显到当前窗口。你可以修改`WM_PAINT`消息的处理部分,如下所示:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HWND targetHwnd = GetWindowHwndByPID(外部程序的进程ID);
if (targetHwnd != NULL) {
CaptureAnImage(targetHwnd);
// 将截图显示在当前窗口
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, L"captureqwsx.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBitmap);
BITMAP bitmap;
GetObject(hBitmap, sizeof(bitmap), &bitmap);
BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
}
EndPaint(hWnd, &ps);
break;
}
这样,当`WM_PAINT`消息被触发时,会获取外部程序窗口的截图,并将截图显示在当前程序窗口上。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 {:10_256:} 厉害啊 当你在使用 GetDC() 函数来捕获一个特定窗口的屏幕图像时,需要注意几个关键点。GetDC(NULL) 实际上获取的是整个屏幕的DC(设备上下文),而当你尝试对一个特定的窗口句柄调用 GetDC(hwnd) 时,你期望得到的是这个窗口的DC。
然而,仅仅使用 GetDC(hwnd) 可能不足以捕获一个窗口的内容,尤其是当窗口有自己的绘图逻辑(如使用DirectX、OpenGL或其他图形API)时。此外,如果窗口是透明的或者绘制在其上的内容是部分或全部透明的,你可能无法看到预期的内容。
下面是一些步骤和建议,帮助你成功捕获一个窗口的图像:
确保窗口存在并可见:
确保你捕获的窗口确实存在并且是可见的。你可以通过调用 IsWindowVisible() 来检查这一点。
使用 PrintWindow 函数:
PrintWindow 函数是一个更高级别的API,它专门用于捕获窗口的图像。这个函数会发送一个 WM_PRINT 消息到目标窗口,这通常会导致窗口按照打印机的方式渲染其内容。这通常比直接使用 GetDC 和位图操作更有效。
cpp
HWND hwnd = GetWindowHwndByPID(dwProcessID);
if (hwnd && IsWindowVisible(hwnd)) {
RECT rc;
GetClientRect(hwnd, &rc);
HDC hdcScreen = GetDC(NULL);
HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
SelectObject(hdcMem, hBitmap);
PrintWindow(hwnd, hdcMem, PW_CLIENTONLY);
// 现在 hBitmap 包含了你想要的窗口图像
// ... 在这里将 hBitmap 绘制到另一个窗口或保存为文件 ...
// 清理资源
DeleteDC(hdcMem);
DeleteObject(hBitmap);
ReleaseDC(NULL, hdcScreen);
}
检查窗口的绘制逻辑:
如果 PrintWindow 仍然不能捕获你想要的图像,可能是因为窗口使用了特定的绘制逻辑,这些逻辑在接收到 WM_PRINT 消息时不会按预期工作。在这种情况下,你可能需要更深入地了解窗口的绘制机制,并考虑使用其他方法(如钩子、API劫持等)来捕获图像。
权限问题:
在某些情况下,尝试捕获其他进程的窗口图像可能会受到操作系统安全性的限制。确保你的应用程序有足够的权限来执行此操作。
错误处理:
确保你的代码中有适当的错误处理机制,以处理任何可能发生的失败情况(如窗口不存在、无法获取DC等)。
资源清理:
在捕获图像后,确保清理所有分配的资源(如HDC、HBITMAP等),以防止资源泄漏。
页:
[1]