IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> electron通过edge-js调用TSCLIB.dll 打印卡白屏或退出的解决方案 -> 正文阅读

[JavaScript知识库]electron通过edge-js调用TSCLIB.dll 打印卡白屏或退出的解决方案

软件版本:

阅读坑记需谨慎,版本要看仔细。

"node":"v14.16.0",
"electron": "^13.0.0",
//当然本文问题和vue 没毛线关系
"vue": "^2.6.14",
//遇到坑的主角是这个
"electron-edge-js": "^14.16.1",
// 打包用到是该工具是 electron-builder,我想我遇到的坑应该和打包工具没有啥关系,但还是列出来,便于交流

几年前,直接使用IE 调用相关的插件执行打印,可,IE在win11已经找不到入口了,需要单独设置edge浏览器,且使用的人,经常“不小心”重置网络的安全,导致检查需要远程帮他们开启,故此需要个桌面软件,一次解决,永不烦人。

遇到的问题

经过查看tsc打印机官网,找到了SDK下载到入口tsc官方SDK下载地址(有些人很贱,从官网下载了资料,上传到其他收费平台上去“赚钱”);找到了nodejs相关的样例。
nodejs的样例,需要用到edge-js,但经过多日的入坑,找到的资料是使用:electron-edge-js
electron-edge-js参考官方的edge-js样例,确实可以打印,但是:

如果循环打印大量数据,会卡死。

卡死的调试场景

  • 渲染进程的打印页面(组件)循环 执行(调用)tsclibnet.dll打印方法;结果:页面卡白了。
  • 我把对tsclibnet.dll是调用、循环打印,新开启一个窗口来执行;结果,该新开的窗口,如果打印数量超过3条同样还是卡死了。
  • 放在“主进程”来执行:通过渲染进程(页面)向主进程发送数据信息,主进程执行数据的循环实施打印,结果还是卡死。

以上是我用electron-edge-js调用tsclibnet.dll遇到的问题,我不确定是不是因为我执行打印代码写的太垃圾,而导致卡死,或者我对electron了解太少,垃圾代码贴出,便于交流:
(此代码不要直接全部复制粘贴使用,我有删改,不保证可执行)

//关于库方法的说明,我在官方的站点中有pdf中文文档
//为了方便交流,我找到一个html版本,方便阅读的 https://www.e-learn.cn/topic/3539545 (以官方为准,此仅方便交流)
var openport = edge.func({
    assemblyFile: './tsc_dll/tsclibnet.dll',
    typeName: 'TSCSDK.node_driver',
	methodName: 'openport'
});
//省略了一堆官方样例给到的如上动态库引入方法
// windowsfont sendcommand barcode sendcommand printlabel 都省略了,看官方样例
let data = [
	[{
		//文本类坐标信息
		"type": "text",
		"printVal": {
			"x": 0,
			"y": 11,
			"fontheight": 40,
			"rotation": 0,
			"fontstyle": 0,
			"fontunderline": 0,
			"szFaceName": "黑体",
			"content": "打印内容1"
		}
	}, {
		"type": "qrcode",
		"printVal": "QRCODE 102,364,H,5,A,0,M2,S5,\"https://www.baidu.com\""
	}
	//此处省略了n组如上结构数据
	]
  //此处也省略了n组如上数据
];
// data的解释
// data的第一纬:出纸的总张数,即循环一次完,打印机打印完成,出一次纸
// data的第二纬度:每个需要打印内容和动态库相应方法匹配,


openport('打印机名或网络地址', true);
// 打印机相关配置
setup(conf,true);
for (let i = 0; i < data.length; i++) {
  clearbuffer('', true);
  let reverseArr = [];
  data[i].forEach(ele => {
  	// 文本类型处理
    if (ele.type == 'text') {
      windowsfont(ele.printVal, true);
    } else if (ele.type == 'qrcode'){// 二维码类处理
      sendcommand(ele.printVal, true);
    } else if (ele.type == 'barcode'){// 条码类处理
      barcode(ele.printVal, true);
    } else if (ele.type == 'reverse'){// 反相打印(黑底白字)类处理
      // 此处为什么不直接执行sendcommand()执行打印命令?
      // 因为反相打印如果在文字之前执行命令,只会打印一个黑块,没有字;如果需要黑底白字,就要放在所有指令的最后执行
      if (Array.isArray(ele.printVal) && ele.printVal.length > 0) {
      	reverseArr.push.apply(reverseArr, ele.printVal);
      }
    }
  });
  // 
  if (reverseArr.length > 0) {
    for (let ri = 0; ri < reverseArr.length; ri++) {
       sendcommand(reverseArr[ri], true);
    }
  }

  let label_variable = { quantity: '1', copy: '1' };
  // 此方法的参数的含义,我没理解透
  printlabel(label_variable, true);
}
// 关闭打印机
closeport('', true);

以上就是我执行打印,页面卡死的代码。

解决方法尝试

思来想去,翻来覆去搜索资料,开启系统任务管理器,打印的时候CPU占用突增,是不是进程资源占用不符合系统规则,系统弄死该进程呢?如何重新开启进程?略熟悉的语言也只有js?
nodejs 好像可以开启临时web服务,我把数据发送到另外一个软件上执行,对吧。且还可以打包exe可执行文件->应该可以完美解决打印:

对,可行,没有卡死,打印流程完美按照我的想法工作;(在本地 执行 node server.js 的时候服务完全正常启动,打印正常完美执行;)
打印执行打循环体,还是上述代码;web 服务我用node的框架 express(完全不懂,参照网络样例使用)

package.json 文件

{
  "name": "print_exe",
  "version": "0.0.1",
  "description": "执行打印服务",
  "main": "server.js",
  "author": {
    "name": "programmer"
  },
  "dependencies": {
    // 重新自己安装,根据自己的版本来
    "edge-js": "^18.4.0",
    "express": "^4.18.1"
  },
  "pkg": {
    "scripts": "build/**/*.js",
    "assets": "views/**/*",
    "targets": [
      "node14-win-x64"
    ],
    "outputPath": "dist"
  }
}

server.js

// 'use strict';
const fs = require('fs');
const crypto = require('crypto');
const path = require ('path');
var edge = require('edge-js');
var express = require('express');
var bodyParser = require('body-parser')
var app = express();

var urlencodedParser = bodyParser.urlencoded({ extended: false });
var urlencodedJsonParser = bodyParser.json();
app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(express.static('./'));



app.listen(5000, function () {
    console.log("Server Start!!");
    log("Server Start!!");
})
// 接受打印数据方法入口
app.post('/printing', urlencodedJsonParser, (req, res) =>{
    
    // 数据合理性验证
    // 节约页面,省去我的垃圾代码
    res.json({code:0,message:'打印执行中请等待',data:null});
    printfile(req.body);
});


var openport;
var setup;
var about;
var sendcommand;
var clearbuffer;
var printerfont;
var barcode;
var printlabel;
var closeport;
var sendcommand_utf8;
var sendcommand_binary;
var windowsfont;
try {
    openport = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'openport'
    });

    setup = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'setup'
    });

    about = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'about'
    });

    sendcommand = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'sendcommand'
    });

    clearbuffer = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'clearbuffer'
    });

    printerfont = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'printerfont'
    });

    barcode = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'barcode'
    });

    printlabel = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'printlabel'
    });

    closeport = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'closeport'
    });

    sendcommand_utf8 = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'sendcommand_utf8'
    });
    sendcommand_binary = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'sendcommand_binary'
    });

    windowsfont = edge.func({
        assemblyFile: 'tsclibnet.dll',
        typeName: 'TSCSDK.node_driver',
        methodName: 'windowsfont'
    });
}catch (error) {
    log(error,'error');
}
// 日志处理方法
function log(logContent,type='info') {
    //省去垃圾代码,
}

/**
 * 执行打印
 * @param {Object} printData 打印数据
 */
function printfile(printData){
    try {
        if (! printData.hasOwnProperty('print_name')) {
            throw "缺少打印机";
        }
        if (! printData.hasOwnProperty('printer_config')) {
            throw "缺少打印纸配置";
        }
        if (! printData.hasOwnProperty('print_data')) {
            throw "缺少打印数据";
        }
        const {print_name,printer_config,print_data} = printData;
        let {width,height,speed,consistence,sensor,spacing,offsetDistance,column} = printer_config;
		// 传入的单位为cm 此处是单位处理
        width = width * 10;
        height = height * 10;
        spacing = spacing * 10;
        // 总宽 mm
        let totalWidth = width * column + spacing * (column-1);
        // 打印纸配置数据
        const conf = {
            width: Math.ceil(totalWidth).toString(),//a
            height: Math.ceil(height).toString(),//b
            speed: speed.toString(),//c
            density: consistence.toString(),//d
            sensor: sensor.toString(),//e
            vertical: spacing.toString(),//f vertical
            offset: offsetDistance.toString(),//g
        };
        for (let i = 0; i < print_data.length; i++) {
            openport(print_name, true);
            setup(conf,true);
            clearbuffer('', true);
            sendcommand('DIRECTION 1');
            let reverseArr = [];
            print_data[i].forEach(ele => {
                log(ele.printVal);
                if (ele.type == 'text') {
                  windowsfont(ele.printVal, true);
                } else if (ele.type == 'qrcode'){
                  sendcommand(ele.printVal, true);
                } else if (ele.type == 'barcode'){
                  barcode(ele.printVal, true);
                } else if (ele.type == 'reverse'){
                  if (Array.isArray(ele.printVal) && ele.printVal.length > 0) {
                    reverseArr.push.apply(reverseArr, ele.printVal);
                  }
                }
              });
              if (reverseArr.length > 0) {
                for (let ri = 0; ri < reverseArr.length; ri++) {
                  sendcommand(reverseArr[ri], true);
                }
              }
              let label_variable = { quantity: '1', copy: '1' };
              printlabel(label_variable, true);
              // 关闭打印机
              closeport('', true);
              log('打印关闭');
       }
    } catch (error) {
        // console.log(error);
        log(error,'error');
    }
}

但是!但是!打包后的exe文件的启动错误问题让我无法解决:

Error: Module did not self-register: '\\?\E:\a\node_modules\edge-js\lib\native\win32\x64\14.19.3\edge_nativeclr.node'.

在这里插入图片描述
打包工具是 pkg
网络上查找了原因,说需要重新针对自己的node版本打包动态库!不想再去研究了,项目需要使用(向老板汇报)。

最终解决方法

之前简单了解过Python 且也尝试过Python 调用该库,(很早之前记录的踩坑记 Electron-vue开发桌面应用调用TSCLIB.dll
只是之前 通过发送命令调用 exe ,这次也借助 node 采用web服务的方式,Python 也同样开启一个服务,这样通信我自己比较容易理解些。

打印数据结构和循环体的逻辑,和最早代码的逻辑一致:
特别注意:Python 的 TSCLIB.dll动态库,和nodejs的不一致,去前文给到的官方地址下载Python 的例子,另外Python样例的动态库参数数据类型 没有描述,参照 java 样例的代码可以知道参数类型。

我遇到的问题,都写在代码注释中:(我的TSCLIB.dll库和.py在同级)

# coding=utf-8
from flask import Flask,request,json,jsonify
# import ctypes
from ctypes import *
import logging
import os
import datetime
from time import strftime
# import chardet 
app = Flask(__name__)

'''
是否是开发
True-开发
False-生产
'''
# develop = False
# 应用执行目录,该应用存在于调用应用下的地址 
appExePath = ""

@app.route('/')
def hello_world():
  # 用于应用检测是否可以正常访问
  return 'Hello World'

@app.route('/printing',methods=['POST'])
def exeprint():
  app.logger.info('info log')
  p_ddata = request.get_data()
  data = json.loads(p_ddata)
  # 的数据格式完全和最开始体到代码 data 数据格式一致
  print_data = data['print_data']
  # 打印机名
  printer_name = data['print_name']
  # 打印机相关配置
  printer_config = data['printer_config']
  column = printer_config['column']

  width = printer_config['width'] * 10
  height = printer_config['height'] * 10
  vertical = printer_config['spacing'] * 10

  totalWidth = width * column + vertical * (column-1)

  speed = printer_config['speed']
  density = printer_config['consistence']
  sensor = printer_config['sensor']
  offset = printer_config['offsetDistance']
  # print(totalWidth)
  
  try:
    path = os.getcwd()
    dllPath = path+"\\TSCLIB.dll"
    tsclibrary = CDLL(dllPath)
    # 如果用了 tsclibrary.setup() 来设置,好像会出错,不执行,我不清楚是否是传入参数的错误,所以我用 tsclibrary.sendcommandW() 来执行我需要的设置命令
    # tsclibrary.setup(str(totalWidth),str(height),str(speed),str(density),str(sensor),str(spacing),str(offset))
    # setup (String width,String height,String speed,String density,String sensor,String vertical,String offset); # java 的
    # tsclibrary.sendcommandW("DIRECTION 1")

    for item in print_data:
      tsclibrary.openportW(printer_name)
      tsclibrary.sendcommandW("DIRECTION 1")
      tsclibrary.sendcommandW("SIZE "+str(totalWidth)+" mm, "+str(height)+" mm")
      tsclibrary.sendcommandW("GAP "+str(vertical)+" mm, 0 mm")
      tsclibrary.sendcommandW("SPEED "+str(speed))
      tsclibrary.sendcommandW("DENSITY "+str(density))
      # tsclibrary.setup(str(round(totalWidth)),str(round(height)),str(round(speed)),str(round(density)),str(round(sensor)),str(round(vertical)),str(round(offset)))
      tsclibrary.clearbuffer()
      
      reverseList = []
      for pd in item:
        if pd['type'] == 'text':
          x = pd['printVal']['x']
          y = pd['printVal']['y']
          fontheight = pd['printVal']['fontheight']
          rotation = pd['printVal']['rotation']
          fontstyle = pd['printVal']['fontstyle']
          fontunderline = pd['printVal']['fontunderline']
          szFaceName = pd['printVal']['szFaceName']
          szFaceName = 'simhei'
          content = pd['printVal']['content']
          # 官方给的 tsclibrary.windowsfontW() 好像调用无效(打印数据好像没传到打印机) 
          # tsclibrary.windowsfontW 和 tsclibrary.windowsfont 有什么区别吗?
          # 还是官方给到例子太老了??
          # tsclibrary.windowsfontW(x,y,fontheight,rotation, fontstyle, fontunderline, szFaceName,content)#无法打印
          # tsclibrary.windowsfont(x,y,fontheight,rotation, fontstyle, fontunderline, szFaceName,content)#打印乱码
          tsclibrary.windowsfontUnicode( x,  y,  fontheight,  rotation, fontstyle, fontunderline, szFaceName, content)#字体为默认宋
        elif pd['type'] == 'qrcode':
          tsclibrary.sendcommandW(pd['printVal'])
        elif pd['type'] == 'reverse':
          reverseList.extend(pd['printVal'])

      for revCmd in reverseList:
        tsclibrary.sendcommandW(revCmd)
        # tsclibrary.sendcommand(revCmd)

      # tsclibrary.printlabelW("1","1")
      tsclibrary.printlabel("1","1")
      tsclibrary.closeport()
    
  except OSError as err:
    app.logger.warning(err)
    print(err)

  return jsonify(status="success")

if __name__ == '__main__':
  # 日志配置 不需要的话此处可以删
  now=datetime.datetime.now()
  logdate = now.strftime("%Y-%m-%d")
  handler = logging.FileHandler(logdate+'.log', encoding='UTF-8')
  handler.setLevel(logging.DEBUG)
  logging_format = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
  handler.setFormatter(logging_format)
  app.logger.addHandler(handler)
  # 日志配置end
  app.run()

Python 打印服务程序 全部代码。
打包 exe 命令pyinstaller -F -w -i my.ico PrintServer.py 参考地址

应用程序启动打印服务(python 打包的exe)

打包后的EXE 和 TSCLIB.dll 库都和应用打包后的启动exe位于同级目录(如果要放在子目录或者其他目录 Python 动态库地址,需要再处理,否则electon 无法启动打印服务)

我的此段代码是位于electron主js文件的最后。

try  {
  // 启动打印服务
  if (是生产) {
    //注意每个人的端口号可能不一样
    child_process.exec('netstat -ano|findstr "5000"',(err, stdout, stderr)=>{
      if (stdout === "") {
        child_process.exec(`${homeDir}\\PrintServer.exe`,(err, stdout, stderr)=>{
          logger.info(`命令执行err:${err}`);
          logger.info(`命令执行stdout:${stdout}`);
          logger.info(`命令执行stderr:${stderr}`);
        });
      }else{
        logger.info('5000 端口被占用,或打印服务已启动');
      }
    })
  }
  // 如果是开发,手动启动打印服务
} catch (e) {
  logger.info(`命令执行异常:${e}`);
}

后记

记录我的坑,便于后来者。希望我遇到的问题您能避免,且把您的解决方案留言告诉我们有共同问题的人。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:13:50  更:2022-07-17 16:17:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:16:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码