在C语言中如何在Windows系统下截屏

5

我想在我的Windows机器上用C语言截取屏幕并保存为jpg或bmp格式等。我尝试自己编写程序,它可以正常工作但是非常慢,不像按下prt scr键那样快速。我想知道是否有方法可以访问prt scr剪贴板并将其粘贴到jpg/png文件中,或者是否有更快捷的方式获取所有屏幕像素。

以下是我的代码:

int main()
{
    bitmap_t pic;
    int i, j;
    pic.width = GetSystemMetrics(SM_CXSCREEN);
    pic.height = GetSystemMetrics(SM_CYSCREEN);

    pic.pixels = (pixel_t**)malloc(sizeof(pixel_t*)*pic.width);
    for(i = 0 ; i < pic.height ; i++)
    {
        pic.pixels[i] = (pixel_t*)malloc(sizeof(pixel_t)*pic.height);
    }

    HDC hdc = GetDC(NULL);
    COLORREF c;
    printf("Size of your monitor is %d by %d.\n", pic.width, pic.height);
    for(i = 0 ; i < pic.width ; i++)
    {
        for(j = 0 ; j < pic.height ; j++)
        {
            c = GetPixel(hdc, i, j);
            pic.pixels[i][j].red = (uint8_t)GetRValue(c);
            pic.pixels[i][j].green = (uint8_t)GetGValue(c);
            pic.pixels[i][j].blue = (uint8_t)GetBValue(c);
        }
    }

    ReleaseDC(NULL, hdc);
    save_png_to_file(&pic, "D:\\pic.png");

    for(i = 0 ; i < pic.height ; i++)
    {
        free(pic.pixels[i]);
    }
    free(pic.pixels);

    return 0;
}

save_png_to_file函数正常工作,但循环次数太多了(我的屏幕分辨率为1366x768,循环次数超过一百万)- 为什么按下 prt scr 键却如此轻松?


像这样:http://www.daniweb.com/software-development/cpp/threads/119804/screenshot-maybe-win32api#post593378? - herohuyongtao
2
很可能你找到了最慢的方法。 :) 这个问题缺乏细节,但建议是正确的,你需要创建一个位图并将屏幕复制到它中,然后对该位图进行操作。GetPixel非常慢,但如果你从视频内存中复制,情况会更糟。 - Retired Ninja
@RetiredNinja 我看到了 BitBlt,我理解了它的用途 - 但是我该如何将其保存为 png/jpeg?甚至是 bmp。 - John
可能是如何在Windows上截取屏幕并保存为JPEG格式?的重复问题。 - Raymond Chen
你在使用哪些库? - Daniel Bartov
2个回答

3

嗯... GetPixel() 单独使用时速度真的很慢,再和这么多次循环结合起来... 不能太好。 函数 BitBlt 快得多 - 这里有一个例子:

BOOL SaveToFile(HBITMAP hBitmap3, LPCTSTR lpszFileName)
{   
  HDC hDC;
  int iBits;
  WORD wBitCount;
  DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;
  BITMAP Bitmap0;
  BITMAPFILEHEADER bmfHdr;
  BITMAPINFOHEADER bi;
  LPBITMAPINFOHEADER lpbi;
  HANDLE fh, hDib, hPal,hOldPal2=NULL;
  hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
  DeleteDC(hDC);
  if (iBits <= 1)
    wBitCount = 1;
  else if (iBits <= 4)
    wBitCount = 4;
  else if (iBits <= 8)
    wBitCount = 8;
  else
    wBitCount = 24; 
  GetObject(hBitmap3, sizeof(Bitmap0), (LPSTR)&Bitmap0);
  bi.biSize = sizeof(BITMAPINFOHEADER);
  bi.biWidth = Bitmap0.bmWidth;
  bi.biHeight =-Bitmap0.bmHeight;
  bi.biPlanes = 1;
  bi.biBitCount = wBitCount;
  bi.biCompression = BI_RGB;
  bi.biSizeImage = 0;
  bi.biXPelsPerMeter = 0;
  bi.biYPelsPerMeter = 0;
  bi.biClrImportant = 0;
  bi.biClrUsed = 256;
  dwBmBitsSize = ((Bitmap0.bmWidth * wBitCount +31) & ~31) /8
                                                * Bitmap0.bmHeight; 
  hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
  lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
  *lpbi = bi;

  hPal = GetStockObject(DEFAULT_PALETTE);
  if (hPal)
  { 
    hDC = GetDC(NULL);
    hOldPal2 = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
    RealizePalette(hDC);
  }


  GetDIBits(hDC, hBitmap3, 0, (UINT) Bitmap0.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) 
    +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);

  if (hOldPal2)
  {
    SelectPalette(hDC, (HPALETTE)hOldPal2, TRUE);
    RealizePalette(hDC);
    ReleaseDC(NULL, hDC);
  }

  fh = CreateFile(lpszFileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS, 
    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

  if (fh == INVALID_HANDLE_VALUE)
    return FALSE; 

  bmfHdr.bfType = 0x4D42; // "BM"
  dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
  bmfHdr.bfSize = dwDIBSize;
  bmfHdr.bfReserved1 = 0;
  bmfHdr.bfReserved2 = 0;
  bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;

  WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);

  WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
  GlobalUnlock(hDib);
  GlobalFree(hDib);
  CloseHandle(fh);

  return TRUE;
} 

int screenCapture(int x, int y, int w, int h, LPCSTR fname)
{
    HDC hdcSource = GetDC(NULL);
    HDC hdcMemory = CreateCompatibleDC(hdcSource);

    int capX = GetDeviceCaps(hdcSource, HORZRES);
    int capY = GetDeviceCaps(hdcSource, VERTRES);

    HBITMAP hBitmap = CreateCompatibleBitmap(hdcSource, w, h);
    HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMemory, hBitmap);

    BitBlt(hdcMemory, 0, 0, w, h, hdcSource, x, y, SRCCOPY);
    hBitmap = (HBITMAP)SelectObject(hdcMemory, hBitmapOld);

    DeleteDC(hdcSource);
    DeleteDC(hdcMemory);

    HPALETTE hpal = NULL;
    if(SaveToFile(hBitmap, fname)) return 1;
    return 0;
}

int main()
{
    screenCapture(0, 0, GetSystemMetric, 768, "D:\\MyFirstScreeshot.bmp");

    return 0;
}

5
这是什么混乱的局面,难道只是为了截图真的需要这么麻烦吗? - CrackSmoker9000
我在Visual Studio中测试了一下,但是没有成功。必须要修复一些问题才能编译通过。需要通过LPCTSTR(fname)进行fname的强制转换,并且还需要在第三个参数GetSystemMetrics(SM_CXSCREEN)中添加一个参数。 - Entree
为了使其可编译,请在顶部添加#include<windows.h>,将GetSystemMetric替换为GetSystemMetrics(0),并添加额外的库文件user32.libgdi32.lib - Gray Programmerz
我使用tcc编译它,命令为tcc -impdef User32.dll & tcc -impdef Gdi32.dll & tcc -lGdi32 -lUser32 screenshot.c - Gray Programmerz
我建议用1280替换GetSystemMetric - Gray Programmerz
"cast doing LPCTSTR(fname)" 不起作用。编译器告诉你在UNICODE构建中存在一个真正的问题。所有的 fname 参数需要改为相同的类型(选项1:LPCSTR 并使用 CreateFileA),(选项2:LPCTSTR 并在主函数中的字面量周围使用 _T()),(选项3:LPCWSTR,在主函数中的字面量前面加上 L,并使用 CreateFileW)。 - undefined

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接