为什么 EnumPrintersA 和 EnumPrintersW 请求相同的内存量?

2024-04-08

I call 枚举打印机A/枚举打印机W函数使用node-ffi获取可从我的电脑访问的本地打印机列表。
您应该创建一个缓冲区,该缓冲区将由 EnumPrinters 函数填充信息。
但您不知道所需的缓冲区大小。
在这种情况下你需要执行枚举打印机A/枚举打印机W twice.
在第一次调用期间,该函数计算有关打印机信息的内存量,在第二次调用期间,该函数用有关打印机的信息填充缓冲区。
如果是 Unicode 版本枚举打印机函数中,打印机名称中的每个字母将在 Windows 中使用两个字符进行编码。

为什么第一次打电话给枚举打印机W返回与第一次调用相同的所需内存量枚举打印机A?
Unicode 字符串的长度是非 unicode 字符串的两倍,但所需的缓冲区大小相同。

var ffi = require('ffi')
var ref = require('ref')
var Struct = require('ref-struct')
var wchar_t = require('ref-wchar')

var int = ref.types.int
var intPtr = ref.refType(ref.types.int)
var wchar_string = wchar_t.string

var getPrintersA =  function getPrinters() {
   var PRINTER_INFO_4A = Struct({
      'pPrinterName' : ref.types.CString,
      'pServerName' : ref.types.CString,
      'Attributes' : int
   });

   var printerInfoPtr = ref.refType(PRINTER_INFO_4A);

   var winspoolLib = new ffi.Library('winspool', {
      'EnumPrintersA': [ int, [ int, ref.types.CString, int, printerInfoPtr, int, intPtr, intPtr ] ]
   });

   var pcbNeeded = ref.alloc(int, 0);
   var pcReturned = ref.alloc(int, 0);

   //Get amount of memory for the buffer with information about printers
   var res = winspoolLib.EnumPrintersA(6, ref.NULL, 4, ref.NULL, 0, pcbNeeded, pcReturned);
   if (res != 0) {
      console.log("Cannot get list of printers. Error during first call to EnumPrintersA. Error: " + res);
      return;
   }

   var bufSize = pcbNeeded.deref();
   var buf = Buffer.alloc(bufSize);

   console.log(bufSize);

   //Fill buf with information about printers
   res = winspoolLib.EnumPrintersA(6, ref.NULL, 4, buf, bufSize, pcbNeeded, pcReturned);
   if (res == 0) {
      console.log("Cannot get list of printers. Eror: " + res);
      return;
   }

   var countOfPrinters = pcReturned.deref();

   var printers = Array(countOfPrinters);
   for (var i = 0; i < countOfPrinters; i++) {
      var pPrinterInfo = ref.get(buf, i*PRINTER_INFO_4A.size, PRINTER_INFO_4A);
      printers[i] = pPrinterInfo.pPrinterName;
   }

   return printers;
};

var getPrintersW =  function getPrinters() {
   var PRINTER_INFO_4W = Struct({
      'pPrinterName' : wchar_string,
      'pServerName' : wchar_string,
      'Attributes' : int
   });

   var printerInfoPtr = ref.refType(PRINTER_INFO_4W);

   var winspoolLib = new ffi.Library('winspool', {
      'EnumPrintersW': [ int, [ int, wchar_string, int, printerInfoPtr, int, intPtr, intPtr ] ]
   });

   var pcbNeeded = ref.alloc(int, 0);
   var pcReturned = ref.alloc(int, 0);

   //Get amount of memory for the buffer with information about printers
   var res = winspoolLib.EnumPrintersW(6, ref.NULL, 4, ref.NULL, 0, pcbNeeded, pcReturned);
   if (res != 0) {
      console.log("Cannot get list of printers. Error during first call to EnumPrintersW. Eror code: " + res);
      return;
   }

   var bufSize = pcbNeeded.deref();
   var buf = Buffer.alloc(bufSize);

   console.log(bufSize);

   //Fill buf with information about printers
   res = winspoolLib.EnumPrintersW(6, ref.NULL, 4, buf, pcbNeeded.deref(), pcbNeeded, pcReturned);
   if (res == 0) {
      console.log("Cannot get list of printers. Eror code: " + res);
      return;
   }

   var countOfPrinters = pcReturned.deref();
   var printers = new Array(countOfPrinters);
   for (var i = 0; i < countOfPrinters; i++) {
      var pPrinterInfo = ref.get(buf, i*PRINTER_INFO_4W.size, PRINTER_INFO_4W);
      printers[i] = pPrinterInfo.pPrinterName;
   }

   return printers;
};

https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd162692(v=vs.85).aspx https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd162692(v=vs.85).aspx

BOOL EnumPrinters(
  _In_  DWORD   Flags,
  _In_  LPTSTR  Name,
  _In_  DWORD   Level,
  _Out_ LPBYTE  pPrinterEnum,
  _In_  DWORD   cbBuf,
  _Out_ LPDWORD pcbNeeded,
  _Out_ LPDWORD pcReturned
);

https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd162847(v=vs.85).aspx https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd162847(v=vs.85).aspx

typedef struct _PRINTER_INFO_4 {
  LPTSTR pPrinterName;
  LPTSTR pServerName;
  DWORD  Attributes;
} PRINTER_INFO_4, *PPRINTER_INFO_4;

我可以确认你发现了什么EnumPrintersA and EnumPrintersW是可重现的。 在我的机器上,它们都需要 240 字节。

这让我很好奇,所以我决定为每个函数分配一个单独的缓冲区,并将每个缓冲区转储到一个文件中,并使用十六进制编辑器打开它们。 每个文件中有趣的部分当然是打印机的名称。

为了简单起见,我将向您展示打印机的前 3 个名称。 第一行来自EnumPrintersA,第二个来自EnumPrintersW:

Fax.x...FX DocuPrint C1110 PCL 6..C.1.1.1.0. .P.C.L. .6...Microsoft XPS Document Writer.o.c.u.m.e.n.t. .W.r.i.t.e.r...
F.a.x...F.X. .D.o.c.u.P.r.i.n.t. .C.1.1.1.0. .P.C.L. .6...M.i.c.r.o.s.o.f.t. .X.P.S. .D.o.c.u.m.e.n.t. .W.r.i.t.e.r...

从这个结果来看,EnumPrintersA calls EnumPrintersW对于实际工作,然后简单地将缓冲区中的每个字符串转换为单字节字符并将结果字符串放在同一位置。 为了证实这一点,我决定追踪EnumPrintersA代码,我发现它肯定会调用EnumPrintersW在位置winspool.EnumPrintersA + 0xA7。 实际位置在不同的 Windows 版本中可能会有所不同。

这让我更好奇,所以我决定测试其他有 A 和 W 版本的函数。 这是我发现的:

EnumMonitorsA 280 bytes needed
EnumMonitorsW 280 bytes needed
EnumServicesStatusA 20954 bytes needed
EnumServicesStatusW 20954 bytes needed
EnumPortsA 2176 bytes needed
EnumPortsW 2176 bytes needed
EnumPrintProcessorsA 24 bytes needed
EnumPrintProcessorsW 24 bytes needed

从这个结果来看,我的结论是EnumPrintersA calls EnumPrintersW用于实际工作并转换缓冲区中的字符串,其他具有 A 和 W 版本的函数也执行相同的操作。 这似乎是一种常见的机制,可以避免以更大的缓冲区为代价而重复代码,也许是因为缓冲区无论如何都可以被释放。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 EnumPrintersA 和 EnumPrintersW 请求相同的内存量? 的相关文章

  • 从窗口内容截取屏幕截图(无边框)

    我正在寻找有关如何使用 C 将表单内容保存在位图中的解决方案 我已经尝试过使用 DrawToBitmap 但它捕获了所有带边框的窗口 这就是这段代码的结果 public static Bitmap TakeDialogScreenshot
  • 枚举器上的 [[maybe_unused]]

    查看规格 maybe unused http en cppreference com w cpp language attributes 它指出 出现在类 typedef 变量 非静态数据成员 函数 枚举或枚举器的声明中 如果编译器对未使用
  • React setState回调返回值

    我是 React 新手 我希望实现这种流程 set the state execute a function f an async one which returns a promise set the state again return
  • 套接字:监听积压并接受

    listen sock backlog 在我看来 参数backlog限制连接数量 这是我的测试代码 server initialize the sockaddr of server server sin family AF INET ser
  • 如何创建显示/隐藏 Docusaurus 项目中所有详细标签状态的按钮?

    根据讨论here https stackoverflow com questions 58579048 how to add or remove the open attribute from all details tags in a r
  • 在不使用 Thread.Sleep c# 的情况下延迟发送电子邮件

    我有一个 for 循环 它循环并每个循环发送一封电子邮件 现在我正在使用 thread sleep 但我希望用户仍然能够与程序交互 只需取消该循环即可 是否可以在不使用 thread sleep 的情况下做到这一点 您是否在 UI 线程上运
  • 使标签充当输入按钮

    我怎样才能做一个 a href http test com tag test Test a 就像表单按钮一样 通过充当表单按钮 我的意思是 当单击链接执行操作时method get 或 post 以便能够通过 get 或 post 捕获它
  • JavaScript 中的安全数据

    我必须为 Web 测试创建生成器 使用 HTML 和 JavaScript 测试必须离线和在线进行 正确答案和分数评估必须是生成的测试的一部分 最终用户的分数仅发送到服务器 无法在服务器上进行评估 并且服务器对问题一无所知 它只保存最终分数
  • 从数组创建树并将父字段的表示形式更改为对象而不是 ID

    我堆满了琐碎的问题 但找不到解决方案 任何帮助将不胜感激 我有一个对象数组 id 1 title home parent null id 2 title about parent null id 3 title team parent 2
  • 使用互斥锁来阻止临界区外部的执行

    我不确定我的术语是否正确 但这里是 我有一个由多个线程使用的函数来写入数据 在注释中使用伪代码来说明我想要的内容 these are initiated in the constructor int data std atomic
  • Active Directory UserPrincipal.Current.GetGroups() 返回本地组而不是 Web 服务器上的组

    以下内容在我的本地开发盒上效果很好 但是 当我将其移动到网络服务器时 它失败了 甚至不会记录错误 public static List
  • 数据表 - 从 AJAX 源过滤数据

    我有一个数据表 正在从 api 获取数据 现在我的状态是活动的 非活动的 如果标志是活动的 那么我需要在数据表中显示 否则我不应该显示过期的 这是我的fiddle https jsfiddle net lakshmipriya001 qLp
  • 使用 Puppeteer 和 Headless Chrome 获取 DOM 节点文本

    我正在尝试使用无头 Chrome 和 Puppeteer 来运行我们的 Javascript 测试 但我无法从页面中提取结果 基于这个答案 https stackoverflow com a 45799744 4794 看起来我应该使用pa
  • System.diagnostics.process 进程在托管后无法在 IIS 上运行?

    我正在尝试从网络应用程序安装 exe 当我在本地运行应用程序 从 asp 开发服务器 时 它安装正确 但当我托管在 IIS 上时 它不起作用 我在asp net页面的Page load方法上编写了这段代码 想要在客户端计算机上安装Test
  • Qt:将拖放委托给子级的最佳方式

    我在 QWidget 上使用拖放 我重新实现了 DragEnterEvent dragLeaveEvent dragMoveEvent 和 dropEvent 效果很好 在我的 QWidget 中 我有其他 QWidget 子级 我希望它们
  • win32 API 和 .NET 框架之间的选择

    我必须开发一个适用于 Windows 的应用程序 该应用程序将能够通过网络摄像头识别手势来控制鼠标 我将使用 vc 2008 进行开发 但我很困惑是使用 NET 框架还是核心 win32 API 性能对于我的应用程序非常重要 根据 Ivor
  • 如何在 getStaticPaths 内添加 params 值数组

    我有一个页面 其结构如下 read slug number 我想要得到slug每个对应的值number in the getStaticPaths这是代码 export async function getStaticPaths const
  • 数组长度未定义[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我试图按如
  • 如何在控制台中隐藏日志消息的来源?

    当将消息输出到控制台时 还会显示源代码 在 Chrome 开发者工具中 它位于右侧 console log Foo Source Foo test js 1 Output 但是 在某些网站上 会显示消息without正在显示的源 例如Fac
  • 如何在 JavaScript 中将日期时间微格式转换为本地时间?

    我有一个页面当前正在使用日期时间微格式 http microformats org wiki datetime design pattern显示时间戳 但我只显示我自己的时区的人类可读时间

随机推荐