AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Woher weiß ich die Puffergröße für einen Aufruf?
Thema durchsuchen
Ansicht
Themen-Optionen

Woher weiß ich die Puffergröße für einen Aufruf?

Ein Thema von Der schöne Günther · begonnen am 8. Nov 2016 · letzter Beitrag vom 8. Nov 2016
Antwort Antwort
Der schöne Günther

Registriert seit: 6. Mär 2013
6.176 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 10:13
Ich habe eine WinApi-Routine die mir einen String gibt. Man übergibt hierbei immer einen Zeiger auf das erste Zeichen und wie groß der bereitgestellte Puffer ist. Die Puffergröße ist immer ein in/out (Delphi-Sprech: "var-Parameter").

Bislang kannte ich das so dass man erst einmal nil und eine Pufferlänge von 0 angibt, hierauf ein ERROR_INSUFFICIENT_BUFFER zurückbekommt und im var-Parameter drinsteht wie groß er den Puffer gerne hätte. Beispiel:

Delphi-Quellcode:
         GetTokenInformation(
            processToken,
            TTokenInformationClass.TokenUser,
            nil,
            0,
            bufferLength
         );
         if (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then RaiseLastOSError();

         SetLength(buffer, bufferLength);
         Win32Check( GetTokenInformation(
            processToken,
            TTokenInformationClass.TokenUser,
            buffer,
            bufferLength,
            bufferLength
         ) );

Ich habe nun ein Problem mit dem Aufruf QueryFullProcessImageName

Die Doku stimmt: Wenn der Puffer zu klein ist gibt der Aufruf False zurück und GetLastError() liefert ERROR_INSUFFICIENT_BUFFER . Aber der Ausgabeparameter wird nicht gesetzt. Ich weiß also immer noch nicht wie groß der Puffer sein muss.

Rein akademisch betrachtet: Wie muss man das richtig machen? Eine Schleife welche den Puffer so lange verdoppelt bis es irgendwann mal gepasst hat ist ja wirklich nicht schön...
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#2

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 10:29
Rein akademisch betrachtet: Wie muss man das richtig machen? Eine Schleife welche den Puffer so lange verdoppelt bis es irgendwann mal gepasst hat ist ja wirklich nicht schön...
Wohl war, aber ist die einzige valide Möglichkeit, wenn der Output-Parameter dir die Länge bei einem nil / 0 Aufruf nicht zurückgibt.

Warum man die API an dieser Stelle so schlecht designed hat, erschließt sich mir allerdings auch nicht. An anderer Stelle ist es nachvollziehbar. Zum Beispiel immer dann, wenn dynamische Daten vorliegen, die sich "schnell" ändern (Prozessliste, etc). Dort macht es keinen Sinn eine feste Größe zurückzugeben, da sich diese beim zweiten Aufruf bereits wieder geändert haben kann. Mit dem Verdoppeln des Buffers hat man im Zweifelsfalle immer noch ein wenig Spielraum nach oben.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl ( 8. Nov 2016 um 10:32 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Neutral General
Neutral General

Registriert seit: 16. Jan 2004
Ort: Bendorf
5.219 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#3

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 10:40
Hab auch grad mal rumprobiert und MSDN hat leider wirklich recht
Reservier einfach beim 1. Versuch schon halbwegs viel. Wenn du 500 Bytes, oder vllt. sogar 1KB reservierst solltest du ja zu 99% keine Probleme haben.
Und 1KB an Speicher ist ja heutzutage nix mehr und du kannst ihn ja kurz darauf ja auch wieder freigeben bzw verkleinern.
Verbraucht mehr Speicher aber ist evtl. schneller als verdoppeln (je nachdem mit welchem Wert du anfängst) und mehrmals zu probieren.
Michael
"Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination,
but because their imagination reveals worlds that others cannot see."
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.176 Beiträge
 
Delphi 10 Seattle Enterprise
 
#4

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 10:41
Danke für den seelischen Beistand, dann ist das wohl wirklich etwas komisch.


Ich nehme einfach 32 KB, die (momentan) maximale Pfadgröße
  Mit Zitat antworten Zitat
Benutzerbild von Assarbad
Assarbad

Registriert seit: 8. Okt 2010
Ort: Frankfurt am Main
1.234 Beiträge
 
#5

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 10:45
Herzlichen Glückwunsch. Du hast die Ungereimtheiten zwischen verschiedenen Generationen von Windows-Schnittstellen für dich entdeckt. Das ist ein wunderbar erbauliches Thema welches zu graumeliertem Haar und wunderbaren Falten führen kann.
  • MSDN-Library durchsuchenQueryFullProcessImageName ist eine neue Funktion die erst eingeführt wurde nachdem Windows 9x/Me und Windows NT zu Windows 2000 verschmolzen wurden (und seitdem sind ja noch weitere Stilblüten hinzugekommen).
  • MSDN-Library durchsuchenGetTokenInformation stammt noch aus der guten alten NT-Zeit.
  • MSDN-Library durchsuchenMultiByteToWideChar stammt noch aus Windows-95-Zeiten.

Und alle arbeiten mit ERROR_INSUFFICIENT_BUFFER

Die Doku stimmt: Wenn der Puffer zu klein ist gibt der Aufruf False zurück und GetLastError() liefert ERROR_INSUFFICIENT_BUFFER . Aber der Ausgabeparameter wird nicht gesetzt.
Die Dokumentation behauptet doch aber auch nicht, daß er gesetzt wird, oder?

Zitat:
On success, receives the number of characters written to the buffer, not including the null-terminating character.
Hab den wichtigen Teil mal hervorgehoben.

Ansonsten hat Zacherl deine Frage ja schon beantwortet. Ja, es ist die einzige sinnvolle Methode. Statt verdoppeln kannst du natürlich auch immer einen festen Wert draufschlagen. Und natürlich kannst du auch versuchen auf den von dir unterstützten Systemen einen guten Basiswert zu ermitteln, um die Anzahl der Allozierungen klein zu halten.
Oliver
"... aber vertrauen Sie uns, die Physik stimmt." (Prof. Harald Lesch)
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.176 Beiträge
 
Delphi 10 Seattle Enterprise
 
#6

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 10:51
Die Dokumentation behauptet doch aber auch nicht, daß er gesetzt wird, oder?
Nein, und sie hat dabei sogar recht. Das ist ja grade das blöde



Vielleicht auch ganz gut, somit wird es nie langweilig. Danke nochmal allen für die Hilfe
  Mit Zitat antworten Zitat
Benutzerbild von Assarbad
Assarbad

Registriert seit: 8. Okt 2010
Ort: Frankfurt am Main
1.234 Beiträge
 
#7

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 11:16
Ich nehme einfach 32 KB, die (momentan) maximale Pfadgröße
Dazu noch einen kleinen Exkurs. Es ist in der Tat die absolut größte Pfadlänge im Moment.

Die Obergrenze stammt daher, daß MSDN-Library durchsuchenUNICODE_STRING einen 16-bittigen vorzeichenlosen Integertyp (in Delphi: Word) benutzt um die Puffergröße in Bytes zu hinterlegen.

Während ehemals Windows NT UCS2 meinte wenn Unicode draufstand (also exakt ein WideChar pro Codepunkt), erweitern moderne Windowsversionen dies auf UTF-16, wodurch Zeichen außerhalb des Unicode BMP zum kodieren eines Codepunkts mehr als ein WideChar brauchen.

Ein Pfadname kann also durchaus kürzer sein als es die Länge in WideChar vermuten lassen würde.

Das eigentliche Thema ist aber die Erweiterung der Pfadnamen im Kernelmodus.

Haste beispielsweise die Datei C:\bootmgr, wird das bei der Benutzung von langen Pfadnamen laut MSDN-Library durchsuchenNaming Files ("Naming Files, Paths, and Namespaces") zu \\?\C:\bootmgr, welches für die nativen Schnittstellen schwuppdiwupp in \??\C:\bootmgr verwandelt wird. Wobei \?? auf modernen Systemen (alle mit eingebauten TS) ein Alias für \GLOBAL?? ist, worin die "DOS"-Gerätenamen abgelegt werden. Es gibt dann noch sitzungsspezifische Alternativen zu \GLOBAL?? (falls dich das interessiert kannste mein kleines Tool hier benutzen - .exe ist AuthentiCode-signiert [1]). Einerlei, in \GLOBAL?? gibt es dann bspw. einen symbolischen Link namens C: der bei mir auf \Device\HarddiskVolume6 zeigt, wodurch aus deinem Pfad \Device\HarddiskVolume6\bootmgr wird. Aus diesem Grund ist die echte Obergrenze für die Pfadlänge unterhalb der 32k anzusiedeln. In Windows ist sie auf 0xFFF0 (dann in Bytes) festgelegt und der Rückgabewert für diesen Fehlerfall ist ERROR_FILENAME_EXCED_RANGE (bzw. intern STATUS_NAME_TOO_LONG). Aber einen exakten Wert für den Benutzermodus kann man so dennoch nicht angeben. Nur das absolute Maximum, bedingt durch die gezählten Strings (MSDN-Library durchsuchenUNICODE_STRING) im Kernelmodus.

Die eigentliche Obergrenze ist also etwas schwammig, weil sie davon abhängt die lang der Gerätename im Namensraum des Objektmanagers ist.

MSDN weist auch drauf hin:

Zitat:
Note The maximum path of 32,767 characters is approximate, because the "\\?\" prefix may be expanded to a longer string by the system at run time, and this expansion applies to the total length.
[1] Als Alternative zu vorgenanntem Programm kann ich noch das hier anbieten. Ich hoffe es kompiliert auf modernem Delphi, ansonsten schickt nen Pull-Request, den ich dann auf einen alternativen Zweig schieben würde. Das ist ein Programm welches Marcel van Brakel und ich 2005 für die Märzausgabe 2006 von The Delphi Magazine entwickelt hatten und zu dem es auch noch einen Artikel mit Erklärungen gab. Auch freue ich mich über Rückmeldungen/Fehlermeldungen zu ntobjx.
Oliver
"... aber vertrauen Sie uns, die Physik stimmt." (Prof. Harald Lesch)
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#8

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 13:44
Es sind nicht nur unterschiedliche Windows Versionen, warum die API nicht konsistent ist. Teilweise liegt es auch daran, dass unterschiedliche Teams an dem gleichen System gearbeitet haben.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#9

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 14:23
Im Prinzip kann man auch eine Repeat-Schleife um den Aufruf machen, wenn sich die Puffergröße zu schnell ändern könnte.
Mit Len=0 die Schleife beginnen, oder gleich blind schonmal paar Bytes bereitstellen, und wenn nicht groß genug, dann die Schleife mit größerem Speicher durchlaufen.

Im Normalfall reichen ja auch zwei aufeinanderfolgende Aufrufe mit nil+0 und dann nochmal mit @buff+len,
aber es gibt auch APIs, die zwar irgendein ERROR_INSUFFICIENT_BUFFER liefern, wenn zu klein, es aber nicht erlauben, dass man NIL+0 rein gibt, um erstmal nur nach der Größe zu fragen.

Bezüglich der erwähnten Prozesslisten kann es auch sein, dass der Aufruf dennoch erfolgreich ist, auch wenn de Puffer zu klein war. (gilt auch für andere APIs, die 'ne Art Record-Array liefern)
Da bekommt man dann einfach nur die x Prozesse aufgelistet, die in den Puffer passen.
Oftmals kann man bei sowas dann aber auch noch einen Offset reingeben, um in Folgeaufrufen weitere Prozesse abzufragen.
Also z.B. statt alle 200.000 auf Einmal einfach immer nur je 1000, bis man dann Alle hat.

PS: MAX_PATH ist zwar 260, aber eigentlich ist der "maximale Pfad" nur 256 inkl. der abschließenden #0. (das DRIVE "X:\" gehört ja eigentlich nicht zum PATH, im jeweiligen Gerätetreiber)
$2B or not $2B

Geändert von himitsu ( 8. Nov 2016 um 14:31 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Assarbad
Assarbad

Registriert seit: 8. Okt 2010
Ort: Frankfurt am Main
1.234 Beiträge
 
#10

AW: Woher weiß ich die Puffergröße für einen Aufruf?

  Alt 8. Nov 2016, 14:43
(das DRIVE "X:\" gehört ja eigentlich nicht zum PATH, im jeweiligen Gerätetreiber)
Der Backslash schon noch, aber nicht mehr alles danach. Richtig.
Oliver
"... aber vertrauen Sie uns, die Physik stimmt." (Prof. Harald Lesch)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:47 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz