_IsPressed() Abfrage strukturieren/verbessern

  • Hallo.


    Dieses mal geht es um Verbesserungsvorschläge zu einer Funktion "KeysForLeftParenthesisWerePressed()", in der verschiedene Tasten mit _IsPressed() abgefragt werden. Es gibt 1 Zeichentaste (Char key) und 3 Steuertasten (Control keys). Damit es nicht zu kompliziert wird (für mich) 8o habe ich die Steuertasten begrenzt auf: Ctrl, Shift und Alt. Den japanischen Hanuta-Keys (richtig: "Hankaku key") habe ich weggelassen, da ich weder einen virtuellen Key-Code zuordnen konnte, noch glaube, dass man ihn zum Schreiben von "(" braucht.


    In der Funktion wird die Zeichentaste immer ausgewertet. Zusätzlich wird ermittelt, ob und wieviele Steuertasten ausgewertet werden sollen. Z.B. beim französischen Keyboard-Layout benötigt man nur die Taste 5, um "(" zu schreiben = 0 Steuertasten. Bei deuschen KB-Layout drückt man für "(" die Kombi Shift+8, also 1 Steuertaste, usw. Ob es Tastenkombis gibt, die Ctrl und/oder Alt benutzen um "(" zu schreiben, konnte ich nicht herausfinden.


    Kurzbeschreibung


    Der Funktion wird 1 Zeichentaste übergeben + 0 bis 3 Steuertasten. Es soll geprüft werden, ob diese Tasten gedrückt wurden. Rückgabe True/False. Wichtig ist, dass dabei auch geprüft werden soll, dass nur die gewünschten Tasten und keine anderen dazu gedrückt werden. Wenn ich wüsste wie, würde ich folgendes programmieren:


    If _IsPressed(Zeichentaste) And _IsPressed(Steuertasten) And Not _IsPressed(AndereTasten) Then


    Bitte seht euch die Funktion "KeysForLeftParenthesisWerePressed()" mal an und sagt, ob ihr Ideen zur Verbesserung oder Vereinfachung habt.


    Edit: Bugs bei der Ermittlung der unbenutzen Steuertasten gefunden und (hoffentlich) behoben.

  • Global Const $User32 = DllOpen("User32.dll")

    Wenn du die Dll schon öffnest, warum benutzt du sie dann nicht?


    If _IsPressed($aKeyCodes[0])

    If _IsPressed($aKeyCodes[0], $User32) ...


    #include <WinAPISys.au3>

    Wofür?


    Du prüfst übrigens auch nicht, ob noch weitere Tasten gedrückt sind... z.B. SHIFT + 8 + 9 -->> 10|38|39|A1


    Es gibt zudem auch billige Tastaturen, die nicht mehr als 3 Zeichen gleichzeitig verarbeiten können...


    Hier mal meine Version:

  • DllOpen("User32.dll") ...

    Wenn du die Dll schon öffnest, warum benutzt du sie dann nicht?

    Vielen Dank dafür! Und wieder hast du mich gerettet aus der Betriebsblindheit. :thumbup:Das hätte ich noch 1.000x durchgucken können, ich hätt's nicht gemerkt. - Gut gemacht! Dann sind wir jetzt wieder Freunde! :)

    #include <WinAPISys.au3>

    Wofür?

    Das mache ich um zu sehen, wie aufmerksam du bist! :P

    Du prüfst übrigens auch nicht, ob noch weitere Tasten gedrückt sind... z.B. SHIFT + 8 + 9 -->> 10|38|39|A1

    Guter Punkt. Allerdings:

    ; Diese Routine prüft, ob die Tasten (1 oder mehrere) gedrückt wurden, die zum
    ; Schreiben einer öffnenden runden Klammer "(" benötigt werden.

    Auf der MS-Seite für "VkKeyScanExW()" steht: "Translates a character to the corresponding virtual-key code and shift state." und für "Return value": "If the function succeeds, the low-order byte of the return value contains the virtual-key code and the high-order byte contains the shift state, which can be a combination of the following flag bits."


    Der langen Rede kurzer Sinn: So wie ich es verstehe, wird nur 1 virtueller Key-Code zurückgegeben, was 1 Taste entspricht.


    Oder irre ich mich? Wie wahrscheinlich ist es, das es ein KB-Layout gibt, bei dem man mehere Zeichen-Tasten drückt um "(" zu schreiben? (Geht das überhaupt?) :/


    Es gibt zudem auch billige Tastaturen, die nicht mehr als 3 Zeichen gleichzeitig verarbeiten können...

    Das sind meine Freunde, denn denen geht es wie mir! :rofl:


    Deinen Code habe ich kurz durchgesehen. Der scheint wie immer sehr interessant zu sein! Ich bin erst heute abend wieder zu hause, dann werde ich ihn mir zu Gemühte führen.

  • Wenn ich wüsste wie, würde ich folgendes programmieren:


    If _IsPressed(Zeichentaste) And _IsPressed(Steuertasten) And Not _IsPressed(AndereTasten) Then

    Interessante Sache, dein Code. Beim Durchlaufen der 255 KeyCodes mit _IsPressed() könnte fast meinen, du hättest das im Zitat als Vorgabe genommen. Wenn ich das richtig verstehe, macht es genau das, was dort beschrieben wird: Es prüft, ob die gesuchten Tasten gedrückt sind und ob alle anderen Tasten nicht gedrückt sind.


    Für wie performant hältst du das ständige Abfragen von 255 Tasten?

  • Ja, das Zitat habe ich als Vorgabe genommen... anders macht es doch auch keinen Sinn... denn SHIFT + 8 + 9 ist ja nicht SHIFT + 8, wobei es bei der Auswertung natürlich auf die Reihenfolge ankommt, in der die Tasten gedrückt wurden... hätte wohl besser SHIFT + 9 + 8 schreiben sollen... denn da darf dann kein "Bingo" kommen!


    Switch...Case...EndSwitch ist übrigens um einiges schneller als For...To...Step...Next... damit könnten die nicht relevanten Tasten (Umschalttasten (Toggle-Keys), z.B. CAPS LOCK ($VK_CAPITAL)) dann auch ohne Mehraufwand ausgelassen werden... Switch benötigt allerdings deutlich mehr Code.


    Für wie performant hältst du das ständige Abfragen von 255 Tasten?

    Das nennt sich Polling... und das ist nur in sehr seltenen Fällen eine gute Wahl... doch zu unserem Glück bietet uns Windows ja Message-Handler an, die man registrieren kann, um zu erfahren ob eine Taste gedrückt oder losgelassen wurde... das ist performant!


    Davon bräuchtest du dann (mindestens) folgende: WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP und WM_SYSKEYUP


    Bsp.:

    GUIRegisterMsg($WM_KEYDOWN, 'WM_KEYDOWN')

    GUIRegisterMsg($WM_SYSKEYDOWN, 'WM_KEYDOWN') ; *1

    GUIRegisterMsg($WM_KEYUP, 'WM_KEYUP')

    GUIRegisterMsg($WM_SYSKEYUP, 'WM_KEYUP') ; *1

    *1 Kann man natürlich auch in eigene Funktionen umleiten, ist aber nicht nötig.


    In der Funktion WM_KEYDOWN fügst du die gedrückte Taste deinem Array *2 hinzu, in der Funktion WM_KEYUP löscht du die losgelassene Taste (falls vorhanden) wieder aus dem Array.

    *2 Array evtl. sortieren, um die Auswertung zu vereinfachen.


    Die (255) Tasten müssen dann nicht mehr überprüft werden... und falls doch, dann nur bei einem KEYDOWN-Event... was evtl. besser mit _WinAPI_GetKeyboardState in einem Rutsch gemacht werden sollte, als 255 mal _IsPressed (_WinAPI_GetAsyncKeyState) aufzurufen.


    Ich habe dafür bereits ein fast fertiges Script, in dem das zu verwendende Tastaturlayout beliebig umgestellt werden kann... getestet habe ich mit de-DE, en-EN und fr-FR... und es funktioniert!


    Es dauert aber noch etwas, bis ich es fertig habe... bin mom. nicht in Form... starke Kopfschmerzen... schätze mal bis zum WE.


    Was mich allerdings interessieren würde... gibt es auch eine (Dll-)Funktion, mit der ich beim Drücken von z.B. SHIFT + 8 aus den VkCodes ($wParam) das auszugebende Zeichen "(" ermitteln kann?

    Bsp. Send("("):

    "(" -->> 10|38|A0 -->> $VK_SHIFT + $VK_8 + $VK_LSHIFT

    "(" -->> 10|38|A1 -->> $VK_SHIFT + $VK_8 + $VK_RSHIFT

    10|38|A0 -->> $VK_SHIFT + $VK_8 + $VK_LSHIFT -->> "(" ?

    10|38|A1 -->> $VK_SHIFT + $VK_8 + $VK_RSHIFT -->> "(" ?


    Alternativ zu den Message-Handlern ginge es auch mit _WinAPI_SetWindowsHookEx...

  • Bevor wir uns der Erkennung zuwenden, ob ein "(" eingegeben wurde, erstmal ein Status-Update.


    Bis hierhin ist das eigentliche Thema dieses Thread zu meiner Zufriedenheit geklärt: "_IsPressed() Abfrage strukturieren/verbessern". Deine Infos waren eine große Hilfe: In meinem Code wurde der Bug entdeckt, dass "User32.dll" in "_IsPressed()" NICHT verwedet wurde und du hast einige nützliche Erklärungen geschrieben, z.B. zur Auswertung der gedrückten Tasten per Stringvergleich, zu Polling, Select..Case, uvm.


    Und nun lass uns meinen Thread kapern! 8o


    ----------- schnipp ----------------------------------------------------------------------------------------------------

    Falls das Nachfolgende umfangreicher wird, kann ich später einen anderen Thread dafür erstellen.

    ----------- schnapp ----------------------------------------------------------------------------------------------------


    In diesem Thread ging es um die Verbesserung der _IsPressed Abfrage aus Posting #1. Diese Abfrage erfolgt erst nach der Erkennung, ob die Tasten für "(" gedrückt wurden. Und natürlich ist es in erster Linie interessant, diese Erkennung zu verbessern. ;)


    Wie kann man in einem fremden Fenster erkennen, ob vom User Tasten gedrückt wurden, die dort eine öffnende runde Klammer "(" ergeben?


    Hier im Forum hatte ich die Möglichkeit erörtert, mit "VkKeyScanEx()" zu ermitteln, welche Tasten benötigt werden um "(" im fremden Fenster zu schreiben und diese Tasten dann ständig systemweit abzufragen. Das ergab einen Workaround, der brauchbar funktioniert. Aber er hat auch Schwächen, wie du geschrieben hast, ob z.B. die Umschalttaste Edit: gemeint ist Feststelltaste (Caps Lock) gedrückt ist u.ä.


    Dann hast du die Frage aller Fragen gestellt:

    Was mich allerdings interessieren würde... gibt es auch eine (Dll-)Funktion, mit der ich beim Drücken von z.B. SHIFT + 8 aus den VkCodes ($wParam) das auszugebende Zeichen "(" ermitteln kann?

    Das war vor Monaten für mich die erfolgsversprechendste Herangehensweise, ... die ich leider nicht lösen konnte. :( Es wäre im Prinzip so einfach: Man prüft, ob die gedrückten Tasten im fremden Fenster (mit dessen KB-Layout) ein "(" schreiben. - Das Prinzip ist einfach, aber die Umsetzung ist mir nicht gelungen.


    Nach einigen fehlgeschlagenen Tests hatte ich die Erkennung der Tastatur-Codes per Windows-Message-Hook erstellt, um die Tasten zu erkennen, die im fremden Fenster gedrückt wurden. Damit könnte man in einem eigenen Fenster das KB-Layout des fremden Fensters einstellen und prüfen, wann ein "(" ankommt. Leider weiß ich bis heute nicht, wie man diese Messages "verdoppeln" kann, sodass man sie zum einen an das fremde Fenster durchreichen und zum anderen in einem eigenen Fenster auswerten kann. UND es darf natürlich nicht zu einer Endlosschleife im Msg-Hook führen! :D


    Wie könnte man das bewerkstelligen?

  • Das Problem liegt im vkCode. Dieser enthält nunmal keine layoutspezifischen Werte, sondern ist für jedes Layout identisch.


    Man muss sich den "Werdegang" eines Zeichens vom Drücken der Taste bis zum Erscheinen im Edit etc. mal genauer ansehen:

    - Tastendruck - Auslösen eines Hardware-Interupts, Scancode (Tastenposition) wird gelesen. Der Scancode gibt nur die Position der gedrückten Taste für eine Standardtastatur an, unabhängig von der Anordnung, der Sprache des BS oder der Tastenbeschriftung.

    - Der Scancode wird vom System in den vkCode übersetzt, dem die ASCII-Zeichen zugrunde liegen

    Bis hier ist das LowLevel - was wir z.B. mit _IsPressed() abfragen. -- Also sind wir da eigentlich falsch, denn das sagt uns noch nichts über das tatsächlich im Editor ankommende Zeichen, nur über die gedrückte Taste.

    - Weiter gehts mit der System-Message WM_CHAR. Hier wird aus vkCode und Layout das letztlich gewünschte Zeichen an die Anwendung gesendet. Somit sollte der Ansatz sein, diese Message auszuwerten.


    Da wir dann aber nicht mehr LowLevel sind, hat das auch ein paar zu beachtende Punkte:

    Wir können nicht einfach auf WM_CHAR warten, sondern müssen mit GetMessage() vom Message-Queue abfragen.

    Die Nachricht ist an ein Fenster gebunden, dessen Handle ich angeben muss. Alle evtl. zu berücksichtigenden Fenster müssen zu einem Prozess gehören.

    Somit ist eine systemweite Abfrage eines Zeichens wohl nicht realisierbar.


    P.S.

    Ist mir gerade noch eingefallen: Bei der Abfrage eines mit Shift generierten Zeichens muss der Vollständigkeit halber immer auch Capslock abgefragt werden.

  • Dein Ansatz hört sich gut an.

    Somit ist eine systemweite Abfrage eines Zeichens wohl nicht realisierbar.

    Das ist bei deinem Ansatz vielleicht gar nicht nötig, denn es geht ja um ein bestimmtes Fenster (das aktive PSPad-Fenster).


    Die Nachricht ist an ein Fenster gebunden, dessen Handle ich angeben muss.

    Wie gesagt, aktives PSPad-Fenster. Wenn man einen Hook auf dieses Fenster setzen könnte und dort den VkCode und den ShiftState (die Flags?) erhalten könnte, bräuchte man sie "nur noch" an eine eigenes Edit weiterzuleiten. Das eigene Edit bekommt das gleiche KB-Layout verpasst wie das PSPad-Fenster. Wenn das klappen würde, bräuchte man hier nur noch zu prüfen, ob ein "(" im eigenen Edit erscheint.

  • Ich habe mal versucht, die Nachricht abzugreifen, bevor die Msg-Queue Verarbeitung beginnt.

    M. M. n. ist das vom Code her korrekt, jedoch erhalte ich ausschliesslich die Messages: WM_TIMER und WM_HOTKEY (beim Beenden) . Das lässt mich etwas ratlos zurück.

    Aber vielleicht hilft es dir als Ansatz.


  • Mir gehts ähnlich! :D Probiere seit Stunden - habe das hier gefunden. Das ist das Einzigste, das überhaupt was erreicht: 1 Zeichen im eigenen Edit, aber kein Shift, ... Caps Lock, ... nichts. Ich habs mal quick & dirty in einen Code von dir reingehauen. Meine vergeblichen Versuche habe ich als Kommentare drin gelassen.


    Es muss doch irgendwie möglich sein, die (Keyboard-)Messages von PSPad 1:1 in ein AutoIt-Edit zu kopieren. :/ Dann bräuchte man sich nicht um ShiftStates, Caps Lock oder zusätzlich gedrückten Tasten zu kümmern.


    Übrigens: Bei der Gelegenheit sei erwähnt, es wäre eine (einfachere?) Möglichkeit, den Text am Caret in PSPad auszulesen und auf "(" zu prüfen. ABER: Das Textauslesen ist mir bisher noch nicht gelungen. Das SynEdit (= Editor-Control von PSPad) erlaubt manchmal das Auslesen von einigen Zeilen Text, aber dann ohne ersichtlichen Grund kommen nur noch chinesische Zeichen. =O Falls jemand weiß, wie man den Text am Caret in PSPad zuverlässig auslesen kann, wäre das auch eine Lösung! :thumbup:

  • M. M. n. ist das vom Code her korrekt, jedoch erhalte ich ausschliesslich die Messages: WM_TIMER und WM_HOTKEY (beim Beenden) .

    Vermute mal, es liegt daran...


    $pProc Pointer to the hook procedure. If the $iThreadId parameter is zero or specifies the identifier of a thread created by a different process, the $pProc parameter must point to a hook procedure in a DLL. Otherwise, $pProc can point to a hook procedure in the code associated with the current process


    Evtl. lässt sich das Problem hiermit lösen: https://www.autoitscript.com/f…-global-setwindowshookex/

  • Mit selbst erstellten Fenstern geht es... WM_TIMER & WM_HOTKEY & WM_KEYDOWN & WM_KEYUP & WM_CHAR

  • Leute, ich werd' noch wahnsinnig! Ich krieg es einfach nicht hin, in der Erkennung den "Caps Lock" und "drücken zusätzlicher Zeichen" (z.B. Shift+8+9) zu verarbeiten.


    Ich denke, der Ansatz ist einfach ungeeignet, die Tastatur-Eingabe zu prüfen um zu erkennen, ob der User in PSPad ein "(" schreibt. Der bessere Ansatz wäre wahrscheinlich, das was in PSPad geschrieben wird in ein eigenes (Edit-)Control zu verdoppeln/kopieren und dort zu prüfen, ob das letzte Zeichen ein "(" ist.


    - Vielleicht werde ich einen anderen Thread aufmachen. :S

  • Hier ist mein überarbeiteter Workaround (basiert auf dem Code in Posting #1), der lediglich einige Fälle prüft, aber bei weitem NICHT alles abfängt. So wird z.B. NICHT geprüft, ob eine weitere Zeichentaste gedrückt wird, z.B. Shift+7+8. Könnt ihr bitte mal reingucken, ob meine Logik stimmt, ob man das eleganter lösen kann und ob Bugs drin sind?



    Edit: Den ersten Bug selbst gefunden und (hoffentlich) behoben. :saint:

  • Das SynEdit (= Editor-Control von PSPad) erlaubt manchmal das Auslesen von einigen Zeilen Text, aber dann ohne ersichtlichen Grund kommen nur noch chinesische Zeichen.

    So geht es...

  • Das Auslesen scheint schonmal besser zu funktionieren! :) (Manchmal zeigt das SynEdit erst nach einiger Zeit (oder nach einigen Zugriffen?) die chinesischen Zeichen.) Wie ich vom Gesamt-Text zum Text am Caret kommen kann, weiß ich noch nicht. Mal sehen, was ich draus machen kann. Auf jeden Fall ein interessanter Ansatz! :thumbup:

  • Wie ich vom Gesamt-Text zum Text am Caret kommen kann, weiß ich noch nicht. Mal sehen, was ich draus machen kann. Auf jeden Fall ein interessanter Ansatz! :thumbup:

    Das korrekte Auslesen des Textes war doch doch schon seit langer Zeit erledigt (oder interpretiere ich das falsch: RE: Selektierten Text in SynEdit finden?

    Zeichen am Caret (also davor) ist doch Erweiterung der Auswahl auf (pos-1, pos). Vielleicht ist das mit der Objektlösung realisierbar.

  • Vielleicht ist das mit der Objektlösung realisierbar.

    Ja, ist es... hier die korrigierte und erweiterte Version von PsPadExt.au3

    f:\AutoIt\AutoIt3_MyInclude\AutoItObject.au3

    f:\Progdir\PSPad4AutoIt3\Script\VBScript\AutoItScriptExtension.vbs

    f:\Progdir\PSPad4AutoIt3\PSPad4Au3\Au3 scripts\PsPadExt.au3

  • Cool vielen Dank euch beiden! Damals hatte ich an diesem Code weitergearbeitet und mangels COM-Fähigkeiten die Arbeiten irgendwann eingestellt. Mein Code war etwa auf deinem Stand Bitnugger, aber bei weitem nicht so sauber (weil ich eigentlich nur probiert und rumgestochert habe). :D


    Ich habe dein Script "PsPadExt.au3" umbenannt in "AutoItObject_PSPadExtBridge.au3" und in das alte Test-Demo gepackt, wo es automatisch mit PSPad gestartet wird. (Übrigens für die Namensfetichisten: Eigenname ist "PSPad", nicht "PsPad".) 8o Die Buttons sind auskommentiert und die Erfassung des Zeichens links vom Caret in PSPad wird in der Hauptschleife der eigenen GUI automatisch in das eigene Edit geschrieben. Es gibt jedoch auch mit deiner Version die gleichen Probleme wie damals:

    • Die Erkennung funktioniert manchmal nicht, bzw. erst, wenn man PSPad und die GUI mit dem Edit 2 oder 3x neustartet.
    • Die Erkennung erfolgt in der Hauptschleife des Scripts. Dort habe ich ein Sleep(100) fürs Demo benutzt, weil sonst nichts ankam. Aber eigentlich dürfte die Schleife nur für 10 ms schlafen, weil sonst die Erkennung der Zeichen nicht korrekt erfasst, wenn jemand ein schneller Tipper ist. ;) Außerdem muss anderer Code ausgeführt werden können und dafür darf nur Sleep(10) benutzt werden.
    • Wenn eine weitere PSPad Instanz gestartet wird, erfolgt die Erkennung in der zweiten Instanz, aber NICHT mehr in der ersten Instanz.


    Wichtig: Dieses Demo ist "uralt", etwa von Juni 2020. Aber für diese Test-Zwecke sollte das gehn. :)


    Edit: 2021-03-12, Tests beendet, Demo entfernt von Professor Bernd. Sollte jemand an weiteren Tests interessiert sein, bitte melden, dann lade ich es wieder hoch. :rock: