isPressed("0D") geht nicht (ENTER)

  • #include <Misc.au3>

    $dll = DllOpen("user32.dll")

    While 1
    Sleep ( 250 )
    If _IsPressed("0D", $dll) Then
    MsgBox(0,"_IsPressed", "Enter-taste wurde gedrückt.")
    ExitLoop
    EndIf
    WEnd
    DllClose($dll)

  • @sedas19,
    man braucht die dll nicht explizit zu öffnen. Ein Blick in die Funktion hätte gereicht...

    [autoit]

    Func _IsPressed($sHexKey, $vDLL = 'user32.dll')
    ; $hexKey must be the value of one of the keys.
    ; _Is_Key_Pressed will return 0 if the key is not pressed, 1 if it is.
    Local $a_R = DllCall($vDLL, "short", "GetAsyncKeyState", "int", '0x' & $sHexKey)
    If @error Then Return SetError(@error, @extended, False)
    Return BitAND($a_R[0], 0x8000) <> 0
    EndFunc ;==>_IsPressed

    [/autoit]

    @MaxChri
    dein Script funktioniert bei mir Win7-64 und 3.3.10.2 einwandfrei. AutoIt ist installiert als 32-Bit-Version.

  • @Andy
    Man korrigiere mich, wenn ich falsch liege, aber es macht doch durchaus Sinn, die DLL explizit zu öffnen.
    Wie am von dir angeführten Funktionskopf ersichtlich, wird ansonsten implizit der String "user32.dll" anstatt eines Handles für den DllCall verwendet.
    Ohne die Nutzung eines Handles verhalten sich DllCall nach meinem Wissen ähnlich wie z.B. FileRead: Bei jedem Aufruf wird die Datei neu geöffnet, dann wird die Operation ausgeführt, dann wird die Datei wieder geschlossen. Ob es bei den DLLs ein Windows-Internes Caching gibt, weiß ich nicht.
    All das sorgt dafür, dass ich mich zu der Vermutung hinreißen lasse: Bei einem iterativen Aufruf einer Dll-Funktion, egal ob gewrappert oder nicht, empfiehlt es sich, die betroffene Dll-Datei explizit zu öffnen und zu schließen.
    Auch die Hilfe zu _IsPressed deutet soetwas an:

    Zitat

    If calling this function repeatedly, you should open 'user32.dll' and pass the handle.

    Man könnte sicherlich auch einfach in der _IsPressed-Funktion eine statische Variable nutzen... Aber naja. ^^

    Gruß

  • @chesstiger,
    "im Prinzip" gebe ich dir Recht! :thumbup:
    Wenn ich dich jetzt aber mal beauftrage, mir ein gestartetes Windows BS zu zeigen, in dem genau diese Dll gerade NICHT geöffnet ist, wirst du merken, wieso ich "im Prinzip" meinte.
    Schau dir einfach mal an, welche Funktionen sich in dieser Dll befinden.

    @Kanhasius,
    der Sinn und Zweck einer Dll ist, dass sie nur EINMAL gleichzeitig geöffnet wird, unerheblich, wie viele Programme die Funktionen nutzen wollen. Das geschieht innerhalb des BS und ist imho auch nicht beeinflussbar. Ansonsten wäre das gesamte Dll/Bibliotheken-Prinzip völlig Schwachsinnig!
    Daher kann ich mir viel vorstellen, aber definitiv NICHT, dass hunderte Kopien der user32.dll GLEICHZEITIG im Speicher stehen...

  • @Andy
    Was du sagst, leuchtet "im Prinzip" ein. :P
    Natürlich ist auf jedem Windows-System die User32.dll geladen; sie ist ja genau so wichtig die die Kernel32.dll.
    Trotzdem wird bei jedem "Öffnen" scheinbar ein neues Handle für den jeweiligen Prozess generiert. Das ist allerdings nur eine Vermutung ohne jegliche empirische Grundlage. Es muss irgendwas beim impliziten Öffnen anders ablaufen, vielleicht liegt es auch an der AutoIt-Internen Umsetzung von DllCall... Jedenfalls ist bei einer Testreihe der beiden Methoden implizit und explizit mit den Wiederholungszahlen 1 bis 1.000.000 (immer den Exponenten von 10^i erhöht) klar ersichtlich, dass das explizite Öffnen der DLL in jedem Fall schneller ist.

    IsPressed Test.au3
    [autoit]


    #include <Misc.au3>

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

    $iLoops = 100

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

    ;Implizite Öffnung durch DllCall
    $hTimer1 = TimerInit()
    For $i = 1 To $iLoops
    _IsPressed("0D")
    Next
    $nTimer1 = TimerDiff($hTimer1)

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

    ;Explizite Öffnung durch DllOpen
    $hTimer2 = TimerInit()
    $hDll = DllOpen("user32.dll")
    For $i = 1 To $iLoops
    _IsPressed("0D", $hDll)
    Next
    DllClose($hDll)
    $nTimer2 = TimerDiff($hTimer2)

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

    ConsoleWrite(StringFormat("### Messergebnisse bei %i Durchgängen ###\n", $iLoops))
    ConsoleWrite(StringFormat("Implizit: %.5f ms (%.5f ms pro Durchgang)\n", $nTimer1, $nTimer1 / $iLoops))
    ConsoleWrite(StringFormat("Explizit: %.5f ms (%.5f ms pro Durchgang)\n", $nTimer2, $nTimer2 / $iLoops))
    ConsoleWrite("-------------------------------------------------" & @CRLF)

    [/autoit]
    Messergebnisse

    Mysteriös. :D

  • DAS ist wesentlich interessanter^^

    [autoit]

    ### Messergebnisse bei 100000 Durchgängen ###
    Implizit: 4611.10374 ms (0.04611 ms pro Durchgang)
    Explizit: 30235.99960 ms (0.30236 ms pro Durchgang)

    [/autoit][autoit]

    ;Explizite Öffnung durch DllOpen
    $hTimer2 = TimerInit()
    For $i = 1 To $iLoops
    $hDll = DllOpen("user32.dll")
    _IsPressed("0D", $hDll)
    Next
    ;DllClose($hDll)
    $nTimer2 = TimerDiff($hTimer2)

    [/autoit]

    Mal angenommen, DU wärest der Vorgesetzte des Programmierers der Routine die dafür sorgt, eine bereits im Speicher befindliche Dll NICHT zu laden. Und würdest den Typen jetzt fragen, wie man ca. 700000 Prozessortakte für diese Abfrage "verballern" kann! Das sind auf heutigen Maschinen 700000 Instruktionen....

  • @Andy
    Schmeiß mal die DllClose() noch mit in die Schleife.
    Wenn man dann noch, anstatt eine Durchschnittsdauer zu ermitteln, die Einzelwerte nimmt und in ein Diagramm abträgt, sieht man, dass der Zeitbedarf linear mit der Anzahl der offenen Handles ansteigt.
    Der benötigte Speicher des Skriptes steigt bei 100000 Durchläufen um gerade einmal 2,3mb an, wovon schonmal 0,76mb die Handles ausmachen sollten.
    Tja was ein vergessenes DllClose() nicht alles ausmacht...

    Der Argumentation, dass man durch Öffnen per DllCall die bereits geöffnete Dll umgeht kann ich daher nicht nachvollziehen.
    Wer sagt denn dass DllOpen die Dll komplett neu einliest anstatt die bereits im Speicher vorhandene zu nutzen?
    Und andersrum: Wer sagt, dass die Nutzung per String dies genau nicht tut?

    Alles deutet für mich darauf hin, dass beide im Falle einer bereits im Speicher befindlichen dll am Ende diese auch nutzen.
    Den Unterschied macht der Overhead. Wenn man das Handle einmalig per DllOpen bereits ermittelt hat, sind die Funktionszeiger schnell gesetzt.
    Nimmt man hingegen den Dateinamen, muss dieser jedesmal erst geparst werden, die Datei im Dateisystem ermittelt werden, dann muss gecheckt werden ob die Datei schon geöffnet ist ... etc. etc.
    Bei einem einmaligen DllOpen sind diese Schritte schon einmalig abgearbeitet weswegen die nachfolgenden Aufrufe fixer als die Dateinamensversion sind.
    Und dies spiegelt sich auch in den Ergebnissen wieder.

    2 Mal editiert, zuletzt von AspirinJunkie (21. November 2014 um 08:35)

  • Man merkt schon das es grade leicht vom Thema abkommt. Der MaxChri hat nur gefragt warum seine Funktion nicht funktioniert, Lösungen wurden in den 3 und 4 post gepostet und ab da an geht es nur noch um dllcall oder String. Es ist verständlich das auch Millisekunden auf Dauer was ausmachen aber ich bezweifle mal ganz stark das es bei sein Script so wichtig ist das er jede Millisekunde braucht.

  • Der benötigte Speicher des Skriptes steigt bei 100000 Durchläufen um gerade einmal 2,3mb an, wovon schonmal 0,76mb die Handles ausmachen sollten.

    Ich habe per ProcessExplorer und auch per Taskmanager nicht ein einziges Handle mehr angezeigt bekommen! Also während meine Schleife lief.
    Das bestätigt nur was ich bereits sagte, dass die Dll nur einmal geladen wird. Die gesammelten "Handles" werden also nur auf ein einziges (das der geöffneten Dll) gemapped. Macht auch Sinn!

    Tja was ein vergessenes DllClose() nicht alles ausmacht...

    Made my day :thumbup:
    Erzähl das den Leuten die ihren kompletten Speicher mit Handles füllen...

    wobei in einer schleife 2x ispressed aufgerufen wird) auf über 2GB Ram hochgeschaufelt

    Nach deiner Feststellung mit der linear ansteigenden Zeit würde mich jetzt echt mal interessieren, wie lange bei 2Gb "Handles" der DllOpen() braucht und wie lange bis dahin die gesamte Scriptlaufzeit ist.
    Habe das lineare Ansteigen mal ausprobiert und über den dicken Daumen interpoliert:
    100000 Durchläufe , der letzte DllOpen() dauert bei mir 0,8ms, sagen wir mal eine, dann lässt es sich schöner rechnen :D
    100000 Durchläufe entsprechen ca. 1mb Handles (lässt sich auch schöner rechnen)
    Um 2 GB mit "Handles" zu füllen bräuchte man 2000mb, macht 2000*100000 = 200E6 Durchläufe, die "letzten" Schleifendurchläufe würden also nur für das DllOpen zwei Sekunden benötigen! Die Summe Zeit bis dahin insgesamt ist imho das Integral unter der linearen Funktion. Na das bekomme sogar ich noch hin :rofl: Die Fläche ist 1/2 *Durchläufe * 2 Sekunden, also 200E6 Sekunden! Sind lange 6 Jahre...
    Bei einem Sleep von 30ms bei GuiGetMsg() pro Schleifendurchlauf sind das zusätzlich 6E9 ms = 6E6 Sekunden also nochmal ca. 3 Monate.

    Mit dieser Info, dass also nach 6,25 Jahren ununterbrochener Scriptlaufzeit der Speicher vollläuft, kann ICH sehr gut leben 8)
    Im Umkehrschluss heisst das nichts anderes, dass es garnicht sein kann, dass ein "vergessenes" DllClose() den Speicher vollschreibt!
    Oder der Rechner unseres Kollegen ist 1000x schneller als meiner und benötigt daher statt 6 Jahren nur 2 Tage. Bei einem Rechner dieser Leistungsklasse hat man aber ganz andere Software laufen als ein Script mit 17 Zeilen, 3 Variablen und _IsPressed() :P
    Hmmm....oder es läuft ein Bot im Hintergrund, der mit dem Script gesteuert werden soll. Und in DEM ist das Speicherleck 8o
    Hehe, wir kriegen EUCH ALLE! ALLLEEEE!!!!

  • Es ist ein Programm zum schnell klicken für Minecraft... damit man die Berge einfacher abkloppen kann :D ist zwar mittlerweile überflüssig, da man ganze bereiche mit bestimmten blöcken, etc. füllen kann, damals wars aber recht nützlich. (früher hat man das ja auch öfter mal gespielt :D). mittlerweile hat man besseres zu tun...
    Vielleicht siehst du ja, wo das Script den SPeicher vollhaut:

    Spoiler anzeigen
    [autoit]

    #Include <Misc.au3>
    HotKeySet("{F11}","getTitle")
    global $title=wingettitle("[active]")
    While 1
    if _IsPressed(05) Then
    ControlClick ($title, "", "", "right")
    endif
    if _IsPressed(06) then
    ControlClick ($title, "", "", "left",10)
    EndIf
    sleep(5)
    WEnd
    exit

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

    Func getTitle()
    $title=wingettitle("[active]")
    EndFunc

    [/autoit]