Merkwürdiges bei Übergabe von Record -> Programm

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Targion »

Hallo!
Ich hoffe, ihr könnt mir helfen, sonst werde ich hier noch verrückt!
Also: In meinem Listaller-Projekt wird ein Record mit folgendem Aufbau generiert:

Code: Alles auswählen

TAppInfo = record
  Name: PChar;
  ShortDesc: PChar;
  Version: PChar;
  Author: PChar;
  Icon: PChar;
  UId: PChar;
  Group: TGroupType;
 end;
In einer Lib wird dann eine Callback-Funktion so aufgerufen:

Code: Alles auswählen

TAppEvent = function(name: PChar;obj: TAppInfo): Boolean;cdecl;
Dieser Funktion wird in der Lib ein TAppInfo-Record (oder ein PAppInfo-Pointer, das macht keinen Unterschied, welche Variante man nimmt, es muss nur der Code im Hauptprogramm etwas angepasst werden) übergeben bzw. diese mit dem Record aufgerufen.
immer, wenn das Event ausgelöst wird, erstellt die Hauptanwendung ein neues Objekt vom Type TListEntry und schreibt dort die Informationen Name, ShortDesc, Author, Icon und Version rein. Gleichzeitig wird einem TAppInfo-Recotd innerhalb des TListEntry-Objektes, alle Werte des Callback-Listobjektes übergeben.
Jetzt passiert folgendes:
Alle Werte werden perfekt Gesetzt, die Captions der Labels auf einem TListEntry-Objekt haben also die korrekten werte. Will man jetzt aber einen Wert des Internen TAppInfo-Records einer Liste auslesen, so erhält man willkürlich irgendwelche Strings, die mal im Speicher waren. Warum? Warum funktioniert das Auslesen der richtigen Werte einmal und dann nie wieder? Das verhalten lässt auf fehlerhafte Pointer schließen, ich kann aber keine Fehler entdecken.

(Die Lib sowie das Hauptprogramm haben die Unit cthreads eingebunden, da in andreren Teilen der Lib exzessiv Threads genutzt werden, die Unit cmem ist jedoch nicht enthalten, da diese einige kuriose Fehler verursacht. Das Projekt wird mit FPC 2.3.1 kompiliert.)

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Hitman »

Vermutung: du nutzt in der lib intern String als Datentyp und gibst den dann als PChar gecastet an die Funktion? Strings werden nämlich referenzgezählt, der PChar nicht. Folglich wird der String am Ende deiner Funktion wieder freigegeben und der PChar zeigt plötzlich ins "Leere".
Du müsstest also manuell Speicher allokieren (GetMem) und den PChar da rein kopieren (Move), um ihn zu "konservieren". Allerdings musst (bzw. solltest) du ihn dann auch irgendwann wieder freigeben ... könnte schwer werden, dafür den Richtigen Zeitpunkt zu finden - möchte also gut durchdacht sein.

Eine weitere Alternative:
Du könntest die AppInfo und den Name in der Library auch als globale Variable halten, die im Initialize (oder sonst wo) entsprechend gefüllt wird. Damit würde das ganze erst freigegeben, wenn deine Library wieder entladen wird.

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Targion »

Stimmt, genau das mache ich. Das ist natürlich extrem blöde... Es werden über 100 Einträge geladen, abhängig vom System, da möchte ich nicht alles global speichern. Gibt es eine C-Kompatible Alternative zu PChar?

Ich habe auch ein weiteres Problem: Eine Prozedur sendet Nachrichten an eine Progressbar (über Callback, wie oben) Das funktioniert aber nur eingeschränkt: Bei einem total beliebigen Zeitpunkt (40%, 10%, 50%) springt der Wert auf 0, die Prozedur beendet sich scheinbar, zumindest werden alle Nachfolgenden Anweisungen im Hauptprogramm weiter ausgeführt. Gleichzeitig läuft die Prozedur aber doch weiter und fühert alles wie gewünscht aus.

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Hitman »

Naja nicht PChar ist das Problem, sondern String. Schau dir am besten mal an, was gängige "Best Practice" im C Umfeld bei Plugins ist - ob die selbst den Speicher allokieren oder einen festen Speicherblock vom aufrufenden Programm erwarten. Ich kann mir durchaus beides vorstellen. Aber so oder so wirst du wohl auf irgendeine Weise den Speicher selbst verwalten müssen - sei es im Hauptprogramm oder in der Bibliothek.
Wobei mir noch nicht ganz klar ist, was du mit "global speichern" meinst. Ich gehe doch mal davon aus, jede Bibliothek hat genau eine solche Struktur (TAppInfo). Also würdest du in der Bibliothek eine globale Variable für jeden String halten und alle Strings die du rausgibst halt darauf referenzieren (myAppInfo.Author := PChar(Author)). Noch besser wäre im Prinzip sogar, du würdest gleich Konstanten nehmen, dann brauchst du sie weder casten noch irgendwo zu initialiseren.
Du kannst wahrscheinlich sogar den gesamten Record als Konstante halten:

Code: Alles auswählen

const AppInfo = TAppInfo(
  Name = 'My Plugin',
  ShortDesc = 'Macht was tolles',
  .........);

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Targion »

Naja, das geht eben nicht... Die entsprechenden funktionen durchsuchen das System nach verfügbaren Anwendungen. Wird eine neue gefunden, so wird ein Signal mit einem TAppInfo, welches die Informationen über die Anwendung enthält, gesendet. Ich könnte jetzt die Werte der PChars einfach in ein Feld setzen, welches Strings verwendet... Das ist aber nicht so toll. Die TAppInfos werden in der Lib nirgendwo gespeichert. Das bedeutet, das müsste das Hauptprogramm machen. Ich probiere da noch mal was aus, vielleicht geht das. :roll:

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Hitman »

Naja wie gesagt - an irgendeiner Stelle musst du leider kopieren bzw. den Speicher verwalten. Aber nach genauerem Nachdenken würde ich auch sagen, dass es das beste ist, das im Hauptprogramm zu machen ... AppInfo abrufen, Kopie davon anlegen (vlt. auch gleich in ein Objekt gekapselt - Records sind out ;)) und die dann in die Liste zur Weiterverarbeitung aufnehmen.

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Targion »

Warum funktioniert folgendes (Im Hauptprogramm) nicht?

Code: Alles auswählen

New(sAI.Name);
 sAI.Name:=ai.Name;
sAI ist das AppInfo-record eines TListEntry, ai ist das von der lib übergebene Objekt. Ich kenne mich mit dem manuellen Speichermanagement nicht so gut aus, erst ein paar mal gemacht.
Vielleicht speichere ich doch direkt alles im TListEntry, dann tritt das Problem bestimmt nicht mehr auf.

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Hitman »

New ist da nicht das Richtige ...

Code: Alles auswählen

sAI.Name := StrAlloc(StrLen(ai.Name) + 1);
StrCopy(sAI.Name, ai.Name);
Dann musst du allerdings irgendwann, wenn du den String nicht mehr brauchst, StrDispose aufrufen - also wahrscheinlich dann, wenn du den Record wieder aus der Liste wirfst.

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Merkwürdiges bei Übergabe von Record -> Programm

Beitrag von Targion »

Funktioniert perfekt! Danke dafür, da wäre ich nicht so schnell drauf gekommen (obwohl es logisch ist)
Jetzt läuft alles genau so, wie es soll.

Antworten