HDC在MSDN中的全稱為:The handle of device context。通常,我們都是用來做相應的顯示操作。
    
    熟悉WIN32的朋友對於其應該不會陌生,經常採用GetDC,GetWindowDC等等來獲取其控制代碼。而用得最多的,可能就是BeginPaint,如:

  1. case WM_PAINT:
  2. HDC hdc = BeginPaint(hWnd,&ps);
  3. ...
  4. EndPaint(hdc,&ps);
  5. break;
  1. case WM_PAINT:      HDC hdc = BeginPaint(hWnd,&ps);     ...     EndPaint(hdc,&ps);      break;

使用起來非常簡單,但如果想將其內容儲存為普通的影象文件,可就沒那麼容易。確切地說,在只知道HDC控制代碼的情況下,我們是無法儲存其內容的;但我們可以劍走偏鋒,將HDC的內容寫到一個快取中,然後我們再儲存該快取的內容即可。
   
   聽起來很簡單,卻又像很複雜,不是麼?沒關係,我們現在一步一步來。
   
   
   首先,我們需要一個HDC的控制代碼。如同前面所說,你可以有多種方法,比如GetDC,GetWindowDC,甚至是CreateDC。反正呢,你用什麼方法我不管,我只要有一個HDC的控制代碼就好了。
   
   有了HDC的控制代碼,接下來我們所需要做的是,知道這HDC的大小,也就是寬度和長度。這個不難,我們只要簡單地呼叫GetDeviceCaps,然後引數給予HORZRES或VERTRES即可:

  1. int iWidth = GetDeviceCaps(hdc,HORZRES);
  2. int iHeight = GetDeviceCaps(hdc,VERTRES);
  1. int iWidth = GetDeviceCaps(hdc,HORZRES);    int iHeight = GetDeviceCaps(hdc,VERTRES);

為什麼要知道大小呢?因為我們要用它來建立快取。而這快取,說白了,其實就是一個BMP格式的資料結構而已。
    
    為了建立這個關鍵的快取,我們必須呼叫CreateDIBSection函式,而該函式形參又用到BITMAPINFOHEADER,所以我們的一切,就先從填充該結構體開始。
    
    該結構體定義如下:

  1. typedef struct tagBITMAPINFO
  2. {
  3. BITMAPINFOHEADER bmiHeader;
  4. RGBQUAD bmiColors[1];
  5. } BITMAPINFO;
  1. typedef struct tagBITMAPINFO    {       BITMAPINFOHEADER bmiHeader;       RGBQUAD bmiColors[1];     } BITMAPINFO;

結構體裡面還有一個BITMAPINFOHEADER,其定義如下:

  1. typedef struct tagBITMAPINFOHEADER
  2. {
  3. DWORD biSize;
  4. LONG biWidth;
  5. LONG biHeight;
  6. WORD biPlanes;
  7. WORD biBitCount
  8. DWORD biCompression;
  9. DWORD biSizeImage;
  10. LONG biXPelsPerMeter;
  11. LONG biYPelsPerMeter;
  12. DWORD biClrUsed;
  13. DWORD biClrImportant;
  14. } BITMAPINFOHEADER;
  1. typedef struct tagBITMAPINFOHEADER     {       DWORD biSize;       LONG biWidth;       LONG biHeight;       WORD biPlanes;       WORD biBitCount       DWORD biCompression;       DWORD biSizeImage;       LONG biXPelsPerMeter;       LONG biYPelsPerMeter;       DWORD biClrUsed;       DWORD biClrImportant;     } BITMAPINFOHEADER;

這麼多變數,是不是有點頭暈?大可不必緊張,其實我們只需要填充其中幾個,其它統統置為0即可:

  1. BITMAPINFO bmpInfo = {0};
  2. bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  3. bmpInfo.bmiHeader.biWidth = iWidth;
  4. bmpInfo.bmiHeader.biHeight = iHeight;
  5. bmpInfo.bmiHeader.biPlanes = 1;
  6. bmpInfo.bmiHeader.biBitCount = 24;
  1. BITMAPINFO bmpInfo = {0};    bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);    bmpInfo.bmiHeader.biWidth = iWidth;    bmpInfo.bmiHeader.biHeight = iHeight;    bmpInfo.bmiHeader.biPlanes = 1;    bmpInfo.bmiHeader.biBitCount = 24;

一切從最簡單做起,對於BMP而言,最簡單的自然是24位點陣圖,這就是為什麼biPlanes和biBitCount分別設定為1和24的原因。
    
    填充完BITMAPINFO結構,我們還是不能馬上呼叫CreateDIBSection,因為形參中還有一個HDC。雖然我們可以直接採用已知的HDC控制代碼,但接下來還要將建立的HBITMAP和HDC相連線,所以我們還是先建立一個快取DC:

  1. HDC hdcMem = CreateCompatibleDC(hdc);
  1. HDC hdcMem = CreateCompatibleDC(hdc);

一切準備就緒之後,就呼叫CreateDIBSection吧:

  1. BYTE *pData = NULL;
  2. hBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,reinterpret_cast<VOID **>(&pData),NULL,0);
  1. BYTE *pData = NULL;    hBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,reinterpret_cast<VOID **>(&pData),NULL,0);

pData是分配的一個記憶體空間,將來用來儲存HDC的內容,只不過現在一切都是空的。如果你將這資料儲存出來,你會發現一團漆黑。
    
    將HBITMAP和HDC結合:

  1. hOldObj = SelectObject(hdcMem, hBmp);
  1. hOldObj = SelectObject(hdcMem, hBmp);

至此為止,我們前期工作已經準備就緒,我們只需要將HDC的內容用BitBlt繪製到快取中即可:

  1. BitBlt(hdcMem,
  2. 0,
  3. 0,
  4. iWidth,
  5. iHeight,
  6. hdc,
  7. 0,
  8. 0,
  9. SRCCOPY);
  1. BitBlt(hdcMem,           0,           0,           iWidth,           iHeight,           hdc,           0,           0,           SRCCOPY);

這裡其實還有一個小技巧,如果你是想繪製HDC的某個區域,你只需要用StretchBlt替代即可:

  1. StretchBlt(hdcMem,
  2. 0,
  3. 0,
  4. iWidth,
  5. iHeight,
  6. hdc,
  7. rcDC.left,
  8. rcDC.top,
  9. rcDC.right - rcDC.left + 1,
  10. rcDC.bottom - rcDC.top + 1,
  11. SRCCOPY);
  1. StretchBlt(hdcMem,                0,                0,                iWidth,                iHeight,                hdc,                rcDC.left,                rcDC.top,                rcDC.right - rcDC.left + 1,                rcDC.bottom - rcDC.top + 1,                SRCCOPY);

喜歡追究問題的你,也許會發現,在呼叫該函式之後,pData所指向的記憶體緩衝區已經改變。是的,沒錯,這些改變的資料就是我們所需要的。接下來我們所需要做的僅僅是,將這資料按BMP檔案的格式,儲存下來即可。
    
    BMP檔案格式其實很簡單,最開始是檔案頭資訊,然後是圖片資訊,接下來是資料。我們只需要按照這格式,順序將資料寫入即可。
    
    檔案頭資訊和圖片資訊,微軟已經為我們定義好了相應的結構體:
    
    BMP資訊:

  1. typedef struct tagBITMAPINFOHEADER
  2. {
  3. DWORD biSize;
  4. LONG biWidth;
  5. LONG biHeight;
  6. WORD biPlanes;
  7. WORD biBitCount
  8. DWORD biCompression;
  9. DWORD biSizeImage;
  10. LONG biXPelsPerMeter;
  11. LONG biYPelsPerMeter;
  12. DWORD biClrUsed;
  13. DWORD biClrImportant;
  14. } BITMAPINFOHEADER;
  1. typedef struct tagBITMAPINFOHEADER     {       DWORD biSize;       LONG biWidth;       LONG biHeight;       WORD biPlanes;       WORD biBitCount       DWORD biCompression;       DWORD biSizeImage;       LONG biXPelsPerMeter;       LONG biYPelsPerMeter;       DWORD biClrUsed;       DWORD biClrImportant;     } BITMAPINFOHEADER;

檔案頭資訊:

  1. typedef struct tagBITMAPFILEHEADER
  2. {
  3. WORD bfType;
  4. DWORD bfSize;
  5. WORD bfReserved1;
  6. WORD bfReserved2;
  7. DWORD bfOffBits;
  8. } BITMAPFILEHEADER;
  1. typedef struct tagBITMAPFILEHEADER     {      WORD bfType;       DWORD bfSize;       WORD bfReserved1;       WORD bfReserved2;       DWORD bfOffBits;     } BITMAPFILEHEADER;

我們首先填充這兩個結構體數值:

  1. BITMAPINFOHEADER bmInfoHeader = {0};
  2. bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
  3. bmInfoHeader.biWidth = iWidth;
  4. bmInfoHeader.biHeight = iHeight;
  5. bmInfoHeader.biPlanes = 1;
  6. bmInfoHeader.biBitCount = 24;
  7. //Bimap file header in order to write bmp file
  8. BITMAPFILEHEADER bmFileHeader = {0};
  9. bmFileHeader.bfType = 0x4d42;  //bmp
  10. bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
  11. bmFileHeader.bfSize = bmFileHeader.bfOffBits + ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8)
  1. BITMAPINFOHEADER bmInfoHeader = {0};    bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER);    bmInfoHeader.biWidth = iWidth;    bmInfoHeader.biHeight = iHeight;    bmInfoHeader.biPlanes = 1;    bmInfoHeader.biBitCount = 24;        //Bimap file header in order to write bmp file    BITMAPFILEHEADER bmFileHeader = {0};    bmFileHeader.bfType = 0x4d42;  //bmp      bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);    bmFileHeader.bfSize = bmFileHeader.bfOffBits + ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8)

接下來的事情,估計大家都輕車熟路了。建立檔案,然後寫入資料,儲存,完畢。

  1. HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  2. DWORD dwWrite = 0;
  3. WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL);
  4. WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL);
  5. WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL);
  6. CloseHandle(hFile);
  1. HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);    DWORD dwWrite = 0;    WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL);    WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL);    WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL);    CloseHandle(hFile);

文章的最後,是參考原始碼:

  1. #ifdef UNICODE
  2. #ifndef TSTRING
  3. #define TSTRING std::wstring
  4. #endif
  5. #else
  6. #ifndef TSTRING
  7. #define TSTRING std::string
  8. #endif
  9. #endif
  10. BOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg);
  11. BOOL WriteBmp(const TSTRING &strFile,HDC hdc);
  12. BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC);
  13. BOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg)
  14. {
  15. BITMAPINFOHEADER bmInfoHeader = {0};
  16. bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
  17. bmInfoHeader.biWidth = sizeImg.cx;
  18. bmInfoHeader.biHeight = sizeImg.cy;
  19. bmInfoHeader.biPlanes = 1;
  20. bmInfoHeader.biBitCount = 24;
  21. //Bimap file header in order to write bmp file
  22. BITMAPFILEHEADER bmFileHeader = {0};
  23. bmFileHeader.bfType = 0x4d42;  //bmp
  24. bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
  25. bmFileHeader.bfSize = bmFileHeader.bfOffBits + ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8)
  26. HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  27. if(hFile == INVALID_HANDLE_VALUE)
  28. {
  29. return FALSE;
  30. }
  31. DWORD dwWrite = 0;
  32. WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL);
  33. WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL);
  34. WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL);
  35. CloseHandle(hFile);
  36. return TRUE;
  37. }
  38. BOOL WriteBmp(const TSTRING &strFile,HDC hdc)
  39. {
  40. int iWidth = GetDeviceCaps(hdc,HORZRES);
  41. int iHeight = GetDeviceCaps(hdc,VERTRES);
  42. RECT rcDC = {0,0,iWidth,iHeight};
  43. return WriteBmp(strFile,hdc,rcDC);
  44. }
  45. BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC)
  46. {
  47. BOOL bRes = FALSE;
  48. BITMAPINFO bmpInfo = {0};
  49. BYTE *pData = NULL;
  50. SIZE sizeImg = {0};
  51. HBITMAP hBmp = NULL;
  52. std::vector<BYTE> vtData;
  53. HGDIOBJ hOldObj = NULL;
  54. HDC hdcMem = NULL;
  55. //Initilaize the bitmap information
  56. bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  57. bmpInfo.bmiHeader.biWidth = rcDC.right - rcDC.left;
  58. bmpInfo.bmiHeader.biHeight = rcDC.bottom - rcDC.top;
  59. bmpInfo.bmiHeader.biPlanes = 1;
  60. bmpInfo.bmiHeader.biBitCount = 24;
  61. //Create the compatible DC to get the data
  62. hdcMem = CreateCompatibleDC(hdc);
  63. if(hdcMem == NULL)
  64. {
  65. goto EXIT;
  66. }
  67. //Get the data from the memory DC
  68. hBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,reinterpret_cast<VOID **>(&pData),NULL,0);
  69. if(hBmp == NULL)
  70. {
  71. goto EXIT;
  72. }
  73. hOldObj = SelectObject(hdcMem, hBmp);
  74. //Draw to the memory DC
  75. sizeImg.cx = bmpInfo.bmiHeader.biWidth;
  76. sizeImg.cy = bmpInfo.bmiHeader.biHeight;
  77. StretchBlt(hdcMem,
  78. 0,
  79. 0,
  80. sizeImg.cx,
  81. sizeImg.cy,
  82. hdc,
  83. rcDC.left,
  84. rcDC.top,
  85. rcDC.right - rcDC.left + 1,
  86. rcDC.bottom - rcDC.top + 1,
  87. SRCCOPY);
  88. vtData.resize(sizeImg.cx * sizeImg.cy * 3);
  89. memcpy(&vtData[0],pData,vtData.size());
  90. bRes = WriteBmp(strFile,vtData,sizeImg);
  91. SelectObject(hdcMem, hOldObj);
  92. EXIT:
  93. if(hBmp != NULL)
  94. {
  95. DeleteObject(hBmp);
  96. }
  97. if(hdcMem != NULL)
  98. {
  99. DeleteDC(hdcMem);
  100. }
  101. return bRes;
  102. }
  1. #ifdef UNICODE#ifndef TSTRING#define TSTRING std::wstring#endif#else#ifndef TSTRING#define TSTRING std::string#endif#endifBOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg);BOOL WriteBmp(const TSTRING &strFile,HDC hdc);BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC);BOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg) { BITMAPINFOHEADER bmInfoHeader = {0};bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER);bmInfoHeader.biWidth = sizeImg.cx;bmInfoHeader.biHeight = sizeImg.cy;bmInfoHeader.biPlanes = 1;bmInfoHeader.biBitCount = 24;//Bimap file header in order to write bmp fileBITMAPFILEHEADER bmFileHeader = {0};bmFileHeader.bfType = 0x4d42;  //bmp  bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);bmFileHeader.bfSize = bmFileHeader.bfOffBits + ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8)HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);if(hFile == INVALID_HANDLE_VALUE){return FALSE;}DWORD dwWrite = 0;WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL);WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL);WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL);CloseHandle(hFile);return TRUE;} BOOL WriteBmp(const TSTRING &strFile,HDC hdc){int iWidth = GetDeviceCaps(hdc,HORZRES);int iHeight = GetDeviceCaps(hdc,VERTRES);RECT rcDC = {0,0,iWidth,iHeight};return WriteBmp(strFile,hdc,rcDC);}BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC){BOOL bRes = FALSE;BITMAPINFO bmpInfo = {0};BYTE *pData = NULL;SIZE sizeImg = {0};HBITMAP hBmp = NULL;std::vector<BYTE> vtData;HGDIOBJ hOldObj = NULL;HDC hdcMem = NULL;//Initilaize the bitmap informationbmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);bmpInfo.bmiHeader.biWidth = rcDC.right - rcDC.left;bmpInfo.bmiHeader.biHeight = rcDC.bottom - rcDC.top;bmpInfo.bmiHeader.biPlanes = 1;bmpInfo.bmiHeader.biBitCount = 24;//Create the compatible DC to get the datahdcMem = CreateCompatibleDC(hdc);if(hdcMem == NULL){goto EXIT;}//Get the data from the memory DChBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,reinterpret_cast<VOID **>(&pData),NULL,0);if(hBmp == NULL){goto EXIT;}hOldObj = SelectObject(hdcMem, hBmp);//Draw to the memory DCsizeImg.cx = bmpInfo.bmiHeader.biWidth;sizeImg.cy = bmpInfo.bmiHeader.biHeight;StretchBlt(hdcMem,0,0,sizeImg.cx,sizeImg.cy,hdc,rcDC.left,rcDC.top,rcDC.right - rcDC.left + 1,rcDC.bottom - rcDC.top + 1,SRCCOPY);vtData.resize(sizeImg.cx * sizeImg.cy * 3);memcpy(&vtData[0],pData,vtData.size());bRes = WriteBmp(strFile,vtData,sizeImg);SelectObject(hdcMem, hOldObj);EXIT:if(hBmp != NULL){DeleteObject(hBmp);}if(hdcMem != NULL){DeleteDC(hdcMem);}return bRes;}

一共有三個WriteBmp函式,使用上非常簡單。
    
    比如,我想儲存一個HDC,只需要簡單地呼叫:

  1. HDC hdc = GetDC(NULL);
  2. WriteBmp(TEXT("//NAND//DCSave.bmp"));
  3. ReleaseDC(NULL,hdc);
  1. HDC hdc = GetDC(NULL);    WriteBmp(TEXT("//NAND//DCSave.bmp"));    ReleaseDC(NULL,hdc);

如果想儲存HDC的某一個部分,同樣也很簡單:

  1. HDC hdc = GetDC(NULL);
  2. RECT rcDC = {0,0,100,100};
  3. WriteBmp(TEXT("//NAND//DCSavePart.bmp"),rcDC);
  4. ReleaseDC(NULL,hdc);
  1. HDC hdc = GetDC(NULL);    RECT rcDC = {0,0,100,100};    WriteBmp(TEXT("//NAND//DCSavePart.bmp"),rcDC);    ReleaseDC(NULL,hdc);

這個函式還能做到一個很有意思的功能,就是擷取螢幕。對於螢幕來說,也是一個HDC,我們只要獲取螢幕的HDC控制代碼,剩下的就沒有什麼難度了:

  1. HDC hdc = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
  2. WriteBmp(TEXT("//NAND//ScreenCapture.BMP"),hdc);
  3. DeleteDC(hdc);