最近Win10更新导致局域网共享打印机出现了问题。虽然尝试后还是解决了。不过之前要实多个系统间共享打印的经历还是比较郁闷。
如果能通过软件简单的实现跨版本、跨系统的实现远程共享打印,这对管理人员来说是种解放。
考虑Golang或Python。Golang是首选,但Python似乎库要多一些。Python有很长一段时间没用了。
服务端:
-
打印机列表
-
打印输出
-
与客户端传送文件
-
打印机状态反馈
-
权限管理
客户端:
-
传送文件
-
打印机状态及选择
-
登陆
收集资料:
Go:
https://github.com/Knisy/printer
https://github.com/alexbrainman/printer
https://github.com/jadefox10200/goprint (打印PDF)
https://github.com/racingmars/virtual1403 (虚拟打印机)
https://github.com/Marneus68/rsvp(网络虚拟打印机)
https://github.com/vvalchev/printer-scanner(打印网络中的打印机)
https://github.com/gerkirill/winprinter
https://github.com/Serendipity-sw/go-http-printer(golang调用windows程序实现pdf调用打印机)
https://github.com/Flix14/printer-spooler(调用打印功能实现的网络服务)
https://github.com/albertnadal/printer-api
https://github.com/janftacnik/sim-printer(使用 LPD 和 RAW 协议模拟打印机接收打印作业)
https://github.com/blackmou5e/printer-tools (获取有关打印机状态、重新启动和将其设置重置为出厂默认设置的信息的工具。)
https://github.com/mmgubo/printers_management_golang_api
https://github.com/richardiux/restful-cups-printer
Python:
https://blog.csdn.net/kaspar1992/article/details/100088079
https://zhuanlan.zhihu.com/p/221834144
https://www.jb51.net/article/179850.htm
package main
import (
"fmt"
"io/ioutil"
"log"
"syscall"
"unsafe"
)
var (
dll = syscall.MustLoadDLL("winspool.drv")
getDefaultPrinter = dll.MustFindProc("GetDefaultPrinterW")
openPrinter = dll.MustFindProc("OpenPrinterW")
closePrinter = dll.MustFindProc("ClosePrinter")
startPagePrinter = dll.MustFindProc("StartPagePrinter")
endPagePrinter = dll.MustFindProc("EndPagePrinter")
endDocPrinter = dll.MustFindProc("EndDocPrinter")
startDocPrinter = dll.MustFindProc("StartDocPrinterW")
writePrinter = dll.MustFindProc("WritePrinter")
)
type DOC_INFO_1 struct {
pDocName *uint16
pOutputFile *uint16
pDatatype *uint16
}
func main() {
printerName, _ := GetDefaultPrinterName()
//open the printer
printerHandle, err := GoOpenPrinter(printerName)
if err != nil {
log.Fatalln("Failed to open printer")
}
defer GoClosePrinter(printerHandle)
filePath := "D:/Lebenslauf.pdf"
//Send to printer:
err = GoPrint(printerHandle, filePath)
if err != nil {
log.Fatalln("during the func sendToPrinter, there was an error")
}
}
func GetDefaultPrinterName() (string, []uint16) {
var pn [256]uint16
plen := len(pn)
getDefaultPrinter.Call(uintptr(unsafe.Pointer(&pn)), uintptr(unsafe.Pointer(&plen)))
printerName := syscall.UTF16ToString(pn[:])
fmt.Println("Printer name:", printerName)
printer16 := syscall.StringToUTF16(printerName)
return printerName, printer16
}
func GoOpenPrinter(printerName string) (uintptr, error) {
printerName16 := syscall.StringToUTF16(printerName)
printerHandle, err := openPrinterFunc(printerName, printerName16)
if err != nil {
return 0, err
}
return printerHandle, nil
}
func openPrinterFunc(printerName string, printerName16 []uint16) (uintptr, error) {
var printerHandle uintptr
_, _, msg := openPrinter.Call(uintptr(unsafe.Pointer(&printerName16[0])), uintptr(unsafe.Pointer(&printerHandle)), 0)
fmt.Println("open printer: ", msg)
if printerHandle == 0 {
return 0, fmt.Errorf("couldn't find printer: printerName")
}
return printerHandle, nil
}
func GoClosePrinter(printerHandle uintptr) {
closePrinter.Call(printerHandle)
//return
}
func GoPrint(printerHandle uintptr, path string) error {
var err error
startPrinter(printerHandle, path)
startPagePrinter.Call(printerHandle)
err = writePrinterFunc(printerHandle, path)
endPagePrinter.Call(printerHandle)
endDocPrinter.Call(printerHandle)
return err
}
func startPrinter(printerHandle uintptr, path string) {
/*arr := strings.Split(path, "/")
l := len(arr)
name := arr[l-1]*/
d := DOC_INFO_1{
pDocName: &(syscall.StringToUTF16("Output pdf file"))[0],
pOutputFile: nil,
// Win7
//pDataType = "RAW";
// Win8+
// pDataType = "XPS_PASS";
pDatatype: &(syscall.StringToUTF16("XPS_PASS"))[0],
}
r1, r2, err := startDocPrinter.Call(printerHandle, 1, uintptr(unsafe.Pointer(&d)))
fmt.Println("startDocPrinter: ", r1, r2, err)
//return
}
func writePrinterFunc(printerHandle uintptr, path string) error {
fmt.Println("About to write file to path: ", path)
fileContents, err := ioutil.ReadFile(path)
if err != nil {
return err
}
var contentLen uintptr = uintptr(len(fileContents))
fmt.Println("contentLen", contentLen)
var writtenLen int
_, _, err = writePrinter.Call(printerHandle, uintptr(unsafe.Pointer(&fileContents[0])), contentLen, uintptr(unsafe.Pointer(&writtenLen)))
fmt.Println("Writing to printer:", err)
return nil
}
一个虚拟打印机代码,看看是啥
/* Simulated printer, listens on 9100 and 515
*/
package main
import (
"fmt"
"net"
"os"
// "os/exec"
// "strings"
"encoding/json"
"io"
"io/ioutil"
"strconv"
)
type Configuration struct {
Lprport string `json:"lprport"`
Rawport string `json:"rawport"`
}
func main() {
var configuration Configuration
configfile, err := os.Open("printerconfig.json")
if err != nil {
configuration.Lprport = "515"
configuration.Rawport = "9100"
} else {
defer configfile.Close()
decoder := json.NewDecoder(configfile)
err := decoder.Decode(&configuration)
if err != nil {
fmt.Println("error:", err)
}
}
fmt.Println("Simulated printer, Jan Ftacnik, 2021 \n")
fmt.Println("Using LPR port: ", configuration.Lprport)
fmt.Println("Using RAW port: ", configuration.Rawport)
go server9100(configuration.Rawport)
server515(configuration.Lprport)
}
func server515(lprport string) {
var name string
service := ":" + lprport
icount := 0
name1 := "lprjob"
tcpAddr, err := net.ResolveTCPAddr("tcp", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
// run as a goroutine
icount = icount + 1
name = name1 + strconv.Itoa(icount)
go handleClient515(conn, name)
}
}
func handleClient515(conn net.Conn, filename string) {
// close connection on exit
defer conn.Close()
// file, err4 := os.Create(filename)
// if err4 != nil {
// fmt.Fprintf(os.Stderr, "Fatal error: %s", err4.Error())
// return
// }
// defer file.Close()
var buf [512]byte
var code byte = 0x0
for {
ack := []byte{code}
// read up to 512 bytes
n, err := conn.Read(buf[0:])
if err != nil {
return
}
// queue := string(buf[1:n])
// fmt.Println(" queue: ",queue)
// write the n bytes read
_, err2 := conn.Write(ack)
if err2 != nil {
fmt.Fprintf(os.Stderr, "Could not write message back....")
return
}
// read up to 512 bytes
_, err = conn.Read(buf[0:])
if err != nil {
return
}
// work := string(buf[1:n])
// i := strings.Index(work, "cfA")
// if i>-1 {
// comp := string(buf[7+i:n-1])
// jobid := string(buf[4+i:7+i])
// fmt.Println(" comp: ",comp," jobid: ",jobid)
// }
// write the n bytes read
_, err2 = conn.Write(ack)
if err2 != nil {
fmt.Fprintf(os.Stderr, "Could not write message back....")
return
}
// read up to 512 bytes
n, err = conn.Read(buf[0:])
if err != nil {
return
}
errc := ioutil.WriteFile(filename+".cfg", buf[0:n], 0666)
if errc != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", errc.Error())
}
// work5 := string(buf[0:n])
// s := strings.Fields(work5)
// for index, element := range s {
// fmt.Println(index, "--", element)
// }
// write the n bytes read
_, err2 = conn.Write(ack)
if err2 != nil {
fmt.Fprintf(os.Stderr, "Could not write message back....")
return
}
// read up to 512 bytes
_, err = conn.Read(buf[0:])
if err != nil {
return
}
// fmt.Println(n," Read: ",string(buf[1:n]))
// write the n bytes read
_, err2 = conn.Write(ack)
if err2 != nil {
fmt.Fprintf(os.Stderr, "Could not write message back....")
return
}
// read up to 512 bytes
n, err = conn.Read(buf[0:])
if err != nil {
return
}
err5 := ioutil.WriteFile(filename+".prn", buf[0:n], 0666)
if err5 != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err5.Error())
}
//OPEN FILE TO APPEND INTO
file, err6 := os.OpenFile(filename+".prn", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err6 != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err6.Error())
return
}
defer file.Close()
// fmt.Println(n," Header of data file: ",string(buf[1:n]))
// write the n bytes read
_, err2 = conn.Write(ack)
if err2 != nil {
fmt.Fprintf(os.Stderr, "Could not write message back....")
return
}
// read up to 512 bytes
_, err = io.Copy(file, conn)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
return
}
// n, err = conn.Read(buf[0:])
// if err != nil {
// return
// }
// fmt.Println(" Data file done \n")
// write the n bytes read
_, err2 = conn.Write(ack)
if err2 != nil {
fmt.Fprintf(os.Stderr, "Could not write message back....")
return
}
file.Close()
fmt.Printf("Saved file %s \n", filename+".prn")
// out, errex := exec.Command(`cmd.exe`, `/C`, `E:\Go\src\simprinter\copyfile.bat`, filename+".prn").Output()
// if errex != nil {
// fmt.Printf("Problem executing batch file: %s", errex)
// }
// fmt.Printf("Output: %s", out)
}
}
func server9100(rawport string) {
var name string
service := ":" + rawport
jcount := 0
name1 := "rawjob"
tcpAddr, err := net.ResolveTCPAddr("tcp", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
// run as a goroutine
jcount = jcount + 1
name = name1 + strconv.Itoa(jcount) + ".prn"
go handleClient9100(conn, name)
}
}
func handleClient9100(conn net.Conn, filename string) {
// close connection on exit
defer conn.Close()
file, err := os.Create(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
return
}
defer file.Close()
_, err = io.Copy(file, conn)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
return
}
fmt.Printf("Saved file %s \n", filename)
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}