Delphi-PRAXiS

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)

hewy 10. Jul 2020 14:45


DLL Schnitstelle
 
Hallo,

Seit mehreren Tagen brüte ich nun schon über folgendes:
Als nicht DLL und auch nicht C/C++ Spezi habe ich hier eine Frage zum Aufruf einer DLL Funktion aus Delphi.
Hier die ‘c’ Seite:

Code:
SE_API_IMPL(se_result_t, se_getCertificationId)(char **certificationId,   uint32_t *certificationIdLength)
Wie würde das Delphi Gegenstück dazu aussehen?

Die DLL function spreche ich in Delphi so an
Delphi-Quellcode:
function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';
Sicher ein Klacks komme aber hier nicht weiter und wäre daher sehr dankbar für eine Hint oder besser
Code:
In VB.Net sieht es so aus. Brauche aber das Delphi äquivalent.
        ' Get certification ID as assigned by BSI ("BSI-K-TR-0374-2019" for cryptovision TSE).
        ' @param[out] certificationId      Returned identifier string (Not null terminated) [REQUIRED]
        '                                  If successfully executed, the buffer has to freed by the function caller [@ref se_free()].
        ' @param[out] certificationIdLength length of the returned identifier string [REQUIRED]
        ' @return @ref ExecutionOk         execution of the function has been successful
        ' @return @ref ErrorSECommunicationFailed Secure Element communication failed
        ' @since v2.1
        ' se_result_t se_getCertificationId(char**certificationId, uint32_t*certificationIdLength);
        <DllImport("se-api-c.dll", EntryPoint:="se_getCertificationId", CharSet:=CharSet.Ansi)>
        Private Function GetCertificationIdSub(ByRef certificationId As IntPtr,
                                               ByRef certificationIdLength As Integer) As SeReturnCode
        End Function
        Public Structure GetCertificationIdReturn
            Public ReturnCode As SeReturnCode
            Public certificationId As String
        End Structure
        Public Function GetCertificationId()
            Dim ReturnStruct As GetCertificationIdReturn
            Dim certificationId As IntPtr
            Dim certificationIdLength As Integer
            ReturnStruct.ReturnCode = GetCertificationIdSub(certificationId, certificationIdLength)
            If SeReturnCode.ExecutionOk = ReturnStruct.ReturnCode Then
                ReturnStruct.certificationId = PtrToStringAnsiAndFree(certificationId, certificationIdLength)
            Else
                ReturnStruct.certificationId = String.Empty
            End If
            Return ReturnStruct
        End Function
Vielen Dank für jegliche Hilfe
Heiri

TiGü 10. Jul 2020 15:01

AW: DLL Schnitstelle
 
Code:
SE_API_IMPL(se_result_t, se_getCertificationId)(char **certificationId, uint32_t *certificationIdLength)
-> declare se_getCertificationId as function (pointer to pointer to char, pointer to uint32_t) returning se_result_t
Delphi-Quellcode:
type
  se_result_t = Cardinal; //? musst du in der Doku gucken was se_result_t sein soll
  PUInt32 = ^System.UInt32;
  PPByte = ^System.PByte;


function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: PUInt32): se_result_t ; cdecl; external 'se-api-c.dll';
//oder
function se_getCertificationId(var certificationId: PAnsiChar; var certificationIdLength: UInt32): se_result_t ; cdecl; external 'se-api-c.dll';
// probiere auch mal so, wenn certificationID eh ne Zahl zwischen 0 und 255 laut Doku ist:
function se_getCertificationId(var certificationId: PByte; var certificationIdLength: UInt32): se_result_t ; cdecl; external 'se-api-c.dll';
// gerne auch
function se_getCertificationId(certificationId: PPByte; certificationIdLength: PUInt32): se_result_t ; cdecl; external 'se-api-c.dll';

hewy 10. Jul 2020 15:30

AW: DLL Schnitstelle
 
Oh vielen Dank für deine Antwort.
- se_result_t enthält ein Fehler Code falls es nicht korrekt durch läuft 0. Bei mir kommt immer 'fehlender Parameter'
- var certificationId: PAnsiChar sollte die gesuchte ID zurückliefern bei der VB Version bekomme ich das hier 'BSI-K-TR-0000-2019' bei Delphi eben noch nicht! Humm
Heiri

TiGü 10. Jul 2020 15:46

AW: DLL Schnitstelle
 
Zeig doch mal deinen Quelltext, wo du die Funktion aufrufst.
Hast du alle beide PAnsiChar-Varianten mal durchprobiert?

EDIT: Ändere mal bei se_result_t von Cardinal zu einen Int32.
Da du nicht die Definition aus dem C-Header gepostet hast, nehme ich einfach mal an, dass da ggf. auch negative Werte rausfallen können?!

himitsu 10. Jul 2020 15:53

AW: DLL Schnitstelle
 
Sicher, dass es cdecl oder nicht vielleicht stdcall ist?

Wie sieht denn SE_API_IMPL aus?




[OT]
Hach, wie schön es bald mit 64 Bit wird ... da gibt's nur noch eine Convention (die fast wie bei unserem Pascal aussieht :stupid:)

hewy 11. Jul 2020 10:21

AW: DLL Schnitstelle
 
Vielen Dank für eure Hilfe versuche.

Jetzt habe ich auch begriffen wie man hier korrekt Code schnipsel einfügt!
Insgesammt sind es über 50 Funktionen, die meisten sind ok nur 'se_getCertificationId' will nicht so recht.
So sehen die Einbindungen der DLL Funktionen aus:
Delphi-Quellcode:
function se_getImplementationVersionString(): PAnsiChar; cdecl; external 'se-api-c.dll';
function se_getImplementationVersion(): PAnsiChar; cdecl; external 'se-api-c.dll';
function se_getUniqueId(): PAnsiChar; cdecl; external 'se-api-c.dll';

function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';

function se_getLifeCycleState(var lcs: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';
function se_getMaxNumberOfClients(var maxNumberClients: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';
Und hier die Aufrufe dazu:
Delphi-Quellcode:
function SeGetPinStatus(var PINState: Cardinal): TSeReturnCode;
function SeGetUniqueId: string;

function SeGetCertificationId(var CertificationId: string): TSeReturnCode;

function SeAuthenticateUser(UserId: string; PIN: array of Byte; var AuthenticationResult: TSeAuthenticationResult; var RemainingRetrie: ShortInt): TSeReturnCode;
function SeGetLifeCycleState(var lcs: TSeLifeCycleState): TSeReturnCode;
Und die Implementation dazu (Hier mache ich eiregend etwas falsch und komme nicht dahinter was wie gemacht werden muss.)

Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: string): TSeReturnCode;
var
  PCertificationID: PAnsiChar;
  PCurrent: PAnsiChar;
  CertificationIDLength: Cardinal;
  i: Cardinal;
begin
  PCertificationID := nil;

  // VB übergibt PCertificationID = &H000000000 und CertificationIDLength = 0

  Result := TSeReturnCode(se_getCertificationID(PCertificationID, CertificationIDLength));

  // Zurück bekomme ich im VB Code BSI-K-TR-0000-2019 für PCertificationID und 18 für CertificationIDLength

  // Mit Delphi und der Zeile oben einen Fehlercode

  //    Da werde ich die Parameter entsprechend übernehme sobalt
  //    mir se_getCertificationID was brauchbares liefert.
  //    Aktuell erhalte ich hier Access Violation oder der Result code sagt
  //    etwas von 'missing parameter'

end;
Vielen Dank schon mal für die Auswertung meiner Angaben und etwelche wie was wo zu tun ist Hints!

venice2 11. Jul 2020 10:46

AW: DLL Schnitstelle
 
Das.
Delphi-Quellcode:
function se_getCertificationId(certificationId: PPByte; certificationIdLength: PUInt32): se_result_t ; cdecl; external 'se-api-c.dll';


kann nur ins leere laufen denn ByRef ist immer var in Delphi ByVal ist einfach ein Parameter der übergeben wird.
Und dann was denn nun?

Delphi-Quellcode:
PCertificationID: PAnsiChar;
oder
Delphi-Quellcode:
CertificationId: string
beides geht auf keinen Fall wenn schon dann
Delphi-Quellcode:
PCertificationID: PAnsiChar;
entfernen und Rückgabe ist
Delphi-Quellcode:
CertificationId: string

Wenn CertificationId ein Pointer auf AnsiString ist (siehe PtrToStringAnsiAndFree) dann bitte
Delphi-Quellcode:
CertificationID: PAnsiChar
und nicht
Delphi-Quellcode:
CertificationID: string
.

Alles durcheinander geworfen sorry.
Siehe

Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: string): TSeReturnCode;

und
Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: PAnsiChar): TSeReturnCode;


Die Definition
Delphi-Quellcode:
PCertificationID: PAnsiChar;

ist dann unsinnig.

Korrekt wäre das ohne Gewähr.

Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: PAnsiChar): TSeReturnCode;
var
  // PCertificationID: PAnsiChar; // Quatsch
  // PCurrent: PAnsiChar; wird nicht verwendet
  CertificationIDLength: Cardinal;
  // i: Cardinal; wird nicht verwendet
begin
  // PCertificationID := nil; Quatsch

  // VB übergibt PCertificationID = &H000000000 und CertificationIDLength = 0

  Result := TSeReturnCode(se_getCertificationID(CertificationID, CertificationIDLength));

  // Zurück bekomme ich im VB Code BSI-K-TR-0000-2019 für PCertificationID und 18 für CertificationIDLength

  // Mit Delphi und der Zeile oben einen Fehlercode

  // Da werde ich die Parameter entsprechend übernehme sobalt
  // mir se_getCertificationID was brauchbares liefert.
  // Aktuell erhalte ich hier Access Violation oder der Result code sagt
  // etwas von 'missing parameter'

end;
Nochmal
Delphi-Quellcode:
var certificationId: PAnsiChar
oder
Delphi-Quellcode:
var certificationId: string
entscheide dich einfach mal.

Delphi-Quellcode:
function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';

himitsu 11. Jul 2020 11:20

AW: DLL Schnitstelle
 
Das PAnsiChar <> String UnicodeString (seit Delphi 2009) wollte ich auch grade ansprechen.

Zitat:

Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: string): TSeReturnCode;
var
  PCertificationID: PAnsiChar;
  PCurrent: PAnsiChar;
  CertificationIDLength: Cardinal;
  i: Cardinal;
begin
  PCertificationID := nil;

  // VB übergibt PCertificationID = &H000000000 und CertificationIDLength = 0

  Result := TSeReturnCode(se_getCertificationID(PCertificationID, CertificationIDLength));
  ...

Der Code ist so nichtmal kompilierbar.
Wieso ignoriert hier jemand die Compilerwarnungen?
Und wenn VB das macht, warum du nicht auch?

CertificationIDLength wurde nicht initialisiert
PCertificationID = string -> PAnsiChar

Und wenn VB das macht, dann vermutlich um erstmal im Result die nötige Länge abzufragen, welche für einen weiteren Aufruf benutzt wird.

Man kann alternativ auch blind den Speicher auf eine Länge festlegen, die "immer" ausreichend wäre,
und anschließend auf das zurück ändern, was wirklich benutzt wurde.

Probleme:
* falsche Typen (ANSI <> Unicode)
* falsche Benutzung (ungültige Parameter)

Entweder intern mit ANSI arbeiten und umkopieren oder die umgebende Funktion mit einem AnsiString bauen.

hewy 11. Jul 2020 11:28

AW: DLL Schnitstelle
 
Vielen Dank venice2,

hilft mir schon mal weiter.
Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: string): TSeReturnCode;
var
  PCertificationID: PAnsiChar;
  PCurrent: PAnsiChar;
  CertificationIDLength: Cardinal;
  i: Cardinal;
begin
  PCertificationID := nil;

  // VB übergibt PCertificationID = &H000000000 und CertificationIDLength = 0

  Result := TSeReturnCode(se_getCertificationID(PCertificationID, CertificationIDLength));

  // Zurück bekomme ich im VB Code BSI-K-TR-0000-2019 für PCertificationID und 18 für CertificationIDLength

  // Der var Parameter im Header der Funktion sollte für meine Zwecke schon [B]string[/B] sein

  // Dann müsste ich halt dies entsprechend konvertieren.
  // Zum Beispiel so
  CertificationId := String(AnsiString(PCertificationID)); // Oder sehe ich das falsch?

  // Nur eben die Funktion se_getCertificationID liefert nichts brauchbares in Delphi
  // Also müsste wohl PCertificationID: PAnsiChar; anders definiert sein oder anders
  // initialisiert werden, [B]da liegt offenbar mein Problem. [/B]

end;

venice2 11. Jul 2020 11:32

AW: DLL Schnitstelle
 
Ich würde den Kram so umschreiben wenn für CertificationIDLength keine Rückgabe erforderlich ist.

Zitat:

CertificationIDLength wurde nicht initialisiert
PCertificationID = string -> PAnsiChar
Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: PAnsiChar; CertificationIDLength: Cardinal): TSeReturnCode;
Delphi-Quellcode:
if SeGetCertificationId(CertificationId, 0) = ExecutionOk then
  bla, bla
EDIT:
Delphi-Quellcode:
CertificationId := String(AnsiString(PCertificationID)); // Oder sehe ich das falsch?

Ja. Du benötigst die Variable PCertificationID nicht!
Die Rückgabe ist ein Pointer auf Ansistring (PAnsiChar) siehe (PtrToStringAnsiAndFree) kein String!

hewy 11. Jul 2020 11:44

AW: DLL Schnitstelle
 
Danke vorerst mal an alle muss es mal ausprobieren die DLL Spricht mit einer Hardware, brauche für den Testaufbau zu aktivier etwas Zeit.

@Himitsu: Lässt sich so wie dargestellt Fehlerfrei Kompilieren die Initialisierung CertificationIDLength := 0; löst das Problem nicht hatte ich auch schon ausprobiert.

Der Vollständige VB Aufruf der einwandfrei funktioniert ist in meinem initial Post enthalten.
Dort wird Length auch nicht weitergereicht.

Melde mich dann wieder.

venice2 11. Jul 2020 11:52

AW: DLL Schnitstelle
 
Zitat:

Zitat von hewy (Beitrag 1469358)
Dort wird Length auch nicht weitergereicht.

Nein?
Irgendwie wiedersprüchlich.
Zitat:

// VB übergibt PCertificationID = &H000000000 und CertificationIDLength = 0
Ich weis nicht wie oft du mit Delphi bisher gearbeitet hast.
Aber diese Zeile ist unnötig
Delphi-Quellcode:
CertificationId := String(AnsiString(PCertificationID)); // Oder sehe ich das falsch?


Die Rückgabe der CertificationId ist schon in der Abfrage enthalten
Delphi-Quellcode:
Result := TSeReturnCode(se_getCertificationID(CertificationID, CertificationIDLength));


Denn CertificationID ist ein var parameter.

Wenn du die Funktion so aufrufst
Delphi-Quellcode:
if SeGetCertificationId(CertificationId, 0) = ExecutionOk then


dann befindet sich das Ergebnis in CertificationId.
Ok bin raus.

hewy 11. Jul 2020 13:00

AW: DLL Schnitstelle
 
Also die Ganze kette steht, so dass ich aktiv testen kann. Und ich habe Venice2 Vorschlag ausprobiert.

Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: string): TSeReturnCode;
var
  PCertificationID: PAnsiChar;
  CertificationIDLength: Cardinal;
begin
  PCertificationID := nil;
  CertificationIDLength:= 0;
    // ################ Das kann Kompiliert werden Mein bisheriger Code!
  Result := TSeReturnCode(se_getCertificationID(PCertificationID, CertificationIDLength));
    // ////////////////// Das lässt sich so nicht kompilieren Venice2 Vorschlag! 
  Result := TSeReturnCode(se_getCertificationID(CertificationID, CertificationIDLength));
    Ergibt: [DCC Fehler] SeApi.pas(545): E2033 Die Typen der tatsächlichen und formalen Var-Parameter müssen übereinstimmen
Den folgendes wird aufgerufen:
Code:
  function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';
Übrigens: >>> Ich weis nicht wie oft du mit Delphi bisher gearbeitet hast. <<< Sei doch froh dass sich jemand in Delphi weiter entwinkeln versucht,
und unterstürze die noch nicht experten das verhilft Delphi zum weiteren überleben.

Mein Problem ist damit leider noch nicht gelöst. Will ja keinem auf den Wecker gehen brauche lediglich eine Lösung auf die ich nicht selber komme.

Danke
Heiri

venice2 11. Jul 2020 13:09

AW: DLL Schnitstelle
 
Zitat:

Übrigens: >>> Ich weis nicht wie oft du mit Delphi bisher gearbeitet hast. <<< Sei doch froh dass sich jemand in Delphi weiter entwinkeln versucht,
und unterstürze die noch nicht experten das verhilft Delphi zum weiteren überleben.
Entschuldige aber du hast es immer noch nicht verstanden.

Delphi-Quellcode:
function SeGetCertificationId(var CertificationId: PAnsiChar): TSeReturnCode;
var
  //PCertificationID: PAnsiChar; nicht nötig
  CertificationIDLength: Cardinal;
begin
  // PCertificationID := nil; nicht nötig
  CertificationIDLength:= 0;

  Result := TSeReturnCode(se_getCertificationID(CertificationId, CertificationIDLength));
end;
Vergleiche meinen und deinen Code.
Die Rückgabe ist PAnsiChar und nicht string. siehe (Wie oft habe ich das jetzt schon gesagt? )

Zitat:

Den folgendes wird aufgerufen:
function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';

Zitat:

// ////////////////// Das lässt sich so nicht kompilieren Venice2 Vorschlag!
Schon wenn man es richtig macht.
Ich sagte PAnsiChar aber du hast immer noch den String in deiner Funktion.
Und dieser Type muss gleich sein bei einem var Parameter.

Daniel 11. Jul 2020 13:21

AW: DLL Schnitstelle
 
Ja, und das geht bestimmt auch etwas weniger schroff, oder?

himitsu 11. Jul 2020 13:22

AW: DLL Schnitstelle
 
bei APIs die man vorher nach der Länge fragt, entsprechend dem vom VB
Delphi-Quellcode:
var S: AnsiString;

S := '';
x := API(nil, 0);
Check(x);
SetLength(S, x);
Check(API(PAnsiChar(S), x));
Result := S;
oder eben nicht fragen, sondern einfach machen
Delphi-Quellcode:
var S: AnsiString; // oder array[0..MAX_LENGTH-1] of AnsiChar;

SetLength(S, MAX_LENGTH);
x := API(PAnsiChar(S), {Length(S)}MAX_LENGTH);
Check(x);
SetLength(S, x);
Result := S;
ich empfehle mal die Dokumentation von der Funktion zu lesen,
oder hier mal von Anderen, die auch sowas machen.
https://docs.microsoft.com/en-us/win...getwindowtexta
https://docs.microsoft.com/en-us/win...odulefilenamea

venice2 11. Jul 2020 13:23

AW: DLL Schnitstelle
 
Zitat:

Zitat von Daniel (Beitrag 1469365)
Ja, und das geht bestimmt auch etwas weniger schroff, oder?

Ich habe ihn wie oft darauf hingewiesen?
Sein Unverständnis dann auf mich abzuwälzen ist weniger schroff ?

hewy 11. Jul 2020 13:34

AW: DLL Schnitstelle
 
Also hier nochmal den ganzen Zusammenhang vielleicht habe ich mich auch nicht wirklich klar ausgedrückt.
Danke dass Ihr nochmals versuchst mich auf den Richtigen Weg zu führen!

Code:
/* 1: ---------- Das ist der C Code in der DLL ------------------------------------------------------- */
SE_API_IMPL(se_result_t, se_getCertificationId)(char **certificationId,
   uint32_t *certificationIdLength)
{
   SE_EXPORT_METHOD;

   return se_getCertificationIdEx(&g_se_ctx, certificationId, certificationIdLength);
}


/* 2: ---------- Das mein Delphi Aufruf (Schnittstelle) der DLL Funktion --------------------------- */
function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';

/* 3: ---------- Das mein Delphi Aufruf auf der DLL Schnittstellen funktion ------------------------ */
function SeGetCertificationId(var CertificationId: PAnsiChar): TSeReturnCode;
var
  CertificationIDLength: Cardinal;
begin
  CertificationIDLength:= 0;
  Result := TSeReturnCode(se_getCertificationID(CertificationID, CertificationIDLength));
     
     // Und hier ist CertificationID =nil

  if PCertificationID<>nil then
  begin
        ...
   ..
   .
VB Verwendet die gleiche DLL und bekommt einen Pointer und kann den Wert (CertificationID) abholen.
Mein Delphi Code nicht??
Also ist möglicherweise im oberen Bereich /* 2: ---- oder /* 3: ---- etwas falsch.

DeddyH 11. Jul 2020 13:40

AW: DLL Schnitstelle
 
Entweder im Vorfeld einen ausreichend großen Puffer reservieren und dessen Länge übergeben oder zunächst die benötigte Länge zurückgeben lassen, entsprechenden Speicher reservieren und diesen samt seiner tatsächlichen Länge übergeben. Das erfordert dann 2 Aufrufe.

venice2 11. Jul 2020 14:04

AW: DLL Schnitstelle
 
Ich glaube du meinst.

Delphi-Quellcode:
  // Und hier ist CertificationID =nil

  if CertificationID <> nil then
  begin
        //...
  end
PCertificationID gibt es nicht.

himitsu 11. Jul 2020 14:06

AW: DLL Schnitstelle
 
Zitat:

VB Verwendet die gleiche DLL und bekommt einen Pointer und kann den Wert (CertificationID) abholen.
Mein Delphi Code nicht??
Also ist möglicherweise im oberen Bereich /* 2: ---- oder /* 3: ---- etwas falsch.
siehe #19 und #16 ... k.A. was man da sonst noch sagen soll.

Das sind allgemeine Grundlagen, die fast überall gleich sind, weil Viele es so machen.


Ich hoffe nur jemand macht sein teures Wormlaufwerk nicht kaputt.
Quelle: https://xbaseforum.de/viewtopic.php?p=132766

hewy 11. Jul 2020 14:09

AW: DLL Schnitstelle
 
Ja richtig danke für den Hinweis!
Sollte heissen:
if CertificationID <> nil then
begin

DeddyH 11. Jul 2020 14:16

AW: DLL Schnitstelle
 
Einmal grundsätzlich: man übergibt 2 Parameter an die Funktion, die auch beide von der DLL überschrieben werden, Rückgabe ist ein Fehlercode. Daraus folgt, dass man auch beide Parameter als Var-Parameter deklarieren muss. Ich habe die Cryptovision-API gerade nicht zur Hand, aber mit ziemlicher Sicherheit gibt die Funktion im 2. Parameter entweder die Anzahl der tatsächlich geschriebenen Bytes des 1. Parameters zurück, oder falls dieser zu knapp dimensioniert ist, die Anzahl der benötigten Bytes. Man kann nun also die benötigten Bytes ermitteln, indem man die Funktion mit den Werten nil für den PAnsiChar und 0 für die Länge übergibt. Anschließend wertet man die zurückgegebene Länge (deshalb Var-Parameter) aus, reserviert entsprechenden Speicher und übergibt diesen dann wie vorhin schon einmal erwähnt.

hewy 11. Jul 2020 14:24

AW: DLL Schnitstelle
 
Heiii danke DeddyH das war das Problem.

function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';
Geändert zu:
function se_getCertificationId(var certificationId: PAnsiChar; var certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';

Und schon funzt alles.
Viele Dank. Auch an alle Andern die an meiner 30 Jährigen Delphi Erfahrung gezweifelt haben.

Heiri

DeddyH 11. Jul 2020 14:29

AW: DLL Schnitstelle
 
Ja, immer diese dummen Sternchen in C, die kann man leicht übersehen :wink:

venice2 11. Jul 2020 14:32

AW: DLL Schnitstelle
 
Zitat:

Zitat von DeddyH (Beitrag 1469377)
Ja, immer diese dummen Sternchen in C, die kann man leicht übersehen :wink:

Zitat:

Viele Dank. Auch an alle Andern die an meiner 30 Jährigen Delphi Erfahrung gezweifelt haben.
Was für ein Ärger aber auch das String nicht gleich PAnsiChar ist.
Wie dem auch sei.

DeddyH 11. Jul 2020 14:33

AW: DLL Schnitstelle
 
Was willst Du mir mit diesem Kommentar mitteilen?

venice2 11. Jul 2020 14:34

AW: DLL Schnitstelle
 
Zitat:

Zitat von DeddyH (Beitrag 1469379)
Was willst Du mir mit diesem Kommentar mitteilen?

Dir nichts. ;) bzw. Im bezug auf die Sternchen die nun mal keinen String repräsentieren.

himitsu 11. Jul 2020 14:50

AW: DLL Schnitstelle
 
Zitat:

Was für ein Ärger aber auch das String nicht gleich PAnsiChar ist.
String ist PChar und da das ein compilerabhängeriger Typ ist, kann es auch passieren, dass im Jahr 2009 das von ANSI zu Unicode wechselte.

String/PChar = AnsiString/PAnsiChar bis Delphi 2007
String/PChar = UnicodeString/PWideChar seit Delphi 2009

und nicht zu verwechseln mit WideString, welches kein Delphi-Typ ist, sondern nur die Kapselung der OLE32-String-API.

TiGü 12. Jul 2020 12:43

AW: DLL Schnitstelle
 
Zitat:

Zitat von hewy (Beitrag 1469376)
Heiii danke DeddyH das war das Problem.

function se_getCertificationId(var certificationId: PAnsiChar; certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';
Geändert zu:
function se_getCertificationId(var certificationId: PAnsiChar; var certificationIdLength: Cardinal): Cardinal; cdecl; external 'se-api-c.dll';

Und schon funzt alles.
Viele Dank. Auch an alle Andern die an meiner 30 Jährigen Delphi Erfahrung gezweifelt haben.

Heiri

Meine Güte, da guckt man auf'n Sonntagmittag in den Thread, sieht ein Haufen Im-Kreis-Gewixe um unwichtige Details und Nichtigkeiten, nur um dann festzustellen, dass die RICHTIGE Lösung aus Beitrag Nummer ZWEI (Hinweis: Translation Nr. 2) schließlich auf der DRITTEN Seite von jemand anderen nochmal gefunden/kopiert wurde.

Hewy, dreißig Jahre Delphi-Erfahrung sind zwar gut und schön, aber mit dreißig Sekunden erhöhtem Leseverständnisses und/oder stumpfen Ausprobieren meiner Vorschläge hätte viel Zeit und Nerven aller Beteiligten gespart werden können.

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 15:10 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