鱼C论坛

 找回密码
 立即注册
查看: 1016|回复: 4

获取一个窗口的截图

[复制链接]
发表于 2024-5-12 22:46:32 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
我想要通过窗口句柄获取一个窗口的截图,这个窗口是一个外部程序的,只知道进程号,然后回显到当前正在运行的程序窗口上。
这些代码是我从https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image复制下来的,在147行GetDC(NULL)获取桌面窗口句柄,这样可以成功显示。
但是当我把GetDC()参数换成上面这种通过进程ID获取的窗口句柄时,显示都是黑屏的。我不知道是什么原因,求大佬解答
  1. BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
  2. {
  3.         EnumWindowsArg* pArg = (EnumWindowsArg*)lParam;
  4.         DWORD  dwProcessID = 0;
  5.         // 通过窗口句柄取得进程ID
  6.         ::GetWindowThreadProcessId(hwnd, &dwProcessID);
  7.         if (dwProcessID == pArg->dwProcessID)
  8.         {
  9.                 pArg->hwndWindow = hwnd;
  10.                 // 找到了返回FALSE
  11.                 return FALSE;
  12.         }
  13.         // 没找到,继续找,返回TRUE
  14.         return TRUE;
  15. }

  16. ///< 通过进程ID获取窗口句柄
  17. HWND GetWindowHwndByPID(DWORD dwProcessID)
  18. {
  19.         HWND hwndRet = NULL;
  20.         EnumWindowsArg ewa;
  21.         ewa.dwProcessID = dwProcessID;
  22.         ewa.hwndWindow = NULL;
  23.         EnumWindows(EnumWindowsProc, (LPARAM)&ewa);
  24.         if (ewa.hwndWindow)
  25.         {
  26.                 hwndRet = ewa.hwndWindow;
  27.         }
  28.         return hwndRet;
  29. }
复制代码


  1. // GDI_CapturingAnImage.cpp : Defines the entry point for the application.
  2. //

  3. #include "framework.h"
  4. #include "GDI_CapturingAnImage.h"
  5. #include "GetHWND.h"

  6. #define MAX_LOADSTRING 100

  7. // Global Variables:
  8. HINSTANCE hInst;                                // current instance
  9. WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
  10. WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

  11. // Forward declarations of functions included in this code module:
  12. ATOM                MyRegisterClass(HINSTANCE hInstance);
  13. BOOL                InitInstance(HINSTANCE, int);
  14. LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
  15. INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

  16. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  17.   _In_opt_ HINSTANCE hPrevInstance,
  18.   _In_ LPWSTR    lpCmdLine,
  19.   _In_ int       nCmdShow)
  20. {
  21.   UNREFERENCED_PARAMETER(hPrevInstance);
  22.   UNREFERENCED_PARAMETER(lpCmdLine);

  23.   // TODO: Place code here.

  24.   // Initialize global strings
  25.   LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
  26.   LoadStringW(hInstance, IDC_GDICAPTURINGANIMAGE, szWindowClass, MAX_LOADSTRING);
  27.   MyRegisterClass(hInstance);

  28.   // Perform application initialization:
  29.   if (!InitInstance(hInstance, nCmdShow))
  30.   {
  31.     return FALSE;
  32.   }

  33.   HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GDICAPTURINGANIMAGE));

  34.   MSG msg;

  35.   // Main message loop:
  36.   while (GetMessage(&msg, nullptr, 0, 0))
  37.   {
  38.     if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
  39.     {
  40.       TranslateMessage(&msg);
  41.       DispatchMessage(&msg);
  42.     }
  43.   }

  44.   return (int)msg.wParam;
  45. }

  46. //
  47. //  FUNCTION: MyRegisterClass()
  48. //
  49. //  PURPOSE: Registers the window class.
  50. //
  51. ATOM MyRegisterClass(HINSTANCE hInstance)
  52. {
  53.   WNDCLASSEXW wcex;

  54.   wcex.cbSize = sizeof(WNDCLASSEX);

  55.   wcex.style = CS_HREDRAW | CS_VREDRAW;
  56.   wcex.lpfnWndProc = WndProc;
  57.   wcex.cbClsExtra = 0;
  58.   wcex.cbWndExtra = 0;
  59.   wcex.hInstance = hInstance;
  60.   wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GDICAPTURINGANIMAGE));
  61.   wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
  62.   wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  63.   wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_GDICAPTURINGANIMAGE);
  64.   wcex.lpszClassName = szWindowClass;
  65.   wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

  66.   return RegisterClassExW(&wcex);
  67. }

  68. //
  69. //   FUNCTION: InitInstance(HINSTANCE, int)
  70. //
  71. //   PURPOSE: Saves instance handle and creates main window
  72. //
  73. //   COMMENTS:
  74. //
  75. //        In this function, we save the instance handle in a global variable and
  76. //        create and display the main program window.
  77. //
  78. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
  79. {
  80.   hInst = hInstance; // Store instance handle in our global variable

  81.   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
  82.     CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

  83.   if (!hWnd)
  84.   {
  85.     return FALSE;
  86.   }

  87.   ShowWindow(hWnd, nCmdShow);
  88.   UpdateWindow(hWnd);

  89.   return TRUE;
  90. }

  91. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  92. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  93. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  94. // PARTICULAR PURPOSE.
  95. //
  96. // Copyright (c) Microsoft Corporation. All rights reserved

  97. //
  98. //   FUNCTION: CaptureAnImage(HWND hWnd)
  99. //
  100. //   PURPOSE: Captures a screenshot into a window ,and then saves it in a .bmp file.
  101. //
  102. //   COMMENTS:
  103. //
  104. //      Note: This function attempts to create a file called captureqwsx.bmp
  105. //        

  106. int CaptureAnImage(HWND hWnd)
  107. {
  108.   HDC hdcScreen;
  109.   HDC hdcWindow;
  110.   HDC hdcMemDC = NULL;
  111.   HBITMAP hbmScreen = NULL;
  112.   BITMAP bmpScreen;
  113.   DWORD dwBytesWritten = 0;
  114.   DWORD dwSizeofDIB = 0;
  115.   HANDLE hFile = NULL;
  116.   char* lpbitmap = NULL;
  117.   HANDLE hDIB = NULL;
  118.   DWORD dwBmpSize = 0;

  119.   // Retrieve the handle to a display device context for the client
  120.   // area of the window.
  121.   // HWND nhdcScreen = GetWindowHwndByPID(27380);
  122.   hdcScreen = GetDC(NULL); // source

  123.   hdcWindow = GetDC(hWnd);  // destination

  124.   // Create a compatible DC, which is used in a BitBlt from the window DC.
  125.   hdcMemDC = CreateCompatibleDC(hdcWindow);

  126.   if (!hdcMemDC)
  127.   {
  128.     MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
  129.     goto done;
  130.   }

  131.   // Get the client area for size calculation.
  132.   RECT rcClient;
  133.   GetClientRect(hWnd, &rcClient);

  134.   // This is the best stretch mode.
  135.   SetStretchBltMode(hdcWindow, HALFTONE);

  136.   // The source DC is the entire screen, and the destination DC is the current window (HWND).
  137.   if (!StretchBlt(hdcWindow,
  138.     0, 0,
  139.     rcClient.right, rcClient.bottom,
  140.     hdcScreen,
  141.     0, 0,
  142.     GetSystemMetrics(SM_CXSCREEN),
  143.     GetSystemMetrics(SM_CYSCREEN),
  144.     SRCCOPY))
  145.   {
  146.     MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
  147.     goto done;
  148.   }

  149.   // Create a compatible bitmap from the Window DC.
  150.   hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);

  151.   if (!hbmScreen)
  152.   {
  153.     MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
  154.     goto done;
  155.   }

  156.   // Select the compatible bitmap into the compatible memory DC.
  157.   SelectObject(hdcMemDC, hbmScreen);

  158.   // Bit block transfer into our compatible memory DC.
  159.   if (!BitBlt(hdcMemDC,
  160.     0, 0,
  161.     rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
  162.     hdcWindow,
  163.     0, 0,
  164.     SRCCOPY))
  165.   {
  166.     MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
  167.     goto done;
  168.   }

  169.   // Get the BITMAP from the HBITMAP.
  170.   GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);

  171.   BITMAPFILEHEADER   bmfHeader;
  172.   BITMAPINFOHEADER   bi;

  173.   bi.biSize = sizeof(BITMAPINFOHEADER);
  174.   bi.biWidth = bmpScreen.bmWidth;
  175.   bi.biHeight = bmpScreen.bmHeight;
  176.   bi.biPlanes = 1;
  177.   bi.biBitCount = 32;
  178.   bi.biCompression = BI_RGB;
  179.   bi.biSizeImage = 0;
  180.   bi.biXPelsPerMeter = 0;
  181.   bi.biYPelsPerMeter = 0;
  182.   bi.biClrUsed = 0;
  183.   bi.biClrImportant = 0;

  184.   dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

  185.   // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
  186.   // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
  187.   // have greater overhead than HeapAlloc.
  188.   hDIB = GlobalAlloc(GHND, dwBmpSize);
  189.   lpbitmap = (char*)GlobalLock(hDIB);

  190.   // Gets the "bits" from the bitmap, and copies them into a buffer
  191.   // that's pointed to by lpbitmap.
  192.   GetDIBits(hdcWindow, hbmScreen, 0,
  193.     (UINT)bmpScreen.bmHeight,
  194.     lpbitmap,
  195.     (BITMAPINFO*)&bi, DIB_RGB_COLORS);

  196.   // A file is created, this is where we will save the screen capture.
  197.   hFile = CreateFile(L"captureqwsx.bmp",
  198.     GENERIC_WRITE,
  199.     0,
  200.     NULL,
  201.     CREATE_ALWAYS,
  202.     FILE_ATTRIBUTE_NORMAL, NULL);

  203.   // Add the size of the headers to the size of the bitmap to get the total file size.
  204.   dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

  205.   // Offset to where the actual bitmap bits start.
  206.   bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

  207.   // Size of the file.
  208.   bmfHeader.bfSize = dwSizeofDIB;

  209.   // bfType must always be BM for Bitmaps.
  210.   bmfHeader.bfType = 0x4D42; // BM.

  211.   WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
  212.   WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
  213.   WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

  214.   // Unlock and Free the DIB from the heap.
  215.   GlobalUnlock(hDIB);
  216.   GlobalFree(hDIB);

  217.   // Close the handle for the file that was created.
  218.   CloseHandle(hFile);

  219.   // Clean up.
  220. done:
  221.   DeleteObject(hbmScreen);
  222.   DeleteObject(hdcMemDC);
  223.   ReleaseDC(NULL, hdcScreen);
  224.   ReleaseDC(hWnd, hdcWindow);

  225.   return 0;
  226. }

  227. //
  228. //  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
  229. //
  230. //  PURPOSE: Processes messages for the main window.
  231. //
  232. //  WM_COMMAND  - process the application menu
  233. //  WM_PAINT    - Paint the main window
  234. //  WM_DESTROY  - post a quit message and return
  235. //
  236. //
  237. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  238. {
  239.   switch (message)
  240.   {
  241.   case WM_COMMAND:
  242.   {
  243.     int wmId = LOWORD(wParam);
  244.     // Parse the menu selections:
  245.     switch (wmId)
  246.     {
  247.     case IDM_ABOUT:
  248.       DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
  249.       break;
  250.     case IDM_EXIT:
  251.       DestroyWindow(hWnd);
  252.       break;
  253.     default:
  254.       return DefWindowProc(hWnd, message, wParam, lParam);
  255.     }
  256.   }
  257.   break;
  258.   case WM_PAINT:
  259.   {
  260.     PAINTSTRUCT ps;
  261.     HDC hdc = BeginPaint(hWnd, &ps);
  262.     CaptureAnImage(hWnd);
  263.     EndPaint(hWnd, &ps);
  264.   }
  265.   break;
  266.   case WM_DESTROY:
  267.     PostQuitMessage(0);
  268.     break;
  269.   default:
  270.     return DefWindowProc(hWnd, message, wParam, lParam);
  271.   }
  272.   return 0;
  273. }

  274. // Message handler for about box.
  275. INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  276. {
  277.   UNREFERENCED_PARAMETER(lParam);
  278.   switch (message)
  279.   {
  280.   case WM_INITDIALOG:
  281.     return (INT_PTR)TRUE;

  282.   case WM_COMMAND:
  283.     if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  284.     {
  285.       EndDialog(hDlg, LOWORD(wParam));
  286.       return (INT_PTR)TRUE;
  287.     }
  288.     break;
  289.   }
  290.   return (INT_PTR)FALSE;
  291. }
复制代码

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-5-12 22:47:01 | 显示全部楼层
你遇到的问题是在将代码中的获取窗口句柄的函数`GetWindowHwndByPID`替换进程号获取窗口句柄代码时出现黑屏的情况。这是因为你使用的是外部程序的进程ID,而不是窗口句柄。

想要通过进程ID获取窗口句柄,你可以使用以下函数来实现:

  1. HWND GetWindowHwndByPID(DWORD dwProcessID)
  2. {
  3.     HWND hwndRet = NULL;
  4.     EnumWindows([&](HWND hWnd, LPARAM lParam) {
  5.         DWORD pid = 0;
  6.         GetWindowThreadProcessId(hWnd, &pid);
  7.         if (pid == dwProcessID) {
  8.             hwndRet = hWnd;
  9.             return FALSE; // 找到了就返回FALSE停止枚举
  10.         }
  11.         return TRUE; // 没找到继续
  12.     }, 0);

  13.     return hwndRet;
  14. }
复制代码


请注意这里我们使用了C++11中的lambda表达式来实现回调函数,以便在回调函数内部使用局部变量。

然后,你需要在`WndProc`中进行截图并回显到当前窗口。你可以修改`WM_PAINT`消息的处理部分,如下所示:

  1. case WM_PAINT:
  2. {
  3.     PAINTSTRUCT ps;
  4.     HDC hdc = BeginPaint(hWnd, &ps);
  5.     HWND targetHwnd = GetWindowHwndByPID(外部程序的进程ID);
  6.     if (targetHwnd != NULL) {
  7.         CaptureAnImage(targetHwnd);
  8.         // 将截图显示在当前窗口
  9.         HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, L"captureqwsx.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  10.         HDC hdcMem = CreateCompatibleDC(hdc);
  11.         SelectObject(hdcMem, hBitmap);
  12.         BITMAP bitmap;
  13.         GetObject(hBitmap, sizeof(bitmap), &bitmap);
  14.         BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
  15.         DeleteDC(hdcMem);
  16.         DeleteObject(hBitmap);
  17.     }
  18.     EndPaint(hWnd, &ps);
  19.     break;
  20. }
复制代码


这样,当`WM_PAINT`消息被触发时,会获取外部程序窗口的截图,并将截图显示在当前程序窗口上。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-5-12 22:50:23 | 显示全部楼层

回帖奖励 +100 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-5-12 22:50:55 | 显示全部楼层
厉害啊
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-5-12 22:55:48 | 显示全部楼层
当你在使用 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等),以防止资源泄漏。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-25 11:57

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表