Fragensteller
Tasten an ein Spiel senden

Frage
-
Ich möchte eine Anwendung programmiere, die es ermöglicht, Rennspiele mit der Maus zu steuern. Dazu will ich je nach abstand der Maus zum Mittelpunkt des Bildschrims unterschiedlich kurz nach einander die Tasten zum Steuern drücken (Pfeiltasten). Ich bekomme es aber nicht hin, dem Spiel die Tasten zu senden. Testprogramme waren Need for Speed World und Trackmana Nations Forever.
Ich habe mir schon folgende Dokumentationen angesehen, hat aber alles nichts genutzt:
- https://www.vb-paradise.de/index.php/Thread/59423-Programmierbare-Tastatur/?postID=462883#post462883
- http://social.msdn.microsoft.com/Forums/de-DE/12c6e419-9a61-4168-ace8-2d933d84899b/eine-tastatureingame-im-spiel-machen-bzw-eine-taste-drcken?forum=vbasicexpresseditionde
Hier mein Code:
Imports System.Runtime.InteropServices Public Class Form1 Dim xAbs As Integer Dim yAbs As Integer Dim xRel As Decimal Dim yRel As Decimal Dim sleepTime As Integer = 200 Private Declare Sub keybd_event Lib "user32.dll" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Int32, ByVal dwExtraInfo As Int32) Private Const VK_1 = &H31 ' Taste 1 Private Const KEYEVENTF_KEYUP = &H2 Dim moniSize As System.Drawing.Size = System.Windows.Forms.SystemInformation.PrimaryMonitorSize 'Timer zur Ermittlung der Mausposition Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles TimerErmittler.Tick moniSize = System.Windows.Forms.SystemInformation.PrimaryMonitorSize monSize.Text = "Bildschirmgröße: " & moniSize.ToString 'Absolute Mausposition ermitteln xAbs = MousePosition.X yAbs = MousePosition.Y 'Absolute Mausposition eintragen xAbsolute.Text = "X: " & xAbs yAbsolute.Text = "Y: " & yAbs 'Relative Mausposition ermitteln xRel = 1 - xAbs / moniSize.Width * 2 yRel = 1 - yAbs / moniSize.Height * 2 'Relative Mausposition eintragen xRelative.Text = "X in %: " & Math.Round(xRel * 100, 2) yRelative.Text = "Y in %: " & Math.Round(yRel * 100, 2) 'Timer einstellen If xRel < 0 Then xRel = 0 - xRel End If If yRel < 0 Then yRel = 0 - yRel End If ''Vertikal-Timer Try TimerVert.Interval = 1 / yRel Label1.Text = TimerVert.Interval Catch ex As Exception End Try ''Horizontal-Timer Try TimerHori.Interval = 1 / xRel Label2.Text = TimerHori.Interval Catch ex As Exception End Try End Sub 'Änderung der Checkbox Private Sub CheckBoxOnOff_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBoxOnOff.CheckedChanged If CheckBoxOnOff.Checked = True Then CheckBoxOnOff.Text = "Maussteuerung ist an" TimerVert.Enabled = True TimerHori.Enabled = True TimerVert.Start() TimerHori.Start() Else CheckBoxOnOff.Text = "Maussteuerung ist aus" TimerVert.Enabled = False TimerHori.Enabled = False End If End Sub 'Timer für vertikale Navigation Private Sub TimerVert_Tick(sender As Object, e As EventArgs) Handles TimerVert.Tick If xRel > 0 Then Call keybd_event(Keys.Left, 0&, 0&, 0) System.Threading.Thread.Sleep(sleepTime) Call keybd_event(Keys.Left, 0&, KEYEVENTF_KEYUP, 0&) Else Call keybd_event(Keys.Right, 0&, 0&, 0) System.Threading.Thread.Sleep(sleepTime) Call keybd_event(Keys.Right, 0&, KEYEVENTF_KEYUP, 0&) End If End Sub 'Timer für horizontale Navigation Private Sub TimerHori_Tick(sender As Object, e As EventArgs) Handles TimerHori.Tick If yRel > 0 Then Call keybd_event(Keys.Up, 0&, 0&, 0) System.Threading.Thread.Sleep(sleepTime) Call keybd_event(Keys.Up, 0&, KEYEVENTF_KEYUP, 0&) Else Call keybd_event(Keys.Down, 0&, 0&, 0) System.Threading.Thread.Sleep(sleepTime) Call keybd_event(Keys.Down, 0&, KEYEVENTF_KEYUP, 0&) End If End Sub End Class
Weiß jemand wie ich das machen kann?
Alle Antworten
-
Hi,
das kann so nicht funktionieren ;-)
Wie soll ich das erklären?! Was dein Programm macht, ist ein Tastaturevent an den eigenen Thread zu senden. Nicht aber an das Spiel.
Um Tastaturereignisse an ein anderes Programm (Spiel) zu senden, bedarf es einer "offenen Schnittstelle" (z.B. per WCF etc.).
Einfach nur im eigenen Programm zu sagen "x" wird gedrückt, lässt bei anderen Programmen nicht das Ereignis werfen "x" wird gedrückt ;-)
Anders ausgedrückt:
Du und dein Kumpel stellst dich auf die Straße. Du hast die Maus, dein Kumpel die Tastatur und "simuliert den Drücker an der Taste". Eine Freundin von euch, steht in der Wohnung 2 Straßen weiter. Wenn du jetzt deine Maus bewegst, sagst du zu deinem Kumpel "1x nach links". Und dein Kumpel ruft zu eurer Freundin "nach links nach links nach links". Aber eure Freundin kann euch nicht hören. Warum kann sie euch nicht hören? Sie ist nicht mit euch "verbunden". Sie sitzt in ihrer Wohnung mit geschlossenen Fenstern, zwei Straßen weiter.EURE STRASSE (also euer eigene Programmthread) kann euch hören, und macht auch die Tastaturevents (u.U., möglicherweise, nicht sicher).
Es sei denn, ein Spiel (wie Need for Speed) bietet eine entsprechende Schnittstelle an. Der Microsoft Flight Simulator bot solche eine Schnittstelle an. Da konnte ich von einem anderen Rechner, mittels VB, mit einem Forcefeedback Lenkrad das Flugzeug (das auf einem anderen Rechner lief) nach links und rechts lenken. Aber auch nur, weil mir die Schnittstelle offiziell "angeboten" (zugänglich) gemacht wurde. Da sendet man auch entsprechende Befehle(!) hin (z.B. per XML).
Aber einfach ein Tastaturevent auswerfen?! Stell dir mal was anderes vor: Ich drücke in MEINER Anwendung die Taste X und in 5 x-beliebigen anderen Word/Excel/Notpad-Anwendungen wird er Text einfach verändert. Wie wäre denn das?!
Dein Code-Schinppsel arbeitet in einem EIGENEN Thread! Und da sollte er auch bleiben ;-) Ein "Angeblich-hat-ein-User-Taste-Links-Gedrückt" reicht nicht aus, um in einer x-beliebigen anderen Anwendung vorzugaukeln "Taste-Links-wurde-gedrückt-erniedrige-dich-und-gehorche".
Wäre ja noch schöner ;-)
Gruß
AndyPS: Falls ich deine Anfrage richtig verstanden habe ;-)
-
Hallo zusammen,
@Andy
Der vom TE gezeigte Code funktioniert grundsätzlich! SendKeys und keybd_event sind durchaus in der Lage einfach einen Tastendruck zu simulieren - das hat nichts mit dem Thread oder Prozess zu tun. Teste es einfach mal mit Notepad, dort funktioniert es ohne Probleme. Gleiches gilt übrigens für die Simulation von Mausevents und wahrscheinlich auch für den Touchscreen.@QR-3
Wie ich in dem anderen Thread schon schrieb, machen DirectX/OpenGL ihre eigene Sache. Diese sprechen nur sehr bedingt auf die Windows API an, da Windows oft umgangen wird um Leistung zu sparen. Ich habe dort damals einige Seiten mit Lösungen verlinkt. Hast du die alle schon durchprobiert? Verschiedene Dinge wie SendInput und auch keybd_event schienen mir damals eine erfolgreiche Aussicht zu haben. Hast du dein Programm auch als Administrator gestartet? (Wenn du VS als Admin startest, wird beim Debuggen das Programm auch mit Admin-Rechten ausgeführt.)Du müsstest auch schon den Hinweis auf die DirectX SDK gelesen haben. Davon weiß ich allerdings nicht wirklich viel und ob es funktioniert kann ich ebenfalls nicht sagen. Zu den WinAPI Lösungen kann ich dir eventuell noch weiter helfen.
Tom Lambert - C# MVP
Bitte bewertet- und markiert Beiträge als Antwort. Danke.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
@Andy
Ja. Ich habe es eben nochmal unter Windows 8.1 (mit allen Updates) getestet. Einmal mit eingeschalteter und mit ausgeschalteter Benutzerkontensteuerung (UAC) unter einem Adminkonto aber ohne Adminrechten. Da die UAC unter Windows Vista und 7 leicht anders reagiert, kann es sich dort auch anders verhalten. Aber Grundsätzlich kann man davon ausgehen, dass es funktioniert.Man kann es sicherlich als Sicherheitslücke betrachten, aber da habe ich auch noch nicht wirklich drüber nach gedacht. Bisher scheint es damit keine wirklichen Probleme zu geben und auch ich habe einige Hilfsprogramme die so arbeiten. Es ist nicht der beste Weg, aber er funktioniert.
Selbst wenn es irgendwann mal blockiert wird, aufgrund der Abwärtskompatibilität sollte es wenigstens in einem erreichbaren Modus (Admin o.ä.) funktionsfähig bleiben. So verhielt es sich zumindest mit anderen Features.Tom Lambert - C# MVP
Bitte bewertet- und markiert Beiträge als Antwort. Danke.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
@Tom:
Also jetzt bin ich echt überrascht. Ich darf aus meiner EIGENEN(!!!) Anwendung (die im Adminmodus läuft) aus dem Explorer NICHTS empfangen, aber an jeder anderen Anwendung x-beliebiges senden? Auch PIN und TANs?
Ich hatte vor ein paar Wochen folgendes Problem: Meine VB.NET-Anwendung lief im Adminmodus. Und NUR, weil sie im Adminmodus lief, durfte vom Explorer NICHTS (per Drag&Drop) empfangen werden. Aber UMGEKEHRT soll jede meiner VB.NET-Anwendung jedes x-beliebige Programm Keyboard-Eingaben steuern/empfangen dürfen?
Jetzt bin ich total verwirrt :-(
Gruß
Andy -
@Andy
Für Windows sind das 2 vollkommen verschiedene Dinge. SendKeys etc. funktionieren wie die Bildschirmtastatur. D.h. das Windows dazwischen hängt.
Drag and Drop Vorgänge dagegen laufen direkt zwischen 2 Programmen ab, da kann Windows nicht viel machen.Ich vermute mal, das die Benutzerkontensteuerung dahinter steckt. In den meisten Fällen muss der Benutzer einem Programm expliziet Administratorrechte geben. D.h. das er auch einen Grund dafür hat und sich dem erhöhten Risiko bewusst ist, wenn er dem Programm nicht vertraut. Wenn nun dein Admin-Programm mit dem normalen Windows Explorer kommunizieren will, dann merkt Windows das und blockiert es. Es könnte schließlich sein das ein Programm versucht durch Sicherheitslücken Adminrechte zu erhalten ohne dass es der Benutzer weiß. Das es dabei die originale explorer.exe ist, spielt da scheinbar keine Rolle.
Beim senden von Tastatur (oder allgemeiner "Eingabe Events") verhält es sich so wie wenn man eine Hardware virtualisiert. Die Programme können über diese nicht kommunizieren, folglich gibt es kein Sicherheitsproblem.
Da PINs und TANs auch nur Zahlen (ggf. Buchstaben) sind, stellt das auch kein Problem dar. Das sind zwar grundsätzlich vertrauliche Informationen, aber kein Programm kann diese direkt auslesen. Über die Tastatur können diese höchstens an ein anderes Programm weitergeleitet werden und das auch nur sehr unsicher ob es wirklich funktioniert.
Zusammenfassung: Wenn 2 Programme kommunizieren, dann stellt das ein Sicherheitsrisiko dar. Wenn 2 Programme nur über eine gemeinsame Schnittstelle (Tastatur, ...) verbunden sind, stellt das i.d.R. kein Problem dar.
Tom Lambert - C# MVP
Bitte bewertet- und markiert Beiträge als Antwort. Danke.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
@Koopakilla:
Dein Link zu SendInput führt zu einer Beschreibung für C++. Das kann ich nicht. Werde mir aber mal was googeln (oder gibts das nur für C++?). Vielleicht hast du ja eine schöne Beschreibung zur Hand?
keybd_event habe ich oben im Beispiel verwendet, funzt nicht.
Habe das Proggi als Admin gestartet, hilft aber nix.
- Bearbeitet QR-3 Samstag, 12. April 2014 19:56
-
Hallo,
grundsätzlich kann man davon ausgehen, dass die WinAPI nur in C++ geschrieben wurde. Ich habe darum mit absicht auf den originalen C++ Artikel verlinkt, da man diese Methoden alle importieren kann. Das hast du auch schon bei keybd_event gemacht.pInvoke.net bietet sehr viele solche Importdeklarationen an. Siehe SendInput (user32). Für VB.NET findest du aber eher selten die zusätzlich benötigten Klassen etc. Den bereitgestellten C#-Code kannst du aber mit dem in der Signatur stehenden Converter übersetzen.
Mehr Ideen, als in dem anderen Thread geschrieben, habe ich aber auch jetzt nicht.
Tom Lambert - C# MVP
Bitte bewertet- und markiert Beiträge als Antwort. Danke.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
Aus dem Code in deinem Link werde ich nicht schlau. Was davon brauche ich, was nicht?
Hier der Übersetzte Code:
Dim pInputs = New () {New StructLib.INPUT() With { _ Key .type = EnumLib.INPUT_TYPE.INPUT_KEYBOARD, _ Key .ki = New StructLib.KEYBDINPUT() With { _ Key .wScan = EnumLib.ScanCodeShort.KEY_S, _ Key .wVk = EnumLib.VirtualKeyShort.KEY_S _ } _ }, New StructLib.INPUT() With { _ Key .type = EnumLib.INPUT_TYPE.INPUT_KEYBOARD, _ Key .ki = New StructLib.KEYBDINPUT() With { _ Key .wScan = EnumLib.ScanCodeShort.KEY_S, _ Key .wVk = EnumLib.VirtualKeyShort.KEY_S, _ Key .dwFlags = EnumLib.KEYEVENTF.KEYUP _ } _ }, New StructLib.INPUT() With { _ Key .type = EnumLib.INPUT_TYPE.INPUT_KEYBOARD, _ Key .ki = New StructLib.KEYBDINPUT() With { _ Key .wScan = EnumLib.ScanCodeShort.KEY_S, _ Key .wVk = EnumLib.VirtualKeyShort.KEY_S _ } _ }, New StructLib.INPUT() With { _ Key .type = EnumLib.INPUT_TYPE.INPUT_KEYBOARD, _ Key .ki = New StructLib.KEYBDINPUT() With { _ Key .wScan = EnumLib.ScanCodeShort.KEY_S, _ Key .wVk = EnumLib.VirtualKeyShort.KEY_S, _ Key .dwFlags = EnumLib.KEYEVENTF.KEYUP _ } _ }, New StructLib.INPUT() With { _ Key .type = EnumLib.INPUT_TYPE.INPUT_KEYBOARD, _ Key .ki = New StructLib.KEYBDINPUT() With { _ Key .wScan = EnumLib.ScanCodeShort.KEY_S, _ Key .wVk = EnumLib.VirtualKeyShort.KEY_S _ } _ }, New StructLib.INPUT() With { _ Key .type = EnumLib.INPUT_TYPE.INPUT_KEYBOARD, _ Key .ki = New StructLib.KEYBDINPUT() With { _ Key .wScan = EnumLib.ScanCodeShort.KEY_S, _ Key .wVk = EnumLib.VirtualKeyShort.KEY_S, _ Key .dwFlags = EnumLib.KEYEVENTF.KEYUP _ } _ }} Dim hWindow = Api.user32.FindWindow("notepad", Nothing) Api.user32.SetForegroundWindow(hWindow) Thread.Sleep(2500) Api.user32.SendInput(CUInt(pInputs.Length), pInputs, StructLib.INPUT.Size)