重温 Win32 API —– 截屏指定窗口并打印

  • Post author:
  • Post category:其他



朋友说在一个VC++6.0开发的项目中要加入打印窗口的功能,让帮忙写个代码供其调用。这么老的IDE当然不想碰了,而且也不喜欢MFC笨拙不清晰的封装,所以决定采用纯Win32 API,然后用C++类简单封装一下。

1 基本思路


窗口DC和打印机DC是两类不兼容的DC,所以它们之间传送位图只能通过DIB。首先,通过BitBlt()把要打印窗口的客户区拷贝到DDB内存位图中,然后通过GetDIBits()把DDB转换为DIB,最后通过StretchDIBits()向打印机DC输出。

2 代码实现

头文件 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);
}



版权声明:本文为smstong原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。