Autor |
Beitrag |
moddin
      
Beiträge: 75
WinXP Pro
Delphi 7 Enterprise ;-)
|
Verfasst: Mo 16.03.09 15:08
Schönen guten Tag!
Ich kämpfe im Moment mit einem Problem, und zwar
versuche ich die Ausgabe der CallCmd-Function ( die vom Plugin ausgeführt wird und wiederholt WriteLine aufruft)
umzuleiten
CallAndCapture -> HookWriteLine -> CallCommand -> [ Wiederholt WriteLine abfangen ] -> Unhook -> Exit
Dabei tritt aber immer eine Exception auf, und zwar in der Hook-Funtion
Am Hook selbst liegt es nicht, die funktion springt immer wichtig zurück
Das ganze diehnt dazu, die Ausgabe meiner Console abzufangen und über das Netzwerk an einen Clienten zu senden
Ideen?
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:
| function CallAndCapture(const Cmd:String;Args : TArguments):String; var OldFunc : Pointer; OutText : TStringList; function HookedWriteLine(Const S:String):boolean; begin OutText.Add(S); end; begin try OutText := TStringList.Create; Outtext.Add('CAPTURED OUTPUT'); HookCode(@WriteLine,@HookedWriteLine,OldFunc); if CallCmd(Cmd,Args) then Result := OutText.Text else Result := ''; finally UnhookCode(OldFunc); OutText.Free; end; end; |
Moderiert von Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 16.03.2009 um 14:26
_________________ Willst du Körper an Körper pressen? Atem spüren? Gerüche wahrnehmen? Verschiedene Stellungen probieren? Rein und raus? Von hinten nach vorne? Ja? Dann nimm den Bus!
Zuletzt bearbeitet von moddin am Mo 16.03.09 16:42, insgesamt 1-mal bearbeitet
|
|
moddin 
      
Beiträge: 75
WinXP Pro
Delphi 7 Enterprise ;-)
|
Verfasst: Mo 16.03.09 16:10
UPDATE
weiß jetzt was falsch ist
jetzt funktioniert das Schreiben auf OutText
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| function HookedWriteLine(Const S:String):boolean; var CorrectAddress : Pointer; begin CorrectAddress := Pointer(DWord(@OutText) + ????????; TStringList(CorrectAddress).Add(S); end; |
die adressen werden hier relativ zueinande compiliert, wie kann ich jetzt ??????????, sprich den Offset errechen?
_________________ Willst du Körper an Körper pressen? Atem spüren? Gerüche wahrnehmen? Verschiedene Stellungen probieren? Rein und raus? Von hinten nach vorne? Ja? Dann nimm den Bus!
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 18.03.09 18:16
Es ist eine GANZ schlechte Idee, aus einem Hook, der als Lokale Prozedur implementiert ist, heraus auf lokale Variablen der äußeren Funktion zuzugreifen. Hier wirst Du wohl Modullokal kurz deine gewünschte Dateninstanz irgendwo halten müssen.
Bessser jedoch wäre es, das Plugin so anzupassen, dass es nicht WriteLine, sondern eine Interface-Funktion deiner Anwendung für Text-Ausgaben verwendet.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Mi 18.03.09 18:31
Es hat wohl mehrere Fehler. Erstens darfst du den Pointer einer lokalen Funktion nicht nach "aussen" geben bzw. ausserhalb des Kontexts aufrufen. Das lässt Delphi auch nicht zu, ausser du verwendest direkt Pointer, dann kann er praktisch nicht wissen, was du tust. Lokale Variablen liegen auf dem Stack und wenn du die lokale Funktion von irgendwo anders ausführst hat's andere Sachen im Stack drin. Zweitens bist du sicher, dass die Aufrufkonvention stimmt?
|
|
moddin 
      
Beiträge: 75
WinXP Pro
Delphi 7 Enterprise ;-)
|
Verfasst: Fr 20.03.09 02:04
Hi!
Das ganze soll für plugins transparent sein, dh
Client -> Sendet auszuführendes Komm ando übers Netz -> Server empfängt,compiliert und führt die Funktion aus
-> abgefangene Daten zurücksenden -> Client -> Ausgabe
WriteLine ist ja bereits die Schnittstelle für Plugins, worüber die Console (TRichEdit) beschrieben wird (hat nix mit WriteLN zu tun!!) oder wie ist das mit interface gemeint? IInterface ?
Habe das ganz jetzt über eine Globale-Stringliste gelöst, welche anstatt der OutText beschrieben wird
gäbe es da noch ne elegantere Lösung?
Man könnte natürlich auch immer das Ziel als formales parameter WriteLine mitgeben,aber dann müsste ich die ganze architektur umschrieben
_________________ Willst du Körper an Körper pressen? Atem spüren? Gerüche wahrnehmen? Verschiedene Stellungen probieren? Rein und raus? Von hinten nach vorne? Ja? Dann nimm den Bus!
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Fr 20.03.09 08:02
Im Grunde, wenn Du die Funktion WriteLine schon selbst geschrieben hast, so kannst Du dort einfach die übergebenen Parameter auswerten. Wenn Du nun noch im globalen Status deiner Anwendung hinterlegst ob eine Umleitung erfolgen soll und wenn ja, wohin, dann brauchst Du keinen Hook.
Ansonsten macht man das i.d.R. so, dass man dem Plugin eine Tabelle von "öffentlichen Funktionen" übergibt und dann das Plugin diese einfach nur aufruft. Möchte man dann eine Funktion debuggen oder austauschen, setzt man einfach in der Tabelle, die man dem Plugin übergibt einen andren Funktionspointer.
Mit Interface kann sowohl die Menge aller Interface-Typen gemeint sein, als auch allgemein eine offene Schnittstelle zwischen Modulen. Ich meinte oben letzteres (auch wenn auch ersteres funktioniert, sich aber in Delphi blöd programmieren lässt).
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Fr 20.03.09 09:52
Wenn im Aufruf die Liste nicht übergeben wird, aber die zu verwendende Liste nicht gobal sondern vom Kontext abhängig ist, dann muss der Aufruf implizit den Kontext enthalten.
Wenn für dich in Frage kommt, Funktionsreferenzen "of object" - sprich TMethod zu verwenden, dann ist gut. Wenn du "kontextlose" Funktionsreferenzen zulassen kannst/willst, musst du dir etwas analog zu MakeObjectInstance basteln. MakeObjectInstance erstellt eine kleine Redirect-Funktion zur Laufzeit und baut den Kontext (hier: eine Referenz auf das Objekt) in die Redirect-Funktion fest ein, und gibt einen Pointer darauf zurück.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Fr 20.03.09 12:32
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Fr 20.03.09 13:28
@BenBE: Der Sender hat einen anderen Zweck. Dort geht es darum, dass der Eventhandler weiss, woher die Anfrage kam. Hier haben wir ein ganz anderes Problem. Es geht darum, dass die Funktion WriteLine selbst weiss, wohin er seine Sachen schreiben muss. Dem Aufrufer von WriteLine ist prinzipiell egal wohin WriteLine etwas schreibt. Der will nur etwas schreiben und dem WriteLine nicht auch noch mitteilen wohin. Ausserdem sind normale Funktionen semantisch gesehen praktisch das "Gegenteil" von Events (= Callbacks)...
MakeObjectInstance ist tatsächlich keine gute Lösung aber die einzige Lösung, wenn der Fall vorliegt, den ich beschrieben habe. Memory-Leaks hat man bei einer korrekten Implementation nicht.
Ich kann mich nur wiederholen. Entweder er macht's mit Methoden-Referenzen (, dort ist der Kontext mit dabei siehe TMethod = Ein Pointer auf den Kontext/Daten/Self und ein Pointer auf die eigentliche Funktion. Self wird dann als unsichtbarer erster Parameter automatisch mitgegeben und WriteLine kann mit Self auf das Objekt zugreifen, wo die StringListe dann liegt) oder er macht's mit einem "nackten" Pointer auf die Funktion. Da kommt man aber nicht um eine MakeObjectInstance ähnliche Implementation herum.
Oder man macht es nochmals ganz anders. Er übergibt nicht eine Menge von Funktionen sondern ein Objekt oder Interface und darin sind eine Menge von Funktionen enthalten.
Wie funktioniert HookCode? Bzw. wie funktioniert dein Plugin Konzept generell? Was wir wann übergeben.
|
|
moddin 
      
Beiträge: 75
WinXP Pro
Delphi 7 Enterprise ;-)
|
Verfasst: Sa 21.03.09 13:32
Zitat: | Wie funktioniert HookCode? Bzw. wie funktioniert dein Plugin Konzept generell? Was wir wann übergeben. |
@uallHook
1. Loader -> Alle plugins finden -> Exportierte "Initialize" des Plugins aufrufen -> Author, Name etc
2. Record (mit allen für das plugin zugänglichen funktionen) wird übergeben (WriteLine,RegisterCmd,...)
3. Plugin registriert über RegisterCmd alle unterstützen commandos und Netzerk-Callbacks
4. Nächstes Plugin ->*
Ich finde auch die Idee mit einem internen Schalter am besten, zumal das ganze ja noch multithreading-mutitab-fähig werden soll, dann muss zwangsweise ja ein Ziel (Stringlist,Memo) angegeben werden, damit das plugin auch aufs richtige tab schreibt
_________________ Willst du Körper an Körper pressen? Atem spüren? Gerüche wahrnehmen? Verschiedene Stellungen probieren? Rein und raus? Von hinten nach vorne? Ja? Dann nimm den Bus!
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Sa 21.03.09 18:07
Irgendwie verstehe ich nicht, weshalb du HookCode verwendest, wenn du dem Plugin selbst die Funktion als Record übergibst. Du kannst ja gleich eine alternative Funktion übergeben, mit der Netzwerkfunktionalität bzw. einfach dein WriteLine erweitern.
Oder habe ich etwas falsch verstanden?
Das einfachste wäre wohl du übergibst statt ein Record in Interface.
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: 40: 41: 42:
| type IPlugin = interface ['{1C3AD56F-FC8D-4FFF-950A-A429BBB6DE03}'] procedure WriteLine(const Value: string); procedure DoSomething; end;
TPlugin = class(TInterfacedObject, IPlugin) private FStrings: TStrings; public constructor Create(AStrings: TStrings); procedure WriteLine(const Value: string); procedure DoSomething; end;
constructor TPlugin.Create(AStrings: TStrings); begin FStrings := AStrings; end;
procedure TPlugin.DoSomething; begin ShowMessage('Do something'); end;
procedure TPlugin.WriteLine(const Value: string); begin FStrings.Add(Value); end;
procedure TForm1.FormCreate(Sender: TObject); var MyPlugin: IPlugin; begin MyPlugin := TPlugin.Create(Memo1.Lines); end;
|
|
|
moddin 
      
Beiträge: 75
WinXP Pro
Delphi 7 Enterprise ;-)
|
Verfasst: Mo 23.03.09 13:03
und wie würdest du dann das plugin aufrufen?
Delphi-Quelltext 1: 2: 3:
| MyPlugin.WriteLine := HookedWriteLine; PluginAufrufen() MyPlugIn.WriteLine := OldWriteLine; |
oder einfach
Delphi-Quelltext 1: 2: 3: 4:
| Capture := TStrL.Create() MyPlugin.Target := Capture; PlugInAufRufen() MyPlugin.Target := Old; |
da plugin braucht dann nur die IInterface klasse, das object ist dann in der "Main" implmentiert, richtig?
_________________ Willst du Körper an Körper pressen? Atem spüren? Gerüche wahrnehmen? Verschiedene Stellungen probieren? Rein und raus? Von hinten nach vorne? Ja? Dann nimm den Bus!
Zuletzt bearbeitet von moddin am Mo 23.03.09 13:11, insgesamt 1-mal bearbeitet
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 23.03.09 13:07
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
moddin 
      
Beiträge: 75
WinXP Pro
Delphi 7 Enterprise ;-)
|
Verfasst: Mo 23.03.09 13:30
Danke! Cooler ansatz
Wie könnte man das ganze jetrzt threadfähig machen, wenn 2 threads auf das selbe plugin/function zugreifen?
_________________ Willst du Körper an Körper pressen? Atem spüren? Gerüche wahrnehmen? Verschiedene Stellungen probieren? Rein und raus? Von hinten nach vorne? Ja? Dann nimm den Bus!
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Mo 23.03.09 14:19
Kommt etwas darauf an, um was für Funktionen es geht. Du kannst entweder eine Lock/Unlock Funktion zur Verfügung stellen oder im Hostprogramm alles synchronisieren bzw. dort alles locken.
Wenn du mehrere Locks hast musst du jedoch ziemlich gut aufpassen, dass du keine Deadlocks bekommst. Entweder machst du Locks hierarchisch (Lock 1 alleine erlaubt, Lock 2 aber nur mit Lock 1 zusammen), oder du sorgst dafür, dass man Locks nicht ineinander schachteln kann. Wenn du in ThreadA Lock1 innerhalb Lock2 anforderst, und im ThreadB Lock2 innerhalb Lock1, dann hast du potentiell ein Deadlock, je nach Timing... Sowas sollte man wenn möglich "by design" aussschliessen, weil die verschiedenen Plugins sollen idealerweise davon ausgehen können, dass sie alleine sind...
Wenn Performance kein Problem ist kannst du alle calls einfach synchronisieren. Ansonsten müsstest du prüfen, ob die Calls asynchron abgearbeitet werden können. Usw...
Ein Wundermittel gegen alle Probleme gibt's da nicht.
|
|
|