![]() |
Delphi-Version: XE5
AnsiStringArray aus DLL an Excel-VBA
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Community,
ich möchte Euch erneut um Hilfe bitten! Bei folgender Aufgabenstellung komme ich leider nicht weiter: Aus einer Delphi-DLL möchte ich einen StringVektor an Excels VBA übergeben. Dazu habe ich eine funktionierende Lösung entwickelt, die folgendermaßen aussieht: Delphi XE5 Professional Zielplattform: 32-Bit-DLL Excel: 2016
Delphi-Quellcode:
Auf Excels Seite sieht der Code wie folgt aus:
library Delphi_String_for_Excel_Test;
uses System.ShareMem, System.SysUtils, System.Classes, System.AnsiStrings ; {$R *.res} Type ExcelLongInt = Int32; // {4 Bytes} CONST AnsiString_Vektor_GLOBAL: Array [0..3] of AnsiString = ('1: Erster String', '2: Das ist der ZWEITE String!', '3: ...und hier ist der DRITTE!', '4: Schlußlicht-String...'); Procedure AnsiString_To_Excel(S: PAnsiChar; i: ExcelLongInt); StdCall; // Aufgabe -> Einen Delphi-AnsiString-Vektor ZEILENWEISE an Excel-VBA übertragen // S: PAnsiChar : zu übertragender Strings // i: ExcelLongInt : der i-te String im globalen Vektor VAR P: AnsiString; Begin P:= AnsiString_Vektor_GLOBAL[i]; // Rückgabewert System.AnsiStrings.StrPLCopy(S, P, Length(P)); // Excels String-Array wird überschrieben End;{AnsiString_To_Excel} {-----------------------} Exports AnsiString_To_Excel ; begin end. VBA-Code:
Delphi-Quellcode:
Der Schönheitsfehler dieser Lösung besteht darin, daß ich die Strings des StringVektors ZEILENWEISE einlese, d.h. von VBA aus wird die DLL-Routine AnsiString_To_Excel n-mal aufgerufen.
Option Explicit
Private Declare Sub AnsiString_To_Excel Lib "Delphi_String_for_Excel_Test.dll" _ (ByVal A As String, ByVal i As Long) ' Es funktioniert! Public Function GetDelphiStrings(Anzahl As Long) ' Zeilenweiser Aufruf der DLL-Routine AnsiString_To_Excel ' Es funktioniert! Dim Vektor() As String Dim NULStr As String Dim i As Long i = 1 'Wichtig: sonst kommen u.U. sinnlos hohe Werte beim Start und der Vektor in der DLL steigt aus: RangeCheckError! ReDim Vektor(1 To Anzahl, 1 To 1) ' SpaltenVektor anpassen NULStr = String(100, vbNullChar) 'Excel MUSS den Speicherbereich zur Verfügung stellen: in der DLL wird dieser nur gepatcht! For i = 1 To Anzahl Vektor(i, 1) = NULStr AnsiString_To_Excel Vektor(i, 1), i - 1 ' !!! -1: Delphi: NULL-basiert! Next GetDelphiStrings = Vektor End Function Meine Idee für eine Verbesserung sieht folgendermaßen aus: VBA-Code:
Delphi-Quellcode:
Und die DLL habe ich um Folgendes erweitert:
Private Declare Sub AnsiStringVektor_To_Excel Lib "Delphi_String_for_Excel_Test.dll" _
(ByVal A As String, ByVal n_Anzahl As Long) ' Es funktioniert NICHT! Public Function GetDelphiStringVektor(Anzahl As Long) ' EINMALIGER Aufruf der DLL-Routine AnsiStringVektor_To_Excel ' Es funktioniert! Dim Vektor() As String Dim NULStr As String Dim i As Long i = 1 'Wichtig: sonst kommen u.U. sinnlos hohe Werte beim Start und der Vektor in der DLL steigt aus: RangeCheckError! ReDim Vektor(1 To Anzahl, 1 To 1) ' SpaltenVektor anpassen NULStr = String(100, vbNullChar) 'Excel MUSS den Speicherbereich zur Verfügung stellen: in der DLL wird dieser nur gepatcht! For i = 1 To Anzahl Vektor(i, 1) = NULStr Next AnsiString_To_Excel Vektor(1, 1), Anzahl GetDelphiStringVektor = Vektor End Function
Delphi-Quellcode:
Leider erhalte ich aus der Procedure AnsiStringVektor_To_Excel stets "Access violation".
Procedure AnsiStringVektor_To_Excel(S: PAnsiChar; n_Anzahl: ExcelLongInt); StdCall;
// Aufgabe -> Einen Delphi-AnsiString-Vektor auf EINMAL an Excel-VBA übertragen // S: PAnsiChar : Erstes Element des zu zu übertragenden StringVektors // n_Anzahl: ExcelLongInt : Gesamtzahl der Strings im StringVektor VAR Vektor: Array of PAnsiChar ABSOLUTE S; P : AnsiString; i : Integer; Begin For i:= 1 To n_Anzahl Do // Excel: 1-basiertes Array Begin P:= AnsiString_Vektor_Global[i-1]; // -1: Delphi: 0-basiertes Array System.AnsiStrings.StrPLCopy(Vektor[i], P, Length(P)); // --> Access violation // System.AnsiStrings.StrCopy(PAnsiChar(Vektor[i]), PAnsiChar(P)); // --> Access violation End; End;{AnsiStringVektor_To_Excel} {-----------------------------} ... Exports ... , AnsiStringVektor_To_Excel Im gezippten Anhang befinden sich folgende dre einsatzferige Dateien: - Delphi_String_for_Excel_Test.dpr - Delphi_String_for_Excel_Test.dll - Delphi_String_for_Excel_Test.xlsm (s. Benutzungshinweise) Was mache ich falsch? Geht es überhaupt durch nur EINEN EINZIGEN Aufruf oder nur ZEILENWEISE? Vielen Dank für Eure Hilfe im Voraus! Gruß Andreas |
AW: AnsiStringArray aus DLL an Excel-VBA
Hallo,
übergib doch die Strings auf einmal mit einem definierten Trenner (#9 z.B.) und drösel dir das in Excel wieder auseinander. Das ABSOLUTE zerschießt dir hier bestimmt den Speicher. Du musst für S speicher reservieren. |
AW: AnsiStringArray aus DLL an Excel-VBA
Na ja zu testen ist da nix..
Wäre vielleicht sinnvoll wenn du auch den VBA Quelltext hochladen würdest. Danach könnte ich mir das mal anschauen. Habe keine Lust das alles selbst von Hand zu machen. Zitat:
Und ja ein Trenner wäre hier am sinnvollsten mache ich auch nicht anders. Helfen könnte dann so etwas..
Code:
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" ( _
ByVal lpString As Long _ ) As Long Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ ByRef Destination As Any, _ ByRef Source As Any, _ ByVal length As Long _ )
Code:
Bsp.
Public Function VBStrFromAnsiPtr(ByVal lpStr As Long) As String
Dim bStr() As Byte Dim cChars As Long On Error Resume Next ' Get the number of characters in the buffer cChars = lstrlen(lpStr) If cChars Then ' Resize the byte array ReDim bStr(0 To cChars - 1) As Byte ' Grab the ANSI buffer Call CopyMemory(bStr(0), ByVal lpStr, cChars) End If ' Now convert to a VB Unicode string VBStrFromAnsiPtr = StrConv(bStr, vbUnicode) End Function
Code:
gruss
Dim mylist As String
Dim LngPointer As Long Dim IntI As Integer Dim uB As Integer Dim alist() As String LngPointer = MeineVbaFunction(MeinDelphiPAnsiChar As Any include Delemiter und Rückgabe As Long) und auf der Delphi Seite als PAnsiChar mylist = VBStrFromAnsiPtr(LngPointer) alist = Split(mylist, ",") If mylist > "" Then uB = UBound(alist) For IntI = 0 To uB list.AddItem alist(IntI) Next End If |
AW: AnsiStringArray aus DLL an Excel-VBA
Habe dir das mal schnell zusammen geklopft..
Alle Strings (PAnsiChars) werden aus der DLL von der const AnsiString_Vektor_GLOBAL an VBA(VB) geschickt. ShareMem ist wohl hier nicht nötig.. Das ist alles..
Delphi-Quellcode:
VBA..
library Delphi_String_for_Excel_Test;
uses ShareMem, SysUtils, Classes; {$R *.res} const AnsiString_Vektor_GLOBAL: Array [0..3] of PAnsiChar = ('1: Erster String', '2: Das ist der ZWEITE String!', '3: ...und hier ist der DRITTE!', '4: Schlußlicht-String...'); function PAnsiChar_To_Excel(Delimiter: AnsiChar = ';'): PAnsiChar; stdcall; var IntI: Integer; erg: AnsiString; begin for IntI := 0 to 3 do begin erg := erg + AnsiString(AnsiString_Vektor_GLOBAL[IntI]) + Delimiter; erg := copy(erg, 1, length(erg)); end; result := PAnsiChar(erg); end; exports PAnsiChar_To_Excel; begin end. Module..
Code:
Form..
Public Declare Function PAnsiChar_To_Excel Lib "Delphi_String_for_Excel_Test" ( _
Optional ByVal Delimiter As Byte = 59 _ ) As Long
Code:
gruss
Option Explicit
Private Sub cmdGet_Click() Dim IntI As Integer Dim flist As String Dim uB As Integer Dim LngP As Long Dim alist() As String Dim Delimiter As Byte ListInput.Clear Delimiter = 124 ' >>> Default ohne Angabe = 59 = ";" LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|" flist = VBStrFromAnsiPtr(LngP) alist = Split(flist, "|") If flist > "" Then uB = UBound(alist) For IntI = 0 To uB ListInput.AddItem alist(IntI) Next End If End Sub |
AW: AnsiStringArray aus DLL an Excel-VBA
Hallo,
muss bei Split nicht ';' rein? |
AW: AnsiStringArray aus DLL an Excel-VBA
Zitat:
|
AW: AnsiStringArray aus DLL an Excel-VBA
Zitat:
Musst halt nur mal laden ;) Kommt halt drauf an was ich übergebe PAnsiChar_To_Excel ohne Delimiter dann ";" ansonsten halt das was übergeben wird. Habe es korrigiert. bsp:
Code:
LngP = PAnsiChar_To_Excel(124) '"|"
Zitat:
gruss |
AW: AnsiStringArray aus DLL an Excel-VBA
Hallo zusammen,
zunächst danke für die Diskussion und die wertvollen Beiträge! Meine Frage wurde allerdings ganz schön mißverstanden: Ich dachte, die drei Demo-Dateien würden mein Ansinnen erklären. Aber ich sehe im Nachhinein, ich hätte mich klarer ausdrücken sollen. Zitat:
Ich erstelle technische-wissenschaftliche Berechnungen in Delphi, die ich als Funktion über eine DLL in Excel einbinde. Meine „Programme“ sind daher keine eigenständigen Programme mit eigener Oberfläche, sondern laufen als „Bestandteil“ von Excel. Der Benutzer gibt seine Eingangsdaten in Excel ein, schickt die Funktion ab, und die Ergebnisse der Berechnungen erscheinen in der von ihm gewählten Platz in der Excel-Tabelle. Das funktioniert mit Zahlenwerten seit langem sehr gut. Ab und zu müsste ich neben den berechneten Zahlenkolonnen (in Excel: „Matrix“ genannt) auch mal Texte mit ausgeben: z. B. Variablen-Bezeichnungen, oder die Ergebnisse oder Zwischenwerte mit einer hohen Genauigkeit (z.B. 100 Ziffern) als Text. Und dazu übertrage ich diese Texte von der DLL an Excel VBA und leite sie von dort in die Tabelle. Meine oben vorgestellte Lösung funktioniert recht gut, allerdings kann ich ausschließlich AnsiStrings transferieren, obwohl Excels BSTR-Strings eigentlich WideStrings sind, wie auch Peter Below richtig festgestellt hat. Warum andere String-Typen nicht funktionieren, konnte ich bisher nicht herausfinden. Aber ich muss nicht alles verstehen… Ich wollte lediglich die Schleife Zitat:
Aber inzwischen habe ich nach weiteren Tests eingesehen, daß mein Ansatz eine Schnapsidee war, und daß es en bloc am ganzen Vektor nicht geht, weil meine Routine nur die von Excel bereitgestellte Kopie des Strings patcht, und das Original String-Array ganz woanders im Speicher liegt, wo ich die Strings auch nicht mit Pointer-Akrobatik verändern kann. Noch einmal danke für Eure Hilfe! Gruß Andreas |
AW: AnsiStringArray aus DLL an Excel-VBA
Zitat:
Na egal läuft unter VB und sollte es auch unter VBA tun. Zitat:
Zitat:
Code:
kommt den WideString am nächsten.
StrConv(bStr, vbUnicode)
Zitat:
gruss |
AW: AnsiStringArray aus DLL an Excel-VBA
Liste der Anhänge anzeigen (Anzahl: 1)
@EWeiss
Hallo, danke für Deine Hinweise. Ich habe versucht Deinen VBA-Code in Excel's VBA umzusetzen, bin allerdings gescheitert... Ich bin halt kein VBA-Programmierer. Meine rudimentären VBA-Kenntnisse dienen nur dazu, die VBA-Schnittstelle zwischen Excel und Delphi zu realisieren. Mit Deinem objektorientierten VBA-Ansatz kenne ich mich leider nicht aus. Könntest Du bitte den Code in der beiliegenden Datei ToExcel_Test_1.xlsm bei Gelegenheit evtl. so anpassen, daß es "dort" in Excel läuft? Vielen Dank! Gruß aus Nürnberg Andreas |
AW: AnsiStringArray aus DLL an Excel-VBA
Zitat:
gruss |
AW: AnsiStringArray aus DLL an Excel-VBA
Was ich dir aber sagen kann das mein Modul so unter Excel 64Bit nicht läuft. (Keine Ahnung was du installiert hast)
Es fehlt PtrSafe in allen Declarationen. Dann muss die DLL von Delphi auch in 64Bit vorliegen. ![]()
Code:
So sollte es aussehen.
Option Explicit
Private Declare PtrSafe Function PAnsiChar_To_Excel Lib "Delphi_String_for_Excel_Test.dll" ( _ Optional ByVal Delimiter As Byte = 59 _ ) As LongPtr Private Declare PtrSafe Function lstrlen Lib "kernel32" Alias "lstrlenA" ( _ ByVal lpString As Long _ ) As LongPtr Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ ByRef Destination As Any, _ ByRef Source As Any, _ ByVal length As LongPtr_ ) Eventuell kommst du ja jetzt weiter. Für die ListBox\ListenFeld könntest du dir ja ein Array erstellen ( bzw. haben wir schon alist() ) und das anstelle dessen verwenden wenn du keine UserForm verwenden möchtest. Oder du schickst die werte von alist() direkt in deine Excel Tabelle. ![]() ![]() PS: Anstelle des Button kannst du dir in Excel ein Macro erstellen was auf die cmdGet_Click() verweist und den Code dort ausführen. gruss |
AW: AnsiStringArray aus DLL an Excel-VBA
Habe mal Excel 32Bit installiert aber keine Ahnung ob es das ist was du willst..
Ich werde da nicht so recht schlau draus. Klick auf Button GetIt füllt die Cells in Excel Klick auf Button CleanUp löscht die Einträge. Ohne Form und ausgeführt in Excel!
Code:
Wie du das nun schlussendlich regelst über Button oder sonst wie entscheide es selbst..
Option Explicit
' Pfad aktualisieren: Private Declare Function PAnsiChar_To_Excel Lib "D:\Test\Delphi_String_for_Excel_Test" ( _ Optional ByVal Delimiter As Byte = 59 _ ) As Long Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" ( _ ByVal lpString As Long _ ) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ ByRef Destination As Any, _ ByRef Source As Any, _ ByVal length As Long _ ) Private Function VBStrFromAnsiPtr(ByVal lpStr As Long) As String Dim bStr() As Byte Dim cChars As Long On Error Resume Next ' Get the number of characters in the buffer cChars = lstrlen(lpStr) If cChars Then ' Resize the byte array ReDim bStr(0 To cChars - 1) As Byte ' Grab the ANSI buffer Call CopyMemory(bStr(0), ByVal lpStr, cChars) End If ' Now convert to a VB Unicode string VBStrFromAnsiPtr = StrConv(bStr, vbUnicode) End Function Private Sub CleanUp() Range("B1:B4").Delete XlDeleteShiftDirection.xlShiftUp End Sub Private Sub GetIt() Dim flist As String Dim LngP As Long Dim alist() As String Dim Delimiter As Byte Dim arTesting As Variant Delimiter = 124 LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|" flist = VBStrFromAnsiPtr(LngP) alist = Split(flist, "|") If flist > "" Then arTesting = Application.WorksheetFunction.Transpose(alist) Range("B1:B4").Value = arTesting End If End Sub Aber der Wunsch die Strings in einem Schub nach Excel zu transponieren sollte dem entsprechen. Delphi > VBA > Excel Ich hoffe du kommst mit dem Rest selber klar. :) gruss |
AW: AnsiStringArray aus DLL an Excel-VBA
Zitat:
Delphi-Quellcode:
ist ja kein
array of PAnsiChar
Delphi-Quellcode:
, sondern ein
PAnsiChar
Delphi-Quellcode:
(Arrays sind Zeiger auf ihr erstes element, mit der Länge, dem Referenzzähler und der Typeninfo vorweg).
PPAnsiChar
Delphi-Quellcode:
gibt dir zwar im gegensatz zu einem Typecast keinen Fehler aus, aber das heißt nicht, dass es so richtig ist.
absolute
Richtig muss
Delphi-Quellcode:
aber entweder ein
S
Delphi-Quellcode:
oder
PPAnsiChar
Delphi-Quellcode:
ein
Vektor
Delphi-Quellcode:
(bzw. ein
array of AnsiChar
Delphi-Quellcode:
) sein.
PAnsiChar
Auch kommt da jetzt noch folgendes hinzu: Arrays sind ja, wie eben schon gesagt, sowohl referenzgezählt, als auch typisiert und haben eine feste Länge. Gut möglich, dass dir das somit auch nochmal den Speicher zerschießt, da dafür in dem
Delphi-Quellcode:
ja gar nichts reserviert ist.
PAnsiChar
|
AW: AnsiStringArray aus DLL an Excel-VBA
Sorry du kramst altes Zeug raus das mittlerweile auf anderem Wege schon gelöst ist.
gruss |
AW: AnsiStringArray aus DLL an Excel-VBA
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für Deine VBA-Lösung!
Ich habe sie so abgewandelt, daß der Text sofort in den Excel-Zellen erscheint. Dazu brauchen wir eine neue Funktion in Excel's VBA wie folgt: VBA-Code
Delphi-Quellcode:
Die beiden Lösungen sind zum Ausprobieren im beiliegenden Excel-File ToExcel_Test_2.xlsm vorhanden. Die DLL bleibt unverändert, daher habe ich sie nicht noch einmal hochgeladen.
Public Function GetDelphiStringVektor(Anzahl As Long) ' entweder OHNE Typangabe oder As Variant, was gleichbedeutend ist, da dies eine Matrix-Funktion sein soll
' ... und so geht es direkt in die Zellen Dim flist As String Dim LngP As Long Dim alist() As String Const Delimiter = 124 Dim StrVektor() As String Dim i As Long Dim n_Anz As Long Dim Bis As Long LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|" flist = VBStrFromAnsiPtr(LngP) alist = Split(flist, "|") n_Anz = UBound(alist) - LBound(alist) + 1 Bis = Anzahl If n_Anz < Anzahl Then Bis = n_Anz ' Hat denn VBA keine Funktion wie Pascal's Min?? --> Bis := Min(a_Anz, Anzahl) ReDim StrVektor(n_Anz, 0 To 0) ' SpaltenVektor If flist > "" Then For i = LBound(alist) To Bis - 1 StrVektor(i, 0) = alist(i) Next End If GetDelphiStringVektor = StrVektor End Function Gruß, Andreas |
AW: AnsiStringArray aus DLL an Excel-VBA
Zitat:
Aber gut wenn es jetzt funktioniert. Mein Lösungsansatz war nur als Denkanstoß gedacht. Zitat:
Code:
Wenn du das nicht willst dann mache dir selber was wenn du denkst es ist von nöten.
WorksheetFunction.Min(20, 501, 561, 45, 78)
LBound = MIN UBound = MAX
Code:
Private Function Min(ParamArray values() As Variant) As Variant
Dim i As Integer Dim min_value As Variant min_value = values(LBound(values)) For i = LBound(values) + 1 To UBound(values) If min_value > values(i) Then min_value = values(i) Next Min = min_value End Function Private Function Max(ParamArray values() As Variant) As Variant Dim i As Integer Dim max_value As Variant max_value = values(LBound(values)) For i = LBound(values) + 1 To UBound(values) If max_value < values(i) Then max_value = values(i) Next Max = max_value End Function ' Teste Min, Max Private Sub cmdGo_Click() txtMin.Text = Format$(Min( _ CInt("2873"), _ CInt("4398"), _ CInt("7846"), _ CInt("1876"), _ CInt("4792"), _ CInt("3982"), _ CInt("4863"), _ CInt("9382"))) txtMax.Text = Format$(Max( _ CInt("2873"), _ CInt("4398"), _ CInt("7846"), _ CInt("1876"), _ CInt("4792"), _ CInt("3982"), _ CInt("4863"), _ CInt("9382"))) End Sub
Code:
Etwas unsauber weil du hier Delimiter ohne Angabe als Variant definierst.. ist aber Byte!
Const Delimiter = 124
Wenn dann so!
Code:
Siehe die Definition von PAnsiChar_To_Excel(bla bla.. Delimiter As Byte
Const Delimiter As Byte = 124
Code:
zu
Dim i As Long
Dim n_Anz As Long Dim Bis As Long
Code:
Ein 32Bit DatenTyp ist für einfache for.. Next schleifen nicht nötig.
Dim i As Integer
Dim n_Anz As Integer Dim Bis As Integer Zitat:
PS: VBA ist kein Delphi daher wäre es besser du verwendest die Format Funktion des Forum
Code:
nicht
Dim x As Integer
Dim i As Integer
Delphi-Quellcode:
dann liest es sich besser. ;)
var
x:Integer; i:Integer; gruss |
AW: AnsiStringArray aus DLL an Excel-VBA
Danke, die Format-Funktion für den sonstigen Code habe ich erst jetzt gefunden...
Gruß, Andreas |
AW: AnsiStringArray aus DLL an Excel-VBA
Sorry das ich das nochmal vor krame..
Habe das vorhin nochmal gelesen und festgestellt das deine abfrage auf
Code:
viele zu spät kommt..
If flist > "" Then
Was machst du wenn
Code:
ist?
If flist = ""
Dann wird das Array alist() nicht initialisiert und die nächste Abfrage lässt deine Anwendung abstürzen.
Code:
mache es direkt nach der Initialisierung von flist.
alist = Split(flist, "|")
n_Anz = UBound(alist) - LBound(alist) + 1
Code:
gruss
Public Function GetDelphiStringVektor(Anzahl As Long) As Variant
Dim flist As String Dim LngP As Long Dim alist() As String Const Delimiter As Byte = 124 Dim StrVektor() As String Dim i As Long Dim n_Anz As Long Dim Bis As Long LngP = PAnsiChar_To_Excel(Delimiter) ' >>> "|" flist = VBStrFromAnsiPtr(LngP) If flist > "" Then alist = Split(flist, "|") n_Anz = UBound(alist) - LBound(alist) + 1 Bis = Anzahl If n_Anz < Anzahl Then Bis = n_Anz ReDim StrVektor(n_Anz, 0 To 0) ' SpaltenVektor For i = LBound(alist) To Bis - 1 StrVektor(i, 0) = alist(i) Next GetDelphiStringVektor = StrVektor else GetDelphiStringVektor = "" End If End Function |
AW: AnsiStringArray aus DLL an Excel-VBA
@EWeiss
Hab herzlichen Dank für die Verbesserungen! Gruß Andreas |
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:41 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