Win32 API 打印

转载 同时被 2 个专栏收录
282 篇文章 0 订阅
381 篇文章 1 订阅

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

http://blog.csdn.net/smstong/article/details/42082039

朋友说在一个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);  
} 


========

win32打印机控制,API打印操作

http://blog.csdn.net/hope_v/article/details/38472241

直接上代码,将下面的代码复制到工程中保存为main.cpp,然后在工程目录下放一张miku.bmp图片,直接就可以编译并且执行了。

#include <windows.h>  
#include <stdio.h>  
  
//这个函数用于读取bmp图像文件,用于给打印机打印的时候使用  
//info是位图信息结构  
//file是文件名  
//dib_ptr是位图rgb像素数据指针,输出用的,所以请提供一个void**  
bool read_bmp(BITMAPINFO& info, const char* file, void** dib_ptr)  
{  
    BITMAPFILEHEADER file_handle;  
    FILE* f = fopen(file, "rb");  
    if(f == 0) return false;  
  
    memset(&info, 0, sizeof(info));  
    fread(&file_handle, 1, sizeof(file_handle), f);  
    fread(&info.bmiHeader, 1, sizeof(info.bmiHeader), f);  
    fseek(f, file_handle.bfOffBits, SEEK_SET);  
    void* dib = malloc(info.bmiHeader.biSizeImage);  
    fread(dib, 1, info.bmiHeader.biSizeImage, f);  
    fclose(f);  
    *dib_ptr = dib;  
    return true;  
}  
  
//释放位图的数据指针,释放空间  
void release_bmp(void** dib_ptr)  
{  
    if(dib_ptr != 0)  
    {  
        if(*dib_ptr != 0)  
        {  
            free(*dib_ptr);  
            *dib_ptr = 0;  
        }  
    }  
}  
  
int main(int argc, char* argv[])  
{  
    PRINTDLG printInfo = {0};  
    printInfo.lStructSize = sizeof(printInfo);  
    printInfo.Flags = PD_RETURNDC | PD_RETURNDEFAULT | PD_ALLPAGES;  
    //PD_RETURNDEFAULT 意味着直接返回当前系统默认的打印机设置,若没有这个标识,则会弹出对话框让用户自己选择  
    //PD_RETURNDC 意味着返回的是dc而不是ic(information context)  
    //PD_ALLPAGES 指定“全部”单选钮在初始时被选中(缺省标志)  
    //对于错误的时候,若需要知道更多的错误消息,请执行CommDlgError来查看返回值  
  
    //PrintDlg目的是获取当前系统设置的默认打印机相关信息,供后面使用  
    if(!PrintDlg(&printInfo))  
    {  
        printInfo.Flags = 0;    //清除标志,然后执行将会弹出对话框让用户选择打印机  
        if(!PrintDlg(&printInfo))   
        {  
            printf("没有选择打印机。\n");  
            return 0;  
        }  
    }  
  
    //获取打印的时候的dc,然后往这个dc上绘图就是打印出来的样子了  
    HDC hPrintDC = printInfo.hDC;  
  
    //锁定全局对象,获取对象指针。 devmode是有关设备初始化和打印机环境的信息  
    DEVMODE* devMode = (DEVMODE*)GlobalLock(printInfo.hDevMode);  
    if(devMode == 0)  
    {  
        printf("获取打印机设置时发生了错误.\n");  
        return 0;  
    }  
  
    devMode->dmPaperSize = DMPAPER_A4;               //打印机纸张设置为A4。  
    devMode->dmOrientation   = DMORIENT_PORTRAIT;        //打印方向设置成纵向打印  
    //DMORIENT_LANDSCAPE 是横向打印  
    //对打印方向的设置,会影响hPrintDC的大小,假设宽度为1024,高度为300  
    //则在横向打印的时候dc大小会是宽1024 * 高300  
    //而纵向打印的时候dc大小会是宽300 * 高1024  
  
    int printQuality = devMode->dmPrintQuality;          //获取打印机的打印质量  
    //devMode->dmPrintQuality = DMRES_MEDIUM;  
    //设置打印质量的,因为像素被打印到纸上的时候是有做转换的  
    //单位是dpi,意为像素每英寸(dots per inch)。就是一英寸的纸张上  
    //打印多少个像素点,意味着这个质量越高,打印结果越精细,越低,越粗糙  
    //设置的质量可以是具体数值,也可以是宏DMRES_MEDIUM  
    //一般我们选择300,或者600,DMRES_MEDIUM = 600dpi  
  
    //应用我们修改过后的设置.  
    ResetDC(hPrintDC, devMode);  
  
    //解锁全局对象,对应GlobalLock  
    GlobalUnlock(printInfo.hDevMode);  
  
    //设置绘图模式,以保证绘图可以不失真的绘制上去,因为StretchDIBits函数要求设置这个才能够不是失真的绘图  
    //当你用StretchDIBits绘图往窗口显示的时候就会发现,24位图,若没有这个设置,是会失真的  
    SetStretchBltMode(hPrintDC, HALFTONE);  
  
    //读取位图,待会画在hPrintDC上面去  
    BITMAPINFO bmp_info;  
    int image_width = 0, image_height = 0;  
    void* dib_ptr = 0;  
    if(!read_bmp(bmp_info, "miku.bmp", &dib_ptr))  
    {  
        printf("读取位图miku.bmp失败了.\n");  
        return 0;  
    }  
    image_width = bmp_info.bmiHeader.biWidth;  
    image_height = bmp_info.bmiHeader.biHeight;  
    printf("位图大小:%d x %d\n", image_width, image_height);  
  
    //设置打印时候的字体  
    LOGFONT lf;  
    lf.lfHeight         = -printQuality * 1 / 2.54;   
                                //打印出来的字像素高度有n个,注意是像素高度,打印到纸上的时候是需要将  
                                //像素转换成实际尺寸单位,比如你需要在纸上打印高度为1cm的字,当你打印质量为600dpi的时候  
                                //这里就设置为 -236, -600dpi * 1cm / 2.54 = -236pix  
    lf.lfWidth          = 0;  
    lf.lfEscapement     = 0;  
    lf.lfOrientation    = 0;  
    lf.lfWeight         = 5;        //这里设置字体重量,意味着字体的厚度  
    lf.lfItalic         = false;    //斜体  
    lf.lfUnderline      = false;    //下划线  
    lf.lfStrikeOut       = 0;  
    lf.lfCharSet        = DEFAULT_CHARSET;  
    lf.lfOutPrecision    = 0;  
    lf.lfClipPrecision    = 0;  
    lf.lfQuality         = PROOF_QUALITY;  
    lf.lfPitchAndFamily  = 0;  
    strcpy (lf.lfFaceName, "宋体");   //使用宋体进行打印  
  
    //实际上这一步并不是必须的,因为默认打印机设置已经配置好了,这里只是我们自己固定好字体  
    HFONT hUseFont = CreateFontIndirect(&lf);                   //创建字体  
    HFONT hOldFont = (HFONT)SelectObject(hPrintDC, hUseFont);   //选用创建的字体  
      
    //获取dc的大小,实际上还有一种HORZRES和VERTRES就是宽度和高度,但是我查得到的结果说计算下来准确的  
    //HORZSIZE 是Horizontal size in millimeters,页面宽度(水平),单位毫米mm  
    //VERTSIZE 是Vertical size in millimeters,页面高度(垂直),单位毫米mm  
    //LOGPIXELSX 是Logical pixels/inch in X,x方向的逻辑像素每英寸.单位 pix / inch,像素每英寸  
    //LOGPIXELSY 是Logical pixels/inch in Y,y方向的逻辑像素每英寸.单位 pix / inch,像素每英寸  
    //不用管逻辑是个什么东西,不理会他,知道单位是pix / inch就行了  
    //1 inch = 2.54 cm,所以这里是 mm / 25.4 * pix / inch,得到的结果就是dc大小像素数为单位  
    int dc_page_width  = GetDeviceCaps(hPrintDC, HORZSIZE) / 25.4 * GetDeviceCaps(hPrintDC, LOGPIXELSX);  
    int dc_page_height = GetDeviceCaps(hPrintDC, VERTSIZE) / 25.4 * GetDeviceCaps(hPrintDC, LOGPIXELSY);  
  
    //好了,可以开始打印了  
    DOCINFO doc_info = {sizeof(DOCINFO), "测试打印机"};  
    //cbSize  
    //结构体的字节大小  
    //lpszDocName  
    //指明文档名的字符串指针,该字符串以null为尾。  
    //lpszOutput  
    //指明输出文件的名称的字符串指针,该字符串以null为尾。如果指针为null,那么输出将会发送至某个设备,该设备将由传递至 StartDoc 函数的‘设备上下文句柄’HDC来指明。  
    //lpszDatatype  
    //指针指向代表某种数据类型的字符串,而数据用于记录打印工作,该字符串以null为尾。合法值可通过调用函数 EnumPrintProcessorDatatypes 可得到,亦可为 NULL。  
    //fwType  
    //指明打印工作的其它信息。可为0或者以下值之一:  
    //DI_APPBANDING  
    //DI_ROPS_READ_DESTINATION  
  
    //开始一个档案,打印的时候是按照档案来区分的,返回作业编号(大于0的为正常)  
    int doc_idd = StartDoc(hPrintDC, &doc_info);  
    if(doc_idd <= 0)  
    {  
        printf("StartDoc 错误,错误代码:%d\n", GetLastError());  
        goto last_code;  
    }  
    printf("作业编号:%d\n", doc_idd);  
  
    //开始一个打印页面,大于等于0为正确  
    if(StartPage(hPrintDC) < 0)  
    {  
        printf("StartPage 错误\n");  
        AbortDoc(hPrintDC); //结束doc打印  
        goto last_code;  
    }  
  
    //设定文字和图像绘制的区域  
    RECT rcText = {0, 30, dc_page_width, 300};  
    RECT rcImage = {30, 300, dc_page_width - 30, dc_page_height - 30};  
  
    //写下文字  
    DrawText(hPrintDC, "miku,测试打印图片", -1, &rcText, DT_VCENTER | DT_SINGLELINE | DT_CENTER);  
  
    //将图片绘制到dc上,给打印机打印出来  
    //这个函数带拉伸功能的,若dest的宽度高度和src不一致的时候他会拉伸  
    StretchDIBits(hPrintDC, rcImage.left, rcImage.top, rcImage.right - rcImage.left,   
        rcImage.bottom - rcImage.top, 0, 0, image_width, image_height,   
        dib_ptr, &bmp_info, DIB_RGB_COLORS, SRCCOPY);  
  
    //结束这一页,其实可以循环的startpage、endpage,这样在一个文档中进行多个页面的打印  
    EndPage(hPrintDC);  
    EndDoc(hPrintDC);  
    printf("打印顺利完成了. o(∩_∩)o \n");  
  
last_code:  
    //选取旧的字体  
    SelectObject(hPrintDC, hOldFont);  
  
    //删除gdi对象,释放内存  
    DeleteObject(hUseFont);  
  
    //释放位图内存  
    release_bmp(&dib_ptr);  
    return 0;  
}

  
========

win32 打印机api

http://www.cnblogs.com/liangxiaofeng/p/5713480.html
 
4. API之打印函数
AbortDoc 取消一份文档的打印
AbortPrinter 删除与一台打印机关联在一起的缓冲文件
AddForm 为打印机的表单列表添加一个新表单
AddJob 用于获取一个有效的路径名,以便用它为作业创建一个后台打印文件。它也会为作业分配一个作业编号
AddMonitor 为系统添加一个打印机监视器
AddPort 启动"添加端口"对话框,允许用户在系统可用端口列表中加入一个新端口
AddPrinter 在系统中添加一台新打印机
AddPrinterConnection 连接指定的打印机
AddPrinterDriver 为指定的系统添加一个打印驱动程序
AddPrintProcessor 为指定的系统添加一个打印处理器
AddPrintProvidor 为系统添加一个打印供应商
AdvancedDocumentProperties 启动打印机文档设置对话框
ClosePrinter 关闭一个打开的打印机对象
ConfigurePort 针对指定的端口,启动一个端口配置对话框
ConnectToPrinterDlg 启动连接打印机对话框,用它同访问网络的打印机连接
DeleteForm 从打印机可用表单列表中删除一个表单
DeleteMonitor 删除指定的打印监视器
DeletePort 启动"删除端口"对话框,允许用户从当前系统删除一个端口
DeletePrinter 将指定的打印机标志为从系统中删除
DeletePrinterConnection 删除与指定打印机的连接
DeletePrinterDriver 从系统删除一个打印机驱动程序
DeletePrintProcessor 从指定系统删除一个打印处理器
DeletePrintProvidor 从系统中删除一个打印供应商
DeviceCapabilities 利用这个函数可获得与一个设备的能力有关的信息
DocumentProperties 打印机配置控制函数
EndDocAPI 结束一个成功的打印作业
EndDocPrinter 在后台打印程序的级别指定一个文档的结束
EndPage 用这个函数完成一个页面的打印,并准备设备场景,以便打印下一个页
EndPagePrinter 指定一个页在打印作业中的结尾
EnumForms 枚举一台打印机可用的表单
EnumJobs 枚举打印队列中的作业
EnumMonitors 枚举可用的打印监视器
EnumPorts 枚举一个系统可用的端口
EnumPrinterDrivers 枚举指定系统中已安装的打印机驱动程序
EnumPrinters 枚举系统中安装的打印机
EnumPrintProcessorDatatypes 枚举由一个打印处理器支持的数据类型
EnumPrintProcessors 枚举系统中可用的打印处理器
Escape 设备控制函数
FindClosePrinterChangeNotification 关闭用FindFirstPrinterChangeNotification函数获取的一个打印机通告对象
FindFirstPrinterChangeNotification 创建一个新的改变通告对象,以便我们注意打印机状态的各种变化
FindNextPrinterChangeNotification 用这个函数判断触发一次打印机改变通告信号的原因
FreePrinterNotifyInfo 释放由FindNextPrinterChangeNotification函数分配的一个缓冲区
GetForm 取得与指定表单有关的信息
GetJob 获取与指定作业有关的信息
GetPrinter 取得与指定打印机有关的信息
GetPrinterData 为打印机设置注册表配置信息
GetPrinterDriver 针对指定的打印机,获取与打印机驱动程序有关的信息
GetPrinterDriverDirectory 判断指定系统中包含了打印机驱动程序的目录是什么
GetPrintProcessorDirectory 判断指定系统中包含了打印机处理器驱动程序及文件的目录
OpenPrinter 打开指定的打印机,并获取打印机的句柄
PrinterMessageBox 在拥有指定打印作业的系统上显示一个打印机出错消息框
PrinterProperties 启动打印机属性对话框,以便对打印机进行配置
ReadPrinter 从打印机读入数据
ResetDC 重设一个设备场景
ResetPrinter 改变指定打印机的默认数据类型及文档设置
ScheduleJob 提交一个要打印的作业
SetAbortProc 为Windows指定取消函数的地址
SetForm 为指定的表单设置信息
SetJob 对一个打印作业的状态进行控制
SetPrinter 对一台打印机的状态进行控制
SetPrinterData 设置打印机的注册表配置信息
StartDoc 开始一个打印作业
StartDocPrinter 在后台打印的级别启动一个新文档
StartPage 打印一个新页前要先调用这个函数
StartPagePrinter 在打印作业中指定一个新页的开始
WritePrinter 将发送目录中的数据写入打印机
========
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值