![]() |
EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Liste der Anhänge anzeigen (Anzahl: 2)
Ich würde gerne mittels EnumPrinterDrivers die Liste der installierten Druckertreiber auslesen.
Das auslesen funktioniert auch, ich bekomme die korrekte Liste doch dann haut das Programm eine Zugriffsverletzung raus. Zitat:
Ich bin leider ein wenig aus der übung und habe auch früher solche API geschichten gemieden. Diesmal würde ich aber gerne verstehen wie es geht und wo da der Fehler ist. Im Debugger wirkt es irgendwie als hätte ich zu viel Speicher reserviert.
Delphi-Quellcode:
function GetPrinterDrivers(): boolean;
var arDriverList : array of TDriverInfo2; pBuffer : Pointer; pcbNeeded : DWORD; pcReturned : DWORD; ErrorTxt : array [0..500] of char; i : integer; begin // Aufruf mit Buffersize 0 soll die benötigte Größe in pcbNeeded schreiben. // ^--------------------------------------------+ if not EnumPrinterDrivers(nil, nil, 2, pBuffer, 0, pcbNeeded, pcReturned) then begin GetMem(pBuffer, pcbNeeded); end; result := EnumPrinterDrivers(nil, nil, 2, pBuffer, pcbNeeded, pcbNeeded, pcReturned); if result then begin arDriverList := pBuffer; for i := 0 to pcReturned -1 do form1.Memo1.Lines.add(arDriverList[i].pName); FreeMem(pBuffer, pcbNeeded); end else begin FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, GetLastError, 0, ErrorTxt, 500, nil); ShowMessage(ErrorTxt); end; end; |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Spar dir den Zeiger pBuffer und nimm direkt das Array:
Delphi-Quellcode:
Der Fehler liegt wohl daran dass du nicht einfach deinen rohen Speicherbereich von x Elementen (C-Array) in dein dynamisches Delphi-Array pressen kannst. Ein Delphi-Array enthält noch mehr Informationen (Anzahl der Elemtente, Referanzzähler). Wochenendlektüre dafür gibt es hier:
function GetPrinterDrivers(): boolean;
var arDriverList : array of TDriverInfo2; //pBuffer : Pointer; pcbNeeded : DWORD; pcReturned : DWORD; ErrorTxt : array [0..500] of char; i : integer; begin // Aufruf mit Buffersize 0 soll die benötigte Größe in pcbNeeded schreiben. // ^--------------------------------------------+ if not EnumPrinterDrivers(nil, nil, 2, nil, 0, pcbNeeded, pcReturned) then // gib ihm doch einen Nullzeiger statt einen nicht initialisierten! begin //GetMem(pBuffer, pcbNeeded); SetLength(arDriverList, pcbNeeded); end; result := EnumPrinterDrivers(nil, nil, 2, @arDriverList[0], pcbNeeded, pcbNeeded, pcReturned); if result then begin //arDriverList := pBuffer; // nope for i := 0 to pcReturned -1 do form10.Memo1.Lines.add(arDriverList[i].pName); //FreeMem(pBuffer, pcbNeeded); //macht das array nach verlassen der methode selber end else begin FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, GetLastError, 0, ErrorTxt, 500, nil); ShowMessage(ErrorTxt); end end; ![]() In der Regel gibst du WinAPI-Aufrufen, wenn sie einen rohen Zeiger auf einen Speicherbereich haben wollen, die Adresse deines Delphi-Arrays mit Index 0 (siehe oben: "@arDriverList[0]"). PS: Ich glaube ich habe noch nie in meinem Leben (und ich mache jetzt vier Jahre Delphi) GetMem(..)/FreeMem(..) gebraucht... |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Herzlichen Dank!
jetzt läuft wirklich rund. Und danke für den Link :-D |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Warum eigentlich so kompliziert? Es gibt doch die Unit Printers dafür, oder zumindest bei meinem Delphi 2007.
Delphi-Quellcode:
lg,
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Printers, StdCtrls; type TForm1 = class(TForm) ComboBox1: TComboBox; procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin combobox1.Items:=Printer.Printers; combobox1.ItemIndex:=Printer.PrinterIndex; end; end. jus |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Zitat:
|
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Das muß auch knallen.
* pBuffer ist beim ersten Aufruf nicht initialisiert (das sollte dir auch dein Compiler um die Ohren hauen) * GetMem ist nur bei einem ganz bestimmten "Fehler" richtig * ist nichts installiert, würde cbBuf=0 passen und es gibt keinen Fehler * und dann ist arDriverList ein Delphi-Array, was absolut garnichts mit dem C-Array zu zun hat ** du "sagst" dem Compiler aber, dass pBuffer dieses Array drin ist, das zufällg so lange gut geht, bis irgendwas auf die Control-Felder (Length und Type) des Delphi-Array zugreifen will, was z.B. am Ende der Prozedur passiert, wenn der Compiler dort den Speicher des Arrays freigeben will. ** das kann maximal in den Zeiger eines statischen
Delphi-Quellcode:
gecastet werden oder man muß umkopieren, bzw. die Daten direkt in ein bestehendes dynamisches Array reinschreiben
array[0..x] of TDriverInfo2
PS: Bei SetLength sollte man natürlich die Anzahl der Records und nicht der Bytes reingeben. |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Zitat:
|
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Zitat:
Mache ich also ein SetLength(arDriverList, pcReturned); kommt beim zweiten / finalen aufruf die Fehlermeldung Zitat:
|
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
ich bin bisher immer noch nicht schlauer.
Der erste aufruf
Delphi-Quellcode:
liefert mir nur die benötigte größe in Byte - in meinem Fall sind das 10112 Byte (18 Treiber sind installiert)
EnumPrinterDrivers(nil, nil, 2, nil, 0, pcbNeeded, pcReturned)
mit
Delphi-Quellcode:
wird der Array 10112 Einträge groß - aber alles funktioniert.
SetLength(arDriverList, pcbNeeded);
Wie kann ich anhand der zurückgelieferten Bytes die Anzahl der Array Einträge ermitteln, die ich für ein SetLength benötige? Oder kann ich dem Array of auch sagen das dieses insgesamt 10112 Bytes groß sein soll? |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Zitat:
Delphi-Quellcode:
Strukturen zusätzlich auch die Strings (die in den einzelnen Einträgen per Pointer referenziert werden) oder andere Sachen im Buffer abgelegt?
DRIVER_INFO_2
Nein: Einfach die Anzahl der Bytes durch
Delphi-Quellcode:
teilen.
SizeOf(TDriverInfo2)
Ja: In diesem Falle würde ich von Anfang an mit einem untypisiertem Buffer (
Delphi-Quellcode:
) arbeiten. In
GetMem
Delphi-Quellcode:
gibt die API dir die Anzahl der Elemente zurück, also kannst du einfach iterieren:
pcReturned
Delphi-Quellcode:
var
Buf: PDriverInfo2; .. begin // Größe ermitteln, etc .. GetMem(Buf, pcbNeeded); try if EnumPrinterDrivers(...) then begin for I := 1 to pcReturned do begin // Mach was // .. // zum nächsten Element springen Inc(Buf); end; end; finally FreeMem(Buf); end; end; |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
SizeOf(TDriverInfo2);
Hatte ich auch schon probiert ergibt bei mir aber 24 byte pcbNeeded / 24 ergibt aber ( 10112 / 24 = 421 ) obwohl es halt nur 18 Treiber sind. Funktioniert also auch net. Da ich bei mir ja weiß das ich 18 Treiber installiert habe ergibt sich bei mir ein Teiler von 10112 / 18 = 561 aber der Wert ist nicht überall gleich auf nem anderen Rechner muss der z.B. 429 sein. Das andere mit GetMem muss ich mir nochmal in Ruhe anschauen.. |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Ok, das bestätigt dann auf jeden Fall meine Vermutung, dass im Buffer auch noch die dazugehörigen Strings abgelegt werden und nicht nur die
Delphi-Quellcode:
Strukturen. Mit dem
DRIVER_INFO_2
Delphi-Quellcode:
Beispiel sollte es aber funktionieren. Zur Erklärung:
GetMem
Delphi-Quellcode:
ist hier als Zeiger deklariert, welcher nach dem Aufruf der API dann auf die erste
Buf
Delphi-Quellcode:
Struktur zeigt. Mit dem
DRIVER_INFO_2
Delphi-Quellcode:
wird der Zeiger um
Inc
Delphi-Quellcode:
erhöht, zeigt also danach auf das 2./3./n-te Element.
SizeOf(TDriverInfo2)
Hier fällt mir grade auf, dass in meinem Beisüiel das
Delphi-Quellcode:
am Ende so natürlich nicht funktioniert, da sich der Zeiger ja geändert hat. Den initialen Buffer-Pointer (direkt nach dem
FreeMem
Delphi-Quellcode:
Aufruf) müsstest du dir also sichern, bevor du anfängst zu iterieren.
GetMem
|
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Zitat:
Dann war mein Geschwafel ("kein Puffer, nimm einfach ein Array") definitiv falsch. |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Man kann ja dennoch das Array verwenden, aber dann eben nicht auf alle, sondern nur auf die richige Anzahl der Einträge zugreifen.
|
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Kann man dann aber nicht so schön inkrementieren :)
|
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
0 bis daswasdieapisagt-1
BOOL EnumPrinterDrivers( _In_ LPTSTR pName, _In_ LPTSTR pEnvironment, _In_ DWORD Level, _Out_ LPBYTE pDriverInfo, _In_ DWORD cbBuf, _Out_ LPDWORD pcbNeeded, _Out_ LPDWORD pcReturned ); |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Dumme Frage aber kann ich es nicht auch einfach so machen:
statt 2x EnumPrinterDrivers aufzurufen rufe ich es 3x auf (1x um die nötige Buffersize zu ermitteln, 1x mit dem pBuffer in der Größe von BytesNeeded um den ItemsCount zu erhalten, 1x der finale aufruf mit dem @array[0] (=SetLength(array, ItemsReturned))
Delphi-Quellcode:
Kann man das so machen oder ist das zu gefährlich / dümmlich ?:|
function GetPrinterDrivers(): boolean;
var aDriverList : array of TDriverInfo2; pBuffer : Pointer; BytesNeeded : DWORD; ItemsReturned : DWORD; ErrorTxt : array [0..500] of char; i : integer; begin // BufferSize ermitteln if not EnumPrinterDrivers(nil, nil, 2, nil, 0, BytesNeeded, ItemsReturned) then begin if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin try // pBuffer Speicher reservieren pBuffer := System.AllocMem(BytesNeeded); // Anz. Items des Arrays ermitteln if EnumPrinterDrivers(nil, nil, 2, pBuffer, BytesNeeded, BytesNeeded, ItemsReturned) then begin // Array Länge setzen SetLength(aDriverList, ItemsReturned); result := EnumPrinterDrivers(nil, nil, 2, @aDriverList[0], BytesNeeded, BytesNeeded, ItemsReturned); end; finally // Speicher freigeben FreeMem(pBuffer); end; end; end; |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Zitat:
Zitat:
Delphi-Quellcode:
in Bytes angegeben ist und ausdrücklich nicht
pcbNeeded
Delphi-Quellcode:
ist. Nichtmal ein Vielfaches von
pcReturned * SizeOf(DRIVER_INFO_2)
Delphi-Quellcode:
ist garantiert. Mit einem
SizeOf(DRIVER_INFO_2)
Delphi-Quellcode:
zu arbeiten, macht doch rein von der Logik her einfach keinen Sinn.
Array of DRIVER_INFO_2
Zitat:
Delphi-Quellcode:
ist kein Vielfaches von
pcbNeeded
Delphi-Quellcode:
. Der Array Ansatz kann nicht funktionieren - du hast enweder zu wenig Platz = Buffer Overflow oder du reservierst zu viel = Logisch fragwürdig. Arrays sollte man wirklich nur dann verwenden, wenn man homogene Strukturen zusammenfassen will.
SizeOf(DRIVER_INFO_2)
Delphi-Quellcode:
Wenn der Zugriff umbedingt über ein Array erfolgen soll, dann geht auch folgende Variante:
function EnumPrinterDriversW(pName: LPSTR; pEnvironment: LPSTR; Level: DWord; pDriverInfo: LPBYTE;
cbBuf: DWord; var pcbNeeded: DWord; var cbReturned: DWord): BOOL; stdcall; external 'winspool.drv'; type DRIVER_INFO_2 = packed record cVersion: DWord; pName: LPTSTR; pEnvironment: LPTSTR; pDriverPath: LPTSTR; pDataFile: LPTSTR; pConfigFile: LPTSTR; end; PDriverInfo2 = ^TDriverInfo2; TDriverInfo2 = DRIVER_INFO_2; procedure TForm6.FormCreate(Sender: TObject); var Buf: Pointer; ErrorCode, pcbNeeded, cbReturned, I: DWord; Driver: PDriverInfo2; begin EnumPrinterDriversW(nil, nil, 2, nil, 0, pcbNeeded, cbReturned); ErrorCode := GetLastError; if (ErrorCode <> ERROR_INSUFFICIENT_BUFFER) then begin RaiseLastOsError(ErrorCode); end; GetMem(Buf, pcbNeeded); try if (not EnumPrinterDriversW(nil, nil, 2, Buf, pcbNeeded, pcbNeeded, cbReturned)) then begin RaiseLastOsError; end; Driver := Buf; for I := 1 to cbReturned do begin ShowMessage(Driver^.pName); Inc(Driver); end; finally FreeMem(Buf); end; end;
Delphi-Quellcode:
procedure TForm6.FormCreate(Sender: TObject);
type PDriverInfo2Array = ^TDriverInfo2Array; TDriverInfo2Array = array[0..0] of TDriverInfo2; var Buf: Pointer; ... I: DWord; Drivers: PDriverInfo2Array; begin ... try ... Drivers := Buf; for I := 0 to cbReturned - 1 do begin ShowMessage(Drivers^[I].pName); end; finally ... end; end; |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Ok, ich hab es jetzt wie in deinem ersten Beispiel gemacht.
Ist das mit pStart und FreeMem so in Ordnung?
Delphi-Quellcode:
function GetPrinterDrivers(): boolean;
var Buffer : PDriverInfo2; pStart : Pointer; i : Integer; BytesNeeded : DWORD; ItemsReturned : DWORD; begin if not EnumPrinterDrivers(nil, nil, 2, nil, 0, BytesNeeded, ItemsReturned) then begin if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin GetMem(Buffer, BytesNeeded); pStart := Buffer; try if EnumPrinterDrivers(nil, nil, 2, Buffer, BytesNeeded, BytesNeeded, ItemsReturned) then begin for i := 1 to ItemsReturned do begin Form1.Memo1.Lines.Add(i.ToString +' :: '+Buffer.pName); inc(Buffer); end; end; finally Buffer := pStart; FreeMem(Buffer); end; end; end; |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Auch wenn ich schon einmal Quark erzählt habe traue ich mich trotzdem noch:
Sieht gut aus, allerdings - Könnte man pStart auch vom Typ PDriverInfo2 sein lassen. - Mach in der Doku unbedingt fest was deine Methode denn angeblich zurückgibt - Denn momentan ist der Rückgabewert uU nicht definiert |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Es muß nur mindestens so viel Speicher bereitgestellt werden, wie nötig.
Also wäre es kein Problem den Speicher auf ganze Recordgrößen aufzurunden. Und nein, denn * erstmal wäre der dritte Aufruf garnicht nötig, da man im Zweiten bereits beide Größen erfährt ** einfach ein SetLength zum Kürzen nach dem 2. Aufruf (was man aber nicht machen darf) * außerdem würde man dann den Speicher mit den Strings freigeben, womit die PChars in den Records ins Nirvana zeigen täten |
AW: EnumPrinterDrivers = Zugriffsverletzung (Anfängerfrage)
Zitat:
ich glaube langsam verstehe ich die Problematik und es macht jetzt auch Sinn das ein Element von DriverInfo2 immer 24 Byte groß ist und somit das Array nicht funktioniert. Ich werde es jetzt nach deinem Beispiel machen was für mich jetzt endlich Sinn ergibt :-D |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:03 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz