天天看點

Win32 API 列印重溫 Win32 API ----- 截屏指定視窗并列印win32列印機控制,API列印操作win32 列印機api

重温 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 将发送目录中的数据写入打印机

========