(原) 远程共享打印

原创文章,请后转载,并注明出处。

最近Win10更新导致局域网共享打印机出现了问题。虽然尝试后还是解决了。不过之前要实多个系统间共享打印的经历还是比较郁闷。

如果能通过软件简单的实现跨版本、跨系统的实现远程共享打印,这对管理人员来说是种解放。

考虑Golang或Python。Golang是首选,但Python似乎库要多一些。Python有很长一段时间没用了。

服务端:

  1. 打印机列表

  2. 打印输出

  3. 与客户端传送文件

  4. 打印机状态反馈

  5. 权限管理

客户端:

  1. 传送文件

  2. 打印机状态及选择

  3. 登陆

收集资料:

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)
	}
}

相关文章