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,位图 是无效的,但设备无关位图是可以使用的。