Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Delphi DLL String übergeben (https://www.delphipraxis.net/214868-delphi-dll-string-uebergeben.html)

DelTurbo 25. Mär 2024 13:31

Delphi DLL String übergeben
 
Hi,
ich habe folgendes Problem. Ein bekannter hat mir unter Delphi 10.4 eine DLL übersetzt weil es nicht unter Delphi 2007 ging. Er hat auch ein kleines Test Programm gemacht damit ich sehe das alles läuft.

Nun komme ich mit meinem Delphi 2007 daher. Die DLL laden usw. klappt alles. Nun versuche ich seit Samstag einen String zu übergeben. In der DLL ist die function so hinterlegt.
Delphi-Quellcode:
function ConnectApi(Key:PChar):Boolean; cdecl;

Egal wie ich es übergebe in der DLL kommt nur "Müll" an. Nicht der String. Wie gehe ich nun am besten vor? Ich habe auch per TV zugriff auf sein Delphi und könnte die Functions Parameter ändern. Was ich auch schon gemacht habe. Aber es passt nix. In 20 Stunden kann man viele (auch blöde) Sachen probieren. Aber ich bekomme es nicht hin.

Ich hoffe es weiß jemand ob das überhaupt geht, also DLL mit 10.4 und Programm mir 2007. Und wenn ja, wie?

Vielen Dank im voraus

PS.: Nehme ich mein Testprogramm (in Delphi 2007 geschrieben) und übersetze es mit 10.4 dann läuft immer alles. Egal wie Doof/unlogisch die Parameter sind.

Papaschlumpf73 25. Mär 2024 13:40

AW: Delphi DLL String übergeben
 
In Delphi 2007 war PChar noch nicht Unicode. In Delphi 10.4. jedoch schon. Liegt wahrscheinlich daran... Besser wäre wohl, ihr würdet einen WideString-Parameter benutzen; der ist in allen Versionen gleich.

DelTurbo 25. Mär 2024 13:42

AW: Delphi DLL String übergeben
 
Und da kann man gar nichts machen?

Papaschlumpf73 25. Mär 2024 13:43

AW: Delphi DLL String übergeben
 
Du könntest ggf. einen PWideChar statt PChar übergeben; das könnte vielleicht klappen.

Rolf Frei 25. Mär 2024 13:48

AW: Delphi DLL String übergeben
 
Das Problem wird sein, dass D2007 (ANSI) und D10.4 (Unicode) nicht die gleiche Definition von PChar haben. Früher zeigte ein PChar auf ein Byte (Zeichen) und seit D2009 auf 2 Bytes = 1 Zeichen. Du musst deshalb sicherstellen, dass beide Delphis die Daten auf die gleiche Weise verarbeiten, also Ansi oder Unicode.

Deklariere mal in D10.4 die DLL Funktion so:

function ConnectApi(Key: PAnsiChar):Boolean; cdecl;

Die Routine in 10.4 muss dann den String entsrechend verarbeiten und als AnsiString übergeben.

Wenn du das umgekehrte machen willst, muss du in D2007 einen WideString als PWideChar übergeben. Das müsste auch gehen. Die Deklaration in D2007 müsste dann so sein:

function ConnectApi(Key: PWideChar):Boolean; cdecl;

Beispiel D2007:
Delphi-Quellcode:
var
  s: WideString;
begin
  s := 'mein key';
  ConnectApi(PWideChar(s));
end;
oder so:
Delphi-Quellcode:
var
  s: String;
begin
  s := 'mein key';
  ConnectApi(PWideChar(WideString(s)));
end;

DelTurbo 25. Mär 2024 13:53

AW: Delphi DLL String übergeben
 
Könnte ich auch PAnsiString nehmen? Weil ich ja einen langen String übergeben muss.

Das hat sich überschnitten... Sorry

DelTurbo 25. Mär 2024 14:01

AW: Delphi DLL String übergeben
 
Das geht. Ich brech ab. WideChar/String ist doch länger als 255, oder? Ich meine der wäre 0 Terminiert.

Rolf Frei 25. Mär 2024 14:07

AW: Delphi DLL String übergeben
 
Zuerst müssen wir wissen, wie es denn nun genau gemacht wird. Bleibt die Deklaration in D1.04 so mit PChar?

Delphi-Quellcode:
function ConnectApi(Key:PChar):Boolean; cdecl;
Wenn dem so ist, kannst du es so machen wie in meinem vorherigen Beispiel. Du benötigst für D2007 eine andere Deklaration als in D10.4:
Delphi-Quellcode:
{$IFDEF UNICODE}  // D2009+
function ConnectApi(Key:PChar):Boolean; cdecl
{$ELSE}  // D2007
function ConnectApi(Key:PWideChar):Boolean; cdecl
{$ENDIF}

Zitat:

Könnte ich auch PAnsiString nehmen? Weil ich ja einen langen String übergeben muss.
Das versthe ich nicht so ganz, was du damit meinst? Die länge des String ist im Prinzip egal. PAnsiString würde ich auf keinen Fall nehmen, weil String in D2007 nicht einem String in D10.4 entspricht und das inkompatibel ist.

Rolf Frei 25. Mär 2024 14:08

AW: Delphi DLL String übergeben
 
Zitat:

Zitat von DelTurbo (Beitrag 1534985)
Das geht. Ich brech ab. WideChar/String ist doch länger als 255, oder? Ich meine der wäre 0 Terminiert.

Nur der Datentyp ShotString besteht aus maximal 255 Zeichen, alles andere ist unlimitiert.

DelTurbo 25. Mär 2024 14:11

AW: Delphi DLL String übergeben
 
Hi,
erstmal danke für deine Hilfe. Ich habe die DLL schon geändert in
function ConnectApi(Key : PWideChar):Boolean; cdecl;

Klappt einwandfrei. Das mit dem Ansi vergiss wieder. Wenn man soooo lange dran sitzt und keinen erfolg hat, dann kommen auch komische Fragen... :oops:

DelTurbo 28. Mär 2024 09:46

AW: Delphi DLL String übergeben
 
Huhu,
es tut mir leid aber ich muss nochmal "Nerven". Ich hole mir einen String (die Antwort) folgendermaßen aus der DLL. Ist der String 2000 Lang klappt alles. Irgendwo bei 2500 liegt die grenze ab der ich dann komischerweise ein "Out of Memory" bekomme. Also 3000 Zeichen geht gar nicht. Ich versuche mal den Source wo es "knallt" hier zu Posten. Das ist ein kleines Testprogramm, dort kann ich die länge von dem String den haben möchte übergeben.

In der DLL mache ich das...
Delphi-Quellcode:
var
  Answer   :WideString;

procedure GetTxt(A:PWideChar;L:Integer); cdecl;
var
  i    :Integer;
begin
    Answer:='';
    for i:=0 to L-1 do begin
      Answer:=Answer+'A';
    end;
    Move(Answer,A^,Length(Answer));
end;
Im Programm das...
Delphi-Quellcode:
procedure TfMain.btnGetTxtClick(Sender: TObject);
var
  line   :WideString;
begin
    SetLength(Antwort,StrToInt(edtTextLen.Text));
    GetTxt(@Antwort,StrToInt(edtTextLen.Text));
    SetString(line,PWideChar(@Antwort[0]),Length(Antwort)); // hier geht es in die Hose. Wie gesagt ab ca. 2500 Zeichen. Bei 2000 klappt es
end;
Ich habe gestern wie wild gegoogelt aber ich finde nichts. Es spielt auch keine Rolle ob die DLL mit Delphi 10.4 oder 2007 erstellt wurde.

Vielen Dank im voraus

himitsu 28. Mär 2024 09:54

AW: Delphi DLL String übergeben
 
0 ist definitiv falsch:!:

Delphi-Quellcode:
PWideChar(@Antwort[1])
oder besser
Delphi-Quellcode:
PWideChar(@Antwort[Low(string)])
(für die kurze Era wo das mal im Mobilen-Delphi ausprobiert wurde)
oder einfach nur direkt
Delphi-Quellcode:
PWideChar(Antwort)
, denn Ersteres knallt dir bei einem leeren String (Length=0) gnadenlos eine Exception entgegen.
https://docwiki.embarcadero.com/RADS...rings_(Delphi)



Für AnsiString's und UnicodeString/String müsste ShareMem verwendet werden, oder eben als PChar übergeben.

Wenn keine #0 im PChar/PWideChar oder PAnsiChar vorkommen, dann braucht man keine Länge einzeln zu übergeben.
MSDN-Library durchsuchenStrLen

Für WideString geht es geht es auch direkt, da es nicht den DelphiMM benutzt, sondern den MemoryManager vom OLE32
und intern ist es eigentlich ein Bei Google suchenBSTR, wofür automatisch die API um MSDN-Library durchsuchenSysAllocString/SysFreeString verwendet wird, welche auch C++ und Co. nutzen können.
Außerdem nutzt es somit auch eine Art von ShareMem (nur halt nicht das vom Delphi), weswegen es keine Probleme mit DLLs und COM-Interfaces gibt.

himitsu 28. Mär 2024 09:56

AW: Delphi DLL String übergeben
 
Zitat:

Zitat von DelTurbo (Beitrag 1535066)
hier geht es in die Hose. Wie gesagt ab ca. 2500 Zeichen. Bei 2000 klappt es

Ich wüsste nicht, warum SetString eine Begrenzung haben sollte. :gruebel:

System._UStrFromPWCharLen für String/UnicodeString
System._WStrFromPWCharLen für WideString

[edit]
Arg, wer hat sich denn diesen totalen Schwachsinn ausgedacht?
Für WideString ist SetString komplett im A***

Echt mal, das einfach blind nach UnicodeString zu casten, ist grob fahrlässig ... hatte wohl auch wer mitbekommen und desswegen die Compilerwarnung via Pointer unterdrückt.
[/edit]

[edit2] Neee, das ist nur für Nicht-Windowse (iOS/ISX/Android/Linux), wo WideString heimlich ein UnicodeString ist. :oops:



So oder so, warum nimmst du nicht einfach
Delphi-Quellcode:
Line := A;
und lässt das mit dem Len weg?
Oder eben direkt WideString als Parameter nutzen.
Delphi-Quellcode:
procedure GetTxt(const A: WideString); cdecl;


Und wozu das cdecl? (möglicher Aufruf aus C++ oder so?)

DelTurbo 28. Mär 2024 10:17

AW: Delphi DLL String übergeben
 
Ich wollte nun sofort wenn ich aus der DLL wieder komme die Length(Antwort) ausgeben. Schon dabei scheppert es. Wenn ich mir den String Antwort im Debugger ansehe ist er meines Erachtens richtig. Auch eine Zusätzliche 0 am ende ändert nichts an der Sache. :(

Edit: @himitsu, Problem war am Anfang das die DLL unter Delphi 10.4 erstellt werden muss, ich aber mit Delphi 2007 arbeite. Da wurd ja ab Delphi 2009 etwas an den Strings geändert. "Rolf Frei" hat mich denn erstmal auf den richtigen Weg gebracht.

himitsu 28. Mär 2024 10:22

AW: Delphi DLL String übergeben
 
nochmal
Zitat:

0 ist definitiv falsch :!:
ShortString: an Adresse 0 steht das LängenByte (ist intern ein Array[0..255] of AnsiChar),
und deswegen fangen auch neueren LongStrings (AnsiString und UnicodeString) seit 20 Jahren mit 1 an zu zählen.

Mit [0] schreibst zu in AdressOffest -1, also mitten in die Verwaltungsdaten, wo zufällig der Längen-Integer sich versteckt.

DelTurbo 28. Mär 2024 10:25

AW: Delphi DLL String übergeben
 
Das mit der 0 habe ich schon geändert. Oder ist auch das Move(Answer,A^,Length(Answer)); in der DLL Falsch? Wenn ja, wie komme ich auf A^ + 1? Also A^+1 geht nicht.

DelTurbo 28. Mär 2024 10:31

AW: Delphi DLL String übergeben
 
Ups :oops:

Das im Programm habe ich falsch dargestellt. Das sieht so aus...

Delphi-Quellcode:
var
  Antwort  :array of Char;

procedure TfMain.btnGetTxtClick(Sender: TObject);
var
  line   :WideString;
begin
    SetLength(Antwort,StrToInt(edtTextLen.Text));
    GetTxt(@Antwort,StrToInt(edtTextLen.Text));
    SetString(line,PWideChar(@Antwort[1]),StrToInt(edtTextLen.Text)); //Length(Antwort));

himitsu 28. Mär 2024 15:03

AW: Delphi DLL String übergeben
 
dynamisches Array:

Delphi-Quellcode:
@Antwort
ist ein Zeiger auf die Variable, nicht auf die Daten.
Delphi-Quellcode:
@Antwort[0]
ist ein Zeiger auf das erste Char, bzw. auf alle Chars im Array.

Wie gesagt, mach doch einfach
Delphi-Quellcode:
procedure GetTxt(var A: WideString; L: Integer); cdecl;
und lass das ganze Rumgepointere sein.

Delphi-Quellcode:
procedure GetTxt(var A: WideString; L: Integer); cdecl;
var
  i :Integer;
begin
  ...
  A := Answer;
end;
Dann kann auch diese beschissene globale Variable weg.

DelTurbo 28. Mär 2024 15:06

AW: Delphi DLL String übergeben
 
Hi,
danke dir habe es auf stdcall umgestellt. Läuft nun. Natürlich ohne Pointer :thumb:

Rolf Frei 28. Mär 2024 15:48

AW: Delphi DLL String übergeben
 
Du kannst ganz normal mit Strings arbeiten und benötigst kein Array of Char. Der Compiler nimmt dir da sehr viel Konvertierungsarbeit ab, wenn du es richtig machst. Ausserdem ist dir anscheinend nicht recht klar was der Unterschied von Unicode und Ansi ist. In Ansi (D2007) ist eine Zeichen (Char) ein Byte. Bei Unicode ist ein Zeichen (Char) 2 Byte. Du kannst also bei einem Speicher Movebefehl nicht einfach die Länge des String nehmen, da das dann zu wenig Speicher ist.

Delphi-Quellcode:
Move(Answer,A^,Length(Answer));
Das ist devinitv in der DLL falsch. Length(Answer) liefet dir die Anzahl Zeichen in Answer, aber du benötigst die Anzahl Bytes. Das muss daher so sein:
Delphi-Quellcode:
Move(Answer[1],A[0],Length(Answer) * SizeOf(Char));
Leider ist mir nicht mehr so recht klar, was du nun wo genau machst. Die DLL ist immer noch mit D10.4 geschrieben? Wenn ja darfst du da keine WideStrings nutzen sondern ganz normal mit String und Char arbeiten. WideString und PWideChar bezieht sich nur auf D2007.

D10.4 DLL:
Delphi-Quellcode:
var
  Answer :String;

procedure GetTxt(A:PChar;L:Integer); cdecl;
var
  i :Integer;
 
begin
    Answer:=StringOfChar('A', L);
    SetLength(A, L);
    Move(Answer[1],A[0],Length(Answer) * SizeOf(Char)); // benötigen hier Bytes nicht Zeichen!!!
end;
D2007 Anwendung:
Delphi-Quellcode:
var
  Antwort :String; // Dein D2007 kann in den TEdits, etc. eh kein Unicode nutzen, da macht es keinen Sinn mit WideString als Resultat.

procedure TfMain.btnGetTxtClick(Sender: TObject);
var
  wAntwort: Array of PWideChar;
begin
    SetLength(wAntwort,StrToInt(edtTextLen.Text));
    GetTxt(wAntwort[0],StrToInt(edtTextLen.Text));
    Antwort := String(WideString(wAntwort));   // Eventuell geht es auch nur mit String(wAntwort)

DelTurbo 28. Mär 2024 15:51

AW: Delphi DLL String übergeben
 
Danke, aber ich habe das schon geändert. Guck hier

jaenicke 28. Mär 2024 16:00

AW: Delphi DLL String übergeben
 
Du kannst auch einfach WideString verwenden und das einfach als Rückgabewert verwenden. Anders als für Strings brauchst du da nicht ShareMem, womit du dir nur unnötige Probleme und Einschränkungen schaffst (geht nur mit Delphi, Probleme bei unterschiedlichen Delphiversionen, ...).

himitsu 28. Mär 2024 20:02

AW: Delphi DLL String übergeben
 
Wie schon erwähnt, brauchst du für WideString und OleVariant (nicht Variant) kein ShareMem/SimpleShareMem, da hierfür bereits eine externe Speicherverwaltung genutzt wird.


@Rolf: Für externe Schnittstellen, wie z.B. DLLs, am Besten niemals dynamische Typen verwenden, also kein Char, PChar oder String, sondern nur statische Typen, wie z.B. PWideChar/PAnsiChar oder WideString.

Rate mal, warum es damals 2009 fast überall geknallt hatte.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:50 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