Delphi-PRAXiS
Seite 4 von 4   « Erste     234   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   DLL Schnitstelle (https://www.delphipraxis.net/204889-dll-schnitstelle.html)

TiGü 12. Jul 2020 12:49

AW: DLL Schnitstelle
 
Und wo ich schonmal am rumranten bin:
Zum Kuckuck, gebe den Buffer gefälligst wieder frei mit se_free().
Einfach mal die Doku lesen, die du selber im ersten Beitrag zum VB Beispiel gepostet hast.

Internet kann für heute zu...

AuronTLG 3. Aug 2020 15:57

AW: DLL Schnitstelle
 
Ich schlage mich gerade mit dem selben Mist herum und hätte auch mal eine Frage:

Ich habe folgende Definition in der C-DLL:
Code:
se_result_t se_authenticateUser   (   const char *    userId,
uint32_t    userIdLength,
const uint8_t *    pin,
uint32_t    pinLength,
se_authentication_result_t *    authenticationResult,
int16_t *    remainingRetries
)
Anmerkung: Die DLL gibt es in sowohl als CDECL- als auch als STDCALL-Variante. Ich benutze logischerweise die STDCALL-Variante, während Hewy wohl die CDECL-Variante verwendet.
Weiterhin lade ich die DLL dynamisch über LoadLibrary.

Ich habe daraus folgende Definition für Delphi gemacht:
Code:
se_authenticateUser              = Function(userId : PAnsiChar; userIdLength : UInt32; pin : PByteArray; pinLength : UInt32;
                                             Var authenticationResult : SeAuthenticationResult; Var remainingRetries : PShortInt) : SeReturnCode; Stdcall;
Die eigentlich Zugriffsmethode, die ich dazwischengeschaltet habe, sieht dann folgendermaßen aus:
Code:
Function AuthentifiziereBenutzer(BenutzerID, BenutzerPIN : String; Var Ergebnis : SeAuthenticationResult; Var Versuche : ShortInt) : Boolean;
Var ReturnCode : SeReturnCode;
    PVersuche : PShortInt;
    ID        : PAnsiChar;
    PIN       : PByteArray;

Begin
Result := False;
PVersuche := @Versuche;

ID := PAnsiChar(AnsiString(BenutzerID));
PIN := StringToPByteArray(BenutzerPIN);

ReturnCode := Self.AuthenticateUser(ID, Length(BenutzerID), PIN, Length(BenutzerPIN), Ergebnis, PVersuche);
Versuche  := PVersuche^;
End;
Anmerkung: Unterer unwichtiger Teil der Methode ist abgeschnitten, also bitte nicht auf etwaiges Result etc fokussieren.

StringToPByteArray ist folgendermaßen implementiert:
Code:
Function StringToPByteArray(Nummer : AnsiString) : PByteArray;
Var AByteArray : TBytes;
    I         : Integer;

Begin
AByteArray := System.SysUtils.TEncoding.ANSI.GetBytes(Nummer);
Result := @AByteArray;
End;
Ich habe zwei Probleme damit:

1. Die Funktion liefert korrekt Rückgabewerte, aber die Authentifizierung klappt nicht trotz richtiger Zugangsdaten, was impliziert, dass etwas mit meinem übergebenen Datentypen nicht stimmt.

2. 1-2 Sekunden nach Ausführen dieser Funktion stürzt das gesamte Programm ab und schließt sich ohne weitere Warnung. Hier bin ich etwas ratlos. Debugger hält mit einer Zugriffsverletzung ganz unten im internen TControl.WndProc an und bricht dann dermaßen katastrophal zusammen ("Aktuelle Debugger-Sitzung muss beendet werden...", "Systemresourcen erschöpft"), dass ich per Task Manager die Entwicklungsumgebung killen muss. Die Funktion wird in einem Button aufgerufen. Ich hoffe stark, dass dieser Punkt mit Punkt 1 zusammenhängt.

Meine Überlegungen:
PAnsiChar für "char *" ist logisch. Bei der Längenübergabe bin ich mir aber nicht ganz sicher, ob das so in Ordnung ist.
PByteArray für "uint8_t * & length"-Konstruktionen zur Übergabe von alphanumerischen Werten in die C DLL sollte generell korrekt sein, jedoch bin ich mir auch hier bei der Längenübergabe unsicher sowie bei meiner Umwandlung eines Eingabe-Strings in ein PByteArray. Eventuell wäre hier ebenfalls PAnsiChar sinnvoller?

Offensichtlich ist der Zugriff auf eine C-DLL von Delphi aus Neuland für mich, weswegen ziemlich dämliche Fehler nicht unwahrscheinlich sind.

Ich wäre für jede Hilfestellung dankbar.

AuronTLG 4. Aug 2020 13:46

AW: DLL Schnitstelle
 
Ich bin gerade ein ganzes Stück weiter gekommen.
Das mit den Datentypen hat sich vermutlich erledigt und lag an etwas anderem. Authentifizieren hat funktioniert.
Interessant wird es hingegen beim Absturzproblem:

Ich habe den Aufruf von "AuthentifiziereBenutzer" mal zum Spaß in einen Timer gesteckt und im Button nur noch den Timer aktiviert.
In diesem Falle kommt es konsistent nicht zum Absturz, während direkt im Button es konsistent IMMER zum Absturz kommt.

Nun stellt sich für mich die große Frage, warum?

Im Debugger zumindest kam der katastrophale Fehler im Wndproc, was insofern Sinn machen würde, dass der TButton eine TWinControl ist, während der Timer eine TComponent ist.
Nur bin ich etwas ratlos, was genau überhaupt schiefgeht.

EDIT: TSpeedButton machts auch. Alles, was keine TWinControl ist, funktioniert.

himitsu 4. Aug 2020 14:28

AW: DLL Schnitstelle
 
Zitat:

Delphi-Quellcode:
Function StringToPByteArray(Nummer : AnsiString) : PByteArray;
Var AByteArray : TBytes;
    I        : Integer;

Begin
AByteArray := System.SysUtils.TEncoding.ANSI.GetBytes(Nummer);
Result := @AByteArray;
End;

Sowas kann niemals funktionieren. (und wenn doch, dann hast zufällig Glück, dass noch niemand den Speicher überschrieb)

Das dynamische Array "AByteArray" wird am Ende der Funktion freigegeben, somit zeigt der Zeiger auf alten/ungültigen Mist.



Außerdem zeigt Result garnicht auf ein Byte-Array, sondern auf die Variable, die auf ein Byte-Array zeigt.
Delphi-Quellcode:
Result := @AByteArray[0];

Aber, wie schon gesagt, sind Variable und das Array nach dem END verschwunden und der Zeiger zeigt so oder so nur auf Mist.

Lösung: Via GetMem Speicher reservieren (bei DLLs nur unter Verwendung von ShareMem), dort die Array-Daten reinkopieren, und später nicht vergessen den Speicher wieder freizugeben.
Oder VirtualAlloc, GlobalMem oder einen anderen "globalen" Speichermanager verwenden, wie z.B. den aus'm Ole32/OleAuth.

AuronTLG 4. Aug 2020 14:42

AW: DLL Schnitstelle
 
Im Prinzip habe ich festgestellt, dass die ganze Function unnötig ist.
Dank dir weiß ich nun auch im Detail, warum sie nicht funktioniert hat. :-D

Das ByteArray, was erwartet wird, ist tatsächlich einfach nur ein Array von Zahlen, weswegen das Ganze wesentlich einfacher zu lösen war... Einfach ein Bytearray füllen und den Zeiger darauf mit der passenden Länge übergeben und es läuft.

Die einzige offene Frage, die ich momentan habe, ist der Grund für das Absturzphänomen, was aber keine sonderlich quälende Frage ist, da die Lösung darin besteht, die DLL-Aufrufe nicht mit TWinControl-TButtons durchzuführen, welche ich sowieso nicht verwende.

AuronTLG 5. Aug 2020 15:29

AW: DLL Schnitstelle
 
Ein neuer Tag, ein neues Problem mit der Schnittstelle...

Ich habe folgende DLL-Methode:
Code:
se_result_t se_readLogMessage   (   uint8_t **    logMessage,
uint32_t *    logMessageLength
)      
Reads a log message that bases on the last log message parts that have been produced and processed by the Secure Element.

Parameters
[out]   logMessage   contains the last log message that the Secure Element has produced [REQUIRED] If successfully executed, the buffer has to freed by the function caller [se_free()].
[out]   logMessageLength   length of the array that represents the last log message [REQUIRED]
Das widerliche daran ist der Datentyp "uint8_t **", was meines Verständnisses nach in C die Darstellung eines Arrays von Byte-Arrays ist. Also vergleichbar mit char **, für welches in Delphi ja bereits der Typ PPAnsiChar bereitgestellt wird. Ich bekomme einen Pointer, der auf den ersten PByteArray zeigt sowie praktisch die Anzahl der PByteArrays.

Mein derzeitiger Ansatz sieht wie folgt aus:

Ich deklariere mir analog einen eigenen Typ "PPByteArray":
Code:
Type PPByteArray = ^PByteArray
Habe ich dann meinen PPByteArray "PPA" und dessen Länge erhalten, so durchlaufe ich dieses und lege die einzelnen PByteArray in einem Array of PByteArray ab:
Code:
PBArray   : Array of PByteArray;

For I := 0 to Laenge - 1
  Do Begin
     PBArray[I] := PPA^;
     Inc(PPA);
     End;
Ab diesem Zeitpunkt sollte ich theoretisch mit den PByteArrays standardmäßig verfahren können. In der Praxis kommt aber letztendlich ein Byte-Salat raus, der definitiv falsche Speicheradressen enthält.

Dementsprechend würde mich mal interessieren, ob in meinen Überlegungen bereits Schwachsinn drin steckt oder ob das tatsächlich der richtige Umgang mit dem C-Datentyp "uint8_t ** ist. Ich musste mich bisher nur sehr selten mit derlei Pointern auseinandersetzen und habe auch nie C gelernt, weswegen mir das etwas Schwierigkeiten bereitet.

himitsu 5. Aug 2020 15:54

AW: DLL Schnitstelle
 
Zitat:

uint8_t **
Sowas kenn ich auch von einem Hersteller, der intern so geile C++-Strings benutzt. (das sind wohl Objekte oder zumindestens auch sowas ähnliches wie Delphi-Strings, also ein Zeiger auf eine Struktur).

Geil ist daran, dass er eben nicht den String rausgibt, sondern einen Zeiger auf seine Variable, die dann auf den String zeigt.


Kann auch gut sein, dass du dir hier auch in deine Variable einen Zeiger reinschreiben lässt, der auf eine Variable zeigt, in der dann der String drin steht.

AuronTLG 7. Aug 2020 09:47

AW: DLL Schnitstelle
 
So ähnlich wars letztendlich auch:

Im Prinzip hat ein PByteArray als Parameter ausgereicht, aus dessem angezeigten ByteArray der zurückgegebenen Länge ich dann die Werte auslesen konnte.
PPByteArray etc war alles unnötig. Ob "uint8_t *" oder "uint8_t **" macht für Delphi keinen Unterschied.

Was ein Schwachsinn...


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:30 Uhr.
Seite 4 von 4   « Erste     234   

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