package service import ( "fmt" "os" "path/filepath" "syscall" "unsafe" ) // CSN Printer SDK — Go wrapper for CsnPrinterLibs.dll (x64) // FindCsnDllPath 按优先级探测 CsnPrinterLibs.dll 位置: // 1. exe 同目录(生产环境:build/bin/ 与 smart-parking.exe 一起分发) // 2. 当前工作目录 // 3. 项目相对路径(wails dev 时 cwd 为 lc_garage/) // 4. 开发机原始绝对路径(兜底) func FindCsnDllPath() string { exePath, _ := os.Executable() candidates := []string{ filepath.Join(filepath.Dir(exePath), "CsnPrinterLibs.dll"), "CsnPrinterLibs.dll", "build/bin/CsnPrinterLibs.dll", `D:\lq\资料\停车场 道闸 车牌识别 收费机\票机自制\打印机\EMD807嵌入式热敏票据打印模组\5、SDK指令集\Win_SDK_Demo_2023_08_14\Win_SDK_Demo_2023_08_14\lib\x64\CsnPrinterLibs.dll`, } for _, p := range candidates { if _, err := os.Stat(p); err == nil { return p } } return candidates[0] } var csnDll *syscall.LazyDLL // parseCSNPortNames parses the SDK's double-NUL-terminated port list. // Port_EnumUSB and Port_EnumPRN return the number of entries, not the byte length. func parseCSNPortNames(buffer []byte) []string { var names []string for start := 0; start < len(buffer); { end := start for end < len(buffer) && buffer[end] != 0 { end++ } if end == start { break } names = append(names, string(buffer[start:end])) start = end + 1 } return names } func csnEnumPortNames(procName string) []string { if csnDll == nil { return nil } buf := make([]byte, 256) ret, _, _ := csnDll.NewProc(procName).Call(uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) if ret == 0 { return nil } return parseCSNPortNames(buf) } func InitCSN(dllPath string) error { csnDll = syscall.NewLazyDLL(dllPath) // Test load if err := csnDll.Load(); err != nil { return fmt.Errorf("加载 CsnPrinterLibs.dll 失败: %w", err) } return nil } // === Port functions === func Csn_EnumUSB() string { names := csnEnumPortNames("Port_EnumUSB") if len(names) == 0 { return "" } return names[0] } func Csn_EnumPRN() string { names := csnEnumPortNames("Port_EnumPRN") if len(names) == 0 { return "" } return names[0] } func Csn_EnumUSBNames() []string { return csnEnumPortNames("Port_EnumUSB") } func Csn_EnumPRNNames() []string { return csnEnumPortNames("Port_EnumPRN") } func Csn_OpenUSB(name string) (uintptr, error) { if csnDll == nil { return 0, fmt.Errorf("DLL 未加载") } nameBytes := append([]byte(name), 0) ret, _, _ := csnDll.NewProc("Port_OpenUSBIO").Call( uintptr(unsafe.Pointer(&nameBytes[0]))) if ret == 0 { return 0, fmt.Errorf("打开 USB 打印机失败: %s", name) } return ret, nil } func Csn_OpenPRN(name string) (uintptr, error) { if csnDll == nil { return 0, fmt.Errorf("DLL 未加载") } nameBytes := append([]byte(name), 0) ret, _, _ := csnDll.NewProc("Port_OpenPRNIO").Call( uintptr(unsafe.Pointer(&nameBytes[0]))) if ret == 0 { return 0, fmt.Errorf("打开 Windows 打印机失败: %s", name) } return ret, nil } func Csn_SetPort(handle uintptr) { if csnDll != nil && handle != 0 { csnDll.NewProc("Port_SetPort").Call(handle) } } func csn_Close(handle uintptr) { if csnDll != nil && handle != 0 { csnDll.NewProc("Port_ClosePort").Call(handle) } } // Csn_Close 关闭调试接口或外部适配层打开的 CSN 端口。 func Csn_Close(handle uintptr) { csn_Close(handle) } // === Print functions === func csn_Reset() { if csnDll != nil { csnDll.NewProc("Pos_Reset").Call() } } func csn_FeedLine() { if csnDll != nil { csnDll.NewProc("Pos_FeedLine").Call() } } func csn_FeedLines(n int) { if csnDll != nil { csnDll.NewProc("Pos_Feed_N_Line").Call(uintptr(n)) } } func csn_Align(value int) { // 0=左, 1=中, 2=右 if csnDll != nil { csnDll.NewProc("Pos_Align").Call(uintptr(value)) } } func csn_FullCut() { if csnDll != nil { csnDll.NewProc("Pos_FullCutPaper").Call() } } // WriteData 直接发送 RAW 字节到当前激活的打印机端口 func Csn_WriteData(data []byte) int { if csnDll == nil { return 0 } ret, _, _ := csnDll.NewProc("WriteData").Call( uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)), 3000) // timeout 3s return int(ret) } // csn_Text 打印一行文字 // nOrgx: -1=左对齐, -2=居中, -3=右对齐 func csn_Text(text string, widthTimes, heightTimes, fontType, fontStyle, nOrgx int) { if csnDll == nil { return } // Pos_Text(const wchar_t *prnText, int nLan, int nOrgx, int nWidthTimes, int nHeightTimes, int FontType, int nFontStyle) wstr, _ := syscall.UTF16FromString(text) csnDll.NewProc("Pos_Text").Call( uintptr(unsafe.Pointer(&wstr[0])), 0, // nLan: 0=GBK uintptr(nOrgx), uintptr(widthTimes), uintptr(heightTimes), uintptr(fontType), uintptr(fontStyle)) csn_FeedLine() } func csn_QRCode(data string, width int) bool { if csnDll == nil { return false } wstr, _ := syscall.UTF16FromString(data) ret, _, _ := csnDll.NewProc("Pos_EscQrcode").Call( uintptr(unsafe.Pointer(&wstr[0])), uintptr(width), 4) // nWidth, nErrLevel=4 return ret != 0 } func csn_Barcode(data string, barcodeType int, unitWidth, unitHeight, fontStyle, fontPos int) { if csnDll == nil { return } dataBytes := append([]byte(data), 0) csnDll.NewProc("Pos_Barcode").Call( uintptr(unsafe.Pointer(&dataBytes[0])), uintptr(barcodeType), 0, uintptr(unitWidth), uintptr(unitHeight), uintptr(fontStyle), uintptr(fontPos)) }