![]() |
Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Hallo zusammen,
folgende Funktion liefert den vollständigen Namen einer Datei oder eines Verzeichnisses anhand eines Handles.
Delphi-Quellcode:
Vermutlich können damit auch die Namen anderer Handles ermittelt werden, allerdings habe ich dies nicht weiter getestet.
const
STATUS_SUCCESS = NTSTATUS($00000000); STATUS_INVALID_PARAMETER = NTSTATUS($C000000D); STATUS_INFO_LENGTH_MISMATCH = NTSTATUS($C0000004); type NTSTATUS = ULONG; HANDLE = Winapi.Windows.THandle; PWSTR = Winapi.Windows.LPWSTR; PUNICODE_STRING = ^UNICODE_STRING; _UNICODE_STRING = record Length: USHORT; MaximumLength: USHORT; Buffer: PWSTR; end; UNICODE_STRING = _UNICODE_STRING; PCUNICODE_STRING = ^UNICODE_STRING; TUnicodeString = UNICODE_STRING; PUnicodeString = PUNICODE_STRING; function GetFilePathFromHandle(hFile: THandle): String; function DeviceNameToFilePath(FileName: String): String; var Buffer: array[0..MAX_PATH - 1] of Char; BufferSize: DWord; LogicalDrives: array of Char; I: Integer; DeviceName: String; begin Result := FileName; BufferSize := GetLogicalDriveStrings(MAX_PATH, @Buffer[0]); if (BufferSize = 0) then Exit; SetLength(LogicalDrives, (BufferSize - 2 * SizeOf(Char)) div 3); for I := Low(LogicalDrives) to High(LogicalDrives) do begin LogicalDrives[I] := Buffer[I * 4]; end; for I := Low(LogicalDrives) to High(LogicalDrives) do begin BufferSize := QueryDosDevice(PChar(LogicalDrives[I] + ':'), @Buffer[0], MAX_PATH); if (BufferSize > 0) then begin DeviceName := AnsiLowerCase(PWideChar(@Buffer[0])); if (AnsiLowerCase(Copy(FileName, 1, Length(DeviceName))) = DeviceName) then begin Result := LogicalDrives[I] + ':' + Copy(FileName, Length(DeviceName) + 1, Length(FileName)); Exit; end; end; end; end; const ObjectNameInformation = 1; var NtQueryObject: function(ObjectHandle: HANDLE; ObjectInformationClass: ULONG; ObjectInformation: PVOID; ObjectInformationLength: ULONG; ReturnLength: PULONG): NTSTATUS; stdcall; var ObjectInformation: PUNICODE_STRING; ReturnLength: ULONG; Status: NTSTATUS; begin Result := ''; @NtQueryObject := GetProcAddress(LoadLibrary('ntdll.dll'), 'NtQueryObject'); if not Assigned(@NtQueryObject) then Exit; Status := NtQueryObject(hFile, ObjectNameInformation, nil, 0, @ReturnLength); if (Status <> STATUS_INFO_LENGTH_MISMATCH) then Exit; GetMem(ObjectInformation, ReturnLength); try Status := NtQueryObject(hFile, ObjectNameInformation, ObjectInformation, ReturnLength, @ReturnLength); if (Status = STATUS_SUCCESS) then begin Result := ObjectInformation.Buffer; Result := DeviceNameToFilePath(Result); end; finally FreeMem(ObjectInformation); end; end; Viele Grüße Zacherl |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Zitat:
Eindeutig wohl eher aber nur per "Copy & Paste" von irgendwoher ungeprüft übernommen.. Denn "Dein" Code funktioniert weder unter Delphi 7 noch unter Delphi XE. Sünde 1: Zunächst einmal fehlen überhaupt grundsätzliche Deklarationen, ohne die der Code erst gar nicht zu compilieren ist, als da wären:
Code:
Damit jedoch nicht genug!
const
STATUS_SUCCESS = $00000000; STATUS_INFO_LENGTH_MISMATCH = $C0000004; type HANDLE = THandle; PVOID = Pointer; NTSTATUS = Cardinal; USHORT = word; UNICODE_STRING = packed record Length: USHORT; MaximumLength: USHORT; Buffer: PWideChar; end; PUNICODE_STRING = ^UNICODE_STRING; Sünden 2ff: Selbst nach Hinzufügen dieser essentiellen Deklarationen wird das entsprechende Programm dann zwar immerhin wenigstens compilierbar und lauffähig, bleibt trotzdem aber eine erfolgreiche, korrekte Ausgabe schuldig. Erste Stolperstelle: Der Status bei folgendem Call
Code:
ist "nicht erfolgreich".
Status := NtQueryObject(hFile, ObjectNameInformation, ObjectInformation, ReturnLength, @ReturnLength);
Im eigentlichen Code sind nämlich massenweise Fehler, z.B. ist in der Funktionsbeschreibung von NtQueryObject() der letzte Parameter nicht als OUT- bzw. VAR-Parameter deklariert. Das aber ist zwingend erforderlich, weil genau dieser Wert anschließend Parameter für den Zugriff auf die Daten der OBJECT_INFORMATION_CLASS ist. Zweitens, die (lokale) Deklaration
Code:
ist völliger Unsinn.
var ObjectInformation: PUNICODE_STRING
ObjectInformation muss vielmehr ein Record vom Typ OBJECT_NAME_INFORMATION sein; entsprechend muss ein pObjectInformation:=@OBJECT_NAME_INFORMATION eingeführt werden, weil der Zugriff über den Pointer läuft. Dadurch ändern sich natürlich zwingend eine Anzahl Anweisungen. Man erhält dann mit der Abfrage auf pObjectInformation^.Name.Buffer den korrekten DeviceNamen zum Filehandle. Letztens (das Letzte i.e.S.): Und diesen DeviceNamen nun mit DeviceNameToFilePath() in den logischen WindowsFilepath konvertieren zu wollen, ist ein völlig falscher Ansatz; das ergibt als Ergebnis einfach nur einen leeren String; Fazit: der Code ist leider nicht zu gebrauchen. Ich frage mich, wer den zusammengeschustert hat (er kursiert seit geraumer Zeit im INet). Statt nun aber mühsam alle Fehler im geposteten Code in allen Einzelheiten aufzuspüren und zu korrigieren, hier folgend (m)ein funktionsfähiger Code. Der führt unter Windows XP sowohl mit Delphi 7 als auch mit Delphi XE zum richtigen Ergebnis; ob auch mit Win7, kann ich gerade nicht nachprüfen.
Code:
// Beispiel:
Type
NTSTATUS = Cardinal; USHORT = word; UNICODE_STRING = packed record Length: USHORT; MaximumLength: USHORT; Buffer: PWideChar; end; OBJECT_NAME_INFORMATION = packed record Name: UNICODE_STRING; NameBuffer: array[0..0] of WCHAR; end; POBJECT_NAME_INFORMATION = ^OBJECT_NAME_INFORMATION; OBJECT_INFORMATION_CLASS = (ObjectBasicInformation, ObjectNameInformation, ObjectTypeInformation, ObjectAllInformation, ObjectDataInformation); function NtQueryObject(ObjectHandle: cardinal; ObjectInformationClass: OBJECT_INFORMATION_CLASS; ObjectInformation: pointer; Length: ULONG; out ResultLength: PDWORD): NTSTATUS; stdcall; external 'ntdll.dll'; function NT_SUCCESS(Status: Integer): WordBool; begin Result := Status >= 0; end; function PosEx(const SubStr, s: string; Offset: Cardinal = 1): Integer; var i, x: Integer; Len, LenSubStr: Integer; begin if Offset = 1 then Result := Pos(SubStr, S) else begin i := Offset; LenSubStr := Length(SubStr); Len := Length(s) - LenSubStr + 1; while i <= Len do begin if s[i] = SubStr[1] then begin x := 1; while (x < LenSubStr) and (s[i + x] = SubStr[x + 1]) do Inc(x); if (x = LenSubStr) then begin Result := i; Exit; end; end; Inc(i); end; Result := 0; end; end; function DevicePathToWin32Path(Path: string): string; var c: char; s: string; i: integer; begin i := PosEx('\', Path, 2); i := PosEx('\', Path, i + 1); result := copy(Path, i, length(Path)); delete(Path, i, length(Path)); for c := 'A' to 'Z' do begin setlength(s, 1000); if QueryDosDevice(pchar(string(c) + ':'), pChar(s), 1000) <> 0 then begin s := pChar(s); if sametext(Path, s) then begin result := c + ':' + result; exit; end; end; end; result := ''; end; function FileHandleToFilePath(hFile: DWORD): WideString; var Status: NTSTATUS; ONI: POBJECT_NAME_INFORMATION; ReturnSize: DWORD; pReturnSize: PDWORD; begin if (hFile <> INVALID_HANDLE_VALUE) then begin pReturnSize := @ReturnSize; NtQueryObject(hFile, ObjectNameInformation, nil, 0, pReturnSize); ONI := VirtualAlloc(nil, ReturnSize, MEM_COMMIT, PAGE_READWRITE); if (Assigned(ONI)) then begin Status := NtQueryObject(hFile, ObjectNameInformation, ONI, ReturnSize, pReturnSize); if (NT_SUCCESS(Status)) then begin SetLength(Result, ONI^.name.Length); Result := ONI^.name.Buffer end; VirtualFree(ONI, 0, MEM_RELEASE); end; end; end;
Code:
procedure TForm1.Button1Click(Sender: TObject);
var FileName: string; FileHandle: THandle; DevicePath: string; Win32Path: string; ErrMsg: string; buffer: array[0..MAX_PATH-1] of Char; begin DevicePath := ''; Win32Path := ''; ErrMsg := ''; GetWindowsDirectory(buffer, max_path); // oder eben ein anderes File FileName := StrPas(buffer)+'\NOTEPAD.EXE'; if not Fileexists(Filename) then begin Showmessage(format('File "%s" not found!', [ExtractFilename(FileName)])); exit; end; FileHandle := CreateFile(pAnsiChar(FileName), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); // alternatively: // FileHandle := FileOpen(FileName,fmOpenRead or fmShareDenyNone); if FileHandle = INVALID_HANDLE_VALUE then ErrMsg := 'Failed: Invalid Filehandle!' else try // der "Dummy"-Zugriff per FileRead ist wichtig, // damit im System durch den (erstmaligen) Zugriff // ein dem FileHandle zugeordneter Record OBJECT_NAME_INFORMATION angelegt wird; // ist nicht notwendig, wenn bereits anderweitig das File aktiv benutzt worden ist Sysutils.FileRead(FileHandle, buffer, 10); DevicePath := FileHandleToFilePath(FileHandle); if DevicePath = '' then ErrMsg := ' Failed: Unable to get DevicePath!' else begin Win32Path := DevicePathToWin32Path(DevicePath); if Win32Path = '' then ErrMsg := ' Failed: Unable to convert DevicePath to Win32Path!'; end finally FileClose(FileHandle); end; if Win32Path <> '' then ShowMessage(Win32Path) else ShowMessage(ErrMsg); end; |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Noch ein sehr wichtiger, ergänzender Hinweis als Nachtrag zu meinem Code:
In der Compileroption (Menu: Optionen >> Compiler-Tab >> Codeerzeugung) muss unbedingt die Optimierung deaktiviert werden, was ohnehin grundsätzlich immer dringend zu empfehlen ist. Der Compiler verhaspelt sich sonst in FileHandleToFilePath() bei der Ermittlung der benötigten Größe des Ausgabepuffers im ersten Aufruf von NtQueryObject(). Irgendetwas geht da unverständlicherweise intern mit der Zuweisung der Speichergröße an den entsprechenden Pointer schief. Folge: weil dann nicht auf den Ausgabepuffer zugegriffen werden kann, kann somit daraus auch nicht der Devicename zum Filehandle ausgelesen werden und somit natürlich auch nicht der Win32Filepath ermittelt. Also nicht vergessen: Compiler-Optimierung AUS. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Zitat:
Das ist so, als wenn man drinend empfiehlt immer auf die VCL zu verzichten und immer nur mit vollen Debuginfos kompiliert. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Faszinierend. Die Annahme daß ein Volume auch einen Laufwerksbuchstaben haben müsse ist mal grob falsch (
![]() ![]() Ebenso kann ein Volume an mehreren Stellen eingehangen sein (genau wie in Linux auch). |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Zitat:
Ich habe - im Falle recht komplexer Projekte - bereits wiederholt äußerst schwerwiegende, fatale Probleme erfahren müssen, insofern die Optimierung zunächst aktiviert gewesen war. Die ist ja leider per default in jedem neuen Projekt aktiviert, was anscheinend nicht durch Voreinstellung abgeschaltet werden kann und immer erst explizit manuell in jedem neuen Projekt vorgenommen werden muss. Sobald die Optimierung dann nachträglich deaktiviert worden war, war damit auch das spezielle Problem beseitigt - wie auch eben jetzt beschrieben. Dagegen habe ich noch nie ein einziges entsprechend compilerbedingtes Problem beobachtet, welches durch eine Deaktivierung der Optimierung verursacht worden war. Unabhängig davon steht im übrigen eine aktivierte Optimierung allzu häufig dem Debugging lokaler Variablen heftig im Wege. Nenne mir also einen einzigen Grund, warum die Aktivierung der Optimierung wirklich sinnvoll und notwendig wäre. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Ich hab noch nie erlebt, daß die Codeoptimierung negative Auswirkungen hatte?
Und dazu soll diese Optimierung auch nicht das Verhalten des Programms verändern, also gibt es auch keine Nachteile. Würde mich also mal interessieren, wie soein Code aussieht, welcher mit Optimierung nicht mehr funktioniert. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Zitat:
Alles von mir Beschriebene bezieht sich auf das Arbeiten mit Delphi 7. Mit dieser Version arbeite ich noch immer standardmäßig (warum auch nicht). Jetzt habe ich den Code gerade noch einmal unter Delphi XE compiliert und zwar einmal mit und einmal ohne Aktivierung der Optimierung. Ergebnis: OK, in Delphi XE ist inzwischen offenbar die Optimierung überarbeitet. Hier im konkreten Fall hat die Einstellung der Optimierung jedenfalls keinen Einfluss mehr auf den erfolgreichen Ablauf. Die Frage bleibt, ob das immer so sein wird; ich bin da aus meiner Erfahrung skeptisch. Bei der Compilierung mit Delphi 7 dagegen kann die Aktivierung/Deaktivierung der Optimierung einen ganz entscheidenden Einfluss auf das korrekte Funktionieren des Programms haben, exakt wie gerade beschrieben. Versuche es doch selbst einmal unter Delphi 7: den Code zur Beoachtung des deutlich unterschiedlichen Erfolgs hast Du ja von mir wie oben beschrieben. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
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:
Und wenn du schon Verbesserungvorschläge machst, warum pruefst du nicht den Rückgabewert deines 1. Aufrufes von NtQueryObject?
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. Im übrigen hab ich auch unter D/ (lange her wo ich es benutzt habe) nie Probleme mit der Optimierung gehabt. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
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. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
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. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Ansonsten: Danke dir für deinen wirklich fundierten und hilfreichen Beitrag! Achja ...: Zitat:
Dass du UNICODE_STRING sogar noch explizit als "packed record" (also nicht aligned) deklariert hast, zeigt nochmal, dass du absolut keine Ahnung hast, was du da überhaupt machst. Edit: 1) (korrekte) Typendeklarationen ergänzt 2): Standardrückgabewert geändert |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Zitat:
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:
Wenn ich also wie folgt deklariert habe:
NtQueryObject(
IN HANDLE ObjectHandle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG Length, OUT PULONG ResultLength );
Code:
dann ist also eindeutig als Parameter an NtQueryObject()zu übergeben pReturnsize (und zwar zwingend als OUT resp.VAR) und nicht einfach nur als ReturnSize.
Var
ReturnSize: DWORD; pReturnSize: PDWORD; 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. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
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:
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.
var
ReturnLength: ULONG; begin // Deklaration als PULONG NtQueryObject(x, x, x, x, @ReturnLength); // Deklaration als var ULONG NtQueryObject(x, x, x, x, 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:
Das Problem mit der Übergabe von Length = 0 kann ich ebenfalls nicht nachvollziehen. Der Aufruf von
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);
Delphi-Quellcode:
funktioniert für mich wunderbar. Aber vermutlich rührt dieses Problem von deiner fehlerhaften Implementation der ReturnLength her.
NtQueryObject(x, x, nil, 0, x)
|
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
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:
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.
NtQueryObject(
IN HANDLE ObjectHandle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG Length, OUT PULONG ResultLength ); Indem Fall ist die korrekte Übersetzung ein
Delphi-Quellcode:
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.
ResultLength: PULONG;
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:
Im übrigen hast du (ASM) es komischerweise ja auch bei der ObjektInformation richtig übersetzt und übergibst im ersten Aufruf auch NIL.
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 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) ![]() eine Aussage gemacht Zitat:
|
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Völlig funktionsfähig und erfolgreich inklusive Codeoptimierung in D7, D2007, D2009, D2010, XE und XE2 getestet. :roll:
Delphi-Quellcode:
Ach ja, das Packed ist mit voller Absicht, denn bei kleinen Codeschnippseln versuche ich möglichst nicht an den Compileroptionen rumzuspielen,
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; 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. :cry: 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:
oder
if StartsText(IncludeTrailingPathDelimiter(DosDevice), Result) then begin
Result := Device + Copy(Result, Length(IncludeTrailingPathDelimiter(DosDevice)) + 1); Exit; end;
Delphi-Quellcode:
if AnsiStartsText(IncludeTrailingPathDelimiter(DosDevice), Result) then begin
Result := Device + Copy(Result, Length(IncludeTrailingPathDelimiter(DosDevice)) + 1, Length(DosDevice)); Exit; end; |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Hast du deinen Code mal für 64 Bit kompiliert? Dort wird dein packed Unicode String einige Probleme verursachen. Wie gesagt, die API erwartet ein Aligned Struct. Unter 32 Bit passt das Alignment zufällig. Unter 64 Bit war das bei meinen Versuchen nicht der Fall.
Da das Align aber, soweit ich weiß, die Standardeinstellung ist, brauchst du im Grunde nur die packed Direktive entfernen, ohne irgendwelche Compilerflags zu setzen. |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Von Standard kann man eben nicht so einfach ausgehn, jedenfalls nicht bei einem ein einzelnen Codestück.
Oder kannst du sicherstellen, daß im Zielprojekt nicht die Projekteinstellungen andersaussehn, bzw. ob in der Unit, wo der Code dann eingefügt wurde, nicht irgendwo am ALGIN gedreht wurde? Wenn man Code als eigenständige Unit veröffentlicht, dann sieht die Sache gleich ganz anders aus. :cry: [add] Aber OK, man könnte das ALIGN zu den Systemvoraussetzengen zählen. Oben editiert ... besser so? Nee, 64 bit, hatte ich noch nicht probiert, hol ich heut Abend aber noch nach. Nur das Mac kann ich mir ersparen. (hab eh keinen und diese komischen Macs haben irgendeine unverständliche Allergie gegen WinAPI-Aufrufe :roll: ) |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Zitat:
Zitat:
Optimierer haben bspw. Probleme mit Pointer-Aliasing, nur um ein Beispiel zu nennen. Auf Architekturen auf denen Alignment dann auch noch eine echte (nicht wie bei x64) Rolle spielt (SPARC usw.) stellt dir der Optimierer dann öfter mal einen Fuß. Nicht umsonst rät bspw. das Gentoo Wiki beim gcc nur bis -O2 zu gehen. Das ist durchaus auf andere Compiler übertragbar, auch wenn die Fehler sich mit jeder Implementierung/Version unterscheiden werden. Zitat:
Es schleichen sich da immer wieder Fehler ein, schließlich haben Marcel und ich das manuell gemacht und dann die Verantwortung ohnehin irgendwann abgegeben. Aber bei aller Kritik: ich finde es cool, daß du deinen Code hier zur Verfügung stellst :thumb: |
AW: Vollständigen Datei- oder Verzeichnisnamen über das Handle ermitteln
Kommt drauf an, ich hab schon einige Fehler gesehn, aber eben nur noch keinen, welcher durch die Codeoptimierung entstand.
Grade eben (vor vielleicht 15 Minuten) hatte ich einen netten Compilerbug. Der Code war richtig, aber ein einer Stelle, welche seit Monaten problemlos funktioniert, wurde proütlich was von >>ungültiges Zeichen "?" gefunden<< bemängelt. Über's SVN konnt ich dann rausfinden, daß ich knapp 120 Zeilen und mehrere Funktionen davor einige Zuweisungen geändert hatte. Sobald ich auch nur irgendeine der 5 Zeilen auskommentierte oder eine Kleinigkeit änderte funktionierte es urplötzlich wieder, also da 120 zeilen später. :wall: Wobei ich gerade diesen Fehler auch schon seit mindestens 2 Jahren nicht mehr hatte. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:51 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