Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Callback Funktionen und DLL? (https://www.delphipraxis.net/94074-callback-funktionen-und-dll.html)

barana 15. Jun 2007 15:10


Callback Funktionen und DLL?
 
Hallo Leute,

ich habe ein Pluginbasierendes Programm geschrieben und hab nun jede Menge Probleme zu bewältigen, die ich nicht einmal verstehe.
Die Plugins sind einfache Win32 DLL's.

Die DLL's besitzen nur 2 Hauptfunktionen, die mit der Delphi Standardkonvetion "register" aufegrufen werden. Die Funktionen heissen "init" und "start" und sehen wie folgt aus.

Delphi-Quellcode:
{DLL Plugin}

type
  TGet = function(link,cookie:pchar;redirect,sendcookie:boolean;
  var location,rawheaders:pchar):pchar ;stdcall;

  TPost = function(link,params,cookie:pchar;redirect,sendcookie:boolean;
  var location,rawheaders:pchar):pchar ;stdcall;
var
  CBGet : TGet ;
  CBPost : TPost ;

function initcb(const get,post:Pointer):pchar ;
begin
  @CBGet := Get ;
  @CBPost := Post ;

  Result := 'Plugin_1' ;
end;

function start(link:pchar):pchar ;
var
  Location, RawHeaders : pchar
begin
  Result := CBPost('http://www.google.de','','',false,false,Location,RawHeaders) ;
end;
Wie man sieht, übergebe ich Init 2 Zeiger auf Funktionen im Hauptprogramm.
Get dient dazu den HTML Code einer beliebigen Seite abzurufen und Post steht eben für einen Post Request zur Verfügung. Diese beiden Fukntionen werden aber mit "stdcall" als Aufrufkonvention aufgerufen, da ich glaube gelesen zu haben, dass Callbackfunktionen sich nur mit "stdcall" vertragen.

Für die echten Funktionen im Hauptprogramm habe ich Indy 9 verwendet und sie sehen folgerndermaßen aus.

Delphi-Quellcode:
{Hauptprogramm}

function PLGHTTPGet(link,cookie:pchar;redirect,sendcookie:boolean;var location,rawheaders:pchar):pchar ; stdcall ;
var
  IDHTTP : TIdHTTP ;
begin
  IDHTTP := TIdHTTP.Create(nil);
  IDHTTP.HandleRedirects := redirect ;
  if sendcookie then
  begin
    IDHTTP.AllowCookies := True ;
    IDHTTP.Request.CustomHeaders.Text := cookie ;
  end
  else
    IDHTTP.AllowCookies := False ;
  try
    Result := pchar(IDHTTP.Get(link)) ;
  except
    Result := 'failed' ;
  end;
  location := pchar(IDHTTP.Response.Location) ;
  rawheaders := pchar(IDHTTP.Response.RawHeaders.text);
  IDHTTP.Free ;
end;

function PLGHTTPPost(link,params,cookie:pchar;redirect,sendcookie:boolean;var location,rawheaders:pchar):pchar ; stdcall;
var
  Parameters : TStringList ;
  HTTP : TIdHTTP ;
begin
  HTTP := TIdHTTP.Create(nil);
  Parameters := TStringList.Create ;
  Parameters.Add(params) ;
  HTTP.HandleRedirects := redirect ;
  if sendcookie then
  begin
    HTTP.AllowCookies := True ;
    HTTP.Request.CustomHeaders.Text := cookie ;
  end
  else
    HTTP.AllowCookies := False ;
  try
    Result := PChar(HTTP.Post(link,Parameters)) ;
  except
    Result := 'failed' ;
  end;
  location := PCHar(HTTP.Response.Location) ;
  rawheaders := pChar(HTTP.Response.RawHeaders.text);
  Parameters.Free ;
  HTTP.Free ;
end;
Die DLL Plugins fasse ich in einem array mit einem Record zusammen und rufe sie nacheinander auf.
Delphi-Quellcode:
{Hauptprogramm} 
  ..
  ...
  try
    MemoOutput.text := PlgArray[i].start(PChar(Link)) ;
  except
  ...
  ..
Soweit sogut.
Es klappt auch alles wunderbar und wird auch richtig ausgeführt, aber dann passieren unter bestimmten Umständen ganz komische Sachen.
Wenn ich die Post Funktion ca. 20 - 30 in einem Plugin aufrufe, dann bekommt das Hauptprogramm nichts mehr als Ergebnis von "start" zurück, obwohl in der DLL alles richtig ablaüft und sie auch das richtige Result zurückgibt.
Dann wird es aber noch abgefahrener und das Hauptprogramm bleibt nach dem Aufruf der DLL einfach sporadisch an irgendeiner Stelle hängen.

Jetzt kann ich es mir absolut nicht erklären.
Es kommt auch nie eine AV, aber manchmal ein Runtime Error.

Könnt ihr einen Fehler entdecken.
Mache ich vielleicht grundsätzlich etwas falsch?

Danke im Voraus.

PS: Ich benutze Delphi 2006.

SirThornberry 15. Jun 2007 15:17

Re: Callback Funktionen und DLL?
 
das liegt daran das du als result ein pchar zurück gibst!
ein PChar ist nichts anderes als ein Zeiger der eben auf das erste Zeichen einer Zeichenkette zeigt.

folgendes klappt noch (ist aber auch unschön)
Delphi-Quellcode:
result := 'staticher Text';
folgendes geht aber nicht!
Delphi-Quellcode:
result := lokaleVarible;
Der Grund ist ganz einfach. PChar ist eben nur ein Pointer auf das erste Zeichen einer Zeichenkette.
Bei dem statichen Text zeigt dieser PChar auf einen fest einkompilierten Text im Speicher (der da fest drin steht).
Bei der lokalen Variblen hingegen zeigt der PChar auf das erste Zeichen der lokalen Variablen die auf dem Stack liegt. Der Stack wird aber beim verlassen der Funktion aufgeräumt und somit zeigt der Pointer auf den Stack wo eventuell schon was ganz anderes liegt wenn du diesen abfragst bzw. nichts mehr liegt.

ste_ett 15. Jun 2007 15:18

Re: Callback Funktionen und DLL?
 
PChar als Rückgabewert einer DLL-Funktion kann schnell zu Fehlern führen, da zwar für die Speicheradresse des PChars Speicher reserviert wird, aber nicht für die Zeichenkette, was dazu führen kann, dass ein anderes Programm diesen Speicherbereich allokiert, und damit u.U. "deine" Daten weg sind.

Entweder sorgst du für eine eigene Speicherverwaltung für die Rückgabewerte, oder, was ich empfehlen würde, die übergibst die PChars als Var-Parameter, dann hast du auch immer den korekten Wert in deiner Funktion.

barana 15. Jun 2007 15:41

Re: Callback Funktionen und DLL?
 
Zitat:

Zitat von ste_ett
Entweder sorgst du für eine eigene Speicherverwaltung für die Rückgabewerte, oder...

Aha und wie würde eine Speicherverwaltung dann aussehen?
Wieso klappt es denn mit einem oder 2 Posts einwandfrei und erst ab ca. 20 wird es erst Fehlerhaft?

SirThornberry 15. Jun 2007 15:53

Re: Callback Funktionen und DLL?
 
eine eigene speicherverwaltung würde auch nichts bringen weil zu dem zeitpunkt wo du drauf zugreifst das ganze gar nicht mehr auf dem stack liegt bzw. du dich da nicht mehr drauf verlassen solltest.

Und warum es am anfang klappt und später nicht liegt daran das irgendwann eben dochmal was auf dem stack landet was deins überschreibt. Somit fehlt dann wohl die abschließende #0 und es wird diese gesucht. Mit etwas pech liegt diese dann außerhalb des Speichers auf den du zugriff hast und schon knallts, hängts etc.
Eine Notlösung wäre das du anstelle einer lokalen Variablen eine globale verwendest, denn diese behält ihren wert auch noch nach verlassen der Procedure da sie nicht auf dem stack liegt. Ist aber eben eine Notlösung. richtiger wäre einen PChar auf bereits reservierten speicher zu übergeben wo deine procedure dann die zeichen rein kopiert.

barana 15. Jun 2007 16:22

Re: Callback Funktionen und DLL?
 
Erstmal danke für die schnellen Antworten, denn ich konnte mir dabei nichts zusammenreimen.

Jetzt übergebe ich das Ergebnis der DLL, per var Pramater zurück ans Hauptprogramm. Dies funktioniert anscheinend auch ganz gut, jedoch kann ich nun das Programm nicht mehr beenden und muss es per Takmanager killen.

Wenn ich debugge, dann erhalte ich am Ende folgende Fehlermeldung.
Zitat:

Es sind zu viele auseinanderfolgende Exceptions aufgetreten: "Zgriffsverletzung bei 0x004015db: Schreiben von Adresse 0x03477e50". Prozess wurde angehalten. Mit Einzelne Anweisung oder Start fortsetzen.

SirThornberry 15. Jun 2007 17:07

Re: Callback Funktionen und DLL?
 
per var-parameter? den Pointer? irgendwas wirst du da durcheinander. kannst du den quelltext mal zeigen? Wenn du einen pointer auf den speicher übergibst wo hinn geschrieben werden soll (also das ergebnis) muss nichts als var-Parameter übergeben werden.

barana 15. Jun 2007 17:38

Re: Callback Funktionen und DLL?
 
Na ich habe aus der start function eine procedure gemacht:

Delphi-Quellcode:
procedure start(link:pchar; var Return:pchar);
var
  Location, RawHeaders : pchar
begin
  Return:= CBPost('http://www.google.de','','',false,false,Location,RawHeaders) ;
end;

SirThornberry 15. Jun 2007 18:19

Re: Callback Funktionen und DLL?
 
so nicht! das ist das gleiche wie mit dem PChar als result. Das musst du erstmal ändern.
Du weißt ja schon wieder die adresse der lokalen Variablen deinem PChar zu.

So macht man das:
Delphi-Quellcode:
//procedure außerhalb der dll
var
  lRes : String;
begin
  SetLength(lRes, 1024);
  ZeroMemory(@lRes[1], Length(lRes));
  DllProcedure(@lRes[1], Length(lRes));
end;

//procedure innerhalb der dll
procedure DllProcedure(ARes: PChar; AResSize);
var
  meinString: String;
begin
  meinString := 'Rückgabe';
  move(ARes^, meinString[1], Min(AResSize, Length(meinString));
end;
(ungetestet)

Luckie 15. Jun 2007 19:05

Re: Callback Funktionen und DLL?
 
Guck mal hier: http://www.michael-puff.de/Developer...ring_DLL.shtml


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