![]() |
Strings und DLL - OnCreate abfangen - und andere Probleme...
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:
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")
function Component_GetChild(parent: TComponent; compname: PChar): TComponent; stdcall;
begin Result := nil; if Assigned(parent) = false then exit; Result := parent.FindComponent(String(compname)); end;
Delphi-Quellcode:
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!")
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;
Delphi-Quellcode:
Und damit wären wir schon bei Problem 1:
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; 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] |
Re: Strings und DLL - OnCreate abfangen - und andere Problem
Versuch es mal mit einer temporären lokalen Stringvariable
|
Re: Strings und DLL - OnCreate abfangen - und andere Problem
Zitat:
Kann es ja gar nicht, da das Stringobjekt dann ja trotzdem von der DLL erstellt und von der EXE gelöscht wird. Das Problem hab ich ja nicht nur bei Strings, sondern auch wenn ich andere Objekte (wie eine TBitmap) in der DLL erstelle und zuweise (sodass sie dann von der EXE gelöscht werden, was ja nicht gehen kann -> falscher Heap). Die Frage war jetzt eher, wie man diese Memory-Management-Probleme lösen kann, ohne ShareMem zu verwenden (geht ja nicht, da Closed Source). Ich muss Objekte in der DLL erstellen. Und ich kann nichtmal das alte Objekt zwischenlagern, WM_DESTROY abfangen und das neue selber "free"en und das alte wieder zuweisen, weil einiges dann nicht funktioniert, z.B. wenn ich einer Listbox Items hinzufüge - wenn ich sie dann am Ende wieder lösche, kann der RPG Maker den Text nicht mehr lesen und speichern - und genau das wäre das Ziel gewesen. |
Re: Strings und DLL - OnCreate abfangen - und andere Problem
Zitat:
Delphi-5/6-DLL benutzt dennoch seine eigene RTTI. Außerdem nutzen beide Module (DLL und EXE) ihren eigenen Speichermanager, wewegen es hier natürlich knallen MUß.
Delphi-Quellcode:
Du müßtest also den Speichermanager der EXE und eventuell noch einige Teile der RTTI erstmal irgendwie in deine DLL umleiten, bzw. es so hinbekommen, daß die DLL den Speichermanager der EXE verwendet.
s.Add(String(buffer));
Denn der String MUß im Speichermanager der EXE liegen, wenn dieser damit arbeiten soll. Und dieses ist nur eines von vielen derartigen Problemen, welche sich nicht leicht, bzw. garnicht lösen lassen. Was möglich wäre, der ListBox, also dem Windows-Control, welches von der VCL dort gekapselt wird, entsprechende Windows-Messages zu schicken und so die Liste zu ändern. Also ich kann nur von derartigen VCL-Zugriffen abraten. Nicht umsonst sollte man bei eigenen Programmen besser entsprechende Packages verwenden, welche den gemeinsamen Zugriff ermöglichen. Welche aber natürlich auch in allen Modulen (DLL und EXE) eingebunden sein müssen. |
Re: Strings und DLL - OnCreate abfangen - und andere Problem
Das Problem ist nur, dass das vielleicht im Falle der Listbox funktioniert, aber bei anderen Nicht-Standard-Controls nicht (die gibts keine passenden Window Messages), und im Falle von Objekten wie z.B. TBitmap eben gar nicht...
Mit RTTI gibts keinerlei Probleme übrigens, nur mit den Strings/Objekten. "Umleiten" - ja, das klingt zwar schlüssig, ich wüsste aber nicht, wie ich das umsetzen kann... Ich kann ja leider nicht die Techniken anwenden, die man normalerweise anwendet, weil ich ja auf den Source Code keinen Zugriff habe. |
Re: Strings und DLL - OnCreate abfangen - und andere Problem
In der Unit System gibt es ja die Prozeduren GetMamoryManager und an diese, bzw. an das Ergbnis welches diese liefert müßtest du rankommen und könntest dann via SetMemoryManager diesen deiner DLL zuweisen.
Ansonsten gäbe es nur eine einzige Möglichkeit einem "fremden" Speichermanager einen externen String unterzujubeln ... man muß den String wie eine Konstante aussehen lassen. Schau dir mal die beiden Threads an > ![]() Strings, welche von der EXE in die eigene DLL ausgelesen werden sollen mäßten entweder über einen PChar gecastet oder über UniqueString in den eigenen Speichermanager geladen werden. PS: Ein TBitmap kapselt intern auch nur Windows-Objekte. bitmap.Hande = HIMAGE und im TBitmap verstecht sich ein TBitmapImage, welches das Bild und Anderes kapselt.
Delphi-Quellcode:
TBitmapImage = class(TSharedImage)
private FHandle: HBITMAP; // DDB or DIB handle, used for drawing FMaskHandle: HBITMAP; // DDB handle FPalette: HPALETTE; FDIBHandle: HBITMAP; // DIB handle corresponding to TDIBSection FDIB: TDIBSection; FSaveStream: TMemoryStream; // Save original RLE stream until image is modified FHalftone: Boolean; // FPalette is halftone; don't write to file |
Re: Strings und DLL - OnCreate abfangen - und andere Problem
Hurraaaaa!
Das mit dem Memory Manager hat mein erstes Problem gelöst. Ich mache das jetzt so: FreeBASIC-Code:
Code:
So kriege ich den Memory Manager der EXE.
' gets pointer to Delphi's memory manager
Function GetMemoryManager() As TMemoryManager Ptr #Define UNKNOWN &hCC Dim pattern(...) As UByte = {&h53, &h85, &hC0, &h7E, &h15, &hFF, &h15, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, _ &h8B, &hD8, &h85, &hDB, &h75, &h0B, &hB0, &h01, &hE8, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, &hEB, &h02, _ &h33, &hDB, &h8B, &hC3, &h5B, &hC3} Dim j As Integer = 0 For i As UByte Ptr = &h401000 To &h40F000 Step 1 If pattern(j) = UNKNOWN OrElse *i = pattern(j) Then j += 1 If j > UBound(pattern) Then Return *CPtr(TMemoryManager Ptr Ptr, i - UBound(pattern) + 7) Else j = 0 EndIf Next Return NULL End Function In der DLL hab ich jetzt diese Funktion, die ich dann aufrufe: Ultimate_SetMemoryManager(GetMemoryManager())
Delphi-Quellcode:
Übrigens: mich wundert, warum es ShareMem und sonstwelche Units dafür gibt, wenn es reichen würde, dass die DLL eine SetDLLMemoryManager-Funktion exportiert und man in der EXE dann einfach SetDLLMemoryManager(GetMemoryManager()) macht.
function Ultimate_SetMemoryManager(memmgr: PMemoryManager): Boolean; stdcall;
begin Result := false; SetMemoryManager(memmgr^); Result := true; end; Danke Leute! Problem 1 ist somit endlich gelöst! Jetzt bleiben allerdings noch Problem 2 und 3! mfG Cherry |
Re: Strings und DLL - OnCreate abfangen - und andere Problem
Zitat:
Und ShareMem nutzt auch die selben SetMemoryManager-Funktionen, nur daß dort die EXE den Manager von der DLL übernimmt. ![]() ![]() |
Re: Strings und DLL - OnCreate abfangen - und andere Problem
Problem 3 hab ich nun selber gelöst.
Aber Problem 2 besteht noch immer! Ich hab jetzt außerdem noch ein Problem 4: - Wie stelle ich es am besten an, im Speicher nach einer Klasse zu suchen, von der ich nur den Namen kenne? - Wenn ich da die Adresse gefunden habe, wie kann ich von dieser Klasse eine Instanz erzeugen? mfG Cherry |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:44 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz