朋友说在一个VC++6.0开发的项目中要加入打印窗口的功能,让帮忙写个代码供其调用。这么老的IDE当然不想碰了,而且也不喜欢MFC笨拙不清晰的封装,所以决定采用纯Win32 API,然后用C++类简单封装一下。
窗口DC和打印机DC是两类不兼容的DC,所以它们之间传送位图只能通过DIB。首先,通过BitBlt()把要打印窗口的客户区拷贝到DDB内存位图中,然后通过GetDIBits()把DDB转换为DIB,最后通过StretchDIBits()向打印机DC输出。
头文件 WinowPrinter.h
#pragma once /******************************************************************************** WindowPrinter 打印窗口类 功能描述: 提供截屏窗口并通过默认打印机,自动进行居中缩放打印 使用说明: 样例代码如下。 HWND hwnd = this->GetSafeWnd(); WindowPrinter::PrintWindowClientArea(hwnd); ********************************************************************************/ class WindowPrinter { public: WindowPrinter(); ~WindowPrinter(); public: /* 功能:获取当前默认打印机的DC 返回:成功返回打印机的DC,失败返回NULL */ static HDC GetPrinterDC(); /* 功能:打印窗口客户区内容到打印机,自动缩放居中打印 参数: hWnd-被打印窗口的句柄 */ static void PrintWindowClientArea(HWND hwnd); };
实现文件 WindowPrinter.cpp
#include "stdafx.h" #include "WindowPrinter.h" #include <Winspool.h> WindowPrinter::WindowPrinter() { } WindowPrinter::~WindowPrinter() { } /* 功能:获取当前默认打印机的DC 返回:成功返回打印机的DC,失败返回NULL */ HDC WindowPrinter::GetPrinterDC() { DWORD dwNeeded, dwReturned; HDC hdc; ::EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwReturned); PRINTER_INFO_4* pinfo4 = (PRINTER_INFO_4*)malloc(dwNeeded); ::EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, (BYTE*)pinfo4, dwNeeded, &dwNeeded, &dwReturned); hdc = ::CreateDC(NULL, pinfo4->pPrinterName, NULL, NULL); free(pinfo4); return hdc; } /* 功能:打印窗口客户区内容到打印机,自动缩放居中打印 参数: hWnd-被打印窗口的句柄 */ void WindowPrinter::PrintWindowClientArea(HWND hWnd) { if (hWnd == NULL) return; RECT rectClient; ::GetClientRect(hWnd, &rectClient); int width = rectClient.right - rectClient.left; int height = rectClient.bottom - rectClient.top; // 通过内存DC复制客户区到DDB位图 HDC hdcWnd = ::GetDC(hWnd); HBITMAP hbmWnd = ::CreateCompatibleBitmap(hdcWnd, width, height); HDC hdcMem = ::CreateCompatibleDC(hdcWnd); ::SelectObject(hdcMem, hbmWnd); ::BitBlt(hdcMem, 0, 0, width, height, hdcWnd, 0, 0, SRCCOPY); // 把窗口DDB转为DIB BITMAP bmpWnd; ::GetObject(hbmWnd, sizeof(BITMAP), &bmpWnd); BITMAPINFOHEADER bi; // 信息头 bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bmpWnd.bmWidth; bi.biHeight = bmpWnd.bmHeight; bi.biPlanes = 1; bi.biBitCount = 32; // 按照每个像素用32bits表示转换 bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; DWORD dwBmpSize = ((bmpWnd.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpWnd.bmHeight; // 每一行像素位32对齐 char *lpbitmap = (char*)malloc(dwBmpSize); // 像素位指针 ::GetDIBits(hdcMem, hbmWnd, 0, (UINT)bmpWnd.bmHeight, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS); ::DeleteDC(hdcMem); ::DeleteObject(hbmWnd); ::ReleaseDC(hWnd, hdcWnd); // 存为文件(可选) BITMAPFILEHEADER bmfHeader; // 文件头 DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); bmfHeader.bfSize = dwSizeofDIB; bmfHeader.bfType = 0x4D42; FILE* fp = NULL; ::_wfopen_s(&fp, L"capture.bmp", L"w"); ::fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER), 1, fp); // 写入文件头 ::fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, fp); // 写入信息头 ::fwrite(lpbitmap, dwBmpSize, 1, fp); // 写入像素位 ::fclose(fp); fp = NULL; // StretchDIBits()缩放打印DIB HDC hdcPrinter = WindowPrinter::GetPrinterDC(); if (hdcPrinter == NULL) return; int pageWidth = ::GetDeviceCaps(hdcPrinter, HORZRES); int pageHeight = ::GetDeviceCaps(hdcPrinter, VERTRES); float scaleX = (float)pageWidth / (float)bmpWnd.bmWidth; float scaleY = (float)pageHeight / (float)bmpWnd.bmHeight; float scale = scaleX < scaleY ? scaleX : scaleY; int xDst, yDst, cxDst, cyDst; cxDst = (int)((float)bmpWnd.bmWidth * scale); cyDst = (int)((float)bmpWnd.bmHeight * scale); xDst = (int)((pageWidth - cxDst) / 2); yDst = (int)((pageHeight - cyDst) / 2); static DOCINFO di = { sizeof(DOCINFO), L"PRINTJOBNAME" }; if (::StartDoc(hdcPrinter, &di) > 0) { if (::StartPage(hdcPrinter) > 0) { ::StretchDIBits(hdcPrinter, xDst, yDst, cxDst, cyDst, 0, 0, bmpWnd.bmWidth, bmpWnd.bmHeight, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS, SRCCOPY); ::EndPage(hdcPrinter); } ::EndDoc(hdcPrinter); } ::DeleteDC(hdcPrinter); ::free(lpbitmap); }