Entwickler-Ecke

Basistechnologien - Tastaturevent abfangen auch ohne Focus (System tray)


Ccenter - Mo 07.12.09 20:24
Titel: Tastaturevent abfangen auch ohne Focus (System tray)
Hallo,
ich probiere gerade einwenig mit Tastaturevents herrum und bin dabei auf ein Problem gestoßen.
Ich habe meine Form auf Visible = false gesetzt und einen Icon in die Systemtray gelegt.
Natürlich kann die Form während man z.B. gerade im Internet ist, kein Tastaturevent abfangen da sie nicht den Focus hat.
Gibt es irgendwie eine Möglichkeit, trotzdem das Tastaturevent abzufangen ?


Ralf Jansen - Mo 07.12.09 23:01

Da müßtest du einen Tastatur Shortcut in Windows registrieren. Mir ist aber kein Weg mit .NET Mitteln bekannt.
Da mußt du wohl auf die klassische WinApi (im speziellen RegisterHotKey [http://pinvoke.net/default.aspx/user32/RegisterHotKey.html]) ausweichen.


Christian S. - Mo 07.12.09 23:05

Ich mag bei solchen Sachen das Managed WinAPI [http://mwinapi.sourceforge.net/]-Projekt, welches unter anderem auch eine Hotkey-Klasse hat :-)


Ccenter - Di 08.12.09 16:01

Ok danke. Verzeit mir diese blöde Frage aber wie bekomme ich die DLL etc nun in mein Visual Studio?
Die Hilfsdatei die dabei war, funktioniert nämlich irgendwie nicht.


JüTho - Di 08.12.09 16:32

Wie sonst auch: DLL sinnvoll speichern, im Projekt Referenz hinzufügen, DLL auswählen, je nach Situation auch ins Ausgabeverzeichnis kopieren lassen. Jürgen


Ccenter - Fr 29.01.10 15:02

so, hab mich entschlossen mit dem Thema weiter zu machen.
Allerdings hab ich gerade ein Problem mit der DLL. Ich habe sie unter Projekt-> Verweis hinzufügen, hinzugefügt. Sie erscheint jetzt auch im Projektmappen Explorer aber ich kann die Befehle und Events aus der Datei nicht benutzen da die Befehle nicht erkannt werden. Ausserdem erscheint im Code auch kein Verweis bei Using.
Muss ich noch irgendetwas anderes machen?


Ccenter - Sa 30.01.10 20:32

Ok, Problem wegen dll gelöst

So, ich habe mir in der Hilfedatei jetzt mal die Klasse Hotkey angeguckt. Ich weiß aber leider nicht wie ich jetzt eine Tastenkombination deklarieren soll. So is ja falsch:


C#-Quelltext
1:
2:
3:
4:
5:
private void Form1_Load(object sender, EventArgs e)
        {
            Hotkey hk1 = new Hotkey();
            hk1.KeyCode = Keys.C + Keys.V;
        }


Kha - So 31.01.10 01:58

C+V? Warum keine gewöhnliche Kombination mit Strg etc., wie es Hotkey von sich aus unterstützt?


Ccenter - So 31.01.10 12:49

Das war ja nur ein Beispiel. Der Syntax ist in diesem Fall einfach falsch. Ich bekomme ja die gleiche Fehlermeldung wie vorher wenn ich nun diesen Code verwende:

C#-Quelltext
1:
2:
          Hotkey hk1 = new Hotkey();
              hk1.KeyCode = Keys.Control + Keys.A;


Die Meldung ist diese: Der Operator "+" kann nicht auf Operanden vom Typ "System.Windows.Forms.Keys" und "System.Windows.Forms.Keys" angewendet werden.

Ich bin mir nichtmal sicher ob ich meine Tastenkombination wirklich richtig zuweise. In der "Hilfedatei" ist nämlich weder ein Codebeispiel noch eine Erklärung hierzu vorhandenn.


Christian S. - So 31.01.10 12:55

Ich glaube aber nicht, dass das bei einem Hotkey wirklich Sinn macht. Die sind ja meist eine Kombinatino aus Strg/Alt/Shift/Windows (dafür hat die Hotkey-Klasse entsprechende Eigenschaften) plus einer "normalen" Taste, die steckt man in die KeyCode-Eigenschaft.


Ccenter - So 31.01.10 13:13

Ich versteh zwar was du meinst aber ich kann das nicht richtig umsetzen, kann mir bitte jemand dafür ein Codebeispiel geben?

Der Teil wird ja schonmal richtig sein:

C#-Quelltext
1:
Hotkey hk1 = new Hotkey();                    


Aber ich bekomme es nicht hin, hk1 eine Tastenkombination zuzuteilen.
Eine Taste klappt ja:

C#-Quelltext
1:
hk1.KeyCode = Keys.A;                    

Aber sobald ich versuche eine 2. Taste mit ins Spiel zu bringen, klappt es bei mir nicht mehr.


Christian S. - So 31.01.10 13:21

Jetzt brauchst Du noch eine Taste wie Shift / Strg / etc. Ich schrieb ja, dass es dazu entsprechende Eigenschaften beim HotKey gibt.


Ralf Jansen - So 31.01.10 13:47

Zitat:
Aber sobald ich versuche eine 2. Taste mit ins Spiel zu bringen, klappt es bei mir nicht mehr.

Ich kenne die HotKey Klasse nicht aber vermutlich hat die eine ~Modifier~ Property über die man Ctrl/Shift/Alt setzen kann. Falls nicht Keys ist ein Flags Enum (damit quasi ein BitField) und Elemente darin werden somit nicht addiert sondern eben über binäre Operatoren kombiniert. Um mehrere Keys darzustellen mußt du die also verodern.


C#-Quelltext
1:
hk1.KeyCode = Keys.Control | Keys.A;                    


Das macht beim Aufbau von Keys aber nur Sinn in der Form ModifierKey | Buchstabe. Buchstabe | Buchstabe ergibt Unsinn. In Keys sind die Buchstaben mit einem Bitmuster entsprechend ihrem ASCII Wert dargestellt. Dein ursprünglich gewünschtet C + V würde, falls ich richtig geguckt habe, W ergeben.


Ccenter - So 31.01.10 14:32

Ok Danke.
Eine Fehlermeldung bekomme ich schonmal nicht mehr.

Innerhalb der Form gibt es ja das Event KeyDown/Keypress, ich möchte das ja aber ausserhalb der Form verwenden.
Ich hab gesehen das es das Event "HotkeyPressed" gibt. Wie verwende ich das nun ohne die Form einbeziehen zu müssen/ausserhalb der Form?

Mit Form wäre es ja z.b. so:

C#-Quelltext
1:
2:
3:
4:
private void Form1_HotkeyPressed(object sender, EventArgs e)
        {
            
        }


Ralf Jansen - So 31.01.10 14:42

Hat die HotKey Klasse keinen entsprechenden Event (oder zumindest eine Doku)?


Christian S. - So 31.01.10 15:58

Hallo!

Schreib mal

C#-Quelltext
1:
hk1.HotkeyPressed +=                    

dann sollte Dir der Editor durch zweimaliges Drücken der Tab-Taste die Zuweisung "bauen" und auch direkt die Methode anlegen.

Grüße
Christian


Ccenter - So 31.01.10 16:37


C#-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:
25:
26:
27:
28:
29:
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ManagedWinapi;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Hotkey hk1 = new Hotkey();
            hk1.KeyCode = Keys.Control | Keys.A;
            hk1.HotkeyPressed += new EventHandler(hk1_HotkeyPressed);
        }

        void hk1_HotkeyPressed(object sender, EventArgs e)
        {
            MessageBox.Show("funktioniert");
        }
    }
}


Egal ob ich die Form nun focusiert habe oder nicht, es passiert nichts wenn ich strg + a drücke. Wodran könnte das liegen?


Christian S. - So 31.01.10 16:47

Ist das wirklich zuviel verlangt, dass Du Dir mal selber die Eigenschaften der Hotkey-Klasse anguckst? :roll:
Da gibt es z.B. die Eigenschaft Enabled.

Und wie oft soll ich noch sagen, dass die Hotkey-Klasse für Shift / Ctrl / etc. eigene Eigenschaften hat? Ich habe langsam keine Lust mehr ...


Ccenter - So 31.01.10 17:35

Natürlich ist es nicht zuviel verlangt.
Allerdings verstehe ich nicht ganz was du mit eigene Eigenschaften für strg etc meinst bzw. wie ich die verwenden soll.
Ausserdem wo soll ich mir denn bitte die Hotkey Klasse angucken?
Der Hilfedatei die bei ManagedWinapi beliegt, kann ich nicht viel entnehmen.


Christian S. - So 31.01.10 17:49

  1. Es reicht doch, im Editor hk1. einzugeben und dann mal in der Liste zu gucken, welche Eigenschaften es gibt.
  2. Was könnte wohl eine Eigenschaft hk1.Ctrl vom Typ Bool machen? So ganz ohne Doku kann man da schon ganz gut raten. Wenn man dann mal hier [http://mwinapi.sourceforge.net/doc/index.html] nachschaut, steht da sogar
    Zitat:
    Whether the shortcut includes the Control modifier.


Ccenter - So 31.01.10 21:47

Ctrl hatte ich vorher zwar auch schon gesehen, wusste aber ehrlich gesagt nicht wie ich damit umgehen soll. Ich hoffe mal, dass ich das jetzt richtig angewendet habe.


C#-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:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ManagedWinapi;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Hotkey hk1 = new Hotkey();
            hk1.Ctrl = true;
            hk1.KeyCode = Keys.Control | Keys.A;
            hk1.HotkeyPressed += new EventHandler(hk1_HotkeyPressed);
        }

        void hk1_HotkeyPressed(object sender, EventArgs e)
        {
            MessageBox.Show("geht");
        }


    }
}


Naja, hätte ich es richtig verwendet, würde es jetzt funktionieren :D
Also ich bekomme zwar nach wie vor keine Fehlermeldung, jedoch wird mir auch keine Messagebox ausgegeben.
Ich geb zu das ich mich ein wenig ungeschickt anstelle, seht mir das bitte nach ^^


Christian S. - So 31.01.10 21:55

user profile iconChristian S. hat folgendes geschrieben Zum zitierten Posting springen:
Da gibt es z.B. die Eigenschaft Enabled.


(Und das Control dann natürlich aus dem Keycode rauslassen)

Ctrl+A könnte außerdem schon belegt sein ("Alles markieren")


Ccenter - So 31.01.10 22:23

So wie der Code da steht, passiert garnichts wenn ich die tasten drücke.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
private void Form1_Load(object sender, EventArgs e)
        {
            Hotkey hk1 = new Hotkey();
            hk1.Ctrl = true;
            hk1.KeyCode = Keys.Control | Keys.A;
            hk1.HotkeyPressed += new EventHandler(hk1_HotkeyPressed);
            hk1.Enabled = true;
        }


Folgendes: Ich habe | einmal durch & ersetzt und siehe da, es funktioniert fasst. Aber ebend nur fasst. Das Programm reagiert nämlich nicht auf die Kombination, sondern nur auf Strg. Ich habe einfach mal aus interesse folgende Zeile entfernt:


C#-Quelltext
1:
hk1.KeyCode = Keys.Control | Keys.A;                    


Und immer noch funktioniert Strg. Es scheint fasst so, als wenn die Zeile überhaupt nicht beachtet werden würde. Eher als ob nur auf hk1.Ctrl = true; geachtet würd. Was mach ich jetzt schonwieder falsch :D?


Christian S. - So 31.01.10 22:31

:autsch:
user profile iconChristian S. hat folgendes geschrieben Zum zitierten Posting springen:
(Und das Control dann natürlich aus dem Keycode rauslassen)


Und jetzt antworte ich nicht mehr, das ist mir zu doof.


Ccenter - So 31.01.10 22:37

Tut mir leid, ich hatte gerade ein Brett vorm Kopf :D

Es funktioniert jetzt zumindest. Vielen Dank an alle^^


C#-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:
25:
26:
27:
28:
29:
30:
31:
32:
33:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ManagedWinapi;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Hotkey hk1 = new Hotkey();
            hk1.Ctrl = true;
            hk1.KeyCode = Keys.A;
            hk1.HotkeyPressed += new EventHandler(hk1_HotkeyPressed);
            hk1.Enabled = true;
        }
        private void hk1_HotkeyPressed(object sender, EventArgs e)
        {
            MessageBox.Show("geht");
        }
    }
}


Ccenter - Sa 30.10.10 11:50

Tut mir leid das ich das alte Thema nochmal raushole aber ich habe gerade ein altes Projekt von mir über die Managed Winapi wiedergefunden.
Damals hatte ich es als Unterstützungstool für Spiele geschrieben.
Der Sinn war: Ich drücke z.B. die Taste "g" und mein Tool sendet 3x "g" zurück. Man konnte einstellen welche Taste wie oft betätigt werden soll.
Unter Win Xp lief das auch alles. Jetzt wollte ich ebend mein Programm unter Win7 testen habe ein Problem festgestellt bei dem ich echt nicht weiter weiß :D

Unter Xp hat das funktioniert:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
public int Anzahl;
private void Form1_Load(object sender, EventArgs e)
{
     Hotkey hka = new Hotkey();
     hka.Enabled = true;
     hka.KeyCode = Keys.A;
     hka.HotkeyPressed += new EventHandler(hka_HotkeyPressed);
}

void hka_HotkeyPressed(object sender, EventArgs e)
{
    int i = 0;
    while(Anzahl != i)
    {     
         SendKeys.Send("a");
         i++;
    }
}


Unter Xp war es scheinbar so, dass SendKeys nicht vom hka_HotkeyPressed Event abgefangen wurden.
Unter Win7 ist das leider so. Wenn ich per SendKey einen Buchstaben schicke, wird der vom Event abgefangen. Dass führt in meinem Code zu einer Unendlichkeitsschleife.
Was kann ich jetzt dagegen tun? Auf andere Buchstaben möchte ich nicht ausweichen, alleine schon aus Lernzwecken. Es müsste doch einen Weg geben, dass Sendkeys nicht vom Event abegangen wird oder?


Kha - Sa 30.10.10 13:52

Benutze ein Klassenfeld als Flag, das angibt, ob der Eventhandler gerade durchlaufen wird. Wird die Methode betreten und das Flag ist true, weißt du, dass sie rekursiv von Send aufgerufen wurde.
Also das Standardmittel gegen Reentrancy.


Ccenter - Sa 30.10.10 15:59

Ich habe gerade nach Flags gegoogled und rausgefunden, dass es im Zusammenhang mit Enums, also Aufzählungen, steht.
Ich begreife ab er nicht wie mir Aufzählungen jetzt weiterhelfen sollen. Ausserdem ist mir nicht klar wie ich von Flags etwas true oder false zurück bekommen.
Ich wäre dir sehr dankbar, wenn du dazu etwas mehr erklären könntest weil damit komme ich jetzt nicht weiter :D


Kha - Sa 30.10.10 16:47

Ein "Flag" bezeichnet erst einmal einfach eine boolesche Variable ;) . Nennen wir sie von mir aus bool isHotKeyPressedExecuted.


Ccenter - Sa 30.10.10 18:38

Ich hab vorhin, bevor ich den Thread eröffnet habe, folgendes ausprobiert (alles unwichtige habe ich jetzt entfernt):


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
public static bool send;
void hka_HotkeyPressed(object sender, EventArgs e)
{
      if (send == false)
      { 
           SendKeys.Send("a"); 
           send = true
      }
      else 
      {
           send = false;
      }
}


Dadurch wird die "Schleife" zwar unterbrochen, es wird aber kein Buchstabe an die Anwendung geschickt. Der Buchstabe löst einfach wieder das Event aus. Es ist falsch umgesetzt aber ist der Ansatz mit bool richtig?


Ccenter - Fr 05.11.10 18:04

Hat sonst jemand eine Idee?


Th69 - Fr 05.11.10 18:49

So müsste es richtig gehen:

C#-Quelltext
1:
2:
3:
4:
5:
6:
 if (!send) // besser als == false! -)
 {
     send = true;
     SendKeys.Send("a");
     send = false;
 }

So wird erst 'send' gesetzt und falls SendKeys wieder aufgerufen wird, passiert dann nichts, da ja mittels "!send" dies abgefangen wird.


Ralf Jansen - Fr 05.11.10 18:49


C#-Quelltext
1:
2:
           SendKeys.Send("a"); 
           send = true;


Einfach mal die beiden Befehle drehen. Wenn das hilft sollte dir klar sein warum.


Ccenter - Mo 08.11.10 22:33

Ok, wieso die Befehle gedreht theoretisch funktionieren sollten ist mir klar, es funktioniert aber leider trotzdem nicht.
Ich glaube so allmählich diese Herangehensweise ist komplett falsch denn der SendKeys-Befehl wird immer vom Event abgefangen. Durch den Code entsteht zwar keine Schleife mehr aber sobald "a" gesendet wird, fängt das Event den Buchstaben sofort ab bevor irgendeine andere Anwendung überhaupt etwas von dem "a" mitbekommet :D

Ich habe ebend meine Benutzerkontensteuerung mal ausgestellt weil ich es einfach nicht verstehe, dass mein Code unter Xp noch funktioniert hat. Mit ausgeschalteter Benutzerkontensteuerung funktioniert der Code seltsamerweise auch. Aber das ist keine Lösung. Allerding sagt das ja vielleich einem etwas :D?


Ralf Jansen - Mo 08.11.10 22:44

Alternativ könntest du auch mal probieren in deinen HotkeyPressed Event den Hotkey einfach vor dem SendKey abzuschalten(über die Enabled Property der HotKey Klasse) und nachher wieder an.


Ccenter - Di 09.11.10 19:17

Na super, darauf bin ich natürlich gar nicht erst gekommen :D
Vielen Dank, damit sollte ich mein Problem lösen können.