Einzelnen Beitrag anzeigen

CherryDT

Registriert seit: 28. Apr 2010
5 Beiträge
 
#1

Strings und DLL - OnCreate abfangen - und andere Probleme...

  Alt 28. Apr 2010, 11:40
Hallo allerseits!

Ich hab mehrere komplizierte Probleme und hoffe, hier eine Lösung zu finden. Ich bin wirklich keiner, der sofort in einem Forum fragt, aber in diesem Fall hab ich im Internet sonst keine Lösung gefunden.

Ich erstelle eine Erweiterung für ein in Delphi geschriebenes Programm, und zwar den RPG Maker 2000 (Delphi 5) bzw. 2003 (Delphi 6). Die Erweiterung selber programmiere ich mit FreeBasic. Und zwar habe ich eine DLL erstellt, die ich über einen Loader beim Programmstart in den Address Space des RPG Makers laden lasse. Hier werden einige Hooks installiert und im Folgenden Fenster des RPG Makers gesubclassed und so die Funktionen und das Verhalten des RPG Makers verändert und erweitert.

Um nun auch auf die Eigenschaften der einzelnen VCL-Controls zugreifen zu können, habe ich eine weitere DLL geschrieben, und zwar in Delphi 5/6 (passend zur Version des RPG Makers), welche mir die RTTI zugänglich macht und den Zugriff auf TStrings-Objekte ermöglicht.

Ein Beispiel:

1. Ich habe ein HWND von einem Fenster.
2. Ich hole den Pointer zum TForm-Objekt aus der ControlOfs-Property des Fensters.
3. Ich rufe eine Funktion aus meiner Delphi-DLL auf, um den Pointer zum TListBox-Objekt einer Listbox zu erhalten: listbox = Component_GetChild(form, "ListBox1")
Delphi-Quellcode:
function Component_GetChild(parent: TComponent; compname: PChar): TComponent; stdcall;

begin
  Result := nil;
  if Assigned(parent) = false then exit;
  Result := parent.FindComponent(String(compname));
end;
4. Ich rufe eine Funktion aus meiner Delphi-DLL auf, um über RTTI die "Items"-Eigenschaft zu bekommen (ich verwende die Unit clRTTI für einfachen RTTI-Zugriff): items = RTTI_GetPropAsObject(listbox, "Items")
Delphi-Quellcode:
function RTTI_GetPropAsObject(obj: TObject; propname: PChar): TObject; stdcall;

var
  RTObj: TrtWrapper;

begin
  Result := nil;
  RTObj := TrtWrapper.Create(obj);
  try
    if RTObj.HasProperty(propname) = false then exit;
    Result := RTObj[propname].AsObject;
  finally
    RTObj.Free;
  end;
end;
5. Und schließlich verwende ich eine weitere Funktion aus meiner Delphi-DLL, um der Listbox einen Eintrag hinzuzufügen: Strings_Add(items, "Ein neuer Eintrag!")
Delphi-Quellcode:
function Strings_Add(s: TStrings; buffer: PChar): Boolean; stdcall;

begin
  Result := false;
  if Assigned(s) = false then exit;
  s.Add(String(buffer));
  Result := true;
end;
Und damit wären wir schon bei Problem 1:

Das funktioniert alles soweit. Wenn allerdings einem von dem RPG Maker erstellten Objekt (hier eben mit listbox.Items.Add) ein String zugewiesen wird, dann gibt es ein Chaos:

Meistens geht alles gut, bis ich die Form, die ich manipuliert habe, schließe, oder manchmal auch bis ich dann eine neue öffne. Doch dann kommen Fehler ohne Ende: Access Violation, Invalid Pointer Operation, Corrupt String A, oder auch gar keine Meldung sondern das Programm beendet sich einfach, mit Exit Code C0000005 (= Access Violation).

Durch ein wenig Recherche im Internet hab ich rausgefunden, dass es ein Problem mit dem Memory Manager gibt, weil ja die Listbox sowie das Items-Objekt vom RPG Maker erzeugt wurden, der neue String jedoch von meiner DLL (und jetzt versucht der RPG Maker dann, ein Objekt zu "free"en, was er gar nicht erzeugt hat) - das passiert nicht nur bei Strings, sondern auch bei TBitmaps oder allem anderen, was die DLL erzeugt (und nicht die EXE). Als Lösung müsste man, wie ich jetzt weiß, als erste Unit "ShareMem" einbinden. Das Problem dabei ist nur: Ich habe ja keinen Zugriff auf den Sourcecode des RPG Makers (sonst müsste ich ja gar nicht diesen Aufwand betreiben)! Gibt es nicht irgendeine Möglichkeit, das anders zu lösen? Ich bin da echt am Verzweifeln...

Problem 2 ist was ganz anderes. Und zwar will ich meinen eigenen Code einschleusen, wenn bei einer Form schon alle Controls erzeugt wurden, aber noch nicht mit Werten befüllt o.ä.

Konkret hab ich das Problem, dass ich z.B. einem TDialEdit-Control (ein Editcontrol mit Auf-Ab-Pfeilen und zwei Eigenschaften "MaxValue" und "MinValue" - ist glaub ich nicht bei Delphi dabei) eine höhere MaxValue verpassen will. Ich hab dazu einen CallWndProc-Hook installiert, und wenn die Form das erste Mal eine der Messages WM_NCPAINT, WM_ERASEBKGND, WM_SHOWWINDOW oder CM_ACTIVATE bekommt, weiß ich, dass alle Controls da sind und setze die MaxValue hoch. Leider wurde dabei auch schon der Wert eingetragen. Wenn ich nun in dieser Form (wo original MaxValue = 20 ist und ich es bereits auf 100 hochgesetzt habe) 100 eintrage, auf "OK" klicke und sie wieder öffne, steht 20 darin, weil der Wert vom RPG Maker eingetragen (und auf den höchsten Wert - 20 - runterkorrigiert) wurde, bevor mein Hook den Maximalwert auf 100 gestellt hat. Das ist sehr ärgerlich.

Der Wert wird offenbar von dem Code eingetragen, der die Form erzeugt hat, nicht von einem Event der Form. Manuelles Aufrufen von OnCreate ändert den Wert nämlich nicht.

Gibt es irgendeinen Weg, von außen den Zeitpunkt abzupassen, wenn OnCreate aktiviert wird (oder gerade fertig ist, das ist nicht so wichtig), und dann eigenen Code einzuschleusen? Irgendwelche charakteristischen Window Messages werden da leider nicht verschickt.

Problem 3 ist sicher irgendwas primitives, aber ich komm trotzdem nicht drauf: Ich hab mit meiner FreeBasic-DLL in einer Delphi-Form mit CreateWindow eine zusätzliche Combobox eingefügt. Die Form hab ich gesubclassed, um die Auswahl in der Combobox auswerten zu können. Das funktioniert auch soweit. Nur zeichnet sich die Combobox nicht ordentlich. Wird sie von einem anderen Fenster überdeckt, verschwindet sie, bis man mit der Maus drüberfährt, und in der Dropdownliste ist alles durchsichtig (man sieht die Form durch) bis auf die Stellen, wo Text steht.

mfG Cherry

[edit=mkinzler]Code-Tags durch Delphi-Tags ersetzt Mfg, mkinzler[/edit]
  Mit Zitat antworten Zitat