Autor Beitrag
avoid
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 230
Erhaltene Danke: 4

MSDos, WinXP32, Win764, Win10, Android, Debian
msl (mIRC edit), html & php & Java (DreamweaverMX), Basic (picaxe PE6), C (Arduino IDE), C# (vs2010,2015,2017,2019,Unity,Android Studio)
BeitragVerfasst: Mi 10.06.20 14:45 
Ich bin gerade dabei einen Sprachassistent in C# zu schreiben, der auf Befehle reagiert und Tastatureingaben an ausgewählte Prozesse sendet.

Mein Assistent reagiert problemlos auf die Sprachbefehle und gibt Antworten.
Auch das Finden der Prozesse per Name und erstellen eines Handle klappt wunderbar.
Ich kann sogar schon Tastatureingaben erzeugen und diese an die Prozesse senden.

Leider gibt es zwei Probleme, deren Ursache mir nicht klar ist.
1. Um einen Prozess und dessen Fenster als Ziel zu wählen verwende ich nachfolgenden Code, bevor ich die Tastatureingaben erzeuge und dort hin sende.
ausblenden C#-Quelltext
1:
using System.Runtime.InteropServices;					

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
Process[] process;
IntPtr hWnd;
[DllImport("User32.dll")]
private static extern int SetForegroundWindow(IntPtr point);
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, int nCmdShow);

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
if (process == null || hWnd == null) {
 process = Process.GetProcesses();
 foreach(Process pr in process) {
  if(pr.ProcessName == "Fenstername des Prozess") {
   hWnd = pr.MainWindowHandle;
  }
 }
}
else {
 SetForegroundWindow(hWnd);
 ShowWindow(hWnd, 5);  // SW_SHOW
 ShowWindow(hWnd, 9);  // SW_RESTORE
 PressKey(comm, true);  // Taste gedrückt
 // bei langem Tastendruck wird hier der press mit true wiederholt
 PressKey(comm, false);  // Taste los gelassen
}

Wenn das Fenster vorher noch keinen Fokus hatte, muss ich den Befehl leider wiederholen weil die Eingabe beim ersten Aufruf nicht an den Prozess gesendet wird.
Ich denke das liegt daran das ich den MainWindowHandle beim ersten Befehlsaufruf setze. Darum wird else erst beim zweiten Aufruf genutzt.
Wie kann ich das besser machen?

2. Nachdem ein par Prozesse die Eingaben per SendKeys.Send() nicht fressen wollten, habe ich mir eine Win32API Lösung gesucht die größtenteils auch das tut was ich brauche.
Hier der Link zum Code: SendInput Example In C#
Leider verwendet dieser Weg die char Darstellung der Tasten also z.b. 'T' oder '4' weswegen ich damit keine Tasten wie F2 oder F11 an meine Prozesse senden kann. P.s. ich habe den Code vom Link um die fehlenden F-Tasten ergänzt.
Ich habe schon versucht es mittels (char)Keys.F11 zu umgehen aber das klappt nicht. Vermutlich weil die F-Tasten kein eigentliches Zeichen besitzen sondern nur WM_KEYDOWN und WM_KEYUP.
Hat jemand eine Idee wie ich trotzdem die Eingabe von F-tasten an Prozesse senden kann?

Moderiert von user profile iconTh69: URL-Titel hinzugefügt.

_________________
Gute Fragen sind wie ein wissenschaftliches Experiment. Sie setzen eine Menge Wissen bereits voraus.
bitcoin:1J5dgQQp8eUy8wkUxyztBUVCkCpo5MQEQs?label=Danke
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 10.06.20 15:32 
Ja, warum verwendest du dann else?

Und wie kommst du drauf, daß SendInput nur Bytes nimmt - es verwendet bei Tastatureingaben die KEYBDINPUT-Struktur, in der du u.a. Virtual-Key Codes angeben kannst (alternativ den Scancode, aber der ist hardware-abhängig).
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 10.06.20 15:47 
zu 1. Warum steht da überhaupt ein else? Im if Zweig wird der Handle ermittelt. Warum nicht anstatt else einfach wieder darauf prüfen ob jetzt hWnd gesetzt ist.

zu 2. Ich würde die F-Tasten per SendKeys senden. Du mußt dich ja nicht für eine der beiden Methoden zwingend entscheiden sondern kannst anhand irgendwelcher Kriterien entscheiden welche jetzt besser passt. Übrigens du sendest die Keys an das Main Window der Anwendung. Niemand zwingt eine Anwendung so zu schreiben das Tastendrücke von da aus an die richtigen Stelle propagieren. Das ist oft so Garantien gibt es aber nicht, je nachdem was die Anwendung wie tut laufen deine Key Ereignis dort einfach ins Leere weil die Anwendung anders funktioniert.

Nebenbei :
Der Prozessname ist nicht zwingend eindeutig. Du sendest jetzt die Tastendrücke an den zufällig als letzten gefunden Process dieses Namens. Und die Process dieses Namens lassen sich leichter mit GetProcessesByName("Fenstername des Prozess") finden anstatt GetProcesses().
SetForegroundWindow hat einen return Wert. Wenn du die Anwendung nicht in den Vordergrund bekommst mußt du dich später nicht wundern wenn die Keys nicht ankommen. Solltest du vielleicht auswerten. Und was die Keys tun hängt für mich davon ab was in der Anwendung gerade den Focus hat. Solange du den Focus in der Zielanwendung nicht steuerst ist es für mich eher Zufall was dann in der Anwendung passiert.

Moderiert von user profile iconTh69: C#-Tags hinzugefügt
avoid Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 230
Erhaltene Danke: 4

MSDos, WinXP32, Win764, Win10, Android, Debian
msl (mIRC edit), html & php & Java (DreamweaverMX), Basic (picaxe PE6), C (Arduino IDE), C# (vs2010,2015,2017,2019,Unity,Android Studio)
BeitragVerfasst: Mi 10.06.20 18:31 
Naja ich wollte den Zielprozess nur beim ersten benutzen des jeweiligen Befehl ermitteln und nicht jedes mal.
Spielt das eine Rolle wenn die Anwendung länger läuft? Wenn nicht kann ich den Prozess ja jedes mal suchen.
Ich lasse das mit dem else mal gut sein.

Ich sag nicht das SendInput nur Byte nimmt sondern das der verwendetet Code das tut.
Ich rufe ja PressKey() auf und das erwartet ein char.
Also kann ich dort hin keine F-Taste senden.

Ich habe aber einen ähnlichen Code gefunden bei dem die F-tasten gehen.
Github Vocals C#
Ich habe mal versucht die beiden Codes zu vergleichen aber weil da viele eigene Klassen drin sind fehlt mir die Übersicht um den Code zu verstehen.

_________________
Gute Fragen sind wie ein wissenschaftliches Experiment. Sie setzen eine Menge Wissen bereits voraus.
bitcoin:1J5dgQQp8eUy8wkUxyztBUVCkCpo5MQEQs?label=Danke
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 10.06.20 19:41 
Zitat:
Ich habe mal versucht die beiden Codes zu vergleichen aber weil da viele eigene Klassen drin sind fehlt mir die Übersicht um den Code zu verstehen.


Die machen Richtung WinAPI ähnliches. Dein erstes SendInput example versucht aber per VKKeyScan aus dem übergebenen Char den Keycode zu machen. Die (virtuellen) KeyCodes sollten alle Tasten abbilden aber ein char eben nur druckbare Zeichen (zumindest ungefähr). Und die F-Tasten sind keine chars. Genauso wenig wie, wenn man mit chars arbeitet, man zwischen den Zahltasten aus dem Standardtastaturbereich und dem Numpad unterscheiden kann. Man arbeitet ja mit chars und nicht mit Tasten. Wenn du mit allen Tasten arbeiten willst und nicht nur mit chars dann solltest du direkt mit den KeyCodes für Tasten arbeiten. Der Keys Enum ist dafür passend und passt auch für WindowsApi Methoden wie z.B. MapVirtualKey das in beiden Codebeispielen benutzt wird (man muß den enum im Zweifel halt manchmal auf int/uint casten).


Aus dem Vocals Projekt sollte die VirtualKeyboard Klasse das passende für dich sein. Sie macht so ziemlich das gleiche wie dein erstes Code Beispiel verzichtet aber auf VkKeyScan und arbeitet mit dem Keys enum anstatt chars.
avoid Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 230
Erhaltene Danke: 4

MSDos, WinXP32, Win764, Win10, Android, Debian
msl (mIRC edit), html & php & Java (DreamweaverMX), Basic (picaxe PE6), C (Arduino IDE), C# (vs2010,2015,2017,2019,Unity,Android Studio)
BeitragVerfasst: Mi 10.06.20 21:57 
Das bedeutet ich könnte VkKeyScan() raus lassen und direckt Keys.F11 übergeben anstelle des char?

Lass mich mal sehen ob ich das richtig verstehe, dafür hab ich mal versucht den Code zu kommentieren.
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
public static void PressKey(char ch, bool press) { // hier wird ein Zeichen und der Status gedrückt oder losgelassen übergeben.
 byte vk = WindowsAPI.VkKeyScan(ch); // hier wird das Zeichen in seinen virtual-key Code konvertiert aber ohne "uMapType" wie ihn MapVirtualKey besitzt.
 ushort scanCode = (ushort)WindowsAPI.MapVirtualKey(vk, 0); // hier wird der virtual-key Code zum ScanCode, gibt 0 zurück wenn keine Übersetzung existiert.
 if(press)
  KeyDown(scanCode); // hier wird der ScanCode verarbeitet.
 // ...
}

public static new void KeyDown(ushort scanCode) {
 INPUT[] inputs = new INPUT[1];
 inputs[0].type = WindowsAPI.INPUT_KEYBOARD; // hier wird der typ des Eingabegerät festgelegt.
 inputs[0].ki.dwFlags = 0// hier wird das Flag mittels 0 auf KeyDown gesetzt.
 inputs[0].ki.wScan = (ushort)(scanCode & 0xff); // hier wird der ScanCode verändert und gesetzt aber wozu wird er verändert?
 uint intReturn = WindowsAPI.SendInput(1, inputs, Marshal.SizeOf(inputs[0])); // hier wird der erste Inhalt von inputs gesendet. 
 if(intReturn != 1) {
  throw new Exception("Could not send key: " + scanCode);
 }
}


Im anderen Code wird VkKeyScan() überhaupt nicht benutzt sondern ein Keys modifier mit gegeben.
Außerdem wird dort ein Scancode = 0x0008 als oder verwendet wenn kein ScanCode gefunden wurde.
Der Keys modifier scheind aus dem nichts zu kommen. :gruebel:

Überfordert mich schon etwas, diese WinApi. :les:

_________________
Gute Fragen sind wie ein wissenschaftliches Experiment. Sie setzen eine Menge Wissen bereits voraus.
bitcoin:1J5dgQQp8eUy8wkUxyztBUVCkCpo5MQEQs?label=Danke
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 10.06.20 22:43 
Zitat:
Das bedeutet ich könnte VkKeyScan() raus lassen und direkt Keys.F11 übergeben anstelle des char?


Genau.Du kannst Keys.F11 einfach auf uint casten und in MapVirtualKey reinschieben.

Zitat:
Außerdem wird dort ein Scancode = 0x0008


Laut Doku markiert man damit das man an SendInput einen ScanCode liefert (anstatt z.b. einen virtuellen KeyCode).
Übrigens die 0 die man als zweiten Parameter in MapVirtualKey schiebt hat eine ähnliche Bedeutung.
Dadurch weist man MapVirtualKey an aus dem übergeben KeyCode einen Scancode zu machen. Und denn gibt man an SendInput der wissen muss das dies ein Scancode ist deshalb die 8.

Magic Numbers sind immer was feines. In den Header Dateien der API sind dafür natürlich überall sprechende Konstanten definiert. Wenn man aus .Net per PInvoke diese API benutzt gehen die leider verloren und man muss die im Zweifel aufwendig nachdefinieren.
Die Kommentare wofür die gut sind aus den Header Dateien hat man dann aber immer noch nicht.
Oft hilft es bei pinvoke.net die API nachzuschlagen da sind meist die passenden Definitionen dabei.
avoid Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 230
Erhaltene Danke: 4

MSDos, WinXP32, Win764, Win10, Android, Debian
msl (mIRC edit), html & php & Java (DreamweaverMX), Basic (picaxe PE6), C (Arduino IDE), C# (vs2010,2015,2017,2019,Unity,Android Studio)
BeitragVerfasst: Do 11.06.20 02:08 
Vielen dank für die Erklärungen.
So weit so gut, läuft das senden beliebiger Tasten an das aktive Fenster das im Focus ist. Leider war es das damit auch schon.

Leider ist es mir erst jetzt aufgefallen, aber seit dem ich heute das WindowsUpdate durchgeführt habe, Funktioniert kein weg mehr um andere Prozesse in den Vordergrund zu holden und den Focus zu setzen.
Nur wenn ich das selbst per Maus oder Alt+Tab mache, quasi per Hand das Fenster wähle kommen dort auch die Eingaben an. :(

_________________
Gute Fragen sind wie ein wissenschaftliches Experiment. Sie setzen eine Menge Wissen bereits voraus.
bitcoin:1J5dgQQp8eUy8wkUxyztBUVCkCpo5MQEQs?label=Danke
avoid Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 230
Erhaltene Danke: 4

MSDos, WinXP32, Win764, Win10, Android, Debian
msl (mIRC edit), html & php & Java (DreamweaverMX), Basic (picaxe PE6), C (Arduino IDE), C# (vs2010,2015,2017,2019,Unity,Android Studio)
BeitragVerfasst: Sa 20.06.20 13:44 
Ich hab den Code nochmal aufgeräumt und aus den Beispielen nur das rein genommen, was ich auch brauche.
Nun hab ich mehr Übersicht darüber was passiert und wie es funktioniert.

nun wüsste ich nur noch gerne wie ich mit dem schon beschriebenen weg einen langen Tastendruck hin bekomme.
laut msdn sollte das gehen wenn man den KeyDown wiederholt bevor man den KeyUp ausführt.
leider spielt es keine rolle wie oft ich den down wiederhole es werden daraus höchstens doppele tastendrücke aber kein langer.

hat dazu noch jemand eine idee?

_________________
Gute Fragen sind wie ein wissenschaftliches Experiment. Sie setzen eine Menge Wissen bereits voraus.
bitcoin:1J5dgQQp8eUy8wkUxyztBUVCkCpo5MQEQs?label=Danke
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 20.06.20 13:55 
user profile iconavoid hat folgendes geschrieben Zum zitierten Posting springen:
nun wüsste ich nur noch gerne wie ich mit dem schon beschriebenen weg einen langen Tastendruck hin bekomme.
laut msdn sollte das gehen wenn man den KeyDown wiederholt bevor man den KeyUp ausführt.
leider spielt es keine rolle wie oft ich den down wiederhole es werden daraus höchstens doppele tastendrücke aber kein langer.
Was verstehst du denn darunter? Meinst du, dass dann der gedrückte Buchstabe immer wieder eingegeben wird?

Solange du kein KeyUp sendest, ist die Taste noch gedrückt.
avoid Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 230
Erhaltene Danke: 4

MSDos, WinXP32, Win764, Win10, Android, Debian
msl (mIRC edit), html & php & Java (DreamweaverMX), Basic (picaxe PE6), C (Arduino IDE), C# (vs2010,2015,2017,2019,Unity,Android Studio)
BeitragVerfasst: Di 23.06.20 00:42 
Darunter gibts doch nichts zu verstehen, der lange Tastendruck ist doch klar definiert.
Zitat:
Ein langer Tastendruck liegt vor, wenn die Taste für länger als 2 Sekunden gedrückt bleibt.

Manche Programm und Spiele haben damit zwei mögliche Aktionen auf einer Taste.

Es klappt nur leider nicht wie auf MSDN beschrieben den KeyDown zu wiederholen.
Da dort (bei WindowsAPI.SendInput) aber kein Beispiel aufgeführt ist sondern nur eine Textbeschreibung, erkenne ich leider nicht wo der Fehler liegt.

Muss ich ein Bit hoch zählen oder sonst was? ich hab keine Ahnung.
Auch mein Buch "Windows-Programmierung: Das Entwicklerhandbuch zur WIN32-API" beschreibt den langen Tastendruck nicht sondern verweist nur darauf das es ihn gibt.

aber ich merk schon, ich frag wieder Sachen die so tief gehen das sich damit keiner befasst hat. :(

_________________
Gute Fragen sind wie ein wissenschaftliches Experiment. Sie setzen eine Menge Wissen bereits voraus.
bitcoin:1J5dgQQp8eUy8wkUxyztBUVCkCpo5MQEQs?label=Danke
icho2099
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 101
Erhaltene Danke: 12

WIN XP, WIN 7, WIN 10
Delphi 6 Prof, Delphi 2005, FPC
BeitragVerfasst: Di 23.06.20 07:37 
wie unterscheidet sich eine für 3 Sekunden gedrückte Taste, für die die Wiederholung bereits läuft, von einer für 3 Sekunden gedrückten Taste, für die eine Sonderfunktion erwartet wird?
ich denke mal gar nicht. auf dem level der simulierten scan-codes gibt es da keine unterscheidung.
das wird, wenn überhaupt, im nachgang entschieden werden müssen, in der empfangenden anwendung.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 23.06.20 08:47 
user profile iconavoid hat folgendes geschrieben Zum zitierten Posting springen:
Manche Programm und Spiele haben damit zwei mögliche Aktionen auf einer Taste.

Es klappt nur leider nicht wie auf MSDN beschrieben den KeyDown zu wiederholen.
Es gibt dabei zwei Funktionalitäten. Die eine ist, dass nach der Wiederholungsverzögerung (repeat delay) entsprechend der Wiederholungsrate (repeat rate) die Taste immer wieder anschlägt, so dass dann der Buchstabe immer wieder erscheint.

Die andere ist, dass man die Taste einfach nicht sofort loslässt, sondern erst nach einer Sekunde z.B. und die Anwendung darauf anders reagiert. Das ist was du möchtest und dazu hatte ich ja schon geschrieben wie das geht:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Solange du kein KeyUp sendest, ist die Taste noch gedrückt.

Sprich als Pseudocode:
ausblenden Quelltext
1:
2:
3:
SendKeyDown
Delay(1000)
SendKeyUp
avoid Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 230
Erhaltene Danke: 4

MSDos, WinXP32, Win764, Win10, Android, Debian
msl (mIRC edit), html & php & Java (DreamweaverMX), Basic (picaxe PE6), C (Arduino IDE), C# (vs2010,2015,2017,2019,Unity,Android Studio)
BeitragVerfasst: Mi 24.06.20 23:35 
und genau das funktioniert eben nicht. :(
hab es mal mit dem spiel Star Citizen ausprobiert, da gibt es so einen langen Tastendruck als zweite Option z.b. auf den Tasten N und B.
bei beiden klappt weder die pause (dann wird die taste nur als einmal gedrückt erkannt) noch das wiederholen (dann wird die taste wie mehrfach gedrückt erkannt).

Ich denke das wiederholen der Taste ist schon der richtige Weg.
Ich bin gerade beim erneuten durchlesen des Tastatur abschnitt im Win32Api Buch auf den Wiederholungszähler in den untern 16Bit von lParam gestoßen.
Das scheint sich allerdings nur auf den letzten Parameter von SendMessage(hwnd, message, wParam, lParam) zu beziehen, nicht auf SendInput().

Könnte ich Tastendrücke auch als SendMessage() an Prozesse senden?

_________________
Gute Fragen sind wie ein wissenschaftliches Experiment. Sie setzen eine Menge Wissen bereits voraus.
bitcoin:1J5dgQQp8eUy8wkUxyztBUVCkCpo5MQEQs?label=Danke