Mein INPUT RECORD struct stimmt unter 64bit wohl nicht - bitte mal drüberschauen

  • Hallo!

    Ich habe mir für ReadConsoleInput folgende Konstruktion gebastelt (ich führe nur die entscheidenden Zeilen auf und lassen den ganzen Funktions-Schleifen-zipzap weg):

    [autoit]


    Global Const $tagKEYEVENTREC = "bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; wchar UnicodeChar; dword dwKontrolKeyState"
    Global Const $tagINPUT_RECORD = "long evtyp;" & $tagKEYEVENTREC

    [/autoit][autoit][/autoit][autoit]

    Local $tInp = DllStructCreate($tagINPUT_RECORD)
    Local $result = DllCall("kernel32.dll", "bool", "ReadConsoleInput", "handle", $GLOBAL_hConsoleIn, "ptr", DllStructGetPtr($tInp), "dword", 1, "dword*", 0)
    $Key_Event = DllStructGetData($tInp, 'evtyp')
    If $Key_Event = 1 then $key = DllStructGetData($tInp, 'UnicodeChar')

    [/autoit]

    Zugehörige Doku im MSDN:
    ReadConsoleInput
    INPUT_RECORD
    KEY_EVENT_RECORD

    Das funktioniert wunderbar (da ich auch nur KEY_EVENT_RECORDs auswerten muss) unter allem, was ich bis jetzt an 32 bit OS unter den Fingern hatte. Bei Win 8 64 bit allerdings passiert nur Müll. Ich kenne mich leider mit den Struct-Datentypen nicht so gut aus, um wirklich beurteilen zu können, welcher Typ im Struct möglicherweise Probleme macht.

    Hier gibt's aber soviele wirklich super fitte Leute und ich hoffe, dass jemand kann mir da einen Tipp geben.

    Schonmal danke im Vorhinein fürs Ansehen...

    Lieber Gruß
    Holger

    Einmal editiert, zuletzt von pandel (28. April 2013 um 14:29)

  • Hey BugFix , danke für den Hinweis! Muss aber, weil ich das noch nie verwendet habe, der Sicherheit halber nochmal nachfragen, ob ich's richtig verstanden habe. Es sind ja eigentlich zwei structs ineinander:

    So?

    [autoit]

    Global Const $tagKEYEVENTREC = "struct;bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; wchar UnicodeChar; dword dwKontrolKeyState;endstruct;"
    Global Const $tagINPUT_RECORD = "struct;long evtyp;" & $tagKEYEVENTREC & "endstruct;"

    [/autoit]

    EDIT: ich glaub, eher so, oder?


    [autoit]

    Global Const $tagKEYEVENTREC = "struct;bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; wchar UnicodeChar; dword dwKontrolKeyState;endstruct;"
    Global Const $tagINPUT_RECORD = "long evtyp;" & $tagKEYEVENTREC

    [/autoit]

    Außerdem hab ich gerade progandy's "Nested DLLStructs" gefunden ;) Ich denke, das kann ich hier verwenden.

    Einmal editiert, zuletzt von pandel (24. April 2013 um 17:57)

  • [autoit]

    Global Const $tagKEYEVENTREC = "bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; wchar UnicodeChar; dword dwKontrolKeyState"
    Global Const $tagINPUT_RECORD = "WORD evtyp;" & $tagKEYEVENTREC

    [/autoit]


  • Hallo zusammen!

    Ich hab jetzt sämtliche Vorschläge und Ideen durch und bin mit meinem Latein am Ende. Mein Problem ist, daß unter Win8 64bit beim Call auf ReadConsoleInput diese Funktion einfach blockt, was sie baer nicht tun dürfte. Sie müßte sofort mit einem Signal zurück kommen, tut sie aber nicht.

    Jetzt weiß ich auch nicht mehr weiter...

    Lieber Gruß
    Holger

  • Nur so, falls noch wer mitliest:

    ich glaube mittlerweile, daß es am byte alignment liegt, leider komm ich grad nicht an meine Win8 Kiste... das ist eigentlich ein packed struct. Ich wühl mich mal durchs Windows SDK, um herauszufinden, was da wie gepacked ist... übrigens dazu ein super Artikel im QB64 Forum, witzigerweise genau mit der ReadConsoleInput Funktion beschrieben ;-):

    http://www.qb64.net/forum/index.ph…g47094#msg47094

    Nur die Datentypen in QB64 sind da etwas unterscheidlich lang, aber zum Verständnis macht das ja nix aus!

    Lieber Gruß
    Holger

  • Das mit dem Handle ist nicht so das Problem:

    [autoit]

    $hConsoleIn = _WinAPI_CreateFile("CONIN$", 3, 6, 7)

    [/autoit]


    Wichtig ist wirklich 3,6,7!!!! Das geht unter x86 und x64.

    Problem ist, daß ReadConsoleInput IMMER blockt! Ich machs mal ganz einfach (ist ne Routine von prog@andy, aber ordentlich umgebaut):

    [autoit]

    Global Const $tagKEYEVENTREC = "bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; char UnicodeChar; char pad; dword dwKontrolKeyState;"
    Global Const $tagINPUT_RECORD = "align 4;word evtyp;struct;" & $tagKEYEVENTREC & "endstruct;"

    [/autoit][autoit][/autoit][autoit]

    Func _ConsoleGetChar()
    Local $ret, $Key_Event = False, $HWND
    Local $keyev[6] ; key event array
    Local $tInp = DllStructCreate($tagINPUT_RECORD)

    [/autoit][autoit][/autoit][autoit]

    ; set console as active window
    $HWND = DllCall("kernel32.dll", "hwnd", "GetConsoleWindow")
    WinActivate($HWND[0])

    [/autoit][autoit][/autoit][autoit]

    DllCall("Kernel32.dll", "bool", "FlushConsoleInputBuffer", "handle", $GLOBAL_hConsoleIn)
    Do
    $ret = DllCall("Kernel32.dll", "dword", "WaitForSingleObject", "handle", $hConsoleIn, "dword", -1)
    If @error Then Return SetError(1, 0, 0)
    If $ret[0] = -1 Then Return SetError(2, 0, 0)
    Local $rce = DllCall("kernel32.dll", "bool", "ReadConsoleInput", "handle", $hConsoleIn, "ptr", DllStructGetPtr($tInp), "dword", 1, "dword*", 0)
    $Key_Event = DllStructGetData($tInp, 'evtyp') = 1 ; keyboard event
    ;$keyev[0] = DllStructGetData($tInp, 'bkeyDown')
    ;$keyev[1] = DllStructGetData($tInp, 'wRepeatCount')
    ;$keyev[2] = '0x' & Hex(DllStructGetData($tInp, 'wVirtualKeyCode'),4)
    ;$keyev[3] = '0x' & Hex(DllStructGetData($tInp, 'wVirtualScanCode'),4)
    $keyev[4] = DllStructGetData($tInp, 'UnicodeChar')
    ;$keyev[5] = '0x' & Hex(DllStructGetData($tInp, 'dwKontrolKeyState'),4)
    Until $Key_Event And StringIsAlpha($keyev[4])
    DllCall("Kernel32.dll", "bool", "FlushConsoleInputBuffer", "handle", $hConsoleIn)
    Return $keyev[4]
    EndFunc ;==>_Console_Pause

    [/autoit]


    Das geht hervorragend unter XP und sogar Win7 x86, eben nur nicht unter Win8 x64...

    Greenhorn
    Nein, tut es leider nicht. Ich hab schon die dollsten alignment Varianten durch. In der wincon.h, wo der Tag definiert ist, wird KEY_EVENT_RECORD einfach nur als PACKED definiert, INPUT_RECORD selber dagegen ist nicht packed! Ich krieg nicht raus, was genau, also ob align 2 oder 4 oder was auch immer. In den Microsoft Unterlagen steht, daß Standardalignment 8 ist. Daher hab ich gedacht, ich probier auch mal folgendes:

    [autoit]


    Global Const $tagKEYEVENTREC = "align 4; bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; char UnicodeChar; char pad; dword dwKontrolKeyState;"
    Global Const $tagINPUT_RECORD = "align 8;word evtyp;struct;" & $tagKEYEVENTREC & "endstruct;"

    [/autoit]


    Aber das tut's auch nicht.
    Dieses schei*** ReadConsoleInput braucht immer genau 1x ENTER, erst dann nimmt es den Buchstaben an und kehrt auch brav sofort zurück. Als müßte ich mit dem ENTER erst die Consoleneingabe aktivieren, oder so'n Quatsch!

    DLL's unter C oder ner anderen Sprache hab ich noch nicht gebaut, sonst hätte ich das schon längst über ne eigene DLL geregelt X(

    Einmal editiert, zuletzt von pandel (27. April 2013 um 15:27)

  • In der WinCon.h aus dem SDK 7.1 ist nichts von beidem PACKED ...
    In meiner WinCon.inc ist die Struktur INPUT_RECORD aber DWORD "aligned" ...

    Spoiler anzeigen


    In x86 als auch in x64 belegt die Struktur 20 Byte.

    Wäre schön, wenn Du ein Beispielskript zur Verfügung stellen könntest.

    Grüße
    Greenhorn


  • Aha, und in meiner wincon.h aus dem Win8 SDK (gestern runtergeladen) sieht es so aus, da is nix mit gesondertem alignment. Merkwürdigerweise finde ich in meiner wincon.h die Definition von ReadConsoleInput(A/W) überhaupt nicht???
    (EDIT: Ok, gefunden, ist in consoleapi.h)

    Und ich finde im Windows SDK keine wincon.inc... habe aber auch nicht Visual Studio, sondern nur das SDK installiert.

    Spoiler anzeigen


    Aber Moment, da fällt mir was auf. Inder Windows MSDN Doku dazu steht:


    Sollte ich also vielleicht ein Array mit einem oder 2 Elementen definieren? Ehrlich gesagt weiß ich dann aber nicht mehr, wie man das schreiben würde...

    Wäre schön, wenn Du ein Beispielskript zur Verfügung stellen könntest.


    Die betroffene Routine ist in dem Post über deinem ;-))

    2 Mal editiert, zuletzt von pandel (27. April 2013 um 21:34)

  • Die WinCon.inc ist nicht im Ms SDK enthalten. Diese Datei ist für Assembler gedacht und gemacht.

    Ein Array aus Strukturen machst Du ganz einfach so:

    [autoit]


    Global Const $tagKEYEVENTREC = "bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; wchar UnicodeChar; dword dwKontrolKeyState;"
    Global Const $tagINPUT_RECORD = "align 4;WORD evtyp;" & $tagKEYEVENTREC

    [/autoit][autoit][/autoit][autoit]

    Global $tInp = DllStructCreate($tagINPUT_RECORD&$tagINPUT_RECORD) ; Array mit zwei Elementen

    [/autoit]


    Aber eigentlich sollte auch eine einfache Struktur ausreichen.

    Zitat

    Die betroffene Routine ist in dem Post über deinem ;-))


    Ich dachte da eher an ein ausführbares Beispiel ;)

    Grüße
    Greenhorn


  • ICH HABS!! JUHUUUU! Eine 2wöchige Odyssee hat ein Ende. Es lag nicht an ReadConsoleInput. Aber hier erstmal das Beispiel:

    Spoiler anzeigen
    [autoit]


    #Region ;**** Directives created by AutoIt3Wrapper_GUI ****
    #AutoIt3Wrapper_UseX64=y
    #AutoIt3Wrapper_Change2CUI=y
    #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
    ; die Consoleroutinen sind ursprünglich von prog@andy,
    ; ich habe sie nur zum Teil an meine Bedürfnisse angepasst

    [/autoit] [autoit][/autoit] [autoit]

    #include <WinAPI.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $hConsole, $hConsoleIn

    [/autoit] [autoit][/autoit] [autoit]

    ;always open new console window (false) or use the existing cmd prompt (true)
    Global Const $_Console_USEWINDOW = true

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $STD_INPUT_HANDLE = -10
    Global Const $STD_OUTPUT_HANDLE = -11

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $tagKEYEVENTREC = "bool bKeyDown;word wRepeatCount; word wVirtualKeyCode; word wVirtualScanCode; char UnicodeChar; char pad; dword dwKontrolKeyState;"
    Global Const $tagINPUT_RECORD = "word evtyp;struct;" & $tagKEYEVENTREC & "endstruct;"

    [/autoit] [autoit][/autoit] [autoit]

    _ConsoleStartupAtDosPrompt()
    _ConsoleWrite("Testkonsole." & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    _ConsoleWrite("Bitte einen Buchstaben eingeben: ")
    local $result = _ConsoleGetChar()

    [/autoit] [autoit][/autoit] [autoit]

    _ConsoleWrite(@CRLF & "Buchstabe war: " & $result)

    [/autoit] [autoit][/autoit] [autoit]

    _ConsoleWrite(@CRLF & "Einen anderen Buchstaben für Weiter drücken...")
    local $result = _ConsoleGetChar()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _ConsoleStartupAtDOSPrompt($ExitOnFatal = 0)
    If Not @Compiled Then
    If Not IsDeclared("_Console_USEWINDOW") Then
    #Region --- CodeWizard generated code Start ---
    ;MsgBox features: Title=Yes, Text=Yes, Buttons=Yes and No, Default Button=Second, Icon=Critical
    If 6 = MsgBox(276, "No Console App specified!", "You hav to copy these lines to your main-Script, just before you call _Console_STARTUP:" & @CRLF & "; These two lines, if the CMD-Console should be used, if possible" & @CRLF & " #AutoIt3Wrapper_Change2CUI=y" & @CRLF & " Global $_Console_USEWINDOW = True" & @CRLF & "; This line, if always a new Console should be created:" & @CRLF & " Global $_Console_USEWINDOW = False" & @CRLF & @CRLF & " COPY TO CLIPBOARD?") Then
    ClipPut("; These two lines, if the CMD-Console should be used, if possible" & @CRLF & _
    "#AutoIt3Wrapper_Change2CUI=y" & @CRLF & _
    "Global Const $_Console_USEWINDOW = True" & @CRLF & _
    "; This line, if always a new Console should be created:" & @CRLF & _
    ";~ Global Const $_Console_USEWINDOW = False")
    EndIf
    EndIf
    MsgBox(16, 'Console UDF error', "Console does not work in uncompiled scripts.")
    Exit
    EndIf
    If Not IsDeclared("_Console_USEWINDOW") Then Local $_Console_USEWINDOW = True
    If Not $_Console_USEWINDOW Then
    $ret = DllCall("Kernel32.dll", "long", "FreeConsole")
    $ret = DllCall("Kernel32.dll", "long", "AllocConsole")
    If $ret = 0 Then
    If $ExitOnFatal Then _WinAPI_FatalAppExit("Could not allocate Console")
    Return SetError(1, 0, 0)
    EndIf
    $HWND = DllCall("kernel32.dll", "hwnd", "GetConsoleWindow")
    WinSetState($HWND[0], "", @SW_SHOW)
    Else
    $ret = DllCall("Kernel32.dll", "long", "FreeConsole")
    $ret = DllCall("Kernel32.dll", "long", "AttachConsole", 'dword', -1)
    If $ret = 0 Then
    If $ExitOnFatal Then _WinAPI_FatalAppExit("Could not allocate Console")
    Return SetError(1, 0, 0)
    EndIf
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Local $aResult = DllCall("kernel32.dll", "handle", "GetStdHandle", "dword", $STD_OUTPUT_HANDLE)
    If @error Then Return SetError(1, 0, 0)
    $hConsole = $aResult[0]
    If $hConsole = -1 Then
    If $ExitOnFatal Then _WinAPI_FatalAppExit("GetStdHandle for Output failed")
    Return SetError(2, 0, 0)
    EndIf
    DllCall("Kernel32.dll", "long", "SetConsoleActiveScreenBuffer", "handle", $hConsole)

    [/autoit] [autoit][/autoit] [autoit]

    $hConsoleIn = _WinAPI_CreateFile("CONIN$", 3, 6, 7)
    If $hConsoleIn = -1 Then
    If $ExitOnFatal Then _WinAPI_FatalAppExit("GetStdHandle through CONIN$ for Input failed")
    Return SetError(1, 0, 0)
    EndIf
    Return 1
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ConsoleGetChar()
    Local $ret, $Key_Event = False
    Local $keyev[6] ; key event array
    Local $tInp = DllStructCreate($tagINPUT_RECORD)
    ;local $tInp = DllStructCreate($tagINPUT_RECORD&$tagINPUT_RECORD)
    ; set console as active window
    $HWND = DllCall("kernel32.dll", "hwnd", "GetConsoleWindow")
    WinActivate($HWND[0])

    [/autoit] [autoit][/autoit] [autoit]

    DllCall("Kernel32.dll", "bool", "FlushConsoleInputBuffer", "handle", $hConsoleIn)
    Do
    $ret = DllCall("Kernel32.dll", "dword", "WaitForSingleObject", "handle", $hConsoleIn, "dword", -1)
    If @error Then Return SetError(1, 0, 0)
    If $ret[0] = -1 Then Return SetError(2, 0, 0)
    Local $rce = DllCall("kernel32.dll", "bool", "ReadConsoleInput", "handle", $hConsoleIn, "ptr", DllStructGetPtr($tInp), "dword", 1, "dword*", 0)
    $Key_Event = DllStructGetData($tInp, 'evtyp') = 1 ; keyboard event
    ;$keyev[0] = DllStructGetData($tInp, 'bkeyDown')
    ;$keyev[1] = DllStructGetData($tInp, 'wRepeatCount')
    ;$keyev[2] = '0x' & Hex(DllStructGetData($tInp, 'wVirtualKeyCode'),4)
    ;$keyev[3] = '0x' & Hex(DllStructGetData($tInp, 'wVirtualScanCode'),4)
    $keyev[4] = DllStructGetData($tInp, 'UnicodeChar')
    ;$keyev[5] = '0x' & Hex(DllStructGetData($tInp, 'dwKontrolKeyState'),4)
    Until $Key_Event And StringIsAlpha($keyev[4])
    DllCall("Kernel32.dll", "bool", "FlushConsoleInputBuffer", "handle", $hConsoleIn)
    Return $keyev[4]
    EndFunc ;==>_Console_Pause

    [/autoit] [autoit][/autoit] [autoit]

    ; Author: ProgAndy
    Func _ConsoleClose()
    Local $ret = DllCall("Kernel32.dll", "int", "FreeConsole")
    If @error Then Return SetError(1, 0, 0)
    Return SetError($ret[0] <> 0, 0, $ret[0])
    EndFunc ;==>_Console_Close

    [/autoit] [autoit][/autoit] [autoit]

    ;Writes Text to CMD
    ; Author: ProgAndy
    Func _ConsoleWrite($text)
    Local $temp = _WinAPI_WriteConsole($hConsole, $text)
    Return SetError(@error, @extended, $temp)
    EndFunc ;==>_Console_Write

    [/autoit]


    Aber jetzt kommts: der Witz der Konstante $_Console_USEWINDOW ist es, daß das Programm entweder immer eine EIGENE Konsole (false) öffnet, oder ob es, wenn am DOS Prompt gestartet, diesen direkt als Console nutzt (true). Ich hab natürlich immer auf TRUE stehen gehabt, da ich ein separates Konsolenfenster nicht gebrauchen kann in meinem Fall. Jetzt hat sich gerade beim Testen herausgestellt, daß es im separaten Konsolenfenster wunderbar funktioniert, nur nicht, wenn ich auf Weiternutzung des bereits bestehenden Konsolenfensters bestehe.

    Die Routine _ConsoleStartupAtDosPrompt machte ursprünglich ein FreeConsole+AllocConsole für eine NEUE Konsole, aber nix besonderes für die alte, sondern holt sich nur STDIN und STDOUT. Lt. Microsoft sollte der STDIN per CreateFile auf CONIN$ verfügbar sein. Damit war für mich ja alles gut. Irgendwie hab ich gerade gedacht, vielleicht wäre ein AttachConsole noch ratsam, ABER ich habe gerade herausgefunden, daß ein einfaches AttachConsole bei dem bestehenden DOS Fenster eben NICHT reicht. Erste FreeConsole+AttachConsole resetten wohl STDIN und STDOUT so sauber, daß es damit funktioniert.

    Wie geil! Jetzt muss die Routine nur noch den XP 32bit Test überstehen, dann ist alles im grünen Bereich.

    Greenhorn, vielen Dank für deine Diskussionsbereitschaft zum Thema. Ich hätte sonst einige Sachen einfach nicht durchprobiert ;-))

    BTW: Über die hohen Mauern der Assembler Programmierung bin ich nie gekommen und habe großen Respekt vor jedem, der damit was vernünftiges Zustande bekommt ;) Spannend fand ich das aber immer...

    Lieber Gruß
    Holger