Laden...

WinApi Hook in der Konsole

Erstellt von rollerfreak2 vor 14 Jahren Letzter Beitrag vor 14 Jahren 4.135 Views
rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren
WinApi Hook in der Konsole

Hallo zusammen,

ich hab mal wieder ein Problem bei dem ich nicht weiter komme. Ich möchte in einer Konsolen Anwendung die ein Key abfangen bei dem ich eine bestimmte Aktion ausführen will.

Bei einer Forms Anwendung ist das ganze keine Problem. In Processing Global Mouse and Keyboard Hooks in C# gibt es eine fertige Library mit der das möglich ist.

Irgendwie hab ich aber kein Plan wie ich eine MessageQueue für eine KonsolenAnwendung starte. Weiß einer von euch rat?

Danke im voraus.

Again what learned...

799 Beiträge seit 2007
vor 14 Jahren

Muss es unbedingt ein globaler Hook sein? Wenn nicht, kannst du Console.ReadKey(true) verwenden. Falls du das brauchst, stellst sich die Frage warum du unbedingt eine Konsolenanwendung verwenden willst.

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren

Ja es muss unbedingt ein globaler Hook sein, sonst hätte ich ja nicht gefragt 😉

Das eine Problem habe ich jetzt gelöst. Die Hook Klasse soll nichts weiter machen als auf eine bestimmte Taste zu hören, und dann ein Event zu feuern. Das habe ich jetzt so gemacht.

Keine Konsolenanwendung sondern eine Form. Form aus der Taskbar entfernt und visible = false gesetzt. Nun in der form den HookManager.KeyDown registriert und auf eine bestimme Taste gehört. Im Falle die Taste wurde gedrückt, dann einfach ein selbst definiertes Event feuern. Soweit so gut.

Jetzt habe ich aber ein anderes Problem. Und zwar wird die Form in einem statischen Konstruktor erzeugt. Die dazugehörige statische Klasse benötigt einfach die Information ob diese Taste gedrückt wurde oder nicht. Wenn ich jetzt aber mit Application.Run(form) die Form starte dann blockiert diese. Daher ich muss dort irgendwie einen neuen Thread bzw. Process starten der die HookForm in einem separaten Process/Thread startet. Leider weis ich nicht wie.


m_HookApplication = new HookApplication();
Application.Run(m_HookApplication);

Das Application.Run(m_HookApplication) muss ich auf einen anderen Thread/Process auslagern. Ich könnte jetzt dort einfach einen anderen Thread starten. Dem Thread einfach die Form übergeben, und dort mit Application.Run(m_HookApplication) die form starten. Die Frage ist ob das praktikabel ist. Was sagt ihr?

Noch eine kleine Frage am Rande dazu. Eine Statische Klasse hat leider keine Destruktur, daher kann ich den Thread nicht sauber beenden. Das heißt der Verwender der statischen Klasse muss selber eine Methode an der statischen Klasse aufrufen um sie korrekt zu beenden. Habt ihr dafür Ideen?

Again what learned...

799 Beiträge seit 2007
vor 14 Jahren

Das ganze hört sich sehr vertrakt an. Ich fasse mal zusammen:

  • Du hast eine statische Klasse die irgendeine Funktionalität zur Verfügung stellen soll.
  • Diese Funktion hängt davon ab ob eine bestimmte Taste gedrückt wurde.

Ist diese statische Klasse auch deine Anwendung oder brauchst du einfach nur eine Form für das Lauschen der Tasten? Falls dem so ist, probier es einfach mal so:


KeyHook = new HookApplication(); // m_ als Präfix ist in c# nicht erwünscht
KeyHook.Show(); // Auf den Coding-Guidelins von MS nachzulesen.

So vermeidest du den zweiten Thread, denn Show blockiert nicht.

Alles in allem frage ich mich auch, ob die Klasse denn unbedingt statisch sein muss. Welchen Vorteil bringt dir das?

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
49.485 Beiträge seit 2005
vor 14 Jahren

Hallorollerfreak2,

ich hab mir jetzt nicht alles genau angeschaut, aber dazu

Das Application.Run(m_HookApplication) muss ich auf einen anderen Thread/Process auslagern. Ich könnte jetzt dort einfach einen anderen Thread starten. Dem Thread einfach die Form übergeben, und dort mit Application.Run(m_HookApplication) die form starten. Die Frage ist ob das praktikabel ist. Was sagt ihr?

kann ich sagen, dass ein Form immer von dem Thread erzeugt werden muss, in dem auch das Application.Run läuft. Insofern, nein, das ist nicht korrekt so.

herbivore

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren

Die klasse muss statisch bleiben. Das hat mehrere Gründe auf die ich hier aber nicht näher eingehen möchte.

Ich geh mal ein bisschen in die tiefe um das besser zu erklären. Die statische Klasse ist eine Logging Klasse und dient dazu logs in Files zu schreiben und bisschen was rundrum noch. Die eigentliche Anwendung führt GUI tests aus und nutzt diese statische Logger Klasse um log ausgaben dort rein zu schreiben.

Die eigentliche Anwenundung (GUI Tests) soll unterbrochen werden (Pause). Die Idee dahinter ist da die Logger Klasse sehr oft angesprochen wird sollte diese einfach die Information bekommen ob die pause taste gedrückt wurde oder nicht.

Wenn nun wieder geloggt wird, einfach checken ob die Pause Taste gedrückt wurde und dort blockieren. MessageBox bzw. dort dann auf Ok klicken und weiter gehts.

1.Problem: Das aufräumen. Die eigentliche Application muss den hook beenden. => nicht gut. Das sollte der FileLogger selber machen, aber wie.

Again what learned...

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren

Hallo herbivore,

also wenn ich im ctor der statischen Klasse die Form mit Application.Run starte geht das schief weil diese dann blockiert. Form.Show habe ich nicht probiert.

Wenn ich einen Thread erstelle und in der dem thread zugeteilten Methode Application.Run(form) aufrufe funktioniert das. Daher die Form bekommt auch die Hooks und feuert im falle der richtigen Taste ein event, das die statische Klasse empfängt. Diese blockiert dann.

[Edit]: Nachtrag: Wenn ich die Form mit .Show aufrufe dann funktioniert der hook mechanismus nicht mehr. Daher die Form bekommt die Hook Events der HookManager Klasse nicht mehr mit.

Again what learned...

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo rollerfreak2,

nur weil ein threadübergreifender Zugriff erstmal funktioniert, heißt das nicht, dass das Programm korrekt ist und unter allen Umständen und in allen Umgebungen korrekt funktioniert. Durch einen Test kann man nur die Anwesenheit von Fehlern feststellen, nicht ihre Abwesenheit.

Warum erzeugst du das Form nicht in dem Thread, der das Application.Run ausführt? Ob der Berg zum Propheten oder der Prophet zum Berge kommt, spielt keine Rolle, solange am Ende die Erzeugung und das Application.Run im selben Thread laufen.

herbivore

799 Beiträge seit 2007
vor 14 Jahren

So wie ich das sehe, gehört die Hook-Form sowieso nicht in die Logger-Klasse da du so GUI u. Funktion vermengst.

Gibt es eine MainForm oder so etwas zu deinem GUI-Test? Falls ja gehört dort der Hook hinein der dann wieder bei Betätigung der Pause-Taste, auch alles pausiert.

So hast du mehrere Fliegen mit einer Klappe geschlagen:

  • Hässliche Design-Entscheidung ausgebessert
  • Du musst dich nicht mit einer statischen Klasse u. der Form-Erstellung beschäftigen.
  • Beendest du GUI-Tests kann auch in einem Wisch der Hook entfernt werden.

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren

Weil das nicht geht herbivore. Ich glaub ich muss noch ein bisschen tiefer graben das es Verständlich für euch wird.

Client = Die zu testenede Anwendung
AutomationLibrary = Die library die GUI tests ermöglicht (maus, key, events etc.)
NUnit Test = enthält die Tests die die AutomationLibrary nutzen um den Client zu testen

NUnit Framework = GUI startet und man wählt die/den test aus.

Dann startet der Test den Client und testet Ihn. Das heißt der Test müsste das Application.Run aufrufen. Das wiederrum heißt aber das jeder der tests schreibt und den Logger benutzen will selber den HookManager initialisieren müsste. Das möchte ich nicht.

Daher der Logger sollte das selber übernehmen, auch das abmelden. Das hab ich jetzt so gemacht das der verwender der Klasse am ende des tests RealseHook aufrufen muss. dann wird die Form geschlossen und der Thread für damit auch beendet. Ob das jetzt schön ist lässt sich sicherlich streiten. Aber es funzt auf jeden Fall erstmal auf meinem Rechner.

Wenn einer eine bessere idee hat dann immer her damit.

Again what learned...

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren

@der-schlingel. Das NUnit Framework darf ich nicht verändert. Das muss so bleiben. Daher kann das dort nicht rein. Und im Client hat es erst recht nichts zu suchen...

Again what learned...

799 Beiträge seit 2007
vor 14 Jahren

Wie weit ist es problematisch wenn das Initialisieren des Hooks in eine abstrakte Klasse ausgelagert wird, die dann wiederum von jeder deiner Test-Klassen geerbt wird?

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
5.299 Beiträge seit 2008
vor 14 Jahren

evtl. kannst du auf die RegisterHotKey-Api ausweichen, statt eines globalen Hooks?
ein registrierter Hotkey wird exklusiv an ein best. WindowHandle gesendet.

Das würde also heißen, nur noch deine App würde den Key mitkriegen, alle anderen Apps nicht mehr - wenn du damit leben kannst...

Das nette am Hotkey ist, er wird autom. freigegeben, wenn das WinHandle freigegeben wird.
Nimm aber nicht das Sample aus der MSDN - ist voll verkorkst, das Gemurkse mittm Atom-Dingsbums schlicht üflüssig.

Ich bastel mal ein Sample.

Der frühe Apfel fängt den Wurm.

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren

@ErfinderDesRades super Idee. Wenn du es fertig hast kannst du es bitte hier posten.

@der-schlingel: Die Idee hatte ich auch erst, allerdings ist es so das den Logger sehr viele Entwickler nutzen. Daher müsste jetzt jeder seine tests ändern.
Derzeit habe ich es so das man am Logger per Methode den Hook mechanismum initialisiert und dann per methode wieder freigibt (ende des tests).

Wenn nun noch das global Hook auf das Window Handle begrenzt wird dann passt das schon. Wer den Logger benutzt und auf den Pause Mechanismum verzichten will der braucht einfach nicht die Mehtod init aufzurufen. Wer schon muss es dann auch wieder frei geben.

Again what learned...

5.299 Beiträge seit 2008
vor 14 Jahren

naja, habn bischen länger gebraucht
RegisterHotkey

Der frühe Apfel fängt den Wurm.

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 14 Jahren

Habs kurz getestet scheint sehr gut zu funktionieren. Ich baue es jetzt mal ein und geb dir das feedback.

Danke schon mal auf jeden Fall 😃

Again what learned...