Private Const CCHDEVICENAME = 32 Private Const CCHFORMNAME = 32 Enum PaperClassSize DMPAPER_LETTER = GDI.DMPAPER_LETTER DMPAPER_LEGAL = GDI.DMPAPER_LEGAL DMPAPER_A4 = GDI.DMPAPER_A4 DMPAPER_CSHEET = GDI.DMPAPER_CSHEET DMPAPER_DSHEET = GDI.DMPAPER_DSHEET DMPAPER_ESHEET = GDI.DMPAPER_ESHEET DMPAPER_LETTERSMALL = GDI.DMPAPER_LETTERSMALL DMPAPER_TABLOID = GDI.DMPAPER_TABLOID DMPAPER_LEDGER = GDI.DMPAPER_LEDGER DMPAPER_STATEMENT = GDI.DMPAPER_STATEMENT DMPAPER_EXECUTIVE = GDI.DMPAPER_EXECUTIVE DMPAPER_A3 = GDI.DMPAPER_A3 DMPAPER_A4SMALL = GDI.DMPAPER_A4SMALL DMPAPER_A5 = GDI.DMPAPER_A5 DMPAPER_B4 = GDI.DMPAPER_B4 DMPAPER_B5 = GDI.DMPAPER_B5 DMPAPER_FOLIO = GDI.DMPAPER_FOLIO DMPAPER_QUARTO = GDI.DMPAPER_QUARTO DMPAPER_10X14 = GDI.DMPAPER_10X14 DMPAPER_11X17 = GDI.DMPAPER_11X17 DMPAPER_NOTE = GDI.DMPAPER_NOTE DMPAPER_ENV_9 = GDI.DMPAPER_ENV_9 DMPAPER_ENV_10 = GDI.DMPAPER_ENV_10 DMPAPER_ENV_11 = GDI.DMPAPER_ENV_11 DMPAPER_ENV_12 = GDI.DMPAPER_ENV_12 DMPAPER_ENV_14 = GDI.DMPAPER_ENV_14 DMPAPER_ENV_DL = GDI.DMPAPER_ENV_DL DMPAPER_ENV_C5 = GDI.DMPAPER_ENV_C5 DMPAPER_ENV_C3 = GDI.DMPAPER_ENV_C3 DMPAPER_ENV_C4 = GDI.DMPAPER_ENV_C4 DMPAPER_ENV_C6 = GDI.DMPAPER_ENV_C6 DMPAPER_ENV_C65 = GDI.DMPAPER_ENV_C65 DMPAPER_ENV_B4 = GDI.DMPAPER_ENV_B4 DMPAPER_ENV_B5 = GDI.DMPAPER_ENV_B5 DMPAPER_ENV_B6 = GDI.DMPAPER_ENV_B6 DMPAPER_ENV_ITALY = GDI.DMPAPER_ENV_ITALY DMPAPER_ENV_MONARCH = GDI.DMPAPER_ENV_MONARCH DMPAPER_ENV_PERSONAL = GDI.DMPAPER_ENV_PERSONAL DMPAPER_FANFOLD_US = GDI.DMPAPER_FANFOLD_US DMPAPER_FANFOLD_STD_GERMAN = GDI.DMPAPER_FANFOLD_STD_GERMAN DMPAPER_FANFOLD_LGL_GERMAN = GDI.DMPAPER_FANFOLD_LGL_GERMAN End Enum Enum PrinterDialogBox [DialogBoxDefault] = 0 [DialogBoxSetup] = PD_PRINTSETUP [DialogBoxHide] = PD_RETURNDEFAULT End Enum Private Type DevNames wDriverOffset As Integer wDeviceOffset As Integer wOutputOffset As Integer wDefault As Integer extra(100) As Byte End Type Private Type DEVMODE dmDeviceName As String * CCHDEVICENAME dmSpecVersion As Integer dmDriverVersion As Integer dmSize As Integer dmDriverExtra As Integer dmFields As Long dmOrientation As Integer dmPaperSize As Integer dmPaperLength As Integer dmPaperWidth As Integer dmScale As Integer dmCopies As Integer dmDefaultSource As Integer dmPrintQuality As Integer dmColor As Integer dmDuplex As Integer dmYResolution As Integer dmTTOption As Integer dmCollate As Integer dmFormName As String * CCHFORMNAME dmUnusedPadding As Integer dmBitsPerPel As Integer dmPelsWidth As Long dmPelsHeight As Long dmDisplayFlags As Long dmDisplayFrequency As Long End Type Private Type PrinterDlg lStructSize As Long hwndOwner As Long hDevMode As Long hDevNames As Long hdc As Long flags As EPrintDialog nFromPage As Integer nToPage As Integer nMinPage As Integer nMaxPage As Integer nCopies As Integer hInstance As Long lCustData As Long lpfnPrintHook As Long lpfnSetupHook As Long lpPrintTemplateName As String lpSetupTemplateName As String hPrintTemplate As Long hSetupTemplate As Long End Type Private Type PrinterPageSetupDlg lStructSize As Long hwndOwner As Long hDevMode As Long hDevNames As Long flags As Long ptPaperSize As POINTL rtMinMargin As RECT rtMargin As RECT hInstance As Long lCustData As Long lpfnPageSetupHook As Long lpfnPagePaintHook As Long lpPageSetupTemplateName As String hPageSetupTemplate As Long End Type Private Type DocInfo cbSize As Long lpszDocName As String lpszOutput As String lpszDatatype As String fwType As Long End Type Private Declare Function PageSetupDlgX Lib "comdlg32.dll" Alias "PageSetupDlgA" (pPagesetupdlg As PrinterPageSetupDlg) As Long Private Declare Function PrintDlg Lib "comdlg32.dll" Alias "PrintDlgA" (pPrintdlg As PrinterDlg) As Long Private Declare Function StartDoc Lib "gdi32" Alias "StartDocA" (ByVal hdc As Long, lpdi As DocInfo) As Long Private Declare Function StartPage Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function EndPage Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function EndDoc Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function AbortDoc Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function CreateDC Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As Long, lpInitData As Any) As Long Private Declare Function ResetDC Lib "gdi32" Alias "ResetDCA" (ByVal hdc As Long, lpInitData As DEVMODE) As Long Private Declare Function SetViewportOrgEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As Any) As Long Private Declare Function SetWindowExtEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Any) As Long Private Declare Function SetViewportExtEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Any) As Long Private Declare Function SetWindowOrgEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As Any) As Long '// 以上是有用无用的相关API声明, 摘用代码 声明就不调着用了 Sub GetSystemDefaultPrinter() '// 获取系统默认打印机打印作业 Dim pd As PrinterDlg Dim pps As PrinterPageSetupDlg pd.lStructSize = Len(pd) pd.flags = PD_RETURNDC Or PD_RETURNDEFAULT If PrintDlg(pd) Then '// 先用 PrintDlg 获取默认打印机DC PG.HDC_Printer = pd.hdc Dim hGlobalData As Long '// 锁定临时空间, 获取驱动配置信息 hGlobalData = GlobalLock(ByVal pd.hDevMode) CopyMemory PG.dev_dlgMode, ByVal hGlobalData, Len(PG.dev_dlgMode) GlobalUnlock (hGlobalData) GlobalFree (pd.hDevMode) '// 锁定临时空间, 获取驱动,设备信息 hGlobalData = GlobalLock(ByVal pd.hDevNames) CopyMemory PG.dev_dlgName, ByVal hGlobalData, Len(PG.dev_dlgName) GlobalUnlock (hGlobalData) GlobalFree (pd.hDevNames) Dim mulbits() As Byte Dim i As Long ReDim mulbits(PG.dev_dlgName.wDriverOffset - 1) CopyMemory mulbits(0), PG.dev_dlgName.extra(0), PG.dev_dlgName.wDriverOffset PG.dev_DriveName = StrConv(mulbits, vbUnicode) '// 获取设备名, 通常为: WINSPOOL , 为固定文本内容;
'// 也见过 CreateDC 时未使用 winspool 仅指定打印机名字的代码,用以创建HDC,虽未测试,但是公布出来的例子代码应该也能成功
i = lstrlenByte(PG.dev_dlgName.extra(PG.dev_dlgName.wDriverOffset + 2)) ReDim mulbits(i - 1) CopyMemory mulbits(0), PG.dev_dlgName.extra(PG.dev_dlgName.wDriverOffset + 2), i PG.dev_PrinterName = StrConv(mulbits, vbUnicode) '// 获取打印机名字
'// 获取默认打印机的打印配置 pps.lStructSize = Len(pds) pps.flags = PSD_INHUNDREDTHSOFMILLIMETERS Or PSD_RETURNDEFAULT If PageSetupDlgX(pps) Then '// 获取打印机默认配置参数 '// 锁定临时空间, 获取驱动配置信息 hGlobalData = GlobalLock(ByVal pps.hDevMode) CopyMemory PG.dev_dlgMode, ByVal hGlobalData, Len(PG.dev_dlgMode) ResetDC PG.HDC_Printer, hGlobalData '// ResetDC 用于将获取的默认打印参数应用到已选择的打印机 DC 上 GlobalUnlock (hGlobalData) GlobalFree (pps.hDevMode) '// 获取打印机分辨率, 每英寸内像素量 PG.PrinterResolveX = GetDeviceCaps(PG.HDC_Printer, LOGPIXELSX) PG.PrinterResolveY = GetDeviceCaps(PG.HDC_Printer, LOGPIXELSY) '// 转换打印机默认边距度量单位为屏幕逻辑像素 PG.dev_RectMargin = pds.rtMargin PG.dev_RectMargin.Left = mmeterPerPixelX(PG.dev_RectMargin.Left \ 100) PG.dev_RectMargin.Right = mmeterPerPixelX(PG.dev_RectMargin.Right \ 100) PG.dev_RectMargin.Top = mmeterPerPixelX(PG.dev_RectMargin.Top \ 100) PG.dev_RectMargin.bottom = mmeterPerPixelX(PG.dev_RectMargin.bottom \ 100) '// 设置打印机视图范围度量单位为像素 SetMapMode PG.HDC_Printer, MM_ANISOTROPIC '// 设置打印机设备窗口范围, 窗口设置屏幕分辨率 SetWindowExtEx PG.HDC_Printer, PG.ScreenResolveX, PG.ScreenResolveY, ByVal 0& SetWindowOrgEx PG.HDC_Printer, 0, 0, ByVal 0& '// 设置打印机设备视图范围, 设置为缩放分辨率,系统自动计算为比例 SetViewportExtEx PG.HDC_Printer, PG.PrinterResolveX, PG.PrinterResolveY, ByVal 0& SetViewportOrgEx PG.HDC_Printer, 0, 0, ByVal 0& '// 以上api: SetWindowExtEx ,SetViewportExtEx , 也就是说以 WindowExt 设置的视图范围, 显示以 ViewPort 设置的视图范围 '// 获取设备物理尺寸, 以像素为单位, 打印分辨率不同 DC 的像素尺寸也不同,分辨率越高越大 PG.dev_PaperSize.x = GetDeviceCaps(PG.HDC_Printer, HORZRES) PG.dev_PaperSize.y = GetDeviceCaps(PG.HDC_Printer, VERTRES) '// 物理尺寸解析为逻辑尺寸 DPtoLP PG.HDC_Printer, PG.dev_PaperSize, 1 '// 下面开始打印作业的具体内容了 StartDoc PG.HDC_Printer, prtDoc StartPage (PG.HDC_Printer)
TextOut PG.HDC_Printer, ... '// 此处为向 PG.HDC_Print 用 GDI API 绘图的过程, 绘制的内容就是要打印的内容, 比如 DrawText,TextOut 绘制文本, FillRect,FrameRect 绘制矩形, LineTo,MoveTo 绘制线段等, 与普通 HDC 绘图基本一致, '// 绘图时,PG.HDC_Print 的 Rect 为:0,0,PG.dev_PaperSize.x,PG.dev_PaperSize.Y EndPage PG.HDC_Printer EndDoc PG.HDC_Printer DeleteDC PG.HDC_Printer End If End If End Sub
Windows 系统的打印作业目前似乎仅有 GDI API 的方法
将打印实现过程以绘图的形式给coder调用,感觉ms coder 真的很天才
普通程序员编程打印前必须用 SetMapMode ,SetWindowExtEx,SetViewportExtEx 参照上述代码内容设置视图
经过测试,字体是矢量的,无视分辨率及视图大小的变化,但是除此之外的自绘图形则无法匹配分辨率
所以必须设置视图范围,将物理尺寸、不同分辨率格式化为近似的固定范围尺寸,如上面代码将A4纸型格式化为 1000多x700多的矩形
----------------------------------------------------------------------
我没有打印机不方便实际测试,所以用 PDF 打印机测试的打印效果
有个重要问题始终没弄明白,DEVMODE 结构的参数设置到底是自动配置的还是一部分需要手动实现?
比如 DEVMODE.dmCopies 打印份数,到底是打印机自己重复打印指定份数还是需要代码实现? PDF 打印机无论指定多少份都只出一份,由于是虚拟打印机所以不确定结果
经过反复测试和搜索例子代码,似乎确定了一个事实,打印预览只能自己手动实现
HDC_Print ,打印机的 HDC 是无法通过 Bitblt 之类api导出的, 普通 HDC -> Bitblt,StrxxxBlt,xxBlt, -> 打印机 HDC,是可以的,但这样的话就相当于打印图片了,复制过去后形成了一个图片,这是单向的,打印机HDC 不能通过同样方法输出图形到普通HDC(肯定是不行的,最好也不要测试,我测试时还蓝过), 而且不要尝试直接给 打印机HDC 配置位图,SelectObject HDC_Print,位图 是无效的,但设备无关位图是可以使用的