Pourquoi EnumPrintersA et EnumPrintersW demande la même quantité de mémoire?
J'appelle EnumPrintersA/EnumPrintersW fonctions à l'aide de nœud-ffi pour obtenir la liste des imprimantes locales accessible à partir de mon PC.
Vous devez créer le tampon qui sera rempli avec des informations par EnumPrinters fonction.
Mais vous ne connaissez pas la taille de la mémoire tampon.
Dans ce cas, vous devez exécuter EnumPrintersA/EnumPrintersW deux fois.
Lors du premier appel de cette fonction calcule la quantité de mémoire pour plus d'informations sur les imprimantes, au cours de la deuxième appel de cette fonction remplit le tampon avec des informations sur les imprimantes.
Dans le cas de la version Unicode de EnumPrinters de la fonction, chaque lettre dans les imprimantes nom sera codé à l'aide de deux caractères dans Windows.
Pourquoi le premier appel à EnumPrintersW renvoie la même quantité de mémoire que le premier appel à EnumPrintersA?
Des chaînes Unicode sont deux fois plus longtemps que les non-unicode, mais la taille de mémoire tampon requise est la même.
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;
OriginalL'auteur Volodymyr Bezuglyy | 2016-12-14
Vous devez vous connecter pour publier un commentaire.
Au début, je pensais qu'il ya quelque chose de mal avec votre code, j'ai donc continué à chercher une erreur (introduit par le ffi ou js couches, ou une faute de frappe ou quelque chose de similaire), mais je ne pouvais pas trouver quoi que ce soit.
Puis, j'ai commencé à écrire un programme similaire à la vôtre, en C (pour éliminer toutes les couches supplémentaires qui pourraient introduire des erreurs).
principal.c:
Note: en termes de noms de variables (je l'ai gardé à court et donc pas très intuitif), le un ou w à la fin de leur nom signifie qu'ils sont utilisés pour ASCII /LARGE version.
Au départ, j'avais peur que EnumPrinters peut ne pas retourner quoi que ce soit, puisque je ne suis pas connecté à une imprimante, à ce point, mais heureusement, j'ai quelques (7 pour être plus précis) "sauvé". Voici la sortie du programme ci-dessus (merci @qxz pour la correction de mon premier (et le type de défaut) de la version):
Étonnamment (au moins pour moi), le comportement que vous avez décrit pourrait être reproduit.
Note que la sortie est de la 32bit version compilée du programme (64bit pointeurs sont plus difficiles à lire 🙂 ), mais le comportement est reproductible lors de la construction de 64bit (je suis en utilisant VStudio 10.0 sur Win10).
Puisqu'il y a pour sûr, chaînes à la fin de la mémoire tampon, j'ai commencé à le débogage:
Ci-dessus est une photo de VStudio 10.0 fenêtre de Débogage, avec le programme interrompu à la fin de testFunc, juste avant de libérer le 1st pointeur. Maintenant, je ne sais pas comment êtes-vous familier avec le débogage sur VStudio, donc je vais marcher à travers la (les) zones de la fenêtre:
Maintenant, revenons à la disposition de la mémoire: pas tous les chars à la droite sont nécessairement ce qu'elles semblent être, certains d'entre eux sont simplement affichés comme ça à des fins de lisibilité. Par exemple, il y a beaucoup de points (.) sur le côté droit, mais ils ne sont pas tous les points. Si vous recherchez un point correspondant hex représentation, vous remarquerez que, pour beaucoup d'entre eux c'est 00 ou NULL (qui est un non imprimable char, mais il est affiché comme un point).
Concernant le contenu de la mémoire tampon de 2 Mémoire windows (en regardant la char représentation), il y a 3 zones:
Regardons les LARGE chaînes de la zone (Mémoire 2 - mi-zone): comme vous l'avez mentionné, chaque personnage dispose de 2 octets: parce que dans mon cas ils sont tous ASCII chars, le MSB (ou le codepage octet) est toujours 0 (c'est pourquoi vous voyez chars et les points entrelacé: par exemple ".L'.un.s.e.r.J.e.t" sur la ligne 4).
Puisqu'il y a plusieurs chaînes de caractères dans la mémoire tampon (ou une chaîne, si vous voulez) - ou mieux encore: de multiples
TCHAR*
s dans unTCHAR*
- ils doivent être séparés: cela se fait par un NULL LARGE char (hex: 00 00, char: "..") à la fin de chaque chaîne de caractères; combiné avec le fait que la prochaine chaîne du 1st byte (char) est également 00 (.), vous verrez une séquence de 3 NULL octets (hex: 00 00 00, char: "...") et qui est le séparateur entre les 2 (LARGE) les chaînes de caractères dans le milieu de la zone.Maintenant, en comparant les 2 mi-parties (correspondant aux 2 tampons), vous remarquerez que la chaîne de caractères séparateurs sont exactement dans la même position et plus: la dernière partie de chaque chaîne sont la aussi même (la dernière moitiés de chaque chaîne pour être plus précis).
Prenant cela en considération, voici ma théorie:
Je pense que EnumPrintersA appels EnumPrintersW, puis il parcourt chacune des chaînes (à la fin de la mémoire tampon), et les appels wcstombs ou encore mieux: [MME Docs]: WideCharToMultiByte fonction sur eux (en les convertissant en place - et, partant, l' ASCII chaîne ne prend le 1st de la moitié de la LARGE chaîne, laissant le 2ème la moitié des non modifié), sans la conversion de tous les tampons. Je vais vérifier cela en regardant avec un désassembleur de winspool.drv.
Personnellement (si je ne me trompe pas) je pense que c'est une lame d'une solution (ou un gainarie comme j'aime l'appeler), mais qui sait, peut-être tous les *Un, *W fonction paires (au moins ceux qui reviennent plusieurs
char*
s dans unchar*
) travailler comme ça. De toute façon, il y a aussi des pros de cette approche (au moins pour ces 2 funcs):implique plus de calculs; après tout, le tampon de consommation ne sont normalement pas atteindre la seconde moitié de chaque ASCII chaîne de caractères dans la mémoire tampon
À partir de
GetDefaultPrinter
: Si ce paramètre est NULL, la fonction échoue et la variable pointée par pcchBuffer retourne dans la taille de la mémoire tampon, caractères. Donc, lors de l'attribution de multiplier quel que soit le 1erGetDefaultPrinter
appel retourne danspcchBuffer
avecsizeof(TCHAR)
(qui pour LARGE caractères est de 2).Merci beaucoup!
Vous êtes les bienvenus!
OriginalL'auteur CristiFati
Je peux confirmer que ce que vous avez trouvé avec
EnumPrintersA
etEnumPrintersW
est reproductible.Dans ma machine, ils ont tous deux besoin de 240 octets.
Cela m'a fait curieux, j'ai donc décidé d'allouer une distinct de la mémoire tampon pour chaque fonction et de vidage de chaque tampon dans un fichier et l'ouvre avec un éditeur hexadécimal.
La partie intéressante de chaque fichier est bien sûr les noms des imprimantes.
De garder ce court, je vais vous montrer les 3 premiers noms des imprimantes.
La première ligne est de
EnumPrintersA
, la deuxième deEnumPrintersW
:À partir de ces résultats, il apparaît que
EnumPrintersA
appelsEnumPrintersW
pour le travail réel et puis tout simplement convertit chaque chaîne de caractères dans la mémoire tampon d'un octet et met la chaîne résultante à la même place.Pour confirmer cela, j'ai décidé de trace
EnumPrintersA
code et j'ai trouvé qu'il appelle définitivementEnumPrintersW
à la positionwinspool.EnumPrintersA + 0xA7
.La position réelle est probablement différente dans une autre version de Windows.
Cela m'a encore plus curieux, j'ai donc décidé de tester d'autres fonctions qui ont Un W et versions.
C'est ce que j'ai trouvé:
À partir de ce résultat, ma conclusion est que
EnumPrintersA
appelsEnumPrintersW
pour le travail réel et convertit la chaîne de caractères dans la mémoire tampon et d'autres fonctions qui ont Un et W versions aussi faire la même chose.Cela semble être un mécanisme commun afin d'éviter la duplication de code dans dépenses de gros tampons, peut-être parce que les tampons peuvent être libérées de toute façon.
OriginalL'auteur Rei