Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Get Printer Type #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cmd/print/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func listPrinters() error {
if err != nil {
return err
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line. Unrelated to your change.

for i, p := range printers {
s := " "
if p == defaultPrinter {
Expand Down Expand Up @@ -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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect you had some problem with this code always passing "RAW" to StartDocument. What is the problem? How can I reproduce it? Can you create an issue about this problem and make this PR fix the problem?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is: i had a printer that doesnt accept RAW datatype, the problem is that print only accepts XPS documents.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what is the data that you need to write to the printer when you pass "XPS_PASS" to it? Can I send text data? Will it print properly? Does every printer that reports PRINTER_DRIVER_XPS will work with "XPS_PASS"? Are there any references to all of this somewhere on the Internet?

Maybe we could just introduce another flag for the program that describes data type, and default it to "RAW" instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normal string data, you can send normal text, its print properly, the driver do the work, and Yes every printer with PRINTER_DRIVER_XPS will work with XPS_PASS

And yes, we can do other flag for program to describe data type and set RAW as default

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for explaining. I will wait for you to address my other comments now.

Alex

if err != nil {
return err
}
Expand Down
99 changes: 69 additions & 30 deletions printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package printer

import (
"syscall"
"unicode/utf16"
"unsafe"
)

Expand Down Expand Up @@ -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 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to implement that function, because it already exists. Search for syscall.UTF16ToString in net package - it will show how to do that.

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)
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -161,6 +164,42 @@ func Open(name string) (*Printer, error) {
return &p, nil
}

func (p *Printer) DriverInfo() (*DriverInfo, error) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New function needs documentation.

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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you using &env and not nil here? &end points to empty environment, while nil is default for "the current environment of the calling application and client machine". Which do we want here?

if err != nil {
if err != syscall.ERROR_INSUFFICIENT_BUFFER {
return nil, err
}
b = make([]byte, n)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But b already points to buffer of n bytes. Don't you want to make bigger buffer - needed bytes big?

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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just:

return &DriverInfo{
Name: ...,
...
}, nil

and you won't need newDriverInfo function.


return ndi, nil
}

func (p *Printer) PrintDataType() (string, error) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, remove this function. The name does not explain what it does, and it is simple enough to be implemented in couple of lines of code.

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],
Expand Down
39 changes: 15 additions & 24 deletions zapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

package printer

import (
"syscall"
"unsafe"
)
import "unsafe"
import "syscall"

var _ unsafe.Pointer

Expand All @@ -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) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}