Autor Beitrag
michaelarban
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34



BeitragVerfasst: Mo 28.06.10 21:48 
Hallo,

ich beziehe mich auf den Artikel von Michael Puff:

www.michael-puff.de/...ikel/StringDLL.shtml

In diesem Artikel wird beschrieben, dass man (im Hauptprogramm "program DLLProg;")die Größe des zu reservierenden Buffers (Buffer: PChar;) herausfindet, in dem man die
funktion "func1" 2mal aufruft:

Hier der relevnate codeausschnitt:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
//DLL
library StringDLL;

uses
  SysUtils;

function func1(s: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
var
  foo: String;
begin
  // Strings aneinanderhängen
  foo := 'foo'+ s;
  // nur String in Buffer kopieren, wenn Buffer nicht nil ist
  if Assigned(Buffer) then
    StrLCopy(Buffer, PChar(foo), lenBuffer);
  // auf alle Fälle immer Länge des Strings zurückgeben
  result := length(foo);
end;



//---------------------
//Programm

// Funktion aufrufen, um Größe des Buffers zu ermitteln
  len := func1('bar'nil0);  // 1.  Aufruf
  Str(len, s);
 
  try
    // Speicher anfordern
    GetMem(Buffer, len + 1);
    // Funktion mit Buffer aufrufen --> 2.Aufruf
    len := func1('bar', Buffer, len + 1);
    Str(len, s);

  finally
    // Speicher wieder freigeben
    FreeMem(Buffer);
  end;




Frage:
Dieser einfacher Fall ist verständlich;

Aber wie ist es, wenn in func1 bspw. ein Verzeichnis gelöscht und der gelöschte Verzeichnisname als PChar zurückgegben werden soll? Ich kann die Funktion nicht mehr 2 mal aufrufen, da bereits beim 1. Aufruf das Verzeichnis gehöscht worden ist und ein 2.Aufruf keinen Sinn machen würde.

Wie kann finde ich in diesem Falle die Größe des zu reservierenden Buffers (im Hauptprogramm)?

PS: Sharemem soll nicht verwendet werden, nur PChar

danke
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 29.06.10 07:02 
Du kannst zum Beispiel auch gleich die maximale Anzahl Zeichens eines Pfads (müsste MAX_PATH sein) anfordern. Dann ist der Puffer auf jeden Fall groß genug und du bekommst einfach die tatsächliche Anzahl Zeichen zurück.

In dem von dir genannten zweistufigen Verfahren darf im ersten Aufruf noch nichts passieren. Sondern erst, wenn ein gültiger und ausreichend großer Puffer übergeben wurde.

Wenn du die Rückgabe des Wertes nur optional machen willst, kannst du das so machen:
ausblenden Quelltext
1:
2:
3:
4:
5:
Buffer    BufSize   Aktion    Rückgabe
nil       0         ja        keine
<> nil    0         nein      Buffergröße  // ist im nächsten Fall auch drin, aber zur Verdeutlichung
<> nil    zu klein  nein      Buffergröße
<> nil    reicht    ja        entsprechender Wert
Das heißt du schaust ob der Buffer nil ist. Wenn ja, dann ist keine Rückgabe im Buffer gewünscht und du führst einfach die Aktion durch.
Wenn Buffer <> nil ist, dann schaust du dir die Buffergröße an. Ist diese zu klein, dann gibst du die benötigte Größe zurück und machst nichts. Wenn die Buffergröße ausreicht, dann führst du die Aktion durch und lieferst den Wert im Buffer.

Nebenbei:
Wenn du es genau so machst wie von dir oben beschrieben, dann fehlt dir im Grunde die Möglichkeit einen Fehlercode zurückzuliefern. Deshalb ist es evtl. sinnvoller die Buffergröße by reference zu übergeben und ggf. zu verändern. Dann kannst du in Result das Ergebnis der Funktion zurückliefern.
Dafür kannst du dann nicht einfach 0 übergeben, sondern musst eine Variable angeben.
michaelarban Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34



BeitragVerfasst: Di 29.06.10 20:25 
hallo jaenicke,

erstmal danke für deine Antwort.

Eine generelle Frage; könnte man auch folgenderweise vorgehen, um Strings zwischen Anwendung und DLL auszutauschen ohne Speicher zu allokieren?:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
//DLL
procedure func1 (pcharInput : PChar; Out pcharOutput : PChar) //Verwendung des Out
var
  myString : String;
begin
  myString := gibVieleX(); // kann eine zufällige Anzahl von  X liefern, also 'X'  oder 'XX' oder 'XXX'  oder 'XXXX'  usw.

 myString := myString + pcharInput ; //zusammenfügen

 pcharOutput := PChar (myString); // Casten von String -->PChar  und an den Out-Parameter zuweisen
  
end;


//Anwendung
procedure testen()
var
   pcharRein : Pchar;
   pcharRaus : PChar;
   
   ergebnis : String;
   
begin
   rein := PChar ("gib mir Xe: ");
  
   func1(pcharRein, pCharRaus);
   
   ergebnis := String (pCharRaus); //Casten PChar --> String
   //Inhalte der variable ergebnis  wäre bspw. "gib mir die Xe: XXXXXXXX"  oder
  //gib mir die Xe: XXX"  oder gib mir die Xe: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"  usw.
  
 
end;


Frage:
- würde das denn nicht funktionieren (ohne irgendwelchen Speicher zu allokieren)??

Danke
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 29.06.10 20:34 
Moin!

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
Eine generelle Frage; könnte man auch folgenderweise vorgehen, um Strings zwischen Anwendung und DLL auszutauschen ohne Speicher zu allokieren?
Nein, das funktioniert leider nicht. :nixweiss:

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
//DLL
procedure func1 (pcharInput : PChar; Out pcharOutput : PChar) //Verwendung des Out
var
  myString : String;
begin
  myString := gibVieleX(); // kann eine zufällige Anzahl von  X liefern, also 'X'  oder 'XX' oder 'XXX'  oder 'XXXX'  usw.

 myString := myString + pcharInput ; //zusammenfügen

 pcharOutput := PChar(myString)// Casten von String -->PChar  und an den Out-Parameter zuweisen
Das ist kein Typecast, sondern hier wird lediglich die Adresse der Daten auf dem Heap geliefert. Wenn die Prozedur beendet wird, sind auch die Daten ungültig. Du musst den Inhalt des Strings an die Adresse kopieren, auf die pcharOutput zeigt (z.B. mit Move()). Und hier kommt dann die größe des Buffers ins Spiel... :idea: ;)

Weiteres Problem: Char/PChar ist ein generischer Typ, bis D2k9 ist das AnsiChar/PAnsiChar, danach Unicode! :shock:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
michaelarban Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34



BeitragVerfasst: Di 29.06.10 20:51 
hallo,

ich verstehe nicht, wie ich ddann bei dem oben genannten Beispiel mit GetMem Speicherplatz für die Pchar-Variable (pcharRaus) in der Hauptanwendung reservieren soll, wenn ich nicht weiss wie groß in der DLL die Variable myString ist, denn bei jedem Aufruf hat dieseVariabe eine andere Anzahl von Xen.

Weisst du wie ich die richtige Speichergröße allokieren kann?
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 29.06.10 21:22 
Moin!

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
ich verstehe nicht, wie ich ddann bei dem oben genannten Beispiel mit GetMem Speicherplatz für die Pchar-Variable (pcharRaus) in der Hauptanwendung reservieren soll, wenn ich nicht weiss wie groß in der DLL die Variable myString ist, denn bei jedem Aufruf hat dieseVariabe eine andere Anzahl von Xen.
Wenn du keine Obergrenze für die Buffergröße angeben kannst, dann bleibt nur, die Funktion mit einem Fehler abzubrechen, wenn der Buffer zu klein ist, da gibt´s leider keine andere Möglichkeit. :?

Das mit den Xen ist doch etwas schwammig, worum geht´s denn da wirklich, vielleicht gibt´s ja Alternativen. :nixweiss:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
michaelarban Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 34



BeitragVerfasst: Di 29.06.10 21:56 
verstehe ich das richtig, dass der Buffer eine Obergrenze haben muss?

Wenn ich also weiss, dass die Funktion func1 nicht mehr als 1020 Xen zurückliefern wird, kann ich die Buffergröße bspw. auf 3000 setzen:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
//DLL
procedure func1 (pcharInput : PChar;  pcharBuffer : PChar) 
var
  myString : String;
begin
  myString := gibVieleX(); // kann eine zufällige Anzahl von  X liefern, also 'X'  oder 'XX' oder 'XXX'  oder 'XXXX'  usw.

 myString := myString + pcharInput ; //zusammenfügen

  if Assigned(pcharBuffer) then
    StrLCopy(pcharBuffer, PChar(myString), Length(myString));//myString hat höchstens 1020 Zeichen 
  
end;


//Anwendung
procedure testen()
var
   pcharRein : Pchar;
   pcharBuffer : PChar;
   
   ergebnis : String;
   
begin
   pcharrein := PChar ("gib mir Xe: ");
   
   GetMem(pcharBuffer, 3000 + 1); //reserviere genug Speicher
 
   func1(pcharRein, pcharBuffer);
   
   ergebnis := String (pcharBuffer); //Casten PChar --> String
    
end;



Frage:
wie sieht dann die String-Variable ergebnis aus, wenn bspw. nur 4 Xen zurückgeliefert wreden:
"gib mir die Xe: XXXX"
oder so
"gib mir die Xe: XXXXvynbcviuhqp98tflkjh3453409lkgdfgsdfglg pwe96230909tu ....datenmüll bis zur 3000.stelle#0" , 3001.Stelle wäre das Null-Zeichen

user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Das mit den Xen ist doch etwas schwammig, worum geht´s denn da wirklich, vielleicht gibt´s ja Alternativen.
ein Beispiel wäre, dass man in einer DLL-Funktion einen SQL-String zusammenbaut, der (je nach Logik) länger oder kürzer sein kann und den SQL-String an die Haupanwendd ng zurückgibt.

bye

Moderiert von user profile iconNarses: Zitat kenntlich gemacht.
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 29.06.10 22:13 
Moin!

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
verstehe ich das richtig, dass der Buffer eine Obergrenze haben muss?
Nun, was heisst "muss" ;) du forderst ja ein Stück Speicher an, und das hat halt eine bestimmte Größe. :nixweiss:

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
Wenn ich also weiss, dass die Funktion func1 nicht mehr als 1000 Xen zurückliefern wird, kann ich die Buffergröße bspw. auf 3000 setzen:
Ja, so etwa sollte das laufen. :)

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
//DLL
procedure func1 (pcharInput : PChar;  pcharBuffer : PChar) 
var
  myString : String;
begin
  myString := gibVieleX(); // kann eine zufällige Anzahl von  X liefern, also 'X'  oder 'XX' oder 'XXX'  oder 'XXXX'  usw.

 myString := myString + pcharInput ; //zusammenfügen

  if Assigned(pcharBuffer) then
    StrLCopy(pcharBuffer, PChar(myString), Length(myString));//myString hat höchstens 1020 Zeichen
Hier fehlt natürlich noch die Abfrage, ob der Buffer groß genug ist, heisst: du musst auch die Buffergröße als Parameter durchreichen. :idea:

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
wie sieht dann die String-Variable ergebnis aus, wenn bspw. nur 4 Xen zurückgeliefert wreden:
"gib mir die Xe: XXXX"
Jup, genau so. ;)

user profile iconmichaelarban hat folgendes geschrieben Zum zitierten Posting springen:
oder so
"gib mir die Xe: XXXXvynbcviuhqp98tflkjh3453409lkgdfgsdfglg pwe96230909tu ....datenmüll bis zur 3000.stelle#0" , 3001.Stelle wäre das Null-Zeichen
Du hast doch bestimmt mal in die DOH zum Thema StrLCopy geschaut, oder? Und da sollte stehen, dass StrLCopy einen Null-Terminator schreibt. :les: :think:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.