Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Weitergabe eines Strings über Pointer funktioniert nicht - manchmal... (https://www.delphipraxis.net/208021-weitergabe-eines-strings-ueber-pointer-funktioniert-nicht-manchmal.html)

s-off 28. Mai 2021 14:44

Delphi-Version: 10.1 Berlin

Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Hallo zusammen,

ich habe mich lange Zeit nicht mehr mit Delphi beschäftigt und stehe nun vor einem Problem, welches ich alleine anscheinend nicht bewältigen kann.
Folgende Ausgangs-Situation:
Ich muss aus C# eine Delphi-DLL mit Parametern (BSTR -> WideString) aufrufen, in der DLL ein PDF erzeugen und dieses base64-decodiert wieder zurückgeben.
Das funktioniert auch alles; naja - der Aufruf zumindest und es kommt auch etwas zurück.

Das Problem, mit welchem ich derzeit kämpfe ist folgendes:
Die Delphi-DLL ist sehr komplex und es erfolgt eine lange Kette von Prozedur-/Funktionsaufrufen.
Am Ende der Kette wird der String erzeugt, welchen ich am Anfang der DLL wieder herausgeben muss. Die PDF-Engine schreibt dazu in einen StringStream.
Da es sehr aufwändig wäre, diesen String - nachträglich - durch die Prozeduren/Funktionen durchzureichen (würde einen sehr großen Aufwand bedeuten), dachte ich mir, dass ich das über Pointer realisiere.
Das Resultat: mal geht es, mal nicht und ich glaube, dass das am Speicherreservieren usw. hängt.

In der DLL habe ich ein paar Variablen in die ich später die Adresse für die PDF-Daten sowie deren Größe schreibe und entsprechende Pointer darauf.
Delphi-Quellcode:
   // in der DLL
   BinaryContentAddress: Integer; // address of the binary content
   PBinaryContentAddress: ^Integer;
   BinaryContentSize: Integer; // size of the binary content
   PBinaryContentSize: ^Integer;
Diese übergebe ich in die Klasse, die das PDF-Dokument erzeugt.
Delphi-Quellcode:
   // Aufruf der Methode Data2Ext der Klasse 1
   Data2Ext(PBinaryContentAddress, PBinaryContentSize, PStringContentAddress, PStringContentSize);

   // ...
   
   // Implementierung in Klasse 1
   FOutputStreamInt: TStringStream; // internal stream for the binary content
   FAddressBinaryContent: Integer; // binary content address
   FAddressBinaryContentSize: Integer; // binary content size address

   // ...

   // Methode Data2Ext in Klasse 1
   FOutputStreamInt := TStringStream.Create;
   FAddressBinaryContent := BinaryContentAddressPointer;
   FAddressBinaryContentSize := BinaryContentSizePointer;

   // ...

   // Methode, die die Pointer für die Rückgabe aufbereitet    
   Var
      PBinaryContent: ^Integer; // pointer to the binary content address
      PBinaryContentSize: ^Integer; // pointer to the binary content size
      BinaryContent: PChar;
   Begin

      If (FAddressBinaryContent > 0) And (FAddressBinaryContentSize > 0) Then Begin

         // get external addresses
         PBinaryContent := Pointer(FAddressBinaryContent);
         PBinaryContentSize := Pointer(FAddressBinaryContentSize);

         // write the pchar address to the external address
         BinaryContent := CoTaskMemAlloc((Length(FOutputStreamInt.DataString) + 1));
         Move(FOutputStreamInt.DataString[1], BinaryContent^, Length(FOutputStreamInt.DataString) + 1);
         PBinaryContent^ := Cardinal(BinaryContent);

         // write the binary content size to the external address
         PBinaryContentSize^ := Length(FOutputStreamInt.DataString);

         If Assigned(FOutputStreamInt) Then
            FreeAndNil(FOutputStreamInt);
      End;
Wenn das Ganze durch ist bin ich wieder zurück am Anfang der DLL.
Dort möchte ich die PDF-Daten auslesen, in einen String schreiben, base64 codieren und an den Aufrufer zurückgeben.
Delphi-Quellcode:
Var
   pBinaryContent: PChar;
Begin
   // Get the pdf data address and its size
   If PBinaryContentAddress <> Nil Then
      BinaryContentAddress := PBinaryContentAddress^;
   If PBinaryContentSize <> Nil Then
      BinaryContentSize := PBinaryContentSize^;

   // Get the content
   SetLength(sBinaryContent, BinaryContentSize);
   Move(PChar(BinaryContentAddress)^, sBinaryContent[1], BinaryContentSize);

   sBase64Content := Coder.EncodeBase64(sBinaryContent);

   // ...
So weit, so gut.
Wie bereits gesagt - manchmal ist der String invalide, der da rauskommt und manchmal nicht.
Und ich fürchte, dass ich da irgendetwas grundlegend falsch mache.
Ich hoffe, dass mir hier jemand behilflich seine kann.

Besten Dank im Voraus.

QuickAndDirty 28. Mai 2021 17:12

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Du schreibst ein C# Programm?
Du Hast eine Delphi DLL welche eine PDF-Engine enthält im Quellcode vorliegen?
Die Delphi DLL hat die Funktionen mit dem Paramter "STDCALL" declariert, so dass sie einem C++ konformen Aufruf ermöglichen? #
Oder wurde "CDECL" verwendet so dass die Funktionen C-konform in C# eingebunden werden müssen?
Warum willst du die Delphi DLL dann noch verändern?
Kannst du sie nicht einfach in den C# projekt einbinden und nutzen?

venice2 28. Mai 2021 17:14

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Delphi-Quellcode:
   If PBinaryContentSize <> Nil Then
      BinaryContentSize := PBinaryContentSize^;
Das scheint mir schon mal falsch.
Welche größe hat BinaryContentSize wenn PBinaryContentSize = Nil ist?
Fehlt da nicht ein begin ?

Nur auf
Delphi-Quellcode:
   SetLength(sBinaryContent, BinaryContentSize);
   Move(PChar(BinaryContentAddress)^, sBinaryContent[1], BinaryContentSize);
zugreifen wenn PBinaryContentSize und PBinaryContentAddress wirklich nicht nil sind.

Delphi-Quellcode:
   If Assigned(PBinaryContentAddress) Then
   begin
     BinaryContentAddress := PBinaryContentAddress^;

     If Assigned(PBinaryContentSize) Then
     begin
       BinaryContentSize := PBinaryContentSize^;

       // Get the content
       SetLength(sBinaryContent, BinaryContentSize);
       Move(PChar(BinaryContentAddress)^, sBinaryContent[1], BinaryContentSize);

       sBase64Content := Coder.EncodeBase64(sBinaryContent);
     end;
   end;

s-off 29. Mai 2021 17:29

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Hi QuickAndDirty,

Zitat:

Zitat von QuickAndDirty (Beitrag 1490334)
Du schreibst ein C# Programm?
Auch, aber es geht hier nicht um die Kommunikation zwischen der C#-Anwendung und der Delphi-DLL; das funktioniert.

Zitat:

Zitat von QuickAndDirty (Beitrag 1490334)
Warum willst du die Delphi DLL dann noch verändern?

Weil sie aktuell nicht das tut, was ich möchte.

Zitat:

Zitat von QuickAndDirty (Beitrag 1490334)
Kannst du sie nicht einfach in den C# projekt einbinden und nutzen?

Nein.
Das Ganze ist etwas komplizierter. Das kann und möchte ich hier aber nicht alles auseinander klamüsern :)
Fakt ist, dass ich das benötige, nach dem ich gefragt habe. Es gibt keine Umgehungslösung oder etwas anderes.
Ich möchte lediglich wissen, ob ich das mit dem Speicher reservieren etc. so richtig mache und warum es mal funktioniert und mal nicht.
Dennoch vielen Dank für Deine Antwort :)

***

Hi venice2

Zitat:

Zitat von venice2 (Beitrag 1490336)
Delphi-Quellcode:
   If PBinaryContentSize <> Nil Then
      BinaryContentSize := PBinaryContentSize^;
Das scheint mir schon mal falsch.
Welche größe hat BinaryContentSize wenn PBinaryContentSize = Nil ist?

Verstehe ich nicht so richtig :?

Zitat:

Zitat von venice2 (Beitrag 1490336)
Fehlt da nicht ein begin ?

Ähm - nein.

Zitat:

Zitat von venice2 (Beitrag 1490336)
Nur auf
Delphi-Quellcode:
   SetLength(sBinaryContent, BinaryContentSize);
   Move(PChar(BinaryContentAddress)^, sBinaryContent[1], BinaryContentSize);
zugreifen wenn PBinaryContentSize und PBinaryContentAddress wirklich nicht nil sind.

Ja.
Ich möchte hier aber nochmal klarstellen, dass ich keine Zugriffsverletzung o.ä. bekomme - das ist nicht das Problem!
Wie oben geschrieben geht es darum, dass das, was letztendlich in meinem String landet, gelegentlich nicht passt - meistens aber schon.
Daher vermute ich, dass ich beim Reservieren des Speichers oder dem Schreiben in den String (oder vorher schon in den PChar) irgendetwas falsch mache. (CoAllocMemSize oder Move falsch verwendet?).

Leider gibt es dazu heutzutage auch nicht mehr viel zu finden :(

[OT]
R.I.P Luckie
Habe es gerade erst gelesen. Seine Tutorials und Expertise haben mir vor ~15 Jahren stets gut geholfen.
[/OT]

venice2 29. Mai 2021 18:09

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Zitat:

Verstehe ich nicht so richtig
Ohne begin

greifst du immer auf diese beiden Zeilen zu.
Delphi-Quellcode:
   SetLength(sBinaryContent, BinaryContentSize);
   Move(PChar(BinaryContentAddress)^, sBinaryContent[1], BinaryContentSize);
Egal ob BinaryContentSize 0 oder einen anderen Wert enthält. Oder PBinaryContentSize nil ist oder nicht.

Warum willst du also einen move bzw. die länge auf 0 setzen wenn es nicht nötig ist.
Egal. Überdenke es noch mal.

EDIT:
Versuche es mal mit FillChar bevor du neue Daten ermitteln willst.

himitsu 29. Mai 2021 18:12

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Zitat:

ich habe mich lange Zeit nicht mehr mit Delphi beschäftigt und stehe nun vor einem Problem
So lange, dass dein Compiler damals noch ANSI war (<2009), aber nun Er mit Unicode arbeitet (2009+) und dein Code durch so Dinge wie String/PChar/... nun nicht mehr das Selbe macht?

Zitat:

Delphi-Quellcode:
CoTaskMemAlloc((Length(FOutputStreamInt.DataString) + 1));

Irgendwie sieht das eher nach ANSI aus, anstatt nach Unicode.

Ich seh ein PChar ... bei externen APIs arbeitet man niemals mit dyniamischen Typen!
PWideChar oder PAnsiChar

Und warum seh ich irgendwie nirgendwo etwas von BSTR/WideString?

venice2 29. Mai 2021 18:33

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Genau wegen deiner Problem habe ich eine Wrapper.dll in C# geschrieben die man einfach als Verweis in C# einbinden kann.

Diese regelt dann die Kommunikation zwischen der Delphi.dll -> Wrapper.dll - Anwendung in C#.

Somit konnte ich auf beide Varianten des Codes Einfluß nehmen (UnmanagedType und managedType)
Zudem habe ich alle Strings aus Delphi als PWideChar übergeben. Nicht als PChar !

Delphi..
Delphi-Quellcode:
Pluginfile           : PWideChar;


C# Wrapper
Code:
    [MarshalAs(UnmanagedType.BStr)]
    public string PluginFile = string.Empty;
Nur so als Anregung.
Zitat:

Achte darauf, das du auch einen Unicode-String in Form von PWideChars zurückgibst, das erspart dir weitere Kopfschmerzen: In C# ist jeder String automatisch Unicode.
https://im-coder.com/wie-verwenden-v...-typ-in-c.html
OK bin raus :)

himitsu 29. Mai 2021 20:09

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Wenn in C# ein BSTR, warum dann in Delphi ein PWideChar, anstatt eines WideString? (WideString ist intern ein BSTR)

Die Delphi-DLL wird überarbeitet?
Wozu dann noch ein Wrapper dazwischen, anstatt direkt?

s-off 29. Mai 2021 20:26

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Hi Himitsu,
Zitat:

Zitat von himitsu (Beitrag 1490373)
Zitat:

ich habe mich lange Zeit nicht mehr mit Delphi beschäftigt und stehe nun vor einem Problem
So lange, dass dein Compiler damals noch ANSI war (<2009), aber nun Er mit Unicode arbeitet (2009+) und dein Code durch so Dinge wie String/PChar/... nun nicht mehr das Selbe macht?

Ganz genau das ist das Problem :(

Zitat:

Zitat von himitsu (Beitrag 1490373)
Zitat:

Delphi-Quellcode:
CoTaskMemAlloc((Length(FOutputStreamInt.DataString) + 1));

Irgendwie sieht das eher nach ANSI aus, anstatt nach Unicode.

Ich bin so lange raus und es fällt mir sehr schwer, mich in diese alten Strukturen reinzudenken :(
Gib mir doch mal einen Anschubs, wie das nun mit Unicode funktioniert.


Zitat:

Zitat von himitsu (Beitrag 1490373)
Und warum seh ich irgendwie nirgendwo etwas von BSTR/WideString?

Weil ich nur einige Stellen des Delphi-Codes gepostet habe :)
Wie gesagt: es geht ausschließlich um den Teil innerhalb der Delphi-DLL.
In C# habe ich das schon mit BSTR deklariert ([MarshalAs(UnmanagedType.BStr)]) und die aufgerufene Routine in der Delphi-Dll verwendet natürlich WideString.
Das schrieb ich ja auch in meinem initialen Beitrag ;)
Zitat:

Ich muss aus C# eine Delphi-DLL mit Parametern (BSTR -> WideString) aufrufen, in der DLL ein PDF erzeugen und dieses base64-decodiert wieder zurückgeben.
**

Zitat:

Zitat von venice2 (Beitrag 1490374)
Achte darauf, das du auch einen Unicode-String in Form von PWideChars zurückgibst

Ist in Delphi 10.1 PChar nicht automatisch PWideChar?

Zitat:

Zitat von venice2 (Beitrag 1490374)

Schaue ich mir mal an - besten Dank.
Edit: Ahhhh - wie kann man das auf Englisch umstellen? Da bekommt man ja die Krise bei dieser Übersetzung.

Zitat:

Zitat von himitsu (Beitrag 1490378)
Wenn in C# ein BSTR, warum dann in Delphi ein PWideChar, anstatt eines WideString?

Mache ich ja (siehe oben) ;)

Zitat:

Zitat von himitsu (Beitrag 1490378)
Die Delphi-DLL wird überarbeitet?
Wozu dann noch ein Wrapper dazwischen, anstatt direkt?

Was meinst Du mit Wrapper?
Ich habe einen Webservice, der in C# geschrieben ist.
Dieser soll halt das, was die Delphi-DLL an PDF-Stream erzeugt, an den Caller zurückgeben.
Das ist der ganze Zauber.

himitsu 29. Mai 2021 20:43

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
vor D2009 war
String = AnsiString
PChar = PAnsiChar
Char = AnsiChar

seit D2009 sind es
String = UnicodeString
PChar = PWideChar
Char = WideChar


Entweder alle String/PChar/Char explitit auf ANSI ändern, so wie es früher war

oder jetzt so lassen, wie es nun ist, aber dennoch alle Typen auf Unicode/Wide ändern, (sie sind es jetzt schon, aber wie gesagt, niemals dynamische Typen in externen Schnittstellen)
aber beachten, dass Chars dort 2 Byte groß sind, also der Speicher ist doppelt so groß, was auch bei Length/Size beachtet werden muß.

s-off 29. Mai 2021 21:02

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Zitat:

Zitat von himitsu (Beitrag 1490380)
vor D2009 war
String = AnsiString
PChar = PAnsiChar
Char = AnsiChar

seit D2009 sind es
String = UnicodeString
PChar = PWideChar
Char = WideChar


Entweder alle String/PChar/Char explitit auf ANSI ändern, so wie es früher war

oder jetzt so lassen, wie es nun ist, aber dennoch alle Typen auf Unicode/Wide ändern, (sie sind es jetzt schon, aber wie gesagt, niemals dynamische Typen in externen Schnittstellen)
aber beachten, dass Chars dort 2 Byte groß sind, also der Speicher ist doppelt so groß, was auch bei Length/Size beachtet werden muß.

Alles klar, ich schaue mir das am Montag nochmal im Detail an :) Besten Dank!

s-off 31. Mai 2021 11:39

AW: Weitergabe eines Strings über Pointer funktioniert nicht - manchmal...
 
Hallo zusammen,

kurze Rückmeldung: es funktioniert nun wie gewünscht.

Ich habe aus PChar einen PWideChar gemacht und aus CoTaskMemAlloc und Move wurde WideStrAlloc und StringToWideChar.
Des Weiteren habe ich zum Rückschreiben des PWideChar in den String die SetString-Methode benutzt.
Nun ist alles lecker.


Vielen Dank nochmals an alle Tippgeber!


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