Saya memanggil fungsi EnumPrintersA/EnumPrintersW menggunakan node-ffi untuk mendapatkan daftar printer lokal yang dapat diakses dari PC saya.
Anda harus membuat buffer yang akan diisi dengan informasi oleh fungsi EnumPrinters.
Tapi Anda tidak tahu ukuran buffer yang dibutuhkan.
Dalam hal ini Anda perlu menjalankan EnumPrintersA/EnumPrintersW dua kali.
Selama panggilan pertama, fungsi ini menghitung jumlah memori untuk informasi tentang printer, selama panggilan kedua, fungsi ini mengisi buffer dengan informasi tentang printer.
Dalam hal fungsi EnumPrinters versi Unicode, setiap huruf dalam nama printer akan dikodekan menggunakan dua karakter di Windows.

Mengapa panggilan pertama ke EnumPrintersW mengembalikan jumlah memori yang dibutuhkan sama dengan panggilan pertama ke EnumPrintersA?
String unicode dua kali lebih panjang dari string non-unicode, tetapi ukuran buffer yang dibutuhkan adalah sama.

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

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

typedef struct _PRINTER_INFO_4 {
  LPTSTR pPrinterName;
  LPTSTR pServerName;
  DWORD  Attributes;
} PRINTER_INFO_4, *PPRINTER_INFO_4;
4
Volodymyr Bezuglyy 14 Desember 2016, 19:10

1 menjawab

Saya dapat mengonfirmasi bahwa apa yang Anda temukan dengan EnumPrintersA dan EnumPrintersW dapat direproduksi. Di mesin saya, keduanya membutuhkan 240 byte.

Ini membuat saya penasaran, jadi saya memutuskan untuk mengalokasikan buffer terpisah untuk setiap fungsi dan membuang setiap buffer ke file dan membukanya dengan hex editor. Yang menarik dari setiap file tentu saja nama-nama printernya.

Untuk mempersingkat ini, saya akan menunjukkan kepada Anda 3 nama pertama printer. Baris pertama dari EnumPrintersA, baris kedua dari 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...

Dari hasil ini, tampak bahwa EnumPrintersA memanggil EnumPrintersW untuk pekerjaan yang sebenarnya dan kemudian hanya mengubah setiap string dalam buffer ke karakter byte tunggal dan menempatkan string yang dihasilkan di tempat yang sama. Untuk mengkonfirmasi ini, saya memutuskan untuk melacak kode EnumPrintersA dan saya menemukan bahwa itu pasti memanggil EnumPrintersW pada posisi winspool.EnumPrintersA + 0xA7. Posisi sebenarnya kemungkinan berbeda di versi Windows yang berbeda.

Ini membuat saya semakin penasaran, jadi saya memutuskan untuk menguji fungsi lain yang memiliki versi A dan W. Inilah yang saya temukan:

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

Dari hasil ini, kesimpulan saya adalah bahwa EnumPrintersA memanggil EnumPrintersW untuk pekerjaan yang sebenarnya dan mengonversi string dalam buffer dan fungsi lain yang memiliki versi A dan W juga melakukan hal yang sama. Ini tampaknya merupakan mekanisme umum untuk menghindari duplikasi kode dengan mengorbankan buffer yang lebih besar, mungkin karena buffer dapat di-deallocated.

3
Rei 15 Desember 2016, 03:58