Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Stringübergabe in DLL´s (https://www.delphipraxis.net/731-stringuebergabe-dll%B4s.html)

Christian Kaufmann 29. Aug 2002 16:10


Stringübergabe in DLL´s
 
Hallo NG,
nachdem ich nicht mehr weiter weiß wende ich mich vertrauensvoll an euch hehe

Ich möchte in einer DLL sehr lange Strings (Texte) bearbeiten. Nun habe ich gelesen, dass eine Stringübergabe an Funktionen in einer DLL nicht sehr geschickt ist, da diese von C - C# nicht verarbeitet werden können (brauchen Nullterminierte Strings).
Soweit so gut, bergebe ich halt PChars statt Strings.
Das funktioniert auch problemlos bei kleineren Texten, wenn aber nun große Texte übergeben werden, bekomme ich eine Exception.

Hat irgendwer eine Idee, wie ich Lange Strings in DLL´s abarbeiten kann?

Danke für eure Hilfe

Christian Seehase 29. Aug 2002 17:23

Moin Christian,

da stellen sich mir doch gleich ein paar Fragen: ;-)
  • Was verstehst Du unter "lang"?
  • Welche Exception wird ausgelöst?
  • Hast Du mal geprüft, an welcher Stelle genau der Fehler auftritt? (z.B. immer an der gleichen Stringposition bei der Verarbeitung, sofort, wenn die Funktion in der DLL angeprochen wird)
  • Wie übergibst Du den String? (Funktionsdeklaration)
  • Hast Du mal probiert statt eines PChar einen Pointer zu übergeben, mit anschliessendem TypeCast auf PChar in der Funktion?

Christian Kaufmann 29. Aug 2002 18:22

Hallo, du hast natürlich vollkommen recht, die Infos sind ein bissl spärlich.

Ich wollte nur mal wissen, ob es problemlos funtioniert deshalb hab ich mir eine Dll gebastelt, die einen Pchar wert bekommt und und den Text spiegelverkehrt wiedergibt.

so etwa:
function(p:pchar):pchar;
begin ...

Über Pointer hab ichs probiert aber ich muß gestehen ich habs damit ned zusammengebracht (schäm) mit Pointer bin ich ned so sattelfest leider, weiß zwar im Groben wie ichs zuweis aber bei folgendem funktionsaufruf hat der Debugger immer geschriehen:
function(p:Pointer):Pointer;
...[Fehler] Unit1.pas(26): Bezeichner erwartet, aber '(' gefunden

Bei pchar funktionierts ganz gut, bei kleinen Textpassagen hatte ich kein Problem. Als ich aber zum Test den Inhalt eines mehrseitigen Worddoc reinkopierte, gabs eine Exception (welche liefere ich nach hab den Source nicht bei mir zum austesten)
Ich hab dann mal einfach einen String erstellt indem ich eine schleife bis 50000 durchlaufen lies und zum String jeweils inttostr((i mod 10)) dazugab. Nun hatte ich einen String 50000 Zeichen lang der immer von 1-10 hochzählte. Da hatte ich witzigerweise keine exception. Nun lies ich jedesmal bei 0 zusätzlich in den String #13#10 einfügen und siehe da, ich hatte eine Exception und zwar immer an der selben Stelle etwa 3574te #13#10.
Das hat übrigens nichts mit der DLL zu tun, denn nachdem ich die nicht gut debuggen konnte (hab bei Start|Parameter die Exe aufgerufen, die die Dll aufruft, dennoch konnte ich sie nicht debuggen verlangte immer nach Server und parameter oder so) kopierte ich die DLL Funktion einfach in die Exe, der Erfolg war derselbe eine Exception.

Hoffe die Beschreibung ist jetzt ein bissl genauer, sodaß ihr damit was anfangen könnt.

lg
Christian

CalganX 29. Aug 2002 19:05

Kurz was mir auffält:
du kannst nicht function(p: Pointer):Pointer schreiben. Erstens brauchst du einen Funktionsnamen (z.B. f), zweitens als Pointer ist gemeint, dass du einen Type deklarierst. Z.B. ist PChar = ^String (o.ä.) (Pointer auf String). Ich würde empfehlen, dass du dir ein Tutorial in Turbo Pascal über Pointer ansiehst...

Chris

Zu Funktionen: Dann schreibst du z.B. function f(p:Pchar): PChar;

Christian Kaufmann 29. Aug 2002 19:07

Zitat:

Zitat von Chakotay1308
Kurz was mir auffält:
du kannst nicht function(p: Pointer):Pointer schreiben. Erstens brauchst du einen Funktionsnamen (z.B. f), zweitens was ist Pointer? Hast du den Type selbst deklariert und hast du das mit Pointer... so verstanden?

Chris

das ich einen funktionsnamen brauch ist eh klar hab ich in der eile jetzt mal weggelassen weils eh wurscht ist wie die heißt.
jo hab das mit dem pointer so verstanden hehe ich weiß eh dass es falsch ist und ned geht (heul) wie wäre der richtige aufruf?
Naja aber mit pchar müßts ja gehn das ist ja auch nur ein pointer will aber ned :(

CalganX 29. Aug 2002 19:10

Sorry, die Antwort ist eigentlich schon da... Hab' den Beitrag grad' geändert...
Also, wie gesagt: ein Pointer ist kein eigener Typ. Sondern, eine Art (sozusagen) also wäre ein Regulärer Pointer (ich nehme jetzt mal TP als Beispiel):
Code:
type
  meintyp = array[1..10] of string;
  meinpointer = ^meintyp;
Soähnlich läuft das dann auch unter Delphi (fast kein Unterschied)...

Chris

d3g 29. Aug 2002 19:26

Hi Chakotay,

ein Pointer ist sehr wohl ein Datentyp... Kompilier mal folgendes:

Code:
procedure ShowMsg(p: Pointer);
begin
  MessageBox(0, PChar(p), 'Test', 48);
end;

procedure Test;
var
  s: String;
begin
  s := 'Test';
  ShowMsg(Pointer(s));
end;
Es wird funktionieren...

MfG,
d3g

CalganX 29. Aug 2002 19:41

Allerdings ist Pointer nicht zu vergleichen mit z.B. ^meintyp, oder?

Chris

d3g 29. Aug 2002 20:18

Doch. Ein Typecast von Pointer auf PMeintyp ist ohne weiteres möglich:
Code:
type
  TMeinTyp = array[1..100] of Integer;
  PMeinTyp = ^TMeinTyp;

procedure ShowValues(p: Pointer);
var
  i: Integer;
begin
  for i := 1 to 100 do
    ShowMessage(IntToStr(PMeinTyp(p)^[i]));
end;

procedure Test;
var
  i: Integer;
  t: TMeinTyp;
begin
  for i := 1 to 100 do
    t[i] := i;
  ShowValues(Pointer(@t));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Test;
end;
MfG,
d3g

Christian Kaufmann 30. Aug 2002 00:46

Hier nun die versprochene Exception die ausgelöst wird:

... ist eine Exception der Klasse EAccessVioalation aufetreten...

Christian Seehase 30. Aug 2002 10:49

Moin Christian,

könntest Du bitte mal den Code der Routine die den string erzeugt, und der verarbeitenden Funktion posten (incl. Aufruf derselben)?
Irgendwie kann ich mir im Moment keine Ursache für das Problem erklären.
Der Funktionsabschnitt, in der die Execpetion ausgelöst wird wäre auch nicht schlecht.

Luckie 30. Aug 2002 12:44

Kuckst du hier: http://home.t-online.de/home/PeterJH...icles/dlls.htm

Christian Kaufmann 30. Aug 2002 12:48

Am Formular ist ein Memofeld und ein Button.


function spiegeln(p:pchar):pchar;
var
i:integer;
test1,test:string ;
begin

test:=string(p);
test1:='';
for i := length(test) downto 1 do
begin
test1:=test1+test[i];
end;
result:=pchar(test1);

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
memo1.Text:=(spiegeln(pchar(memo1.Text)));
end;

jbg 30. Aug 2002 13:19

Code:
[b]function[/b] spiegeln(p: PChar): PChar;
[b]var[/b] test: [b]string;
begin[/b]
  test := [b]string[/b](p);
  ...
  Result := PChar(test);
[b]end[/b];
Um dir mal zu erklären, warum das nicht funktioniert:

Dein Programm ruft spiegeln(PChar(MyString)) auf, dabei wird PChar(MyString) auf dem sog. Stack abgelegt und p zeigt auf die Speicherstelle im Stack, an der PChar(MyString) liegt. (Danach kommt noch die Rücksprungadresse auf den Stack.)
Nun wird Platz auf dem Stack für die lokale Variable test gemacht. In diese kopierst du den PChar, auf den p zeigt und führst deine Umkehrung durch.
Bis jetzt funktioniert alles, doch nun kommt der entscheidende logische Fehler:
Du weist Result nun einen Zeiger (PChar ist ja nur ein Zeiger) auf die Speicheradresse, an der test abgelegt ist, zu. Doch da test eine lokale Variable ist und somit auf dem Stack liegt, zeigt Result auf eine Adresse im Stack.
Durch das end; wird nun aber der von test belegte Stack-Speicher freigegeben, womit Result auf einen bereits freigegebenen Speicherbereich zeigt. Wenn nun ein weiterer Funktionsaufruf nach dem Zurückkehren der spiegeln-Funktion stattfindet (es reicht auch eine einfache Zuweisung von Variablen), dann überschreibt diese die noch auf dem Stack an der Result-"Adresse" liegenden, unveränderten Bytes mit ihren eigenen lokalen Variablen und Parametern.
Deswegen liefert die Funktion nur noch Bytesschrott zurück, der auch noch zum teilweise auch noch aus den "orignal"-Daten bestehen kann (was die Fehlersuche dann eher auf die Funktion selbst ziegt).


Das Problem könntest du z.B. so lösen, dass du den String "in-place" spiegelst (also Eingabe-Parameter = Ausgabe-Parameter):
Code:
[b]procedure[/b] spiegeln(p: PChar);
[b]var[/b] s: [b]string[/b];
[b]begin[/b]
  s := p;
  ...
  StrLCopy(p, PChar(s), StrLen(p)); // Daten in p zurückschreiben
[b]end[/b];

s := 'Dies ist ein Test';
spiegeln(PChar(s));
ShowMessage(s);
Das in-place-Verfahren funktioniert nur, wenn die Länge der Eingabedaten mit der der Ausgabe-Daten übereinstimmt.

Oder um es WinAPI gerecht zu machen:
Code:
[b]function[/b] spiegeln(p: PChar; Buffer: PChar; MaxBufferLen: Integer): Integer;
[b]var[/b] s: [b]string[/b];
[b]begin[/b]
  s := p;
  ...
  [b]if[/b] Buffer <> [b]nil then[/b]
    StrLCopy(Buffer, PChar(s), MaxBufferLen); // Daten in Buffer schreiben
  Result := Length(s); // Rückgabewert = Länge des bearbeiteten Strings
[b]end[/b];

[b]var[/b]
  Buf: PChar;
  BufSize: Integer;
[b]begin[/b]
  s := 'Dies ist ein Test';
  BufSize := spiegeln(PChar(s), nil, 0);
  Buf := StrAlloc(BufSize);
  [b]try[/b]
    spiegeln(PChar(s), Buf, BufSize);
  [b]finally[/b]
    StrDipose(Buf);
  [b]end[/b];
  ShowMessage(s);
[b]end[/b];

[i]// Wenn die max. Aufgabelänge bekannt ist, dann geht das auch so:[/i]
[b]var[/b] Buf: [b]array[/b][0..1024] [b]of[/b] Char;
SetString(s, Buf, spiegeln(PChar(s), Buf, 1024));
ShowMessage(s);
Ein function Xyz: PChar; sollte man nur verwenden, wenn man konstante oder global deklarierte Strings einsetzt. Konstante Strings sind nur Strings, die in '' stehen. Wenn ein + oder ein Funktionsaufruf notwendig sind, dann handelt es sich nicht um konstante Strings, da sie erst zur Laufzeit zusammengesetzt werden müssen. Am einfachsten kann man herausbekommen, ob ein String konstant ist oder nicht, indem man sich selbst nur Fragen muss, könnte man diesen String auch so deklarieren:

const MyString = 'Hier steht der zu "prüfende" String';

Christian Kaufmann 30. Aug 2002 17:23

Alles klar, ich hab einfach übersehen, daß ich mit dem result natürlich einen zeiger auf die stringvariable in der funktion zurückgebe, die natürlich nach dem end dereferenziert wird.
manchmal ist man schrecklich blind


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