| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- package service
- import (
- "fmt"
- "os"
- "syscall"
- "unsafe"
- "github.com/tarm/serial"
- )
- // SerialPrinter 通用打印机接口(串口或 Windows USB)
- type Printer interface {
- Write(data []byte) error
- Close() error
- }
- type SerialPrinter struct {
- Port *serial.Port
- }
- func NewSerialPrinter(portName string, baudRate int) (*SerialPrinter, error) {
- if baudRate <= 0 {
- baudRate = 9600
- }
- // 非 COM 端口 → 尝试 Windows 打印机路径
- if len(portName) >= 3 && portName[:3] != "COM" {
- return nil, fmt.Errorf("非串口设备,请使用 USB 驱动安装虚拟 COM 口,或连接真实 COM 口")
- }
- c := &serial.Config{Name: portName, Baud: baudRate}
- port, err := serial.OpenPort(c)
- if err != nil {
- return nil, fmt.Errorf("打开串口失败 %s: %w", portName, err)
- }
- return &SerialPrinter{Port: port}, nil
- }
- func (p *SerialPrinter) Write(data []byte) error {
- _, err := p.Port.Write(data)
- return err
- }
- func (p *SerialPrinter) Close() error {
- return p.Port.Close()
- }
- // USBPrinter Windows USB 打印机(原生 API 发送 RAW 数据)
- type USBPrinter struct {
- handle syscall.Handle
- }
- func NewUSBPrinter(printerName string) (*USBPrinter, error) {
- paths := []string{
- fmt.Sprintf("\\\\.\\%s", printerName), // Win32 device
- fmt.Sprintf("\\\\127.0.0.1\\%s", printerName), // UNC share
- }
- var handle syscall.Handle
- var lastErr error
- for _, path := range paths {
- pathPtr, _ := syscall.UTF16PtrFromString(path)
- handle, lastErr = syscall.CreateFile(pathPtr, syscall.GENERIC_WRITE,
- 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
- if lastErr == nil {
- return &USBPrinter{handle: handle}, nil
- }
- }
- // 所有路径都失败 → 用临时文件 + copy 命令的方式
- return nil, fmt.Errorf("无法打开打印机 %s (\\\\.\\ 和 UNC均失败): %w。"+
- "请到 Windows 控制面板→设备和打印机→右键 %s→打印机属性→共享→勾选'共享这台打印机'并设置共享名",
- printerName, lastErr, printerName)
- }
- func (p *USBPrinter) Write(data []byte) error {
- var written uint32
- return syscall.WriteFile(p.handle, data, &written, nil)
- }
- func (p *USBPrinter) Close() error {
- return syscall.CloseHandle(p.handle)
- }
- // PrintRawFile 备选方案:通过临时文件 + copy 命令打印
- func PrintRawFile(printerName string, data []byte) error {
- tmpFile, err := os.CreateTemp("", "ticket_*.bin")
- if err != nil {
- return err
- }
- defer os.Remove(tmpFile.Name())
- if _, err := tmpFile.Write(data); err != nil {
- tmpFile.Close()
- return err
- }
- tmpFile.Close()
- _, _, _ = syscall.NewLazyDLL("kernel32.dll").NewProc("CopyFileW").Call(
- uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(tmpFile.Name()))),
- uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fmt.Sprintf("\\\\127.0.0.1\\%s", printerName)))),
- 0)
- return nil
- }
- var _ = os.Stderr // keep os import
|