这个屏幕截图代码有什么问题?

时间:2014-02-06 01:48:28

标签: c++ c windows winapi screen-capture

我在这里找到了一个屏幕截图代码,但是当我尝试构建它时它不会构建,所以我自己修复了代码,现在它已经构建但是我认为它不起作用,因为当调试VS说它无法从hBitmap读取(没有数据?)。我是新手程序员,所以我真的不知道该做什么...而且我可能没有正确修复代码...

感谢您的帮助。

#include <Windows.h>

int main()
{
    // get the device context of the screen
    HDC hScreenDC = CreateDC(L"DISPLAY", NULL, NULL, NULL);     
    // and a device context to put it in
    HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

    int x = GetDeviceCaps(hScreenDC, HORZRES);
    int y = GetDeviceCaps(hScreenDC, VERTRES);

    // maybe worth checking these are positive values
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, x, y);

    // get a new bitmap
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

    BitBlt(hMemoryDC, 0, 0, 640, 480, hScreenDC, 0, 0, SRCCOPY);
    hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
    // now your image is held in hBitmap. You can save it or do whatever with it
}

2 个答案:

答案 0 :(得分:2)

简短回答:没有错。

hBitmap包含位图的句柄,该位图具有通过BitBlt检索的screencap数据。当您将鼠标悬停在Visual Studio中的hBitmap上时,它只是通知您hBitmap不是指向内存的有效指针,这是一个正确的报表 - 窗口句柄只是解析为内存位置的结构的标记和实现由Windows API私有管理。

为了证明您的代码确实从屏幕中提取了某些内容,请尝试将其写入文件。使用GDI +写入文件很有帮助,因为它可以为您节省大量的手动编写的初始化代码。

这是一个快速控制台应用,它将使用您的代码和a helper function GetEncoderClsid to get the PNG encoder发出PNG文件。

#include "stdafx.h"
#include <Windows.h>
#include <gdiplus.h>

using namespace Gdiplus;

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
  UINT  num = 0;          // number of image encoders
  UINT  size = 0;         // size of the image encoder array in bytes

  ImageCodecInfo* pImageCodecInfo = NULL;

  GetImageEncodersSize(&num, &size);
  if (size == 0)
    return -1;  // Failure

  pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
  if (pImageCodecInfo == NULL)
    return -1;  // Failure

  GetImageEncoders(num, size, pImageCodecInfo);

  for (UINT j = 0; j < num; ++j)
  {
    if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
    {
      *pClsid = pImageCodecInfo[j].Clsid;
      free(pImageCodecInfo);
      return j;  // Success
    }
  }

  free(pImageCodecInfo);
  return -1;  // Failure
}

int _tmain(int argc, _TCHAR* argv[])
{
  GdiplusStartupInput gdiplusStartupInput;
  ULONG_PTR gdiplusToken;
  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

  // get the device context of the screen
  HDC hScreenDC = CreateDC(L"DISPLAY", NULL, NULL, NULL);
  // and a device context to put it in
  HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

  int x = GetDeviceCaps(hScreenDC, HORZRES);
  int y = GetDeviceCaps(hScreenDC, VERTRES);

  // maybe worth checking these are positive values
  HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, x, y);

  // get a new bitmap
  HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

  BitBlt(hMemoryDC, 0, 0, 640, 480, hScreenDC, 0, 0, SRCCOPY);
  hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
  // now your image is held in hBitmap. You can save it or do whatever with it

  CLSID pngClsid;
  GetEncoderClsid(L"image/png", &pngClsid);

  Bitmap *bmp = new Bitmap(hBitmap, NULL);
  bmp->Save(L"desktop_slice.png", &pngClsid, NULL);
  delete bmp;

  GdiplusShutdown(gdiplusToken);
  return 0;
}

确保将gdiplus.lib添加到项目设置中的源库列表中。运行此命令将创建一个名为“desktop_slice.png”的文件,以便发出。

如果在检索包含屏幕数据的位图后需要执行其他工作,则应将其选择为兼容的DC并使用该DC调用其他GDI函数,或者在交换位图之前对hMemoryDC执行其他修改与SelectObject

如果您需要在像素级别执行较低级别的工作,您应该考虑使用满足您需求的已知像素格式创建DIB部分,并使用从ppvBits参数返回的指针。

CreateDIBSection @ MSDN

答案 1 :(得分:1)

让我们看看。第一次使用SelectObject()

HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);  

您将hBitmap与hMemoryDC相关联。只要hMemoryDC存在,你就不能删除hBitmap!所以打电话

DeleteObject(hBitmap); 

什么都不做。为了正确删除hBitmap,你可以使用hOldBitmap并执行

SelectObject(hMemoryDC, hOldBitmap);

在此之后,您将无法再使用hMemoryDC。所以擦除你的最后一行

hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);

你应该没事。当你不再需要hMemoryDC时释放资源。

修改

发布hBitmap和hMemoryDC的正确方法是:

SelectObject(hMemoryDC, hOldBitmap);
DeleteObject(hBitmap);
hBitmap = NULL;
DeleteDC(hMemoryDC);
hMemoryDC = NULL;

瓦尔特