欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

win32 打印机任务管理的 node 模块 (3)详解Win32 Spooler API 获取打印机列表及状态

程序员文章站 2022-07-05 12:54:34
...

上篇讲了如何写一个 node addon,这篇开始讲述如何调用 Win32 Spooler API 实现打印的完整功能。项目的代码在 https://gitee.com/csling/win32-printer ,用 c++ 编写。下面一步一步来讲解。

代码结构

主要文件 2 个:

win32_printer.cc : 定义 node 的接口,接受参数和返回数据结构
win32_printer.h:接口的具体实现,调用 Win32 Spooler API 管理打印任务。

如何定义 node 接口,传参和返回

定义好接口给 nodejs 调用。按照 node-addon-api 的格式,传入参数的方式:

Napi::Value getUserDefaultOptions(const Napi::CallbackInfo& info) {

  std::string arg1 = info[0].As<Napi::String>().Utf8Value();
  std::string printerName(
    utf8ToGBK(const_cast<char*>(arg1.c_str()))
  );

Napi::CallbackInfo不单能传入普通的数据类型,还能传入 Function 类型作为回调。当传入多个参数,依次按照 info 下标获取。
为了兼容中文打印机名字,代码还做了 printerName 转成 GBK 。原因是 win 存储中文用的是 GBK 编码,而 js 那部分用的是 utf8 编码。

返回的数据只能是 node-addon-api 的数据类型

  Napi::Value userOptions = _getUserDefaultOptions(env, printerName);
  return userOptions;

在哪里找到 node-addon-api 的接口文档呢?
官方文档 https://github.com/nodejs/node-addon-api#api

获取打印机列表

用到 EnumPrinters 接口,官方文档 https://docs.microsoft.com/en-us/windows/win32/printdocs/enumprinters
https://docs.microsoft.com/en-us/windows/win32/printdocs/retrieving-a-printer-device-context

BOOL fnReturn = EnumPrinters(
      PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME,
      pPrinterName,
      level,
      (LPBYTE)NULL,
      0L,
      &dwNeeded,
      &dwReturned
  );

  if (dwNeeded > 0) {
    pInfo = (LPBYTE)HeapAlloc(GetProcessHeap(), 0L, dwNeeded);
  }

  if (NULL != pInfo) {
    dwReturned = 0;
    fnReturn = EnumPrinters(
      PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME,
      pPrinterName,
      level,
      (LPBYTE)pInfo,
      dwNeeded,
      &dwNeeded,
      &dwReturned
    );

存储打印机信息的结构是 PRINTER_INFO_* ,* 号对应level 参数的取值。在上面代码中的变量是 pInfo 。开始并不知道该给 pInfo 分配多大的空间,第一次调用后,dwNeeded 返回实际需要的字节数。然后第二次调用后 pInfo 保存了 PRINTER_INFO_* 以及 PRINTER_INFO_* 内部指针指向的内存空间。

 

打印机状态

如果你读取 PRINTER_INFO_2 的 Status 作为打印机的状态,奇怪的事情发生了。即使打印机并没有连接,Status 也显示正常值。因此并不能反映准确的状态。正确的方法判断是否离线:

pThisInfo->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE

另外,如何知道打印机是否空闲呢?查 cJobs 属性,代表当前正在打印队列中任务的数量。0 代表当前为空闲状态。

 

中文字符的处理

windows 用宽字节类型来表示中文,直接拿它来构造 Napi::String 结果是乱码。因此用 char16_t * 强制转换类型,表示这是双字节的类型。

prt.Set("name", Napi::String::New(env, (char16_t *)pThisInfo->pPrinterName));

 

区分物理打印机和虚拟打印机

检查 printProcessor 的值,物理打印机对应的值是驱动的名字,而虚拟打印机取值是 winprint。