AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Code-Bibliothek Neuen Beitrag zur Code-Library hinzufügen Delphi Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

Ein Thema von Zacherl · begonnen am 24. Apr 2012 · letzter Beitrag vom 27. Apr 2012
Antwort Antwort
brechi

Registriert seit: 30. Jan 2004
823 Beiträge
 
#1

AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

  Alt 26. Apr 2012, 12:50
Der Compiler verhaspelt sich da nicht, der letzte Parameter muss entweder als PDWORD oder "out DWORD" deklariert sein, denn dein code funktioniert bei mit erst recht nicht.
Und danach funktioniert es betimmt auch mit D7 + Optimierung.

Code:
ReturnLength [out, optional]
    An optional pointer to a location where the function writes the actual size of the information requested. If that size is less than or equal to the ObjectInformationLength parameter, the function copies the information into the ObjectInformation buffer; otherwise, it returns an NTSTATUS error code and returns in ReturnLength the size of the buffer required to receive the requested information.
Und wenn du schon Verbesserungvorschläge machst, warum pruefst du nicht den Rückgabewert deines 1. Aufrufes von NtQueryObject?
Im übrigen hab ich auch unter D/ (lange her wo ich es benutzt habe) nie Probleme mit der Optimierung gehabt.
  Mit Zitat antworten Zitat
brechi

Registriert seit: 30. Jan 2004
823 Beiträge
 
#2

AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

  Alt 26. Apr 2012, 13:06
So also ich hab mich bei der Dokumentation verlesen, aber anscheinend ist ein "out DWORD" doch richtig, jedenfalls funktioniert der Code dann damit und man erhaelt als Rückgabe "C0000004" = STATUS_INFO_LENGTH_MISMATCH + die Richtige Laenge in ReturnSize.

[This function may be changed or removed from Windows without further notice.] -> mit Windows7 /D2007 hier getestet

Edit2:
bin ich blöd?:
"optional pointer to a location" = "Adresse eines Speicherbereichs" oder? -> demnach MSDN richtig und muss ein "out DWORD" besser "PDWORD" sein da optional, also ggf nil.

Geändert von brechi (26. Apr 2012 um 13:21 Uhr) Grund: zu doof
  Mit Zitat antworten Zitat
Benutzerbild von Aphton
Aphton

Registriert seit: 31. Mai 2009
1.198 Beiträge
 
Turbo Delphi für Win32
 
#3

AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

  Alt 26. Apr 2012, 13:33
Es ist bestimmt ein var ReturnLength: DWord oder pReturnLength: PDWord (beides sind ja ~mehr oder weniger~ intern Pointer)
Doppelte Pointer -> var pReturnLength: PDWord oder mit out wäre sinnlos.
-Edit-
Das ist auch üblich so bei der Win API!
-/Edit-

@Himitsu Bezgl. der Codeoptimierung:
Mir ist es unter dem verbuggten Turbo Delphi Explorer auch einige Male passiert, dass die Optimierung Codeteile zerfetzt hat und generell das Debuggen unmöglich machte.
Es wurden zB Codestellen gar nicht kompiliert - ich schätz mal, der Compiler dachte sich (warum auch immer), dass die Stelle nie ausgeführt wird und "optimierte" sie weg... Hab mich sehr oft darüber aufregen dürfen. Ist also nichts seltenes.
das Erkennen beginnt, wenn der Erkennende vom zu Erkennenden Abstand nimmt
MfG
  Mit Zitat antworten Zitat
ASM

Registriert seit: 15. Aug 2004
165 Beiträge
 
Delphi 7 Enterprise
 
#4

AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

  Alt 26. Apr 2012, 16:21
Es ist bestimmt ein var ReturnLength: DWord oder pReturnLength: PDWord (beides sind ja ~mehr oder weniger~ intern Pointer)
Offenbar aber doch nicht gleichwertig.
Dazu noch einmal hinichtlich dieses letzten Parameters von NtQueryObject() die MSDN zitiert: "An optional pointer to a location where the function writes the actual size of the information requested.".
Explizit dazu die Deklaration:
Code:
NtQueryObject(
IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG Length,
OUT PULONG ResultLength
);
Wenn ich also wie folgt deklariert habe:
Code:
Var
  ReturnSize: DWORD;
  pReturnSize: PDWORD;
dann ist also eindeutig als Parameter an NtQueryObject()zu übergeben pReturnsize (und zwar zwingend als OUT resp.VAR) und nicht einfach nur als ReturnSize.

Was passiert nämlich im Ergebnis ?:

Parameter als OUT PDWORD deklariert:
Optimierung OFF: ResultLength = 9460301
Optimierung ON: ResultLength = Accession Error

Parameter als PDWORD deklariert:
Optimierung OFF: ResultLength = 9460301
Optimierung ON: ResultLength = 48401

Parameter als OUT DWORD deklariert:
Optimierung OFF: ResultLength: = 10
Optimierung ON: ResultLength = 1308584

Eindeutig völlig neben der Spur liegt also die Verwendung des Parameters als einfaches OUT (oder VAR) DWORD.

Wie außerdem zu sehen ist, kommt es bei aktivierter Optimierung selbst bei der Deklaration des Parameters als PDWORD zu völlig unterschiedlichem Ergebnis, was so eigentlich gar nicht passieren dürfte, da die Adressen der entsprechenden Pointer vor und nach dem Aufruf von NtQueryObject() unverändert an derselben Speicherstelle liegen. Unter deaktivierter Optimierung tritt dieser Effekt nicht auf, ganz wie es zu erwarten ist.


Aber noch etwas anderes:
Der Eingabeparameter Length bezieht sich auf die Größe des Ausgabepuffers zu ObjectNameInformation.Eigentlich sollte nun diese Größe von Length auf die Ausgabe der ResultLength gar keinen Einfluss haben, denn NtQueryObject() stellt angeblich nur die Größe des Ausgabepuffers entsprechend der im 2.Parameter genannten Anforderung fest. Und allein das Funktionsergebnis teilt mit, ob die in Length angegebene Größe des Ausgabepuffers zur Durchführung der vorgesehenen Aktion ausreichend ist. Setzt man jedoch den Wert von Length=0, was ja auf das Ergebnis von ResultLength gar keinen Einfluss haben dürfte, dann kommt es sowohl mit als ebenso auch ohne Optimierung zum Accession Error. Andererseits genügt bereits ein eingegebenes Length=100, um dem Erfolg von NtQueryObject() zu erreichen, obwohl dieser Wert weit niedriger ist als der in ResultLength zurückgegebene Wert der benötigten Größe des Ausgabepuffers. Alles sehr merkwürdig und auf Grund der recht dürftigen Dokumentation seitens MS kaum nachvollziehbar.
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

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

AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

  Alt 26. Apr 2012, 16:30
Ich verweise hier nochmal auf das Alignment bzw. die Enum Typ Größe. Mit den entsprechenden Compiler Switches funktionieren bei mir folgende Varianten problemlos:
Delphi-Quellcode:
var
  ReturnLength: ULONG;
begin
  // Deklaration als PULONG
  NtQueryObject(x, x, x, x, @ReturnLength);
  // Deklaration als var ULONG
  NtQueryObject(x, x, x, x, ReturnLength);
Ich kann mich irren, aber soweit ich weiß, interpretiert Delphi einen OUT Parameter genauso wie einen VAR Parameter. Sprich, wenn du, wie in deinem Beispiel ReturnLength als OUT PULONG deklarierst, ist das dann der Pointer auf den Pointer auf ReturnLength und nicht nur der Pointer auf ReturnLength.

Außerdem: Wenn du ReturnLength lokal als PULONG deklarierst, dann zeigt dieser Pointer entweder ins Leere (auf 0) oder wird mit einem Zufallswert initialisiert. Ruftst du dann die API auf und übergibst die Variable direkt, dann wird natürlich versucht die Rückgabelänge in einen invaliden Speicherbereich zu schreiben.
Wenn du ReturnLength aber als ULONG deklarierst und dann @ReturnLength übergibst, zeigt der Pointer auf die korrekt adressierte lokale Variable.

Zur Verdeutlichung:
Delphi-Quellcode:
var
  ReturnLength: ULONG;
  pReturnLength: PULONG;
begin
  // Wir gehen von einer ReturnLength deklaration als PULONG aus
  // FALSCH:
  // pReturnLength ist hier ein uninitialisierter Pointer.
  // die API versucht die Länge zu schreiben, indem sie dereferenziert: pReturnLength^ := Länge
  // da der Pointer aber auf einen undefinierten Speicherbereich zeigt, bekomst du eine AV
  NtQueryObject(x, x, x, x, pReturnLength);
  // RICHTIG:
  // der Speicher von ReturnLength wurde von Delphi korrekt alloziiert
  // wir übergeben die Referenz auf diesen Speicher, also den Pointer, der auf ReturnLength zeigt
  NtQueryObject(x, x, x, x, @ReturnLength);
Das Problem mit der Übergabe von Length = 0 kann ich ebenfalls nicht nachvollziehen. Der Aufruf von NtQueryObject(x, x, nil, 0, x) funktioniert für mich wunderbar. Aber vermutlich rührt dieses Problem von deiner fehlerhaften Implementation der ReturnLength her.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl (26. Apr 2012 um 16:50 Uhr)
  Mit Zitat antworten Zitat
brechi

Registriert seit: 30. Jan 2004
823 Beiträge
 
#6

AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

  Alt 26. Apr 2012, 19:17
Du kannst den Code der WinAPI übersetzen indem du einfach die in/outs weglässt, das "out" in der MSDN API Beschreibung hat rein gar nichts mit dem Delphi "out" zu tun.
Delphi-Quellcode:
NtQueryObject(
IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG Length,
OUT PULONG ResultLength
);
Korrekterweise wird ein "in/out" als "VAR typ" übersetzt, ein "out_opt" als "pointer typ" und ein "out" als "OUT typ" wobei Typ der entsprechende Bezeichner ohne dem P ist.
Indem Fall ist die korrekte Übersetzung ein

ResultLength: PULONG; VAR typ / CONST typ / OUT typ /Ptyp unterscheiden sich rein von der Übergabe nicht voneinander, es sind alles Pointer auf den TYP. Der einzige Unterschied ist, dass Delphi zusätzliche Überprüfungen durchführen kann.

VAR: der Parameter muss vorher initalisiert werden (Warnung)
CONST: der Parameter darf nicht abgeändert werden (Fehler)
OUT: initalisierter Parameter wird nie verwendet (Warnung)

In dem Fall sollte es mit PULONG oder OUT LONG übersetzt werden, richtigerweise aber PULONG, da es optional ist, d.h. eine Übergabe von NIL ist erlaubt! Dies funktioniert aber nicht mit OUT LONG, da dann immer ein Parameter angegeben werden muss.

Logisch wäre also folgender Aufruf:

Delphi-Quellcode:
  ReturnLength: LONG;
  
  NtQueryObject(x, nil, x, x, @ReturnLength); // Laenge ermitteln
  NtQueryObject(x, @Info, x, x, nil); // auslesen, Laenge ist total egal und wollen wir nicht mehr haben
Im übrigen hast du (ASM) es komischerweise ja auch bei der ObjektInformation richtig übersetzt und übergibst im ersten Aufruf auch NIL.

Die AccessViolation kommt, weil bei dir ein zufälliger Wert für die Adresse verwendet wird (da nie initalisiert). Wenn es trotzdem funktioniert (reiner Zufall), dann hast du im Rückgabewert den du überprüfst auch immer etwas <> 0 stehen.
D.h. dein VirtualAlloc funktioniert auch mit hoher Wahrscheinlichkeit (sofern genug Speicher vorhanden). Wäre der Rückgabewert aber zu klein (für die ObjektInformation) dann funktioniert es trotzdem, da VirtualAlloc dir IMMER genügend Speicher reserviert (PageSize, 0x1000).

Das Problem ist ObjectInformationClass, die EnumTyp-Größe. Du kannst nicht einfach ein Enum von Delphi übergeben, entweder passt du das mit dem Compilerschalter an ob der du übersetzt einen Enum als DWORD und castest den Delphi-Enum. Wenn du das bedachtest dann klappt das auch bei dir.

Im übrigen solltest du in dem Fall bei dir mal die Optimierung einschalten und die Bugs beheben. Du wirst bei deinen Übersetzungen bestimmt viele Fehler eingebaut haben (doppelter Pointer, Enums).

Du hast zumindest auch in einem älteren Thread (C Callback)
http://www.delphipraxis.net/159876-c...aus-c-dll.html
eine Aussage gemacht
Zitat:
Deshalb können grundsätzlich auch nur die mit cdecl deklarierten Funktionen/Prozeduren Variablenparameter haben, während an die mit stdcall deklarierten Funktionen/Prozeduren ausschließlich Werteparameter übergeben werden dürfen.
die überhaupt nicht stimmt, da bis auf das Aufräumen des Stacks kein Unterschied zwischen CDECL/STDCALL besteht.

Geändert von brechi (26. Apr 2012 um 19:22 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln

  Alt 27. Apr 2012, 00:40
Völlig funktionsfähig und erfolgreich inklusive Codeoptimierung in D7, D2007, D2009, D2010, XE und XE2 getestet.

Delphi-Quellcode:
uses
  Windows, SysUtils, StrUtils;

type
  NTSTATUS = Cardinal;
  TUnicodeString = record
    Length, MaximumLength: Word;
    Buffer: PWideChar;
  end;
  TObjectNameInformation = record
    Name: TUnicodeString;
    NameBuffer: array[0..MAX_PATH-1] of WideChar;
  end;

const
  STATUS_SUCCESS = NTSTATUS($00000000);
  STATUS_INVALID_PARAMETER = NTSTATUS($C000000D);
  STATUS_INFO_LENGTH_MISMATCH = NTSTATUS($C0000004);
  ObjectNameInformation = 1;

function DevicePathToFileName(const DevicePath: string): string;
var
  DeviceList, DosDevice: array[0..MAX_PATH-1] of Char;
  Device: PChar;
  Size: LongWord;
begin
  Result := DevicePath;
  Size := GetLogicalDriveStrings(MAX_PATH, @DeviceList);
  if (Size = 0) or (Size > MAX_PATH) then
    RaiseLastOSError;
  Device := @DeviceList;
  while Device^ <> #0 do begin
    Size := QueryDosDevice(PChar(ExcludeTrailingPathDelimiter(Device)), @DosDevice, MAX_PATH);
    if Size = 0 then
      RaiseLastOSError;
    if StartsText(IncludeTrailingPathDelimiter(DosDevice), Result) then
      Exit(Device + Copy(Result, Length(IncludeTrailingPathDelimiter(DosDevice)) + 1));
    Inc(Device, Length(Device) + 1);
  end;
end;

function GetFilePathFromHandle(hFile: THandle): string;
var
  NtQueryObject: function(Handle: THandle; ObjectInformationClass: Cardinal; ObjectInformation: Pointer;
    ObjectInformationLength: Cardinal; ReturnLength: PCardinal): NTSTATUS; stdcall;
  NameInformation: TObjectNameInformation;
  Status: NTSTATUS;
begin
  Result := '';
  NtQueryObject := GetProcAddress(GetModuleHandle('ntdll.dll'), 'NtQueryObject');
  if not Assigned(NtQueryObject) then
    Exit;
  Status := NtQueryObject(hFile, ObjectNameInformation, @NameInformation, SizeOf(NameInformation), nil);
  if Status <> STATUS_SUCCESS then
    Exit;
  Result := DevicePathToFileName(NameInformation.Name.Buffer);
end;
Ach ja, das Packed ist mit voller Absicht, denn bei kleinen Codeschnippseln versuche ich möglichst nicht an den Compileroptionen rumzuspielen,
denn ich weiß ja nicht, was im restlichen Code für Einstellungen vorgesehn sind.

Und leider bietet Delphi immernoch keine Möglichkeit die aktuellen Einstellen zwischenzuspeichern und nachher wieder zurückzusezten.
Das könnte man sich nur für {$#+} und {$#-} selber basteln, aber für {$A2}, {$A4} und {$A16} ist keine Bestimmung möglich.

TUnicodeString: oder ohne packed, aber mit {$A4} bzw. {$ALIGN 4}

TObjectNameInformation: alternativ ebenfalls mit {$A4} bzw. {$ALIGN 4}
Aber auch ohne packed und mit unkorrekter Ausrichtung, würde es dennoch funktionieren, solange keiner NameBuffer auslesen will.


Achtung: Im Zielprogramm muß eine Speicherausrichtung von mindestens 8 Byte eingestellt sein. (in den Projektoptionen oder über {$ALIGN 8}).
Ein {$ALIGN 16} währe vermutlich aber auch möglich.



Zitat:
Delphi-Quellcode:
if StartsText(IncludeTrailingPathDelimiter(DosDevice), Result) then
  Exit(Device + Copy(Result, Length(IncludeTrailingPathDelimiter(DosDevice)) + 1));
Und wenn es in älteren Delphis damit ein paar Problemchen gibt. (Letzeres geht immer)
Delphi-Quellcode:
if StartsText(IncludeTrailingPathDelimiter(DosDevice), Result) then begin
  Result := Device + Copy(Result, Length(IncludeTrailingPathDelimiter(DosDevice)) + 1);
  Exit;
end;
oder
Delphi-Quellcode:
if AnsiStartsText(IncludeTrailingPathDelimiter(DosDevice), Result) then begin
  Result := Device + Copy(Result, Length(IncludeTrailingPathDelimiter(DosDevice)) + 1, Length(DosDevice));
  Exit;
end;
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (27. Apr 2012 um 09:20 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 21:56 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