Forum: PC-Programmierung C# WPF Tastendruck simulieren und an ein Programm weiterleiten


von Daniel M. (daniel_91)


Lesenswert?

Hallo zusammen,

Ich möchte mit C# WPF eine Benutzeroberfläche programmieren, mit der auf 
ein Programm einer CNC-Steuerung zugegriffen werden kann.

- Die Benutzeroberfläche hat das Ziel, die Bedienung der CNC-Maschine 
auf das Nötigste zu reduzieren. Die Oberfläche muss mit WPF erstellt 
werden, Windows Forms kommt aufgrund von Vorgaben nicht in Frage.

- Das Programm, welches die CNC-Maschine steuert, läuft unter einem 
normalen Windows-Rechner. Die Befehle, die das Programm ausführen soll, 
können mit den Funktionstasten F2 bis F9 durchgeführt werden.

- Beim Start der Oberfläche wird das CNC-Steuerprogramm unsichtbar mit 
geöffnet. Die Oberfläche besteht aus Buttons mit der Aufschrift 
"Referenzfahrt durchführen", "Parkposition anfahren", "Job starten", 
etc.

- Bei einem Klick auf einen dieser Buttons soll der Druck auf eine 
Funktionstaste (F2 bis F9, je nach gedrückten Button) simuliert und an 
das CNC-Programm weitergeleitet werden.

Das Problem dabei ist, das ich zum einen nicht weiß, wie ich den Druck 
auf eine Taste unter WPF simulieren kann (ich habe nur was gefunden, wie 
das unter Windows Forms geht) und zum anderen habe ich keine Idee, wie 
ich dann diesen Tastendruck an das CNC-Programm senden kann.

Ich hoffe Ihr könnt mir helfen oder zumindest Tipps geben, in welche 
Richtung ich arbeiten sollte. Danke

: Verschoben durch User
von Peter II (Gast)


Lesenswert?

Daniel M. schrieb:
> Das Problem dabei ist, das ich zum einen nicht weiß, wie ich den Druck
> auf eine Taste unter WPF simulieren kann (ich habe nur was gefunden, wie
> das unter Windows Forms geht) und zum anderen habe ich keine Idee, wie
> ich dann diesen Tastendruck an das CNC-Programm senden kann.

das hat auch nicht mit Windows Form oder WPF zu tun.

Unter Windows werden Tastatureingaben per Event an ein Programm 
gesteuert. Du musst also nur die ProcessID von dem anderen Programm 
ermitteln und dann kannst du dort einen Event hin schicken.

von Stefan (Gast)


Lesenswert?


von Daniel M. (daniel_91)


Lesenswert?

Peter II schrieb:
> Daniel M. schrieb:
>> Das Problem dabei ist, das ich zum einen nicht weiß, wie ich den Druck
>> auf eine Taste unter WPF simulieren kann (ich habe nur was gefunden, wie
>> das unter Windows Forms geht) und zum anderen habe ich keine Idee, wie
>> ich dann diesen Tastendruck an das CNC-Programm senden kann.
>
> das hat auch nicht mit Windows Form oder WPF zu tun.
>
> Unter Windows werden Tastatureingaben per Event an ein Programm
> gesteuert. Du musst also nur die ProcessID von dem anderen Programm
> ermitteln und dann kannst du dort einen Event hin schicken.

Vielen Dank für die Antwort. Ist die Prozess ID dynamisch, d.h. bei 
jedem Start des Programms anders, oder ist sie immer gleich?

von adamzwerg (Gast)


Lesenswert?

Daniel M. schrieb:
> Ist die Prozess ID dynamisch, d.h. bei
> jedem Start des Programms anders, oder ist sie immer gleich?

Dynamisch. Die Frage hättest Du Dir mit Hilfe des Taskmanagers (oder 
Konsole & tasklist) aber auch leicht selbst beantworten können.

von bluppdidupp (Gast)


Lesenswert?

Daniel M. schrieb:
> ich habe nur was gefunden, wie
> das unter Windows Forms geht)

Vermutlich SendKeys? Einfach die System.Windows.Forms Referenz zum 
Projekt hinzufügen und man kann sie trotz WPF nutzen?
http://msdn.microsoft.com/de-de/library/system.windows.forms.sendkeys.aspx

von jo (Gast)


Lesenswert?

Windows + Echtzeitmaschinensteuerung = bäh.

Gruß Jonas

von Daniel M. (daniel_91)


Lesenswert?

adamzwerg schrieb:
> Daniel M. schrieb:
>> Ist die Prozess ID dynamisch, d.h. bei
>> jedem Start des Programms anders, oder ist sie immer gleich?
>
> Dynamisch. Die Frage hättest Du Dir mit Hilfe des Taskmanagers (oder
> Konsole & tasklist) aber auch leicht selbst beantworten können.

Ja, das ist mir im Nachhinein auch aufgefallen. Sorry für die unnötige 
Frage. Habe mittlerweile auch mit der Funktion: 
"System.Diagnostics.Process.GetProcessesByName(Prozessname)[0].Id"
die PID in meinem Programm einholen können.

jo schrieb:
> Windows + Echtzeitmaschinensteuerung = bäh.
>
> Gruß Jonas

Das mag eine Ansicht von dir sein, die ich auch respektiere. Allerdings 
sind das Parameter, die ich nicht ändern kann und mit denen ich ein 
vorgegebenes Ziel erreichen muss. Von daher wäre ich über eine 
konstruktivere Antwort dankbar.

von Daniel M. (daniel_91)


Lesenswert?

Ich habe es jetzt geschafft mit folgenden Code einen Tastendruck zu 
simulieren:
1
/// <summary>
2
/// Interaktionslogik für MainWindow.xaml
3
/// </summary>
4
public partial class MainWindow : Window
5
{
6
  // Konstruktor für das Fenster
7
  public MainWindow()
8
  {
9
    InitializeComponent();
10
  }
11
  // Eventhandler für das Event "Taste nach unten gedrückt"
12
  private void OnKeyDownHandler(object sender, KeyEventArgs e)
13
  {
14
    // Wenn die gedrückte (simulierte) Taste die F5-Taste ist...
15
    if (e.Key == Key.F5)
16
    {
17
      //... Infotext in der Textbox "textBox1" ausgeben
18
      textBox1.Text = "F5-Tastendruck wurde simuliert";
19
    }
20
  }
21
  // Eventhandler für das Event "button1 wurde geklickt"
22
  private void button1_Click(object sender, RoutedEventArgs e)
23
  {
24
    KeyEventArgs args = new   KeyEventArgs(Keyboard.PrimaryDevice,
25
26
    Keyboard.PrimaryDevice.ActiveSource, 0, Key.F5);
27
28
    args.RoutedEvent = Keyboard.KeyDownEvent;
29
30
    InputManager.Current.ProcessInput(args);
31
  }    
32
}

Auch kann ich die Process-ID, an welches Programm der Tastendruck 
gesendet werden soll, mittels der im vorigen Beitrag genannten Funktion 
ermitteln.

Jetzt stehe ich allerdings vor dem Problem, wie ich diesen Tastendruck 
an das Programm weiterleiten kann.

von jo (Gast)


Lesenswert?

So wird das nicht gehen. Einfachste Möglichkeit die ich sehe, ist sowas 
wie

https://www.autoitscript.com/site/autoit/

Gruß Jonas

von jo (Gast)


Lesenswert?

Wenn müsstest du das handle von dem anderen Fenster(deines Programms) 
irgendwie bekommen um darauf mouseevents zu feiern... nur mal so noch

Gruß Jonas

von bluppdidupp (Gast)


Lesenswert?


von adamzwerg (Gast)


Lesenswert?

Dass das andere Programm versteckt im Hintergrund läuft, könnte ein 
Problem sein. Überhaupt ist das eine ziemlich unsaubere Methode, bei der 
die Details evtl. sogar von der Windows-Version abhängen. Es wäre sehr 
viel besser, wenn das CNC-Programm irgendeine Art von externer Steuerung 
zuließe (über Pipes, Sockets oder wie auch immer).

Tipp: Probiere es erst einmal mit dem oben bereits genannten AutoIt 
(Send, ControlSend). Wenn du damit etwas erreichst, sollte es auch mit 
einem eigenen Programm klappen; wenn nicht, sieht es vermutlich eher 
schlecht aus.

von Daniel M. (daniel_91)


Lesenswert?

Ich habe jetzt folgenden Code zum Testen geschrieben, der bis auf einen 
Kleinigkeit auch soweit funktioniert. Der Prozess "Notepad" wird in den 
Vordergrund geholt (dieser wurde zuvor gestartet) und auch wird ein 
F5-Tastendruck simuliert (der entsprechende Handler wird aufgerufen). 
Nur leider wird das Event nicht an den Prozess "Notepad" weitergeleitet. 
Wie kann das sein?

Hier der Code:
1
private void button1_Click(object sender,
2
                           RoutedEventArgs eventhandlerDelegate)
3
{
4
  // F5-Tastendruck simulieren
5
  KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice, 
6
                                      Keyboard.PrimaryDevice.ActiveSource, 
7
                                      0, 
8
                                      Key.F5);
9
  // Event zuordnen
10
  args.RoutedEvent = Keyboard.KeyDownEvent;
11
  // 
12
  InputManager.Current.ProcessInput(args);
13
  // Prozess  in den Vordergrund holen (als Beispiel Notepad)
14
  SetForegroundWindow(Process.GetProcessesByName("notepad")[0].MainWindowHandle);
15
}

von adamzwerg (Gast)


Lesenswert?

Daniel M. schrieb:
> Ich habe jetzt folgenden Code zum Testen geschrieben, der bis auf
> einen Kleinigkeit auch soweit funktioniert.

Der funktioniert eigentlich überhaupt nicht ... Du rufst ProcessInput 
(BTW: das bedeutet nicht "Sende etwas an Prozess xyz") auf und bringst 
dann das Fenster in den Vordergrund. Aber selbst mit dem entsprechenden 
Fenster im Vordergrund klappt es so nicht.

> Wie kann das sein?

Das liegt daran, dass du einen Denkfehler machst:

> Der Prozess "Notepad" wird in den Vordergrund geholt

Nein, das Fenster wird in den Vordergrund geholt.

> Nur leider wird das Event nicht an den Prozess "Notepad" weitergeleitet.

An das Fenster.

Lade mal das hier runter, wenn du willst:
http://inputsimulator.codeplex.com/
bzw. installiere es direkt über die NuGet-Konsole in Visual Studio per
Install-Package InputSimulator

Dann sollte dein Notepad-Test ungefähr so funktionieren 
(Minimalbeispiel, alles in die Fenster-Klasse gepackt):

1
public partial class MainWindow : Window
2
  {
3
    public MainWindow()
4
    {
5
      InitializeComponent();
6
      _keysim = new KeyboardSimulator(new InputSimulator());
7
    }
8
9
    [DllImport("user32.dll")]
10
    static extern bool SetForegroundWindow(IntPtr hWnd);
11
12
    [DllImport("user32.dll")]
13
    private static extern IntPtr GetForegroundWindow();
14
15
    private void OnSend(object sender, RoutedEventArgs e)
16
    {
17
      if(ActivateTargetWindow("notepad"))
18
      {
19
        _keysim.KeyPress(VirtualKeyCode.F5);
20
      }
21
    }
22
23
    private static bool ActivateTargetWindow(string procName)
24
    {
25
      var proc = Process.GetProcessesByName(procName);
26
      if(proc.Length == 0) return false;
27
28
      var windowHandle = proc[0].MainWindowHandle;
29
      SetForegroundWindow(windowHandle);
30
31
      const int maxTries = 10;
32
      const int delay = 20;
33
      int curTry = 0;
34
      bool targetIsForeground;
35
      do
36
      {        
37
        Thread.Sleep(delay);
38
        targetIsForeground = GetForegroundWindow() == windowHandle;
39
        curTry++;
40
      } while(!targetIsForeground && curTry < maxTries);
41
      
42
      return targetIsForeground;
43
    }
44
45
    private readonly KeyboardSimulator _keysim;
46
  }

Dir ist sicher klar, dass dir das - auch wenn es funktioniert - bei 
deinem versteckten Hintergrundfenster kaum weiter hilft. Darum mein 
Hinweis auf AutoIt und ControlSend (zum Testen, ob das Ganze überhaupt 
funktioniert, nicht als Ersatz für dein C#-Programm).

von Daniel M. (daniel_91)


Lesenswert?

adamzwerg schrieb:
> Lade mal das hier runter, wenn du willst:
> http://inputsimulator.codeplex.com/
> bzw. installiere es direkt über die NuGet-Konsole in Visual Studio per
> Install-Package InputSimulator

Danke für die Antwort.

Das Programm habe ich jetzt runter geladen. Aber ich verstehe nicht, wie 
ich das benutzen kann. Ich finde nirgends ein Menüpunkt mit Namen 
"Install-Package" im Visual Studio.

Wie kann ich das Programm nutzen?

von jo (Gast)


Lesenswert?

Na das gibst du in der Console ein:

"Install-Package InputSimulator"

dann wird es installiert. Eventuell musst du dann noch die passenden 
Usings in deinem Program einbauen.

Gruß Jonas

von Daniel M. (daniel_91)


Lesenswert?

jo schrieb:
> Na das gibst du in der Console ein:
>
> "Install-Package InputSimulator"
>
> dann wird es installiert. Eventuell musst du dann noch die passenden
> Usings in deinem Program einbauen.
>
> Gruß Jonas

Ich habe aber keine Consolenanwendung...

von bluppdidupp (Gast)


Lesenswert?

Du kannst auch rechte Maustaste auf dein Projekt im "Solution Explorer" 
und dann "Manage NuGet Packages" und dort nach InputSimulator suchen.
(Die nuget-Konsole kann ansonsten via "Tools" > "NuGet-Package Manager" 
> "Package Manager Console" eingeblendet werden)

von jo (Gast)


Lesenswert?

>Ich habe aber keine Consolenanwendung...

Eh, du hast eher kein Plan. Ich rede von einer Console (hier die nuget 
console) die man über Visual-Studio aufrufen um anschließend damit 
komfortabel neue Packete für die Integration in eigene Projekte 
installieren kann.

Gruß Jonas

von Daniel M. (daniel_91)


Lesenswert?

bluppdidupp schrieb:
> Du kannst auch rechte Maustaste auf dein Projekt im "Solution Explorer"
> und dann "Manage NuGet Packages" und dort nach InputSimulator suchen.
> (Die nuget-Konsole kann ansonsten via "Tools" > "NuGet-Package Manager"
>> "Package Manager Console" eingeblendet werden)

"Tools" gibt es leider bei meiner Version nicht und auch im 
Solutionexplorer gibt es keinen Eintrag mit "Manage NuGet Packages".
Wie heißt das denn bei der deutschen VS-Version?

: Bearbeitet durch User
von Borislav B. (boris_b)


Lesenswert?

Daniel M. schrieb:
> "Tools" gibt es leider bei meiner Version nicht und auch im
> Solutionexplorer gibt es keinen Eintrag mit "Manage NuGet Packages".

Ist auch erst in den neuern Visual Studio Versionen (2013?) enthalten.

von Gustav (Gast)


Lesenswert?

Daniel M. schrieb:
> Wie heißt das denn bei der deutschen VS-Version?

Ganz ehrlich: laß es einfach.

Gärtnern ist doch ein schönes Hobby. Töpfern. Vielleicht auch Minigolf?

von Daniel M. (daniel_91)


Lesenswert?

Gustav schrieb:
> Daniel M. schrieb:
>> Wie heißt das denn bei der deutschen VS-Version?
>
> Ganz ehrlich: laß es einfach.
>
> Gärtnern ist doch ein schönes Hobby. Töpfern. Vielleicht auch Minigolf?

Es geht hier ja nicht ums Hobby, sondern um eine Problemstellung 
innerhalb einer Abschlussarbeit. Von daher kein hilfreicher Ratschlag

von Gustav (Gast)


Lesenswert?

Du hast recht.

Es gibt doch auch andere schöne Abschlußarbeiten. Im Ausbildungsberuf 
Gärtner, Keramikmeister oder Spitzensportler. :-)

von Daniel M. (daniel_91)


Lesenswert?

Gustav schrieb:
> Du hast recht.
>
> Es gibt doch auch andere schöne Abschlußarbeiten. Im Ausbildungsberuf
> Gärtner, Keramikmeister oder Spitzensportler. :-)

Du scheinst das nicht ganz zu verstehen.
Es geht hier um eine elektrotechnische/ informationstechnische 
Problemstellung.

Und es geht auch nicht um eine Ausbildung sondern ein Studium. Und da 
ich fast fertig bin, kommt ein Wechsel der Fachrichtung auch nicht in 
Frage.

von funky (Gast)


Lesenswert?

Leg dir lieber erstmal einen vernünftigen Ironiedetektor zu...

von bluppdidupp (Gast)


Lesenswert?

Boris P. schrieb:
> Ist auch erst in den neuern Visual Studio Versionen (2013?) enthalten.

Ich meine ab 2012 und auch in Express-Editions.


Daniel M. schrieb:
> "Tools" gibt es leider bei meiner Version nicht und auch im
> Solutionexplorer gibt es keinen Eintrag mit "Manage NuGet Packages".
Tools heisst dort meine ich "Extras", aber der Begriff "NuGet" sollte 
einem an zig Stellen über den Weg laufen, wenn man drauf achtet.
Welches VS-Version ist denn installiert?

von Daniel M. (daniel_91)


Lesenswert?

Ich habe das Problem jetzt erfolgreich gelöst:

Mit der Funktion EnumWindos() zuerst sämtliche Prozesse einholen, diese 
dann nach Titel und Klassennamen filtern. Die Klasse "Process" ist dafür 
nicht geeigent, da diese nur die MainHandles zurückgibt und das in 
meinem Fall nicht ausreichend war.

Anschließend das gesuchte Handle des Fensters ermitteln und an dieses 
mit der Funktion PostMessage() einen simulierten Tastendruck senden.

Vielen Dank für die zahlreichen Antworten, auch wenn einer hier nicht 
ganz verstanden hat, worum es eigendlich ging :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.