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 all commits
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
103 changes: 103 additions & 0 deletions printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@ package printer

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

const (
PRINTER_DRIVER_XPS uint32 = 0x00000002
Copy link
Owner

Choose a reason for hiding this comment

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

s/PRINTER_DRIVER_XPS uint32/PRINTER_DRIVER_XPS/

make PRINTER_DRIVER_XPS untyped. Untyped consts fit anything.

Copy link
Owner

Choose a reason for hiding this comment

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

Maybe add PRINTER_DRIVER_PACKAGE_AWARE and others.

RAW = "RAW"
XPS_PASS = "XPS_PASS"
Copy link
Owner

Choose a reason for hiding this comment

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

Delete both RAW and XPS_PASS. Users won't need them.

)

//go:generate go run mksyscall_windows.go -output zapi.go printer.go

type DOC_INFO_1 struct {
Expand All @@ -26,6 +33,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
Expand All @@ -40,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 Down Expand Up @@ -83,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 @@ -97,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
13 changes: 13 additions & 0 deletions zapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var (
procStartPagePrinter = modwinspool.NewProc("StartPagePrinter")
procEndPagePrinter = modwinspool.NewProc("EndPagePrinter")
procEnumPrintersW = modwinspool.NewProc("EnumPrintersW")
procGetPrinterDriverW = modwinspool.NewProc("GetPrinterDriverW")
)

func GetDefaultPrinter(buf *uint16, bufN *uint32) (err error) {
Expand Down Expand Up @@ -128,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
}