【问题标题】:What's wrong with this screen capture code?这个屏幕截图代码有什么问题?
【发布时间】:2014-03-02 18:45:23
【问题描述】:

我在这里找到了一个屏幕截图代码,但是当我尝试构建它时它不会构建,所以我自己修复了代码,现在它构建了,但我认为它不起作用,因为调试 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
}

【问题讨论】:

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


    【解决方案1】:

    简短的回答:没有错。

    hBitmap 包含一个位图句柄,该位图具有通过BitBlt 检索到的屏幕截图数据。当您在 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 执行其他修改987654331@.

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

    CreateDIBSection @ MSDN

    【讨论】:

    • 我有一个问题,#include "stdafx.h"(没有这样的文件或目录)。 (我创建了一个空的 Win32 项目...)
    • 如果我将 stdafx 注释掉,我仍然会收到 diff 错误:错误 C2061:语法错误:标识符 '_TCHAR'。如果我包含 tchar.h,我得到一个 diff 错误:错误 LNK2019:未解析的外部符号 _WinMain@16 在函数 ___tmainCRTStartup 中引用
    • int main() 可以用于探索,而不是 int _tmain(int argc, _TCHAR* argv[]),但您应该考虑在 UNICODE 下编译,您是对的,“stdafx.h”将不存在,如果你创建一个空的 Win32 项目(尝试创建一个控制台项目)
    【解决方案2】:

    让我们看看。当你第一次使用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;
    

    变态

    【讨论】:

    • 对不起,你有点失去我了。
    • @Tez 第一个 SelectObject() 使 hMemoryDC 从 1x1 到 XxY 维度。第二个SelectObject() 使 hMemoryDC 从 XxY 回到 1x1,所以你不能使用它。
    • 所以换句话说,一切都是正确的,除了我只需要删除: hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);并从 hOldBitmap 获取位图信息?比特币呢?如果在 hOldBitmap 之后可以吗?
    • @Tez 你想绘制屏幕的一部分或者你想把它保存为bmp?
    • @Tez 如果您想使用 hMemoryDC 进行绘图,请删除第二个 SelectObject(). 如果您想将其另存为 bmp,请使用以下链接 here
    猜你喜欢
    • 1970-01-01
    • 2012-12-20
    • 2012-08-07
    • 2014-08-29
    • 2013-06-05
    • 2014-06-04
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多