Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

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

AW: Delphi 7 DLL-Exports überladener Methoden?

  Alt 17. Sep 2023, 12:41
Es ist nicht Delphi, sondern Windows, bzw. das PE-Dateiformat (COFF).
Ja, statische DLL-Importe laufen über eine Import-Tabelle, welche aus vielen JMP+Adresse besteht,
wo Delphi erstmal mit einem CALL rein springt und von dort dann erst zum eigentlichen DLL-Export.

Und ja, wenn ich diese Adressen selbst verwalte (statt Methoden also MethodenZeiger verwende), könnte ich jeweils den einen JMP weglassen.

Aber erstmal ist die Verwendung solcher Interfaces unintuitiver und ich habe eh noch Hoffnung auf bessere Lösungsansätze.
* Direkt den originalen Quellcode neu kompilieren.
* Oder zumindestens statt der .DCU an .OBJ-Dateien zu gelangen und statt der DLL alles direkt in die EXE zu linken.

Zukünftig ist noch geplant die Einzel-Funktionen und Interfaces in Custom-Records zu verpacken.
Dann würde ich da eventuell weitere Überlegungen in Betracht ziehen, dort nochmal etwas zu optimieren, bezüglich der JUMP/CALL.
Mir ging es erstmal darum zusätzlich noch weitere/eigene JUMP/CALL dort reinzuschieben,
und auch dafür noch tausende Wrapper-Funktionen schreiben zu müssen.

(das hat sich zum Glück nun erledigt)


Prinzipiell kompiliert jetzt erstmal alles,
auch wenn ich an einigen Stellen noch rumfummeln muß.
(aktuell sind dynamische Arrays und Strings dran)
Delphi 7 DLL-Exports : /Part_II/DECMath.dpr
Delphi 2006+ Imports : /Part_II/DECMath.pas


Zitat:
Delphi-Quellcode:
procedure NSet(var A: IInteger; B: Integer); overload;
procedure NSet(var A: IInteger; B: Int64); overload;
procedure NSet(var A: IInteger; B: Extended); overload;
procedure NSet(var A: IInteger; const B: IInteger = nil; Abs: Boolean = False); overload;
procedure NAdd(var A: IInteger; B: Integer); overload;
procedure NAdd(var A: IInteger; const B: IInteger); overload;
procedure NSub(var A: IInteger; B: Integer); overload;
procedure NSub(var A: IInteger; const B: IInteger); overload;
procedure NMul(var A: IInteger; B: Integer); overload;
procedure NMul(var A: IInteger; B: Int64); overload;
procedure NMul(var A: IInteger; const B: IInteger; C: Int64); overload;
procedure NMul(var A: IInteger; const B: IInteger; C: Integer); overload;
procedure NMul(var A: IInteger; const B: IInteger); overload;
function NStr(const A: IInteger; Base: TBase = 0): RetString; overload;
function NStr(const A: IInteger; const Format: TStrFormat): RetString; overload;
NStr als MyRec.ToString
Und die anderen Methoden haben fast alle den wichtigsten Parameter als Ersten,
somit kann ich es als Methode in einen Record verschieben,
und die Methode einfach als "external" deklarieren, womit dieser Parameter zum Self wird und direkt auf die DLL verweist.
Standard-Oprtationen ala ADD, SUB und MUL, lassen sich dann zusätzlich auch schön in ClassOperator kapseln.

Die Grundfunktionen ließen sich dann also wie beim normalen Integer benutzen.


---


Es gab Anfangs sehr viele Fehler im Compilier,
siehe Antwort #16, aber das ist nun behoben.
Es handelte sich um folgene 3 Arten von Fehlern:

overloaded functions with typeless parameters
NInts.NSet(var A: IInteger; const B; Size: Integer; Bits: Integer) name 'NInts_NSet_IIBInIn',
[Fehler] DECMath.dpr(66): Es existiert keine überladene Version von 'NSet' mit dieser Parameterliste.

unnecessary overload and parameters not declared
NECBuild name 'NECBuild_XXX',
[Fehler] DECMath.dpr(625): Es existiert keine überladene Version von 'NECBuild' mit dieser Parameterliste.

not overload, but still parameters declared
NInts.NCat(var A: IInteger; const B: IIntegerArray; Bits: Integer) name 'NInts_NCat_IIIAIn',
[Fehler] DECMath.dpr(109): ',' oder ';' erwartet, aber '(' gefunden
[Fehler] DECMath.dpr(109): '=' erwartet, aber ';' gefunden
[Fehler] DECMath.dpr(109): '=' erwartet, aber ')' gefunden
[Fehler] DECMath.dpr(109): Undefinierter Bezeichner: 'name'
[Fehler] DECMath.dpr(109): Inkompatible Typen: 'Integer' und 'String'
[Fehler] DECMath.dpr(110): '=' erwartet, aber '.' gefunden
...

Erstmal habe ich ein SharedMemory eingerichtet,
womit es keine Probleme mit dynamischen Arrays und keine grundsätzlichen Probleme mit den DelphiStrings mehr geben sollte.

Es gibt nun also die nur noch zwei Wrapper-Methoden, für die untypisierten Parameter.
Ja, da könnte ich mit einem JMP in Assembler den einen Rücksprung des Wrappers einsparen,
allerdings wäre dort der Aufwand höher im Assembler die richtige überladene Methode zu treffen.

Dann gibt es eine Methode, wo ich aus einem Objekt ein Interface mache,
durch eine zurätzliche Ableitung und cast des Result als Interface,
wo sich nicht viel optimieren lässt. bzw. es ist bestimmt den Aufwand nicht wert.

function Primes: TSmallPrimeSieve;
wurde zu
Delphi-Quellcode:
type
  ISmallPrimeSieve = interface
    ['{897D56D7-7514-4473-917E-3DEFCD9A54E3}']
    function MinPrime: Cardinal; // min. Prime, allways 2
    function MaxPrime: Cardinal; // max. possible Prime, dependend from Compiler Version
    function MinIndex: Cardinal; // min. Index, allways 1
    function MaxIndex: Cardinal; // max. Index, see MaxPrime

    function Count(LowerBound, UpperBound: Cardinal): Cardinal; // compute Primecount beetwen Bounds
    function IndexOf(Value: Cardinal; LowerBound: Boolean{$IFDEF VER_D4H} = False{$ENDIF}): Cardinal; // give Primeindex of Value

    procedure LoadCache(const FileName: AnsiString); // load a Prime Cache
    procedure BuildCache(const FileName: AnsiString; Bound: Cardinal); // create and save a Cache

    function GetPrime(Index: Cardinal): Cardinal;
    property Prime[Index: Cardinal]: Cardinal read GetPrime; default; // return Prime with Index

    function CacheMaxPrime: Cardinal; // max. cached Prime
    function CacheMaxIndex: Cardinal; // max. cached Index of max. Prime
                                      // cached min. Values are allways equal to MinPrime, MinIndex
  end;
  TSmallPrimeSieveIntf = class(TSmallPrimeSieve, ISmallPrimeSieve)
    function MinPrime: Cardinal;
    function MaxPrime: Cardinal;
    function MinIndex: Cardinal;
    function MaxIndex: Cardinal;

    function GetPrime(Index: Cardinal): Cardinal;
    function CacheMaxPrime: Cardinal;
    function CacheMaxIndex: Cardinal;
  end;

function PrimesIntf: ISmallPrimeSieve;
begin
  Result := Primes as ISmallPrimeSieve;
end;

exports
  PrimesIntf name 'Prime_Primes',
  IsPrime name 'Prime_IsPrime';
(natürlich fehlt noch der Hook, um die Klassen auszutauschen, damit beim Create in der DCU der richtige Typ erzeugt wird)

Da sich seit Delphi 2009 die AnsiString intern verändert haben, muß ich dort unbedingt noch etwas anpassen.
Jenes bezieht sich aber nur auf die wenigen function NStr(x: Interface): AnsiString; .

Delphi 7:
Delphi-Quellcode:
type
  PStrRec = ^StrRec;
  StrRec = packed record
    refCnt: Longint;
    length: Longint;
  end;
Delphi 11.3
Delphi-Quellcode:
type
  // For System.pas internal use only.
  // Note, this type is duplicated in getmem.inc for diagnostic purposes. Keep in sync.
  PStrRec = ^StrRec;
  StrRec = packed record
    {$IF defined(CPU64BITS)}
    _Padding: Integer; // Make 16 byte align for payload..
    {$ENDIF}
    codePage: Word;
    elemSize: Word;
    refCnt: Integer;
    length: Integer;
  end;
Const-Parameter in die DLL rein, stellen nahezu keine Probleme dar,
da die zusätzlichen Felder für das alte Delphi praktisch unsichtbar/irrelevant sind.

Aber als Funktion-Result sieht das anders aus.
Da hätte ich die Wahl es via Wrapper auf WideString zu casten.
Allerdings werde ich hier das Delphi-Referenz durchsuchenSetString des neuen Delphi in die DLL durchreichen
und dort aus dem Delphi7-AnsiString in NeuesDelphi-AnsiString erzeugen.
Denn das neue Delphi würde knallen, wenn es auf die nicht-vorhandenen Felder eines "alten" String zugreifen will -> BufferOverflow.
Außerdem stimmt die Brechnung StringPointer->MemoryBlock nicht überein und es würde spätestens neuen Delphi beim Freigeben des String in LStrClr->FreeMem knallen.


Es gibt aber noch ein paar weitere Hindernisse, mit anderen zu teilenden Dingen.
Exception-Klassen und andere übergebene Klassen und Objekte, wie z.B. TStream.
Siehe meine "TODO" bzw. Shared-Liste in \Part_II\DECMath.Shared.txt.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu (17. Sep 2023 um 12:54 Uhr)
  Mit Zitat antworten Zitat