Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   C++ Dll soll Zeichenkette manipulieren (https://www.delphipraxis.net/128565-c-dll-soll-zeichenkette-manipulieren.html)

rey003 1. Feb 2009 15:05


C++ Dll soll Zeichenkette manipulieren
 
Hallo DPler,
ich stehe grade mal wieder voll auf dem Schlauch. Und zwar möchte ich den Zeiger zu einem String an eine in C++ geschriebene DLL übergeben. Dort soll er dann verändert werden.

Mit Zahlen ist das Ganze kein großes Problem, aber bei der Umsetzung mit Zeichenketten scheitert es dann komplett bei mir!
Ich bin C++ Anfänger. Will mich jedoch mal der Herrausforderung stellen ein Teil in eine C++ Dll auszulagern (als Übung).

Hier mal mein Code für Integer:

C++ Dll:
Code:
extern "C" __declspec(dllexport) void Zahl_aendern(int * zahl);

void Zahl_aendern(int * zahl)
{
    *zahl = 50;
}
Aufruf in Delphi:
Delphi-Quellcode:
procedure Zahl_aendern(zahl: PInteger); cdecl; external 'meine_dll.dll';

...

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  i:= 12;
  showmessage(inttostr(i)); //12
  Zahl_aendern(@i);
  showmessage(inttostr(i)); //50
end;
Das funktioniert perfekt, aber wie gestaltet man das Ganze mit Strings?
Ich hoffe Ihr könnt mir helfen :)

Luckie 1. Feb 2009 15:17

Re: C++ Dll soll Zeichenkette manipulieren
 
In C gibt es keine Zeichenketten, die in Delphi einem String entsprächen. In C gibt es nur Charakter-Arrays. Diese entsprechen in Delphi dem Datentyp PChar. Und dann geht eigentlich alles so wie hier http://www.michael-puff.de/Artikel/StringDLL.shtml beschrieben.

IN C++ gibt es noch die String Klasse, definiert in String.h, glaube ich. In wie fern diese mit den Delphi Stringtyp kompatibel sind kann ich nicht sagen. Ich vermute aber sie sind eher nicht kompatibel.

rey003 1. Feb 2009 18:41

Re: C++ Dll soll Zeichenkette manipulieren
 
ja so ungefähr war mir das ganze schon klar auch wenn es ziemlich komplex gemacht ist, aber mir fehlt jegliche vorstellung wie ich es in c++ umsetzen soll. :(

PChar liefert mir zwar den Zeiger auf den Char-Array, aber ich kann den Inhalt nicht verändern in C++.

Das führt zu folgendem Fehler:
Code:
extern "C" __declspec(dllexport) void String_aendern(char * sstring);

void String_aendern(char ** sstring)
{
   *sstring = "Hallo";
}
error C2440: '=': 'const char [6]' kann nicht in 'char' konvertiert werden

Mir fehlt die Grundlage in C++ :(

Der Jan 2. Feb 2009 13:07

Re: C++ Dll soll Zeichenkette manipulieren
 
Hallo,

erstmal kannst du nicht einmal char* und einmal char** schreiben. char* reicht :) . Da hast du dann auch die kompatibilität mit dem Delphi PChar.
Dann solltest du den neuen String mittels

Code:
strcpy(sstring, "Hallo");
einkopieren. Dann sollte es gehen. Kann sein, dass Quelle und Ziel vertauscht sind, da musst du mal in der Hilfe schauen, hab ich jetzt nicht im Kopf.

Edit: Die C++Klasse string kommt aus der STL (Standard Template Library oder Standard C++ Library, wie sie heute heißt, trotzdem sagen alle STL :) ) und ist definitiv nicht kompatibel mit einem Delphi-Stringtyp.

rey003 2. Feb 2009 14:00

Re: C++ Dll soll Zeichenkette manipulieren
 
Der Fehler oben im Quelltext war unbeabsichtigt. Hab ihn im Original auch nicht gehabt.
StrCpy führt immer zu einer Zugriffsverletzung in der DLL (meldet Delphi).

Der Jan 2. Feb 2009 14:08

Re: C++ Dll soll Zeichenkette manipulieren
 
Kommt die AV beim Aufruf oder direkt bei strcpy? Du könntest mal versuchen, die DLL-Funktion direkt aus C aufzurufen, ob das da auch passiert.

himitsu 2. Feb 2009 14:28

Re: C++ Dll soll Zeichenkette manipulieren
 
Wenn der String aka AnsiString verändert werden soll, dann geht das maximal nur dann, wenn der String nicht länger wird und die Referenzzählung auf 1 steht.

Wenn de String länger werden soll, mehr als eine Referenz existiert und bei Konstanten müßte man Speicher reservieren und dazu müßtest du auch noch die Funktionen des Speichermanagers exportieren.


Bei WideString ist es einfacher, da dieser Delphiintern einen OleStr kapselt, welcher auch in C verfügbar wäre.

sirius 2. Feb 2009 15:10

Re: C++ Dll soll Zeichenkette manipulieren
 
Einen Delphistring in C zu verändern, das ist ja, wie mit dem Flugzeug zum Bahnhof zu fliegen um dann mit dem Zug nach Mallorca zu fahren.

Wie himitsu schon schreibt, du kannst einen Delphistring nicht in C verändern. Entweder du nimmst einen widestring, oder du präparierst den string vorher um. Dazu musst du ihn in ein array[0..x] of char kopieren (am besten mit "move").

himitsu 2. Feb 2009 15:45

Re: C++ Dll soll Zeichenkette manipulieren
 
Liste der Anhänge anzeigen (Anzahl: 1)
ich bin nich so gut in C ... drum hab ich's mal in Delphi gemacht, aber läßt sich bestimmt leicht konvertieren


drei Funktionen welche dann in der C-DLL drin wären
1: versucht 'nen kürzeren String einzutragen
2: versucht 'nen längeren String einzutragen
3: trägt einen String ein, incl. Speichermanagement
Delphi-Quellcode:
  Type PAnsiStringInfo = ^TAnsiStringInfo;
    TAnsiStringInfo = packed Record
      RefCount:    LongInt;
      ElementCount: LongInt;
      DataAddr:    packed Array[0..0] of AnsiChar;
    End;

  Function _SetString1(PString: PPointer): Boolean; StdCall;
    Var i: Integer;
      S: AnsiString;
      P: PAnsiStringInfo;

    Begin
      SetLength(S, 5);
      For i := 1 to 5 do S[i] := Chr($41 + Random(26));


      P := Pointer(Integer(PString^) - 8);
      If (PString^ <> nil) and (P.RefCount = 1)
          and (P.ElementCount >= Length(S)) Then Begin
        P.ElementCount := Length(S);
        MoveMemory(@P.DataAddr, PAnsiChar(S), Length(S) + 1);
        Result := True;
      End Else Result := False;
    End;

  Function _SetString2(PString: PPointer): Boolean; StdCall;
    Var i: Integer;
      S: AnsiString;
      P: PAnsiStringInfo;

    Begin
      SetLength(S, 10);
      For i := 1 to 10 do S[i] := Chr($41 + Random(26));


      P := Pointer(Integer(PString^) - 8);
      If (PString^ <> nil) and (P.RefCount = 1)
          and (P.ElementCount >= Length(S)) Then Begin
        P.ElementCount := Length(S);
        MoveMemory(@P.DataAddr, PAnsiChar(S), Length(S) + 1);
        Result := True;
      End Else Result := False;
    End;

  Type TDelphiGetMemory = function(Size: Integer): Pointer; CDecl;
       TDelphiFreeMemory = function(P:   Pointer): Integer; CDecl;

  Var DelphiGetMemory: TDelphiGetMemory;
      DelphiFreeMemory: TDelphiFreeMemory;

  Procedure SetString3Memory(GetMem: TDelphiGetMemory; FreeMem: TDelphiFreeMemory); StdCall;
    Begin
      DelphiGetMemory := @GetMem;
      DelphiFreeMemory := @FreeMem;
    End;

  Function _SetString3(PString: PPointer): Boolean; StdCall;
    Var i: Integer;
      S: AnsiString;
      P: PAnsiStringInfo;

    Begin
      SetLength(S, 7);
      For i := 1 to 7 do S[i] := Chr($41 + Random(26));


      P := Pointer(Integer(PString^) - 8);
      If S > '' Then Begin
        If (PString^ = nil) or (P.RefCount = -1) Then Begin
        End Else If P.RefCount > 1 Then Begin
          Dec(P.RefCount);
        End Else If P.ElementCount <> Length(S) Then Begin
          DelphiFreeMemory(P);
        End;
        P := DelphiGetMemory(8 + Length(S) + 1);
        P.RefCount    := 1;
        P.ElementCount := Length(S);
        MoveMemory(@P.DataAddr, PAnsiChar(S), Length(S) + 1);
        PString^ := Pointer(Integer(P) + 8);
      End Else Begin
        If PString^ <> nil Then
          If P.RefCount = -1 Then
            PString^ := nil
          Else If P.RefCount <> 0 Then Begin
            Dec(P.RefCount);
            If P.RefCount = 0 Then Begin
              DelphiFreeMemory(P);
              PString^ := nil;
            End;
          End;
      End;
      Result := True;
    End;
und die Testaufrufe:
Delphi-Quellcode:
  Var ZweitString: AnsiString;

  Procedure TForm1.FormCreate(Sender: TObject);
    Const BoolStr: Array[Boolean] of String = ('False', 'True ');
      ConstString = '123456789';

    Var S: AnsiString;
      B: Boolean;

    Begin
      Memo1.Lines.Add('');
      Memo1.Lines.Add('Leerstring');
      Memo1.Lines.Add('ConstString');
      Memo1.Lines.Add('String mit Referenzzähler = 1');
      Memo1.Lines.Add('String mit Referenzzähler = 2');

      Memo1.Lines.Add('');
      S := '';
      Memo1.Lines.Add(Format('x1 - False - "%s"', [S]));
      S := ConstString;
      Memo1.Lines.Add(Format('x2 - False - "%s"', [S]));
      SetLength(S, 8);
      Memo1.Lines.Add(Format('x3 - False - "%s"', [S]));
      ZweitString := S;
      Memo1.Lines.Add(Format('x4 - False - "%s"', [S]));

      Memo1.Lines.Add('');
      S := '';
      B := SetString1(S); Memo1.Lines.Add(Format('11 - %s - "%s"', [BoolStr[B], S]));
      S := ConstString;
      B := SetString1(S); Memo1.Lines.Add(Format('12 - %s - "%s"', [BoolStr[B], S]));
      SetLength(S, 8);
      B := SetString1(S); Memo1.Lines.Add(Format('13 - %s - "%s"', [BoolStr[B], S]));
      ZweitString := S;
      B := SetString1(S); Memo1.Lines.Add(Format('14 - %s - "%s"', [BoolStr[B], S]));

      Memo1.Lines.Add('');
      S := '';
      B := SetString2(S); Memo1.Lines.Add(Format('21 - %s - "%s"', [BoolStr[B], S]));
      S := ConstString;
      B := SetString2(S); Memo1.Lines.Add(Format('22 - %s - "%s"', [BoolStr[B], S]));
      SetLength(S, 8);
      B := SetString2(S); Memo1.Lines.Add(Format('23 - %s - "%s"', [BoolStr[B], S]));
      ZweitString := S;
      B := SetString2(S); Memo1.Lines.Add(Format('24 - %s - "%s"', [BoolStr[B], S]));

      Memo1.Lines.Add('');
      SetString3Memory(@GetMemory, @FreeMemory);
      S := '';
      B := SetString3(S); Memo1.Lines.Add(Format('31 - %s - "%s"', [BoolStr[B], S]));
      S := ConstString;
      B := SetString3(S); Memo1.Lines.Add(Format('32 - %s - "%s"', [BoolStr[B], S]));
      SetLength(S, 8);
      B := SetString3(S); Memo1.Lines.Add(Format('33 - %s - "%s"', [BoolStr[B], S]));
      ZweitString := S;
      B := SetString3(S); Memo1.Lines.Add(Format('34 - %s - "%s"', [BoolStr[B], S]));
    End;
Code:
Leerstring
ConstString
String mit Referenzzähler = 1
String mit Referenzzähler = 2

x1 - False - ""
x2 - False - "123456789"
x3 - False - "12345678"
x4 - False - "12345678"

11 - False - ""
12 - False - "123456789"
13 - True - "CMBVB"
14 - False - "CMBVB"

21 - False - ""
22 - False - "123456789"
23 - False - "12345678"
24 - False - "12345678"

31 - True - "CUMWNOY"
32 - True - "RIASZUT"
33 - True - "FDXUPWR"
34 - True - "HCQHWEH"
wie man sieht, geht es nur bedingt.
11 - kein String vorhanden
12 - geht nicht, da Konstante und somit ist kein Speicher reserviert
13 - Sring ist kurz genug und Referenzzählung steht auf 1
14 - Referenzzählug > 1

21 - kein String vorhanden
22 - geht nicht, da Konstante und somit ist kein Speicher reserviert
23 - Referenzzählung steht zwar auf 1, aber einzutragender String ist zu lang
24 - Referenzzählug > 1

Im Endefekt muß man den String auf seine interne Struktur zerlegen und alles selbst verwalten ... es sei denn, einer erfindet 'ne Klasse dafür

bzw. man macht auf _SetString3 eine Prozedur, welcher man zusätzlich noch 'nen C-String übergeben kann, welcher in den Delphi-String kopiert wird.

einzig und allein die 23 liese sich noch ändern, indem alles was länger ist abgeaschnitten wird.

Der Jan 2. Feb 2009 16:26

Re: C++ Dll soll Zeichenkette manipulieren
 
Was mir wegen der AV grad noch eingefallen ist: Reservierst du in deinem Delphiprogramm Speicher für den String?

[OT]
Zitat:

Zitat von sirius
Einen Delphistring in C zu verändern, das ist ja, wie mit dem Flugzeug zum Bahnhof zu fliegen um dann mit dem Zug nach Mallorca zu fahren.

LOL. Das muss ich mir merken

[\OT]


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:54 Uhr.
Seite 1 von 2  1 2      

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