DllStructGetPtr ... Speicher-Dump schlägt fehl

  • Code
    Local $kbdState = DllStructGetPtr(_WinAPI_GetKeyboardState())

    Ich möchte herausbekommen, welches UTF-8 Zeichen eine Windows Applikation sieht, wenn eine bestimmte Taste (gegeben durch den Hardware-Scan Code) gedrückt wird, zusätzlich möglicherweise auch noch eine Umschalttaste wie [Shift], [Alt-Gr] usw. Im Prinzip möchte ich alle Scan-Codes einer PC Tastatur in die Routine stopfen können, und danach die Tastenbelegungen verschiedener länderspezifischer Tastaturen im Klartext ausgeben.

    Problem: PC Tastaturen schicken nur einen Scan-Code, der je nach Windows Gebietsschema-Einstellung in einen Windows Code (Virtual Key Code) umgewandelt wird.

    Die Umwandlung Scancode zu Virtual Key Code habe ich soweit im Griff:

    Im Aufruf von

    Code
    _WinAPI_GetKeyboardLayout()

    habe ich der EInfachheit halber im Moment die eigene Tastatur (Layout Deutsch) stehen, aber ich habe auch schon Code der eine andere länderspezifische Tastatur laden kann --> Problem zur Hälfte gelöst.

    Nun woillte ich noch verschiedene Modifier (Alt, Shift, Alt-gr) mitgeben um die Zweit- und Drittbelegungen der Tasten zu bekommen, und das will mir nicht gelingen.

    Es gelingt mir auch nicht, das $kbdState Array in die Konsole zu schreiben, ich bekomme immer 256 Nullwerte, obwohl ich am zurückgegebenen vkCode sehen kann, dass die Umwandung von z.B. Shift-1 in "!" geklappt hat. Natürlich rührt sich auch nichts, wenn ich versuche, Werte in $kbdState selber zu setzen, also z.B. die linke Shift-Taste per Programm zu drücken (die auskommentierte Zeile). Da aber auch dann die Funktion DumpDLLArray nur Nullen ausgibt habe ich vermutlich zwei Fehler, die ich ums ver***** nicht finden kann: das Setzend es Wertes mit DLLStructSetData schlägt irgendwie fehl, und abgesehen davon stehen in dem kbdState Array sowieso nicht die Statuswerte drinnen die ich lt. Microsoft Doku dort erwartet hätte, oder ich bin zu doof die richtigen Werte in die Konsole zu schreiben. Aktiviere ich z.B. Caps-Lock, müsste mindestens ein Byte <> 0 auftauchen, tut es aber nicht. :cursing:

    Wer kann mir einen Tipp geben wod er Wurm steckt?

    Thx, Armin

  • Herzlich Willkommen im Forum. :part:

    Wer kann mir einen Tipp geben wod er Wurm steckt?

    Wenn du uns ein lauffähiges Skript bereitstellst können wir uns das gerne mal näher anschauen, du hast nur einen kleinen Abschnitt gepostet der so nicht läuft.

    Ein paar Kommentare dran was wo rauskommen sollte bzw. was nicht klappt wäre auch sehr hilfreich (neben der textuellen Beschreibung). Ansonsten können wir auch nur spekulieren.

  • Gerne! Da das Gesamtprojekt doch etwas größer ist, habe ich einen Teil herausgetrennt, der das Problem zeigt.

    Problem 1: DumpDLLArray hat ein Problem, da es BEIM dUMP VON $kbdState 256 Nullwerte ausgibt. Es muss aber mindestens 3 Werte <> 0 geben, da ich in der Zeile vorher ja einen zu setzen versuche, und auzßerdem bevor ich das Scriupt gestartet habe Caps-Lock nd Num-Lock aktiviert habe. Selbst wenn mein Versuch, DLLStructSetData zu verwenden, irgendwie faul gewesen wäre, müssten noch die Bytes VK_CAPITAL (0x14) und VK_NUMLOCK (0x90) den Wert 0x80 haben. Der Dump sagt, haben sie nicht, aber die Konsole sagt,:


    Du siehst am [!] dass die Umwandlung prinzipiell funktioniert hat (weil Capslock aktiviert war). Ich bin also nicht allzu weit vom Ziel entfernt. Erreicht wäre es, wenn ich Capslock deaktiveren kann,

    Code
    DLLStructSetData($kbdState,1,0x80,$VK_LSHIFT)

    sollte ja dasselbe bewirken, indem es in der Keyboard State Map das Flag für Links-Shift-Down einträgt, und dann die veränderte Map in den Aufruf von toUnicodeEx stopfe.

    So lese ich jedenfalls die Doku: https://docs.microsoft.com/en-us/windows/…etkeyboardstate

    Die zur Suche nach dem eigentlichen Fehler gedachte Dump Routine tut,d as ist denke ich bewiesen, nicht so wie ich es haben wollte, ich finde ums verr**** keinen Fehler, und wenn ich herausfinden kann, was bei ihr faul ist bin ich schon mal einen Schritt weiter weil ich dann everntuell sehen kann, was beim DLLStructSetData Aufruf mit der Struktur passiert.

    Armin.

  • Gerne! Da das Gesamtprojekt doch etwas größer ist, habe ich einen Teil herausgetrennt, der das Problem zeigt.

    Hervorragend, genau so wollte ich es haben.

    Problem 1: DumpDLLArray hat ein Problem, da es BEIM dUMP VON $kbdState 256 Nullwerte ausgibt. Es muss aber mindestens 3 Werte <> 0 geben, da ich in der Zeile vorher ja einen zu setzen versuche, und auzßerdem bevor ich das Scriupt gestartet habe Caps-Lock nd Num-Lock aktiviert habe. Selbst wenn mein Versuch, DLLStructSetData zu verwenden, irgendwie faul gewesen wäre, müssten noch die Bytes VK_CAPITAL (0x14) und VK_NUMLOCK (0x90) den Wert 0x80 haben. Der Dump sagt, haben sie nicht, aber die Konsole sagt,:

    Das Problem ist hier schnell identifiziert. Du greifst auf den DllStruct falsch zu.

    ConsoleWrite(StringFormat("%4s",DllStructGetData($Array,$ElementNumber,$i)))

    $Array ist der 1. Parameter deiner Funktion, und du übergibst

    Local $kbdState = DllStructGetPtr(_WinAPI_GetKeyboardState())

    $kbdState als $Array.

    Die DllStructSetData/DllStructGetData-Funktion kann allerdings mit DllStruct-Pointern NICHTS anfangen. Du musst innerhalb der Funktion erst ein DllStruct erzeugen.

    DllStructCreate akzeptiert als 2. Parameter einen "pointer". Dieser Pointer ist genau das was DllStructGetPtr zurückgibt.

    Wenn du nun DllStructCreate mit dem selben Struct erstellst wie in _WinAPI_GetKeyboardState() und als Pointer $kbdState übergibst, dann kannst du innerhalb deiner Funktion auch die Werte lesen.

    Du kannst dir die Arbeit auch sparen und einfach den Struct übergeben den _WinAPI_GetKeyboardState erzeugt.

    Hier die Variante nur mit dem DllStruct ohne Pointer:

    Hier mal wie du es mit dem Pointer hättest machen müssen:

    Beide liefern mir 0en und 1en und einmal 128.

  • Klappt! Ich bin blass begeistert, und danke Dir herzlich für die Hilfe!

    Die Dump-Routine hat sich übrigens bewährt, mit der MS Doku alleine käme man nicht weit.

    Da das Array, das GetKeyboardState zurückbringt, 0-basiert ist während DLLStructSetData Arrays immer 1-basiert behandelt, muss ich zum VK Code eins dazuzählen, um den richtigen Wert zu setzen

    Einige Erkenntnise:

    - ich muss statt der 0x80 eine 0x81 setzen, damit das richtige Zeichen zurückkommt. In der MS Doku steht, es ist egal...

    Code
    DLLStructSetData($tKeyboardState, 1, 0x81, $VK_CAPITAL + 1)

    Das setzt die Hochstelltaste. Erst einmal habe ich versucht, VK_LSHIFT oder VK_RSHIFT zu setzen, das hat aber nicht geklappt.

    Um den Code für Alt-Gr zu bekommen, z.B. für den Klammeraffen, muss ich Alt-Control und den Tastencode (q = 0x10/0x51) setzen. Auf VK_RMENU reagiert die ToUnicodeEx Function nicht, genausoi wie es auf VK_LSHIFT und VK_RSHIFT nicht reagiert.:

  • Du hast glaube ich dein Skript angepasst da jetzt keine Dezimalwerte ausgegeben werden sondern die Hex-Werte abgeschnitten sind im Dump (81 müsste 0x81 sein oder 129).

    Kannst du dein neues Skript nochmal posten? Das angepasste von mir liefert ganz andere Werte (von der Hex/Dec-Verwechslung mal abgesehen.)

  • Noch schnell: ja, die Werte sind jetzt alle hex, damit ich direkt mit der MS-Doku zu den VKCodes arbeiten kann, ohne jedes Mal Hex in Dec umrechnen zu müssen.

    Für heute habe ich schon Schluss gemacht, die Entwicklungsanlage ist bereits aus ... aber ich poste morgen früh das geänderte Script. Ich schwöre, es hat genau die Werte ausgespuckt, und Du siehst ja in der letzten ausgegebenen Zeile genaud as was TounicodeEx ausgibt: das korrekte "@" Zeichen, wenn ich AltGr-q simuliere.

    Das mit der 129 hat ebenfalls seine Richtigkeit: in der MS Doku steht, man müsse für eine gedrückte taste das HOB setzen, also 128. Das LOB, das ebenfalls eine (nicht näher beleuchtete) Funktionalität hat, werde, so steht es da, ignoriert. Wird es wohl nicht, denn wenn ich 0x80 statt 0x81 setze bekomme ich statt "@" ein kleines "q" zurück. Ich muss das LOB setzen, sonst tut sich nichts.

    ---------------------

    Eventuell hast Du auch noch eine schnelle Antwort zu einem Problem, an dem ich morgen kauen wollte: es wäre natürlich angesagt, wenn ich schon am KeyboardState herumpatche, den ursprünglichen State wiederherzustellen. Da gäbe es jetzt 3 Wege:

    - wenn das was $kbdState = DllStructGetPtr(_WinAPI_GetKeyboardState()) zurückbringt sowieso nur ein Pointer auf eine lokale Speicherstruktur ist, ist alles gut, ich kann die Werte getrost vergessen weil sie beim nächsten Mal sowieso neu gesetzt werden.

    - wenn das aber ein Pointer auf eine Windows Struktur ist, wenn ich also permanent etwas verändere auf das auch andere Windows Prozesse zugreifen, wäre das eher nicht gut. und ...

    - ich müsste herausfinden, wie ich eine lokale Kopie dieser Struktur anlege, die ich dann verändern und in den ToUnicodeEx Aufruf stopfen kann.

    Ob es reicht, einfach $temp = $tKeyboardState setzen um eine lokale Kopie zu erzeugen wollte ich erst mal erforschen, viele Systeme würden das als Befehl, den Pointer zu kopieren sehen, und das wäre nichtd as was ich möchte. Ich brauche eine unabhängige Kopie der gesamten Struktur.

    Ich habe bereits heute Nachmittag ein paar Seiten in der AutoIt Doku durchgeackert, bin aber unterbrochen worden und war mir am Ende noch nicht ganz schlüssig, wie das alles zusammenhängt.

    Vielleicht weißt Du es einfach so aus dem Stand?

    Gute Nacht,

    Armin.

  • - wenn das was $kbdState = DllStructGetPtr(_WinAPI_GetKeyboardState()) zurückbringt sowieso nur ein Pointer auf eine lokale Speicherstruktur ist, ist alles gut, ich kann die Werte getrost vergessen weil sie beim nächsten Mal sowieso neu gesetzt werden.

    - wenn das aber ein Pointer auf eine Windows Struktur ist, wenn ich also permanent etwas verändere auf das auch andere Windows Prozesse zugreifen, wäre das eher nicht gut. und ...

    - ich müsste herausfinden, wie ich eine lokale Kopie dieser Struktur anlege, die ich dann verändern und in den ToUnicodeEx Aufruf stopfen kann

    Da musst du dir keine Sorgen machen, die MSDN schreibt Copies the status of the 256 virtual keys to the specified buffer..

    Das ist referenzfrei, also egal was du dort veränderst, du kannst nichts in Windows selbst verändern.

    Solltest du dennoch Zweifel haben kannst du ja einfach die Funktion nochmal aufrufen und schauen was für Werte du ausliest nachdem du den Buffer, der vorangegangen Funktion, manipuliert hattest.

    Du bist ziemlich vorschnell was die DllStructs angeht, du solltest nicht direkt DllStructGetPtr auf die Funktion anwenden sondern erst das Speichern, was GetKeyBoardState zurückgibt.

    Das gibt dir einen DllStruct zurück die du verändern kannst. Wenn du diese weitergeben willst solltest du erst dann den Pointer erzeugen und verwenden.

    Im schlimmsten Fall ist der Pointer ungültig, weil der DllStruct vom Interpreter beseitigt wurde, weil er denkt es würde niemand mehr drauf zugreifen, da es nirgends mehr auftaucht.

    Oder du findest später einen Bug nicht, weil diese Variable nicht existiert.

    So, nun zu dem Fall was du machen müsstest wenn es KEINE lokale Kopie wäre (was hier NICHT der Fall ist):

    Einfach eine Variable gleich einem anderen Struct setzen klappt leider nicht, wie man hier in diesem Testskript sieht:

    Du müsstest also ein neues Struct erstellen (selbe Struktur wie das was du kopieren möchtest) und dann anschließend alle Elemente einzeln kopieren.

    Wenn du Pointer hast, darfst du diese natürlich nicht blind kopieren! Du musst herausfinden auf welche Struktur sie zeigen, diese vorher instanziieren und dann alle Daten davon rüberkopieren und anschließend in deinem neuen Struct (welches das 1. ersetzen sollte) auf den kopierten Inhalt zeigen.

  • Ok, bin wieder da. Ich habe das Testprogramm ein wenig umgestrickt, aber die zentralen Routinen sind gleich geblieben.

    Hier erst mal die Ausgaben: es klappt alles wunderbar. Getestet werden jewils:

    - die Taste

    - die Taste mit Shift

    - die Taste mit Alt + Ctrl (= Alt-Gr)

    - die Taste mit VK_LSHIFT (tut nicht)

    - die Taste mit VK_RMENU (was laut MS Doku Alt-Gr sein soll - tut nicht)

    Wichtig sind mir also nur die ersten drei Zeilen, und da schauts m.E. sehr gut aus

    Der Code

    Ändere ich die Deifnition von $StateON auf 0x80, klappt nix mehr.

    Das Einzige was man noch verbessern kann, wäre den Fall $result = -1 (dead Key) zu behandeln. Da ich das aber für meine Aufgabe nicht brauche, schenke ich es mir.

    ------

    Deinen Hinweis auf "fahrlässigen Umgang mit Variablen" habe ich ernst genommen und im Rahmen meiner AutoIt Kenntnisse überdacht, finde aber beim konkreten Code nichts dabei, ihn so zu schreiben. Das direkte Übergeben von Funktionsresultaten als Parameter ist m.E. ein absoluter Standard, sollte ein Interpreter damit nicht zurecht kommen würde ich das als gravierenden Mangel betrachten. Wo genau siehst Du hier kokretes Potenzial, dass die interne Optimierung mir irgendwo dazwischen funkt?

    HG, Armin

  • Deinen Hinweis auf "fahrlässigen Umgang mit Variablen" habe ich ernst genommen und im Rahmen meiner AutoIt Kenntnisse überdacht, finde aber beim konkreten Code nichts dabei, ihn so zu schreiben. Das direkte Übergeben von Funktionsresultaten als Parameter ist m.E. ein absoluter Standard, sollte ein Interpreter damit nicht zurecht kommen würde ich das als gravierenden Mangel betrachten. Wo genau siehst Du hier kokretes Potenzial, dass die interne Optimierung mir irgendwo dazwischen funkt?

    Was ich meinte war, dass du schnell den Überblick darüber verlierst wenn du den Pointer von GetKeyboardState übergibst.

    Du wärst besser bedient einfach das Ergebnis zu speichern und den Struct zu übergeben statt das neu zu instanziieren mit dem Pointer.

    Im schlimmsten Fall wäre die Referenz ungültig weil du die Variable nicht gespeichert hast (in einem Scope welches noch gültig ist wenn du die Funktion verlässt auch wenn du momentam im globalen Kontext bist - ich spreche von dem Fall wenn du es nicht wärst) und jeder vernünftige Kompiler/Interpreter würde die Referenz entfernen, da du sie nicht mehr brauchst.

    Solche Fehler sind schwierig auszufinden in AutoIt, da keine Warnung oder Fehler geschmissen wird sondern der Interpreter hard-crashed.

    Ich schätze mal du musst 0x81 nehmen, um zu sagen, dass die Taste gedrückt wurde, oder getoggled wurde.

    Wenn du dich in die MSDN einliest wirst du da bestimmt schlau, das was du suchst ist ja der KeyState welchen du zu setzen versuchst.

    Ich bin momentan nicht am Rechner und kann dir nur bedingt aushelfen. Lies dich einfach mal durch die MSDN und schau ob du es nich doch gefixt kriegst.

    Kannst ja berichten und wenn wieder Zeit hab und am Rechner sitz schau ich mir das gerne an, dann kann ich dir auch genauer beschreibne was ich mit den Variablen meinte.