From bd371d155ac6ed5109287615f431f04fab0e6edb Mon Sep 17 00:00:00 2001 From: Newton Rocha Date: Tue, 12 Sep 2017 14:56:27 -0300 Subject: [PATCH 1/2] Adding Get Printer Type --- printer.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ zapi.go | 26 ++++++++++++++++++++-- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/printer.go b/printer.go index e8accc5..d5e44b1 100644 --- a/printer.go +++ b/printer.go @@ -10,6 +10,12 @@ import ( "unsafe" ) +const ( + PRINTER_DRIVER_XPS uint32 = 0x00000002 + RAW = "RAW" + XPS_PASS = "XPS_PASS" +) + //go:generate go run mksyscall_windows.go -output zapi.go printer.go type DOC_INFO_1 struct { @@ -26,6 +32,34 @@ type PRINTER_INFO_5 struct { TransmissionRetryTimeout uint32 } +type DRIVER_INFO_8 struct { + Version uint32 + Name *uint16 + Environment *uint16 + DriverPath *uint16 + DataFile *uint16 + ConfigFile *uint16 + HelpFile *uint16 + DependentFiles *uint16 + MonitorName *uint16 + DefaultDataType *uint16 + PreviousNames *uint16 + DriverDate syscall.Filetime + DriverVersion uint64 + MfgName *uint16 + OEMUrl *uint16 + HardwareID *uint16 + Provider *uint16 + PrintProcessor *uint16 + VendorSetup *uint16 + ColorProfiles *uint16 + InfPath *uint16 + PrinterDriverAttributes uint32 + CoreDriverDependencies *uint16 + MinInboxDriverVerDate syscall.Filetime + MinInboxDriverVerVersion uint32 +} + const ( PRINTER_ENUM_LOCAL = 2 PRINTER_ENUM_CONNECTIONS = 4 @@ -58,6 +92,36 @@ func Default() (string, error) { return syscall.UTF16ToString(b), nil } +func GetDefaultPrinterType() (string, error) { + printerName, _ := Default() + return GetPrinterType(printerName) +} + +func GetPrinterType(printerName string) (string, error) { + b := make([]byte, 1024*10) + n := uint32(len(b)) + err := GetPrinterDriver(printerName, &b[0], n) + if err != nil { + if err != syscall.ERROR_INSUFFICIENT_BUFFER { + return "", err + } + b = make([]byte, n) + err = GetPrinterDriver(printerName, &b[0], n) + if err != nil { + return "", err + } + } + di := (*DRIVER_INFO_8)(unsafe.Pointer(&b[0])) + + driverType := RAW + + if di.PrinterDriverAttributes&PRINTER_DRIVER_XPS == 2 { + driverType = XPS_PASS + } + + return driverType, nil +} + // ReadNames return printer names on the system func ReadNames() ([]string, error) { const flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS diff --git a/zapi.go b/zapi.go index 923c32e..571f211 100644 --- a/zapi.go +++ b/zapi.go @@ -2,8 +2,10 @@ package printer -import "unsafe" -import "syscall" +import ( + "syscall" + "unsafe" +) var _ unsafe.Pointer @@ -19,6 +21,7 @@ var ( procStartPagePrinter = modwinspool.NewProc("StartPagePrinter") procEndPagePrinter = modwinspool.NewProc("EndPagePrinter") procEnumPrintersW = modwinspool.NewProc("EnumPrintersW") + procGetPrinterDriver = modwinspool.NewProc("GetPrinterDriverW") ) func GetDefaultPrinter(buf *uint16, bufN *uint32) (err error) { @@ -33,6 +36,25 @@ func GetDefaultPrinter(buf *uint16, bufN *uint32) (err error) { return } +func GetPrinterDriver(name string, buf *byte, bufN uint32) (err error) { + p, err := Open(name) + if err != nil { + return err + } + defer p.Close() + var needed uint32 + r1, _, e1 := syscall.Syscall6(procGetPrinterDriver.Addr(), 6, uintptr(p.h), uintptr(0), uintptr(8), uintptr(unsafe.Pointer(buf)), uintptr(bufN), uintptr(unsafe.Pointer(&needed))) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + panic(err) + } else { + err = syscall.EINVAL + } + } + return +} + func ClosePrinter(h syscall.Handle) (err error) { r1, _, e1 := syscall.Syscall(procClosePrinter.Addr(), 1, uintptr(h), 0, 0) if r1 == 0 { From dce5edbfff20c3032ffc03f70488e8f505c9e709 Mon Sep 17 00:00:00 2001 From: Newton Rocha Date: Wed, 13 Sep 2017 02:54:15 -0300 Subject: [PATCH 2/2] PR fixes --- cmd/print/print.go | 8 +++- printer.go | 99 ++++++++++++++++++++++++++++++++-------------- zapi.go | 39 +++++++----------- 3 files changed, 91 insertions(+), 55 deletions(-) diff --git a/cmd/print/print.go b/cmd/print/print.go index 078aa54..c29c377 100644 --- a/cmd/print/print.go +++ b/cmd/print/print.go @@ -41,6 +41,7 @@ func listPrinters() error { if err != nil { return err } + for i, p := range printers { s := " " if p == defaultPrinter { @@ -77,7 +78,12 @@ func printOneDocument(printerName, documentName string, lines []string) error { } defer p.Close() - err = p.StartDocument(documentName, "RAW") + dataType, err := p.PrintDataType() + if err != nil { + return err + } + + err = p.StartDocument(documentName, dataType) if err != nil { return err } diff --git a/printer.go b/printer.go index d5e44b1..eaab0cb 100644 --- a/printer.go +++ b/printer.go @@ -7,6 +7,7 @@ package printer import ( "syscall" + "unicode/utf16" "unsafe" ) @@ -74,6 +75,21 @@ const ( //sys StartPagePrinter(h syscall.Handle) (err error) = winspool.StartPagePrinter //sys EndPagePrinter(h syscall.Handle) (err error) = winspool.EndPagePrinter //sys EnumPrinters(flags uint32, name *uint16, level uint32, buf *byte, bufN uint32, needed *uint32, returned *uint32) (err error) = winspool.EnumPrintersW +//sys GetPrinterDriver(h syscall.Handle, env *uint16, level uint32, di *byte, n uint32, needed *uint32) (err error) = winspool.GetPrinterDriverW + +func convertLPTSTRToString(ptr *uint16) string { + a := (*[1<<30 - 1]uint16)(unsafe.Pointer(ptr)) + size := 0 + for ; size < len(a); size++ { + if a[size] == uint16(0) { + break + } + } + runes := utf16.Decode(a[:size:size]) + goString := string(runes) + + return goString +} func Default() (string, error) { b := make([]uint16, 3) @@ -92,36 +108,6 @@ func Default() (string, error) { return syscall.UTF16ToString(b), nil } -func GetDefaultPrinterType() (string, error) { - printerName, _ := Default() - return GetPrinterType(printerName) -} - -func GetPrinterType(printerName string) (string, error) { - b := make([]byte, 1024*10) - n := uint32(len(b)) - err := GetPrinterDriver(printerName, &b[0], n) - if err != nil { - if err != syscall.ERROR_INSUFFICIENT_BUFFER { - return "", err - } - b = make([]byte, n) - err = GetPrinterDriver(printerName, &b[0], n) - if err != nil { - return "", err - } - } - di := (*DRIVER_INFO_8)(unsafe.Pointer(&b[0])) - - driverType := RAW - - if di.PrinterDriverAttributes&PRINTER_DRIVER_XPS == 2 { - driverType = XPS_PASS - } - - return driverType, nil -} - // ReadNames return printer names on the system func ReadNames() ([]string, error) { const flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS @@ -147,6 +133,23 @@ func ReadNames() ([]string, error) { return names, nil } +type DriverInfo struct { + Name string + Environment string + DriverPath string + Attributes uint32 +} + +func newDriverInfo(di *DRIVER_INFO_8) *DriverInfo { + var info DriverInfo + info.Attributes = di.PrinterDriverAttributes + info.Name = convertLPTSTRToString(di.Name) + info.DriverPath = convertLPTSTRToString(di.DriverPath) + info.Environment = convertLPTSTRToString(di.Environment) + + return &info +} + type Printer struct { h syscall.Handle } @@ -161,6 +164,42 @@ func Open(name string) (*Printer, error) { return &p, nil } +func (p *Printer) DriverInfo() (*DriverInfo, error) { + b := make([]byte, 1024*10) + n := uint32(len(b)) + var needed uint32 + var env uint16 = 0 + err := GetPrinterDriver(p.h, &env, 8, &b[0], n, &needed) + if err != nil { + if err != syscall.ERROR_INSUFFICIENT_BUFFER { + return nil, err + } + b = make([]byte, n) + err = GetPrinterDriver(p.h, &env, 8, &b[0], needed, &needed) + if err != nil { + return nil, err + } + } + di := (*DRIVER_INFO_8)(unsafe.Pointer(&b[0])) + + ndi := newDriverInfo(di) + + return ndi, nil +} + +func (p *Printer) PrintDataType() (string, error) { + di, err := p.DriverInfo() + if err != nil { + return "", err + } + + if di.Attributes&PRINTER_DRIVER_XPS != 0 { + return XPS_PASS, nil + } + + return RAW, nil +} + func (p *Printer) StartDocument(name, datatype string) error { d := DOC_INFO_1{ DocName: &(syscall.StringToUTF16(name))[0], diff --git a/zapi.go b/zapi.go index 571f211..6549c96 100644 --- a/zapi.go +++ b/zapi.go @@ -2,10 +2,8 @@ package printer -import ( - "syscall" - "unsafe" -) +import "unsafe" +import "syscall" var _ unsafe.Pointer @@ -21,7 +19,7 @@ var ( procStartPagePrinter = modwinspool.NewProc("StartPagePrinter") procEndPagePrinter = modwinspool.NewProc("EndPagePrinter") procEnumPrintersW = modwinspool.NewProc("EnumPrintersW") - procGetPrinterDriver = modwinspool.NewProc("GetPrinterDriverW") + procGetPrinterDriverW = modwinspool.NewProc("GetPrinterDriverW") ) func GetDefaultPrinter(buf *uint16, bufN *uint32) (err error) { @@ -36,25 +34,6 @@ func GetDefaultPrinter(buf *uint16, bufN *uint32) (err error) { return } -func GetPrinterDriver(name string, buf *byte, bufN uint32) (err error) { - p, err := Open(name) - if err != nil { - return err - } - defer p.Close() - var needed uint32 - r1, _, e1 := syscall.Syscall6(procGetPrinterDriver.Addr(), 6, uintptr(p.h), uintptr(0), uintptr(8), uintptr(unsafe.Pointer(buf)), uintptr(bufN), uintptr(unsafe.Pointer(&needed))) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - panic(err) - } else { - err = syscall.EINVAL - } - } - return -} - func ClosePrinter(h syscall.Handle) (err error) { r1, _, e1 := syscall.Syscall(procClosePrinter.Addr(), 1, uintptr(h), 0, 0) if r1 == 0 { @@ -150,3 +129,15 @@ func EnumPrinters(flags uint32, name *uint16, level uint32, buf *byte, bufN uint } return } + +func GetPrinterDriver(h syscall.Handle, env *uint16, level uint32, di *byte, n uint32, needed *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetPrinterDriverW.Addr(), 6, uintptr(h), uintptr(unsafe.Pointer(env)), uintptr(level), uintptr(unsafe.Pointer(di)), uintptr(n), uintptr(unsafe.Pointer(needed))) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +}