1. Dashboard
  2. Mitglieder
    1. Letzte Aktivitäten
    2. Benutzer online
    3. Team
    4. Mitgliedersuche
  3. Forenregeln
  4. Forum
    1. Unerledigte Themen
  • Anmelden
  • Registrieren
  • Suche
Alles
  • Alles
  • Artikel
  • Seiten
  • Forum
  • Erweiterte Suche
  1. AutoIt.de - Das deutschsprachige Forum.
  2. Mitglieder
  3. AspirinJunkie

Beiträge von AspirinJunkie

  • AutoIt Community (GitHub Organization)

    • AspirinJunkie
    • 18. Januar 2026 um 19:18
    Zitat von water

    Frage: Verwendet Ihr eine grafische Oberfläche für Git? Falls ja, welche verwendet/empfehlt ihr?

    Meine persönliche Empfehlung hierzu wäre am Anfang erstmal rein auf Kommandozeilenebene mit git zu arbeiten.
    Irgendwann später - wenn soweit grundsätzlich klar ist was es da für Befehle gibt und was die genau machen, dann kann man auf grafische Tools umsteigen.
    Aber man hat dann den Vorteil, dass man das Konzept dahinter verinnerlicht hat und weiß was diese grafischen Tools im Hintergrund eigentlich genau tun.

    Übrigens: git for windows bringt bereits zwei GUI-Tools mit: git gui und gitk. Insbesondere gitk nutze ich gerne um schnell nen Überblick über ein Repository zu erhalten.

  • GPS-Koordinaten in 2D-Karte eintragen - oder Workaround.

    • AspirinJunkie
    • 17. Dezember 2025 um 12:56
    Zitat von UPIA

    mir fehlen 2 au3: JSON.au3, BinaryCall.au3

    Du hast die falsche UDF gezogen.
    Ich habe dir oben die richtige UDF verlinkt gehabt.
    Hier nochmal ganz explizit auf die Datei direkt: JSON.au3.
    Der zweite Hinweis da oben ist ebenso wichtig: Du musst dir erst noch einen API-Key dort besorgen.

    Zitat von UPIA

    woher hast du das script?

    Das hab ich dir vorhin im Zug geschrieben.

  • GPS-Koordinaten in 2D-Karte eintragen - oder Workaround.

    • AspirinJunkie
    • 17. Dezember 2025 um 08:52
    Zitat von UPIA

    ich benötige aber immer noch für die Zukunft eine Lösung mit GPS auf 2D (wenn mgl UTM)

    Das ist im Grunde kein Problem.
    Aber man muss sich hierbei ganz klar sein was man genau für Systeme meint.
    Nochmal: Was ist mit "GPS" gemeint? WGS84 mit geographischen Koordinaten?
    Was ist mit "UTM" gemeint? UTM ist erstmal nur eine Projektion die man auf beliebige Bezugssysteme packen kann.

    Für eine Koordinatentransformation muss man daher wissen, welches Bezugssystem und dann welcher Streifen der UTM-Projektion das Zielsystem ist.
    Beispiel: Das amtliche Bezugssystem in Deutschland ist ETRS89.
    Amtliche Koordinatenreferenzsysteme gibt es jedoch primär zwei: Einmal ETRS89 UTM32 N (für den westlichen Teil Deutschlands) und ETRS89 UTM33 N (für den östlichen Teil Deutschlands).
    Das muss man also genau wissen bevor man umrechnet. Nur "UTM" ist schlicht nichtssagend.

    Wenn man das dann weiß, dann gibt es zig Tools zum Transformieren zwischen verschiedenen Koordinatenreferenzsystemen.
    Wenn man Online-APIs wie die von MapTiler verwendet, dann bekommt man das auch gut in AutoIt integriert (hierfür brauchst du noch die JSON-UDF und einen API-Key von MapTiler) :

    AutoIt
    #include <Array.au3>
    #include "JSON.au3"
    
    ; EPSG-Codes der verwendeten Koordinatenreferenzsysteme
    ; Datenbank der EPSG-Codes findet sich hier: https://epsg.io/
    Global $dQuellsystem = 4326, _ ; geographische Koordinaten im WGS84
           $dZielsystem  = 25833   ; UTM 33N-Koordinaten im ETRS89-Bezugssystem (für den östlichen Teil Deutschlands das amtliche System)
    
    ; API-Key zum Zugriff auf die MapTiler-API (hier erhältlich: https://cloud.maptiler.com/account/keys/)
    Global $sMapTilerAPIKey = 'xxxxxxxxxxxxxxxxx'
    
    ; Quellkoordinaten im Quellsystem, welche in das Zielsystem transformiert werden sollen
    ; Bei EPSG=4326 sind die Koordinaten in der Reihenfolge [Längengrad, Breitengrad]
    Global $aCoords[][] = [   [13.37609, 52.51860], _ ; Reichstag
                              [13.01590, 52.40130], _ ; Schloss Sanssouci
                              [13.74159, 51.05195]  _ ; Frauenkirche Dresden  
                          ]
    
    ; Transformation durchführen
    Global $aNewCoords = _geo_coordsTransform($sMapTilerAPIKey, $dQuellsystem, $dZielsystem, $aCoords)
    If @error Then MsgBox(48, "Fehler", "@error: " & @error & @CRLF & "@extended: " & @extended & @CRLF & "Return: " & $aNewCoords)
    
    ; Transformierte Koordinaten anzeigen
    _ArrayDisplay($aNewCoords, "Transformierte Koordinaten", "", 64)
    
    
    ; #FUNCTION# ======================================================================================
    ; Name ..........: _geo_coordsTransform
    ; Description ...: Transforms geographic coordinates between different coordinate reference systems 
    ;                  using the MapTiler API.
    ; Syntax ........: _geo_coordsTransform($sAPI_Key, $dSystemFrom, $dSystemTo, $aCoordinates [, $sProxy = Default])
    ; Parameters ....: $sAPI_Key      - API key for MapTiler service (String)
    ;                  $dSystemFrom   - Coordinate system from which coordinates will be transformed (String, EPSG code or name)
    ;                  $dSystemTo     - Coordinate system to which coordinates will be transformed (String, EPSG code or name)
    ;                  $aCoordinates  - Array of coordinates to transform, can be a single coordinate (2 or 3 elements) 
    ;                                    or multiple coordinate (up to 50) 
    ;                  [$sProxy]      - (Optional) Proxy server to use for the HTTP request (String, Default: no proxy)
    ; Return values .: Success - Returns a 2D array with transformed coordinates:
    ;                       @extended = number of coordinates transformed
    ;                  Failure - Returns an error code and sets @error:
    ;                       @error = 1 - Invalid input array dimensions
    ;                              = 2 - Invalid number of coordinate components (must be 2 or 3 elements)
    ;                              = 3 - Invalid number of coordinate components (must be 2 or 3 elements)
    ;                              = 4 - Too many points (over 50)
    ;                              = 5 - Failed to parse response as valid JSON
    ;                              = 6 - Response does not contain valid coordinate results
    ; Author ........: AspirinJunkie
    ; =================================================================================================
    Func _geo_coordsTransform($sAPI_Key, $dSystemFrom, $dSystemTo, $aCoordinates, $sProxy = Default)
        Local $sCoordinates
    
        Switch UBound($aCoordinates, 0)
            Case 1
                ; check number of coordinate components (must be 2 or 3)
                If UBound($aCoordinates, 1) < 2 Or UBound($aCoordinates, 1) > 3 Then Return SetError(2, UBound($aCoordinates, 1), Null)
                
                $sCoordinates = _ArrayToString($aCoordinates, ",")
    
            Case 2
                ; check number of coordinate components (must be 2 or 3)
                If UBound($aCoordinates, 2) < 2 Or UBound($aCoordinates, 2) > 3 Then Return SetError(3, UBound($aCoordinates, 1), Null)
    
                ; MapTiler API-Limit = 50 Points per request
                If UBound($aCoordinates, 1) > 50 Then Return SetError(4, UBound($aCoordinates, 1), Null)
    
                $sCoordinates = _ArrayToString($aCoordinates, ",", -1, -1, ";")
    
            Case Else
                ; only 1D/2D Array as input coordinates
                Return SetError(1, UBound($aCoordinates, 0), Null)
        EndSwitch
    
        ; build Request-String
        Local $sOptExpVarStrings = Opt( "ExpandVarStrings", 1)
        Local $sRequest = 'https://api.maptiler.com/coordinates/transform/$sCoordinates$.json?s_srs=$dSystemFrom$&t_srs=$dSystemTo$&key=$sAPI_Key$'
        Opt( "ExpandVarStrings", $sOptExpVarStrings)
    
        ; send request
        Local $sResponse
        With ObjCreate("winhttp.winhttprequest.5.1")
            If IsKeyword($sProxy) <> 1 Then .SetProxy(2, $sProxy)
            .Open("Get", $sRequest, False)
            .Send()
            $sResponse = .ResponseText
        EndWith
    
        ; check if response is valid JSON
        Local $vResponse = _JSON_Parse($sResponse)
        If @error Then Return SetError(5, @error, $sResponse)
    
        ; check if response contains the result coordinates
        Local $aNewCoordsRaw = _JSON_Get($vResponse, ".results")
        If @error Then Return SetError(6, @error, $sResponse)
    
        ; convert result into 2D-Array
        Local $aNewCoordinates[UBound($aNewCoordsRaw, 1)][3], $mCoord, $iIdx = 0, $j, $sKey
        For $mCoord In $aNewCoordsRaw
            $j = 0
            For $sKey In MapKeys($mCoord)
                $aNewCoordinates[$iIdx][$j] = $mCoord[$sKey]
                $j += 1
            Next        
            $iIdx += 1
        Next
    
        Return SetExtended(UBound($aNewCoordinates), $aNewCoordinates)
    EndFunc
    Alles anzeigen


    Dann hast du zwischen zwei Koordinatenreferenzsystemen transformiert.
    Aber eigentlich bist du da ja noch nicht am Ziel - oder?
    So wie ich dich verstanden habe möchtest du auf dieser Basis Pixel in einer Karte ansprechen.
    Es fehlt dir also immer noch eine Konvertierung von deinen transformierten Koordinaten in den Bildraum.
    Je nach Genauigkeitsanforderung reicht hierfür evtl. schon aus die UTM-Koordinaten zu verschieben und zu skalieren.
    Aber es muss halt noch getan werden.

  • GPS-Koordinaten in 2D-Karte eintragen - oder Workaround.

    • AspirinJunkie
    • 16. Dezember 2025 um 15:38
    Zitat von UPIA

    Bekomme ich das in python überhaubt ausreichend genau hin.

    In Python bekommt man das noch gut hin.
    Es gibt zig Bibliotheken für Python um z.B. Karten darzustellen, geographische Berechnungen durchzuführen usw.
    Da ist im Grunde schon alles implementiert.
    Man muss halt die richtige Bibliothek finden und diese entsprechend verwenden.
    Hier habe ich allerdings keinen aktuellen Überblick mehr.
    Ich hatte früher mal matplotlib dafür verwendet aber das ist nicht unbedingt hierauf spezialisiert.
    Plotly, Folium und Cartopy habe ich noch im Kopf - aber wie gesagt: Da fehlt mir ein aktueller "Marktüberblick".
    Für sowas würde ich persönlich schnell QGIS anschmeißen oder eine kleine Webseite in Leaflet erstellen.

    Zitat von UPIA

    *bei GPS ist ja der Breitengrad oben größer im gegensatz zum image

    Ne - GPS betrifft ja nur die Koordinaten und deren mathematische Formatierung. Und Breitengrade haben nunmal immer den gleichen Abstand (nämlich Grad-Schritte).
    Was du höchstwahrscheinlich meinst ist die Kartenprojektion des Bildes und wie du es beschreibst klingt es stark nach einer Mercatorprojektion.
    Das wiederum hat rein gar nichts mit GPS zu tun.
    Jedes Bezugssystem welches man über Mercator abbildet wird in der Darstellung "dickere" Breitenabstände hin zu den Polen haben.

  • GPS-Koordinaten in 2D-Karte eintragen - oder Workaround.

    • AspirinJunkie
    • 16. Dezember 2025 um 14:11

    Hui - wo fängt man da an?
    Fangen wir mal so an: Was sind "GPS-Koordinaten"?
    Hier muss man zwischen zwei Begriffen unterscheiden:

    1. Bezugssystem: Die Grundlage für ein Koordinatensystem. Dies gibt (bei globalen Bezugssytemen) an, wie das System gegenüber der Erde gelagert ist, wie es sich mathematisch verhält usw. Unter anderem sind das die Parameter eines Rotationsellipsoiden (quasi als Modell der Erdform) sowie dessen Lage und Ausrichtung in Bezug zur Erde. Bekannt wären hier z.B. WGS84 (das von GPS) oder ETRS89 (das amtliche System in Deutschland).
    2. Koordinatensystem: Mathematisches System, welches definiert wie der Bezug von Zahlendarstellungen auf eine Lage im Raum (oder Ebene,...) definiert ist. Klassisch wäre hier das ebene kartesische Koordinatensystem (x,y), das räumliche kartesische Koordinatensystem (x,y,z), geographische Koordinaten (Breitengrad, Längengrad), Polarkoordinaten (Winkel, Strecke) aber auch projektive Koordinaten wie z.B. die Gauß-Krüger-Koordinaten oder UTM-Koordinaten (Rechtswert, Hochwert).

    Wenn du nun also sagst, dass du "GPS-Koordinaten" vorliegen hast, dann kann man sicherlich davon ausgehen, dass sie sich auf das Bezugssystem WGS84 beziehen. Die wirklich wichtige Frage ist jedoch: Welches Koordinatensystem? Koordinaten im WGS84 kann man im Grunde beliebig angeben. Sind es geographische Koordinaten? oder doch UTM? Bei der internen Berechnung selbst werden auch häufig räumliche kartesische Koordinaten verwendet. Das ist wichtig, weil wir ja den Brückenschlag zur Karte brauchen.

    Gehen wir jetzt einfach mal davon aus, du hast geographische Koordinaten - also Längen und Breitenangaben. Diese Koordinatenangaben beziehen sich auf die Oberfläche eine Rotationsellipsoiden. Also eine nichtebene, gekrümmte Fläche. Deine Karte ist jedoch lediglich zweidimensional und flach. Und wenn man sie als Pixelansammlung betrachtet, dann ist deren Koordinatensystem ein ebenes kartesisches Koordinatensystem. Aber wie geschah die Zuordnung von der eigentlich gekrümmten Form auf der Erde hin zur zweidimensionalen Karte? Und hier sind wir im großen Gebiet der Projektion und Abbildungsgeometrien.

    Wie dir sicherlich bewusst ist, gibt es nur eine Erde - aber zig unterschiedliche Darstellungsarten von ihr als Karte. Das hängt damit zusammen, dass man die gekrümmte Erdoberflächenform nicht verzerrungsfrei auf eine zweidimensionale Karte pressen kann. Daher gibt es zig verschiedene Projektionsarten die alle bestimmte Vor- und Nachteile haben und daher je nach Zweck unterschiedlich sind. Manche sind Winkeltreu, was die Navigation erleichtert, manche sind Streckentreu, was Messungen in der Karte erleichtert, manche sind Flächentreu, was Größenvergleiche ermöglicht usw. Wichtig: Keine Projektion erfüllt alle diese Anforderungen gleichzeitig!

    Das heißt für dich: Damit du von deinen globalen Koordinaten in deine Kartenkoordinaten kommst, brauchst du die Information welche Projektion deiner Karte denn zugrunde lag.
    Ohne diese Information kannst du keine rein geometrische Umrechnung durchführen.
    Es gibt nur eine einzige Projektionsart bei der du nahezu 1:1 von geographischen Koordinaten in die Pixel übertragen kannst (da dort sowohl Längen- als auch Breitengrade linear abgebildet werden): Die Rektangularprojektion (auch "Plattkarte" genannt). Wenn du das vorliegen hast, dann wäre es sehr einfach machbar: Du brauchst nur Koordinatenursprung und den Maßstab (also wieviele Pixel sind ein Grad im Bild).

    Wenn du diese Information nicht vorliegen hast aber dennoch eine Umrechnung haben willst, dann müsste man eine Transformation rechnen. Hierfür braucht es aber Koordinatenpaare. Sprich: Punkte deren Koordinaten in deinen "GPS-Koordinaten" bekannt sind, als auch deren Pixelposition im Zielbild.
    Bei hinreichender Anzahl derartiger Koordinatenpaare berechnet man sich dann die Parameter für eine Transformation und kann sich dann mit dieser "Formel" die Koordinaten von einem ins andere System rüberrechnen. Je nach Verzerrung des Zielsystems kann das entweder rechenaufwendiger werden oder mit einem höheren Genauigkeitsverlust einhergehen.

    Also zusammengefasst: Was genau hast du für Koordinaten? Kennst du die Projektionsart deiner Zielkarte? Hast du eventuell Bekannte Punkte deren GPS-Koordinaten du kennst und deren Pixelposition im Zielbild bekannt ist?

  • Anfängerfrage zum Ausführen einer Datei

    • AspirinJunkie
    • 12. Dezember 2025 um 09:05

    Das könnte durchaus am Programm selbst hängen.
    Es gibt mehrere Arten wie ein Programm Tastendrücke empfängt.
    Klassisch wären z.B. die Windows-Messages aber insbesondere Spiele nutzen auch andere Techniken wie z.B. Direct Input (gibts das noch?) oder gehen gleich auf den Raw-Input des Eingabegerätes.
    Send() nutzt intern die Windows-API-Funktion keybd_event und diese generiert eben diese Messages.

    Es kann also sein, dass der Flugsimulator diese Messages gar nicht auswertet, da er selbst anders auf Tastendrücke reagiert, bzw. eine Anti-Cheat-Engine derartige externe synthetische Messages verwirft.
    Man müsste also mal andere Methoden des Inputs probieren.
    Es gibt da z.B. noch die API-Funktion SendInput.
    Hier mal ein kleines Testskript, welches noch nicht sehr komfortabel ist aber F3 über SendInput triggern sollte:

    AutoIt
    ; Hotkey-Setup
    HotKeySet("{PAUSE}", _Stop)
    HotKeySet("{NUMPAD3}", _Send_F3)
    
    ; Endlosschleife um Skript am Leben zu halten
    Do
    	Sleep(20)
    Until 0
    
    
    Func _Send_F3()
    	; Sende F3 gedrückt:
        _SendInputKey(0x72, 0x3D, False)
    	If @error Then MsgBox("48", "Fehler bei down", "@error: " & @error & @CRLF & "@extended: " & @extended & @CRLF)
    	Sleep(10)
    
    	; Sende F3 losgelassen:
        _SendInputKey(0x72, 0x3D, True)
    	If @error Then MsgBox("48", "Fehler bei up", "@error: " & @error & @CRLF & "@extended: " & @extended & @CRLF)
    EndFunc
    
    Func _Stop()
    	Exit
    EndFunc
    
    
    Func _SendInputKey($iVK, $iScan, $bKeyUp)
        Local Static $tINPUT = @AutoItX64 _
    		? DllStructCreate("dword type; dword pad; ushort wVk; ushort wScan; dword dwFlags; dword time; uint64 dwExtraInfo; dword pad1; dword pad2") _
    		: DllStructCreate("dword type; ushort wVk; ushort wScan; dword dwFlags; dword time; dword dwExtraInfo; dword pad1; dword pad2"), _
    		$iStructSize = DllStructGetSize($tINPUT)
        
        DllStructSetData($tINPUT, "type", 1)     ; 1 = INPUT_KEYBOARD
        DllStructSetData($tINPUT, "wVk", $iVK)
        DllStructSetData($tINPUT, "wScan", $iScan)
        DllStructSetData($tINPUT, "dwFlags", $bKeyUp ? 2 : 0)
        DllStructSetData($tINPUT, "time", 0)
        DllStructSetData($tINPUT, "dwExtraInfo", 0x123)
        
        Local $aRet = DllCall("user32.dll", "uint", "SendInput", "uint", 1, "struct*", $tINPUT, "int", $iStructSize)
        
        If @error Then Return SetError(1, @error, False)
        If $aRet[0] = 0 Then Return SetError(2, 0, False)
        
        Return $aRet[0]
    EndFunc
    Alles anzeigen
  • _GDIPlus_ImageCalcDif

    • AspirinJunkie
    • 2. November 2025 um 19:33

    Grandios! "Script-Breaking" hinschreiben aber sich zum Inhalt komplett ausschweigen. "Internal optimisation" und fertig.

    Kein Mensch kann damit abschätzen in welchen Fällen es knallen wird. Sorry aber das ist wirklich wirklich unterirdisch unprofessionell.

    Edit: Ich rudere Mal ein Stück verbal zurück, da ich in dem Zusammenhang auch nichts von Script Breaking lese bislang.

  • PCIe 5 SSD

    • AspirinJunkie
    • 2. November 2025 um 08:31

    Ja das sind ja dann entsprechende Anwendungen.
    Diese habe ich persönlich nicht.
    Das habe ich vor einiger Zeit auch realisiert und daher hab ich da jetzt einen anderen Blick darauf.

    Aber dein Vergleich ging ja anscheinend allgemein SATA SSD --> M2. SSD.
    Ja das können tatsächlich heftige Unterschiede sein.
    Aber ob eine SSD 5.000 MBit/s schafft oder 15.000 Mbit/s wird (wenn man nicht, wie du, die entsprechenden Anwendungsfälle hat) man kaum bemerken (insb. beim angesprochenen USB3-Speicher-Kopieren ist man in beiden Fällen schon über dem Maximum).

    Zum Geraderücken: Das war nur für mein persönliches Anwendungsverhalten meine ganz eigenen Erkenntnisse zum Thema Performance.
    Wollte dir die SSD natürlich nicht madig reden (die ist offensichtlich ja wirklich gut). War nur ein allgemeiner Gedankengang zur Performancediskussion.

  • PCIe 5 SSD

    • AspirinJunkie
    • 1. November 2025 um 19:23

    Was habt ihr für Anwendungen wo derart hohe Datenraten relevant sind?
    Seit einigen Jahren habe ich für mich festgestellt, dass jegliche Hardware auf dem Markt die notwendige Performance für nahezu alle Dinge die ich mit dem PC mache mitbringt.
    Seither schaue ich nicht mehr auf die Performance sondern nur noch auf andere Dinge wie Ergonomie, Akkulaufzeit, Portabilität etc.
    Ich habe mir vor etwa einem Jahr für mein Surface eine neue SSD gegönnt wegen der Größe (256 GB --> 1 TB).
    Da das damalige absolute Topmodell für die Bauform M2 2230 (Corsair MP600 Mini R2) nur unwesentlich teurer war als der Rest dachte ich mir: OK gönnst du dir halt mal High-End.
    Die Benchmarks bestätigen das auch. Die neue liefert nun ~7.000 MBit/s. Deutlich mehr als die eingebaute SSD mit ~2000 Mbit/s.
    Nur wenn ich ehrlich bin: Ich merke davon schlicht nichts im Alltag im Vergleich zu vorher.
    Es ist im Grunde nur eine Zahl.
    Vielleicht habe ich sogar eher Nachteile, da ich ja den Performancesprung nicht bemerke aber dafür weniger Akkulaufzeit z.B. habe da das Ding mehr Strom zieht oder wärmer wird oder sowas.
    Ich weiß es nicht.

    Zitat von argumentum

    "Chris Titus Tech's Windows Utility"

    Zitat von Kanashius

    Ich kann dabei Atlas OS ( https://atlasos.net/ ) empfehlen.

    Oh so langsam schließt sich der Kreis wieder.
    Genau das war der Grund wie ich zu AutoIt gekommen bin.
    Ich war damals (>20 Jahre her) äußerst aktiv in der Welt von nLite.
    Und AutoIt war für diverse Automatisierungen im Zuge des Setups hilfreich.
    Ich war dort im Forum auch Moderator und habe so etwa 10x in der Woche XP installiert mit dem Ziel irgendwann ein möglichst perfekt entschlacktes, schlankes und kleines XP-Installations-Image zu erzeugen.

    Warum? Nun für uns war damals XP einfach ein riesenhaufen Bloatware-verseuchtes überladenes Monstrum, welches den Anwender wo es nur ging gängelte und versucht hat Zeug unterzuschieben was niemand braucht.
    Mal so als kleiner Wink an die Fraktion mit der Nostalgiebrille die heute über Win11 herziehen und glauben es war früher besser.

    Am Ende passe ich heute immer noch mein Windows meinen Bedürfnissen an.
    Aber nicht mal im Ansatz so exzessiv wie damals.
    Performance kostet ja heute nichts mehr im Vergleich zu damals - da kann man mit mehr drumherum leben.

    Aber nun als Tipp für Leute die sich ihre Win11-Installation individualisieren möchten: Windows bringt in Form der autounattended.xml im Grunde bereits alles mit.
    Die Datei erstellt man sich einmal, schmeißt sie mit in seine Windows-Installation und hat sein Windows dann so wie man es sich wünscht.
    Und anstatt sich durch die Doku zu wälzen gibt es eine super Seite wo man sich seine autounattend.xml zusammenklicken möchte wie man es braucht:
    Generate autounattend.xml files for Windows 10/11

  • MachMichVoll (MP3 zu Stick kopieren bis er voll ist)

    • AspirinJunkie
    • 31. Oktober 2025 um 14:52

    Ich finde es klasse!
    Es ist sowohl übersichtlich - auch ein fremder Betrachter weiß sehr schnell was das Skript wie macht und zum anderen hast du Fehlermanagement implementiert um auf verschiedene ungeplante Dinge zu reagieren.
    Und da das Skript offensichtlich genau das macht, was es soll, ist eigentlich schon 100% auf der Liste abgearbeitet.

    Um deine Übersichtlichkeit noch weiter zu erhöhen hätte ich ein paar Vorschläge.
    Wichtig ist aber, dass diese an der Funktionalität selbst nichts ändern - es geht eher um Code-Ästhetik.

    Aktuell liest du die die Datei mit der Funktion _FileReadToArray() ein.
    Standardmäßig gibt diese die Anzahl der Elemente im ersten Element ($Array[0]) zurück.
    Damit iteriert man über alle Elemente des Arrays mit der Form For $i = 1 To $Array[0] anstatt per For $i = 0 To Ubound($Array) - 1.
    Das kann hübscher sein, hat aber einen Haken: Du nimmst die Schleife um einzeln auf die ganzen Elemente des Arrays zuzugreifen.
    Dazu verwendest du (logischerweise) die Form $HSPSerieTitel[$Titelnummer].
    Es gibt aber noch eine andere Form der For-Schleife - nämlich die For-In-Schleife.
    Diese geht einfach über alle Elemente des Arrays und schmeißt diese in eine benannte Variable.
    Man spart sich damit die Index-Behandlung und das ganze wird deutlich übersichtlicher.
    In deinem Fall hier sollte aus meiner Sicht diese Variante die schönere sein (Beispiel folgt unten), denn anstatt immer in der Schleife zu schreiben $HSPSerieTitel[$Titelnummer], reicht dann ein $AktuellerTitel und $HSPSerieTitel kann ganz verschwinden.

    Dazu muss man aber die Anzahl aus dem ersten Element bekommen, sonst behandelt die For-In-Schleife diese ebenfalls als normales Element.
    Bei _FileReadToArray() kann man dazu das Flag $FRTA_NOCOUNT setzen oder man nimmt gleich das Pendant FileReadToArray (ohne Unterstrich).

    Bei _FileListToArray() bekommt man die Anzahl hingegen leider nicht so einfach weg, so dass du das mit deiner For-To-Schleife schon auf dem besten Weg bist (ich persönlich verwende stattdessen aus dem Grund direkt FileFindFirstFile/FileFindNextFile - aber das muss hier nicht sein).

    Nun zu deiner Frage mit FileOpen/FileClose.
    Man kann eine Datei "öffnen", dann wird ein sogenannter FilePointer auf die Datei (genauer auf eine bestimmte Stelle in der Datei) erstellt und gehalten.
    Das muss im Grunde immer gemacht werden wenn mit einer Datei gearbeitet wird.
    Manuell macht das in AutoIt aber eigentlich nur Sinn wenn du mehrere Operationen auf eine Datei ausführen möchtest (z.B. mehrere FileRead oder FileReadLine oder FileWrite usw.).
    Die meisten Funktionen in AutoIt machen nämlich selbst das FileOpen/FileClose wenn man nur den Dateinamen angibt.
    So auch _FileReadToArray(). Das heißt du brauchst hier gar nicht FileOpen/FileClose, da sich _FileListToArray schon darum kümmert.
    Und in deinem Fall war das leider auch sowieso wirkungslos, da man den Rückgabewert von FileOpen (das sogenannte FileHandle) dann in den File-Funktionen weiterverwendet anstatt dem Dateinamen.

    Das heißt konkret den Anfang deiner Region 2 könnte man folgendermaßen umschreiben:

    AutoIt
    #Region 02 - HSP
    
    ;große Schleife Serientitel aus Datei
    For $AktuellerTitel In FileReadToArray($ListeTXT)
    
    	;Ordner einer Serie erfassen
    	$ArrayEpisodenOrdner = _FileListToArray($HoerspielRoot & $AktuellerTitel, "*", $FLTA_FOLDERS)

    Aus meiner Sicht übersichtlicher, aber am Ende immer noch eine Geschmacksfrage.
    Der Effekt bleibt dennoch der gleiche - das Skript macht weiter was es soll.

    Zitat von Seppuku

    3) Beim If-Block If $KopieReturn = 0 Then hab ich in einer früheren Version If @error Then benutzt. Das hat aber nicht funktioniert und ich kapiere das überhaupt nicht.

    Das ist ganz von der jeweiligen Funktion abhängig. Eine Funktion hat 3 Ausgabemöglichkeiten (von ByRef-Parametern mal abgesehen): Den Return-Wert, den @error-Wert und den @extended-Wert.
    Ob und wie diese genutzt werden ist Sache der Funktion. Bei Funktionen die keinen wirklichen Rückgabewert benötigen (wie eben DirCopy) wird dort gerne ein Erfolgswert zurückgegeben.
    In dem Fall braucht man dann also kein @error mehr, da das schon durch den Return-Wert abgedeckt wird.
    Du musst also für jede Funktion schauen wie ihre Ausgaben funktionieren - daran kommst du nicht vorbei.
    Und ein Beispiel wo man unbedingt @error braucht ist im Grunde jede Funktion wo der Rückgabewert schon belegt ist. Z.B. eben _FileListToArray. Dort muss die Fehlerinformation auf anderem Wege erfolgen, da der Nutzer als Rückgabe ja ein Array erwartet und keinen Erfolgswert.

  • JSON UDF - beim erweitern eines JSON werden Escapezeichen geschrieben

    • AspirinJunkie
    • 12. Oktober 2025 um 21:52

    Ich hab's nicht ganz herausgelesen: Ist dein Problem gelöst oder ist noch eine Frage offen geblieben?


    Zitat von Yaerox

    Ich mache ein curl an die gleiche API aber einen anderen Controller. Der gibt ebenfalls JSON zurück. Den Output schreibe ich in eine Datei. Die Datei lese ich ein, mache ein

    Der Umweg über Dateien klingt etwas unnötig. Man kann die Ausgabe von curl auch direkt mit AutoIt weiterverarbeiten (mit StdOutRead).

    Und anstatt curl sollte in den allermeisten Fällen auch ein InetRead() reichen.

  • JSON UDF - beim erweitern eines JSON werden Escapezeichen geschrieben

    • AspirinJunkie
    • 9. Oktober 2025 um 21:24

    Hier fehlt ein bisschen was in deinem größeren Codebeispiel.
    Du hast erst einen JSON-String in der Variable $sString definiert.
    Danach machst du ein _JSON_addChangeDelete() auf die Datenstruktur $oJSON_Ad - keine Ahnung wo die auf einmal herkommt.

    Was ist das für eine Datenstruktur? Wo kommt die her?
    Egal - wir gehen mal davon aus, dass das eine gültige Datenstruktur ist und diese offensichtlich eine Map ist, welche den Key "images" besitzt oder diesen erhalten soll.

    Das Problem bei dir ist, dass du der Map $oJSON_Ad gar keine Datenstruktur hinzufügst sondern einfach nur den JSON-String. Es ist kein Array mit 3 Maps als Elementen - nein es ist einfach nur eine Zeichenkette.
    Und da diese Zeichenkette Zeichen enthält die in JSON eine Bedeutung haben müssen diese natürlich escaped werden.

    Kurz: Du musst aus dem JSON-String erstmal eine AutoIt-Datenstruktur machen - also ein _JSON_Parse. Sonst fügst du einfach nur einen langweiligen String hinzu und keine dadurch beschriebene Datenstruktur.

    Mal als Code zum besseren Verständnis:

    AutoIt
    #include "JSON.au3"
    
    ; eine Map, welche die Datenstruktur in $oJSON_Ad repräsentieren soll
    Global $oJSON_Ad[]
    $oJSON_Ad["Test"] = "Testwert" ; ein Beispielwert in der Map
    
    ; der Return-Wert der API als String (wichtig - es ist nur eine Zeichenkette - noch keine AutoIt-Datenstruktur!)
    $sAPIReturn = '{"ref":"https://blabla","hash":"3882db"}'
    
    ; erhaltenen JSON-String in eine AutoIt-Datenstruktur überführen
    $oMyStructure = _JSON_Parse($sAPIReturn)
    
    ; die erhaltene Datenstruktur als 3 Elemente eines Arrays wiederholen:
    Local $aArray[] = [$oMyStructure, $oMyStructure, $oMyStructure]
    
    ; zum Testen mal in einen JSON-String überführen und ausgeben lassen:
    ConsoleWrite(_JSON_GenerateCompact($aArray) & @CRLF & @CRLF)
    
    ; Das Array mit den 3 Elementen der Map $oJSON_Ad hinzufügen
    $oJSON_Ad["images"] = $aArray
    
    ; Das Endresultat ausgeben:
    ConsoleWrite(_JSON_Generate($oJSON_Ad) & @CRLF)
    Alles anzeigen


    Mal zum Grundverständnis der UDF:
    JSON ist einfach nur ein Format, welches verschachtelte Datenstrukturen in Textform abbildet.
    Ich kann z.B. in Python Dictionaries, Arrays, Strings, Zahlen etc. in irgendeiner verschachtelten Form in einer Variablen gespeichert haben und überführe dies in einen JSON-String.
    In irgendeiner anderen Programmiersprache schnappe ich mir dann den String und bilde diese Datenstruktur anhand des JSON-Strings dort nach.
    JSON ist also nur ein Textformat, welches die Datenstrukturen in Textform gießt zum einfachen Austausch - nicht mehr und nicht weniger.

    Das heißt im Grunde in der ganzen Prozessierung taucht JSON in dem ganzen Prozess nur zweimal auf: Einmal um einen externen _JSON-String in eine AutoIt-Datenstruktur zu überführen: Das macht _JSON_Parse() und einmal am Ende wenn man aus der AutoIt-Struktur einen _JSON_String erzeugen möchte der diese Struktur wiedergibt: Das macht _JSON_Generate() bzw. _JSON_GenerateCompact().

    Alles dazwischen ist im Grunde völlig unabhängig von JSON. Es sind reine AutoIt-Datenstrukturen und müssen auch so behandelt werden.
    Also Maps, Arrays, Strings, Zahlen usw. und üblicherweise auch ineinander verschachtelt.
    Da der Umgang mit derart verschachtelten Daten in AutoIt nicht immer ganz trivial ist, gibt es in der UDF Hilsfunktionen um mit diesen verschachtelten Daten besser zu arbeiten: _JSON_Get() und _JSON_addChangeDelete(). Die haben eigentlich gar nichts mit JSON zu tun sondern nur mit verschachtelten reinen AutoIt-Datenstrukturen. Aber da sie im Kontext von JSON sehr oft notwendig sind tragen sie dies mit im Namen.

    Also kurz: Wenn du einen JSON-String bekommst - egal wann, egal woher - das erste was du machst ist diesen mit _JSON_Parse() in eine AutoIT-Datenstruktur zu überführen.
    Alles was du dann mit den Daten machst ist rein auf Ebene von AutoIt und unabhängig von JSON.
    Je nachdem ob es sinnvoll ist kannst du hierbei die Funktionen _JSON_Get() und _JSON_addChangeDelete() zu Hilfe nehmen aber mit JSON hat das in dieser Phase nichts zu tun.
    Erst wenn du wieder einen JSON-String sehen willst (z.B. beim Ausgeben in eine Datei) dann überführst du die AutoIt-Datenstruktur in einen JSON-String per _JSON_Generate() bzw. _JSON_GenerateCompact().

  • xlsxNative - XLSX/XLSM-Dateien ohne Excel einlesen und erzeugen

    • AspirinJunkie
    • 9. Oktober 2025 um 11:03

    Es gab einige Anpassungen innerhalb der UDF:

    1. Schreiben von Excel-Funktionszellen

    _xlsx_WriteFromArray() kann nun auch Excel-Funktionen schreiben. Hierzu muss der Elementwert im Eingabearray ein String sein der mit einem = beginnt. Der String selbst wird dann als Excel-Funktion interpretiert.

    Beispiel: =IF(F2>E2,"yes","no")

    Hinweis: Damit Excel das korrekt interpretiert, ist ausschließlich die englische Notation zulässig.

    Sollte ein String der nicht als Funktion interpretiert werden soll geschrieben werden der aber mit einem = beginnt, dann muss dieses erste = durch Dopplung (==) escaped werden.

    2. Kleinstmögliche .xlsx-Ausgabedateien

    Die mit _xlsx_WriteFromArray() erzeugte .xlsx-Datei ist konsequent auf minimale Größe hin optimiert worden. Die hiermit erzeugten Dateien bewegen sich nah am möglichen Minimum des Dateiformates für die jeweiligen Daten.

    Beispielsdatensatz: [[1,2,3],[4,5,6]]

    ToolDateigröße
    Excel8.45 KB
    _xlsx_WriteFromArray()1.23 KB

    3. Formatierung von Datum und Zeit

    Wenn ein Elementwert im Eingabearray für _xlsx_WriteFromArray() ein Datum oder eine Zeit enthält, dann wird die Zelle in der .xlsx-Datei entsprechend als Datums-, Zeit- bzw. Datum/Zeit-Stil formatiert.

    Das Stringformat für diese Zellen muss hierbei folgendermaßen aufgebaut sein:

    WasFormat
    DatumYYYY-MM-DD
    ZeitHH:MM[:SS[.mmmmmm]]
    Datum + ZeitYYYY-MM-DD[T ]HH:MM[:SS[.mmmmmm]]

    Hinweis: Datum sowie Datum+Zeit bleiben als Stringwert erhalten. Eine reine Zeit hingegen wird als Zahl (Excel-Notation bei der 24h = 1.0) kodiert.

    Bugfixes und Stil

    • _xlsx_2Array() kann nun auch mit Dateien umgehen, deren sharedstrings.xml-Elemente mit einem Präfix versehen sind (= höhere Kompatibilität).
    • _xlsx_2Array() liest nun auch Dateien die Leerzeilen überspringen (= höhere Kompatibilität)
    • Au3Check wirft keine (teilweise falschen) Warnungen mehr bei Verwendung der UDF.
  • Datumsformat

    • AspirinJunkie
    • 25. September 2025 um 10:00
    Zitat von Peter S. Taler

    AM bedeutet Vormittag (die Zeit zwischen 00:00 und 12:00) ---> 12:00 AM = Mittags 12:00 --> Da stimmt der Code

    Das entspricht aber nicht den Standards.
    Für die US-Verwaltung z.B. gilt explizit >>folgende Vorgabe<< in deren offiziellen Style-Guide:

    Code
    9.54. References to meridian in statements of time are abbreviated as 
    follows:
    12 p.m. (12 noon)
    12 a.m. (12 midnight)

    Das handhaben auch alle mir bekannten Online-Converter und Programmierbibliotheken so (einfach mal probieren).

    Spannender ist die Behandlung von 00:00 AM/PM. Beide sollte es nicht geben, da im 12h-System nur die Werte 01-12 zugelassen sein sollten.
    Manche Konverter bringen daher hierbei einen Fehler, andere lassen zumindest 00:00 AM = 12:00 AM zu. Wirklich undefiniert ist hingegen 00:00 PM.
    Halb Eins Nachts ist demzufolge 12:30 AM - 00:30 AM wäre nämlich streng genommen auch nicht zulässig.
    Habe es auch mal gerade unter Python probiert und das eingebaute datetime.strptime schmeißt mir im Fall von 00:30:00 AM auch folgerichtig einen Fehler.

  • AutoIt 3.3.18.0 - bereits getestet?

    • AspirinJunkie
    • 15. September 2025 um 14:32

    Ich konnte die Messung über ProcessSetPriority noch stabilisieren.
    Die Ergebnisse nun zeigen dann doch ein klares Bild: Die 3.3.18.0 ist tatsächlich immer ein paar Prozentpunkte der 3.3.16.1 voraus:

    Markdown
    | Test                                      | 3.3.16.1 | 3.3.18.0 | Δ in % |
    | ----------------------------------------- | -------- | -------- | ------ |
    | GetCurrentProcessId (no params)           | 2,3 µs   | 2,3 µs   | 0,0%   |
    | GetTickCount (no params)                  | 2,3 µs   | 2,2 µs   | 1,9%   |
    | GetTickCount (no params)                  | 2,3 µs   | 2,2 µs   | 2,5%   |
    | GetLastError (no params)                  | 2,3 µs   | 2,2 µs   | 3,0%   |
    | GetLastError (no params)                  | 2,3 µs   | 2,2 µs   | 3,2%   |
    | lstrlenW (100 chars)                      | 3,5 µs   | 3,3 µs   | 3,2%   |
    | GetCurrentProcessId (no params)           | 2,4 µs   | 2,3 µs   | 3,4%   |
    | SetLastError (1 DWORD Parameter)          | 3,3 µs   | 3,2 µs   | 3,5%   |
    | lstrlenW (4 chars)                        | 3,4 µs   | 3,3 µs   | 4,0%   |
    | lstrlenW (10000 chars)                    | 7,0 µs   | 6,7 µs   | 4,8%   |
    | GetSystemTime (struct SYSTEMTIME)         | 5,9 µs   | 5,6 µs   | 5,4%   |
    | Sleep(0) (1 DWORD Parameter)              | 4,1 µs   | 3,8 µs   | 6,4%   |
    | RtlMoveMemory (1 KB)                      | 7,1 µs   | 6,6 µs   | 6,9%   |
    | RtlMoveMemory (1 MB)                      | 53,6 µs  | 49,9 µs  | 6,9%   |
    | GetSystemTimeAsFileTime (struct FILETIME) | 4,7 µs   | 4,3 µs   | 8,4%   | 
    Alles anzeigen
  • AutoIt 3.3.18.0 - bereits getestet?

    • AspirinJunkie
    • 15. September 2025 um 11:52

    Ja das wäre eine mögliche Interpretation.
    Hab das auch gleich mal so getestet - aber auch das sehe ich kein relevantes Muster:

    Code
             Test                             3.3.16.1   3.3.18.0  Δ in %
    ---------------------------------------------------------------------
    GetCurrentProcessId (no params)            2,4 µs	 2,4 µs	    1,6%
    GetTickCount (no params)                   2,3 µs	 2,3 µs	    1,6%
    GetLastError (no params)                   2,5 µs	 2,3 µs	    7,1%
    GetCurrentProcessId (no params)            2,4 µs	 2,4 µs	    3,0%
    GetTickCount (no params)                   2,4 µs	 2,3 µs	    6,4%
    GetLastError (no params)                   2,4 µs	 2,3 µs	    3,8%
    Sleep(0) (1 DWORD Parameter)               4,1 µs	 3,8 µs	    7,9%
    SetLastError (1 DWORD Parameter)           4,0 µs	 3,3 µs	   18,0%
    lstrlenW (4 chars)                         3,8 µs	 3,3 µs	   12,0%
    lstrlenW (100 chars)                       3,5 µs	 3,4 µs	    4,9%
    lstrlenW (10000 chars)                     6,9 µs	 6,7 µs	    3,1%
    GetSystemTime (struct SYSTEMTIME)          5,7 µs	 6,0 µs	   -4,6%
    GetSystemTimeAsFileTime (struct FILETIME)  4,3 µs	 4,5 µs	   -4,5%
    RtlMoveMemory (1 KB)                       6,8 µs	 6,9 µs	   -2,3%
    RtlMoveMemory (1 MB)                      51,7 µs	50,6 µs	    2,1%
    Alles anzeigen
  • AutoIt 3.3.18.0 - bereits getestet?

    • AspirinJunkie
    • 15. September 2025 um 08:03

    Mich hat vor allem der folgende Eintrag im Changelog interessiert:

    Zitat

    Added #3891: DllCall() performance optimisation.

    Was genau gemacht wurde steht da nicht und im verlinkten Ticket wird sich im Grunde auch ausgeschwiegen.
    Da steht nur "Internal optimisation to not allocate data at each DllCall".
    Das ist umso verwirrender, da im Ticket steht, dass dieses bereits in 3.3.16.1 umgesetzt wurde.

    Ich wollte daher mal herausfinden, was diese Änderung konkret ausmacht und habe mal verschiedene Szenarien für DllCall mit folgendem Skript getestet:

    AutoIt
    #include <Array.au3>
    #include <String.au3>
    
    Global Const $iRUNS = 1e5 ; number of iterations for each test
    Global $aResults[0][2] ; result array
    Global Const $hDLLKernel32 = DllOpen("kernel32.dll"), $hDLLUser32 = DllOpen("user32.dll")
    
    
    ConsoleWrite("===========================================" & @CRLF & _
                 "DllCall Performance Benchmark" & @CRLF & _
                 "AutoIt Version: " & @AutoItVersion & @CRLF & _
                 "Iterations: " & $iRUNS & @CRLF & _
                 "===========================================" & @CRLF & @CRLF)
    
    ; Warm-up (for waking the CPU)
    For $i = 1 To 1e6
        DllCall($hDLLKernel32, "DWORD", "GetTickCount")
    Next
    
    ; determine overhead for the loop itself
    Global $iOverhead = TimerInit()
    For $i = 1 To $iRUNS
    Next
    $iOverhead = TimerDiff($iOverhead)
    
    
    ; run the test
    ConsoleWrite("=== Test 1: functions without parameters ===" & @CRLF)
    _Test_NoParams()
    ConsoleWrite(@CRLF)
    
    ConsoleWrite("=== Test 2: functions with simple parameters ===" & @CRLF)
    _Test_SmallParams()
    ConsoleWrite(@CRLF)
    
    ConsoleWrite("=== Test 3: functions with string parameters ===" & @CRLF)
    _Test_StringParams()
    ConsoleWrite(@CRLF)
    
    ConsoleWrite("=== Test 4: functions with dll structs ===" & @CRLF)
    _Test_StructParams()
    ConsoleWrite(@CRLF)
    
    ConsoleWrite("=== Test 5: memory operations ===" & @CRLF)
    _Test_MemoryOps()
    ConsoleWrite(@CRLF)
    
    
    ; print results
    ConsoleWrite("===========================================" & @CRLF)
    ConsoleWrite("Results" & @CRLF)
    ConsoleWrite("===========================================" & @CRLF)
    
    For $i = 0 To UBound($aResults) - 1
        ConsoleWrite(StringFormat("%-50s % 10.3f µs\n", $aResults[$i][0], $aResults[$i][1] * 1000 / $iRUNS))
    Next
    
    ConsoleWrite(@CRLF)
    
    
    
    
    ; Test 1: functions without parameters
    Func _Test_NoParams()
    
    	; GetCurrentProcessId
        Local $hTimer = TimerInit()
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "DWORD", "GetCurrentProcessId")
        Next    
        Local $fTime = TimerDiff($hTimer)
        _PrintTestResult("GetCurrentProcessId (no params)", $fTime)
        
        ; GetTickCount
        $hTimer = TimerInit()
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "DWORD", "GetTickCount")
        Next    
        $fTime = TimerDiff($hTimer)
        _PrintTestResult("GetTickCount (no params)", $fTime)
        
        ; GetLastError
        $hTimer = TimerInit()
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "DWORD", "GetLastError")
        Next    
        $fTime = TimerDiff($hTimer)
        _PrintTestResult("GetLastError (no params)", $fTime)
    EndFunc
    
    ; Test 2: functions with simple parameters
    Func _Test_SmallParams()
        ; Sleep with 0 ms 
        Local $hTimer = TimerInit()
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "NONE", "Sleep", "DWORD", 0)
        Next    
        Local $fTime = TimerDiff($hTimer)
        _PrintTestResult("Sleep(0) (1 DWORD Parameter)", $fTime)
        
        ; SetLastError
        $hTimer = TimerInit()
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "NONE", "SetLastError", "DWORD", 0)
        Next    
        $fTime = TimerDiff($hTimer)
        _PrintTestResult("SetLastError (1 DWORD Parameter)", $fTime)
    EndFunc
    
    ; Test 3: functions with string parameters
    Func _Test_StringParams()
        ; short String
        Local $tCharStruct = DllStructCreate("WCHAR[5]")
    	Local $hPtr = DllStructGetPtr($tCharStruct)
    	DllStructSetData($tCharStruct, 1, "Test")
        Local $hTimer = TimerInit()    
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "int", "lstrlenW", "PTR", $hPtr)
        Next    
        Local $fTime = TimerDiff($hTimer)
        _PrintTestResult("lstrlenW (4 chars)", $fTime)
        
        ; medium string
    	$tCharStruct = DllStructCreate("WCHAR[101]")
    	$hPtr = DllStructGetPtr($tCharStruct)
    	DllStructSetData($tCharStruct, 1, _StringRepeat("A", 100))
        $hTimer = TimerInit()    
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "int", "lstrlenW", "PTR", $hPtr)
        Next    
        $fTime = TimerDiff($hTimer)
        _PrintTestResult("lstrlenW (100 chars)", $fTime)
        
        ; long string
    	$tCharStruct = DllStructCreate("WCHAR[10001]")
    	$hPtr = DllStructGetPtr($tCharStruct)
    	DllStructSetData($tCharStruct, 1, _StringRepeat("B", 10000))
        $hTimer = TimerInit()    
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "int", "lstrlenW", "PTR", $hPtr)
        Next    
        $fTime = TimerDiff($hTimer)
        _PrintTestResult("lstrlenW (10000 chars)", $fTime)
    EndFunc
    
    ; Test 4: functions with dll structs
    Func _Test_StructParams()
        ; struct definitions
        Local $tSystemTime = DllStructCreate("word Year;word Month;word DayOfWeek;word Day;" & _
                                             "word Hour;word Minute;word Second;word Milliseconds")
    	Local $tFileTime   = DllStructCreate("DWORD dwLowDateTime;DWORD dwHighDateTime")
        
    	; GetSystemTime										   
        Local $hTimer = TimerInit()    
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "NONE", "GetSystemTime", "struct*", $tSystemTime)
        Next    
        Local $fTime = TimerDiff($hTimer)
        _PrintTestResult("GetSystemTime (struct SYSTEMTIME)", $fTime)
        
        ; GetSystemTimeAsFileTime 
        $hTimer = TimerInit()    
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "NONE", "GetSystemTimeAsFileTime", "struct*", $tFileTime)
        Next    
        $fTime = TimerDiff($hTimer)
        _PrintTestResult("GetSystemTimeAsFileTime (struct FILETIME)", $fTime)
    EndFunc
    
    
    ; Test 5: memory operations
    Func _Test_MemoryOps()
        Local $iSize   = 1024 ; 1KB
        Local $tSource = DllStructCreate("byte[" & $iSize & "]")
        Local $tDest   = DllStructCreate("byte[" & $iSize & "]")
        ; fill source with data
        For $i = 1 To $iSize
            DllStructSetData($tSource, 1, Random(0, 255, 1), $i)
        Next
        
    
    	; RtlMoveMemory 1 KB block
        Local $hTimer = TimerInit()
        For $i = 1 To $iRUNS
            DllCall($hDLLKernel32, "NONE", "RtlMoveMemory", _
                    "struct*", $tDest, _
                    "struct*", $tSource, _
                    "ulong_ptr", $iSize)
        Next    
        Local $fTime = TimerDiff($hTimer)
        _PrintTestResult("RtlMoveMemory (1 KB)", $fTime)
        
    
        ; RtlMoveMemory 1 MB block
        $iSize   = 1048576 ; 1 MB
        $tSource = DllStructCreate("byte[" & $iSize & "]")
        $tDest   = DllStructCreate("byte[" & $iSize & "]")
    	For $i = 1 To $iSize
            DllStructSetData($tSource, 1, Random(0, 255, 1), $i)
        Next
        
        $hTimer = TimerInit()    
        For $i = 1 To $iRUNS / 10
            DllCall($hDLLKernel32, "NONE", "RtlMoveMemory", _
                    "struct*", $tDest, _
                    "struct*", $tSource, _
                    "ulong_ptr", $iSize)
        Next    
        $fTime = TimerDiff($hTimer)
        _PrintTestResult("RtlMoveMemory (1 MB)", $fTime * 10)
    EndFunc
    
    
    
    ; Hilfsfunktion: Ergebnis hinzufügen
    Func _PrintTestResult($sTest, $fTime)
        Local $iN = UBound($aResults)
    
    	; add current result
        ReDim $aResults[$iN + 1][2]
        $aResults[$iN][0] = $sTest
        $aResults[$iN][1] = $fTime - $iOverhead
            
        ConsoleWrite(StringFormat("%-50s: % 10.2f µs\n", $sTest, ($fTime - $iOverhead) * 1000 / $iRUNS))
    EndFunc
    Alles anzeigen

    Die Ergebnisse habe ich dann zwischen der Version 3.3.16.1 und 3.3.18.0 gegenübergestellt und erhielt folgendes Ergebnis:

    Code
             Test                             3.3.16.1   3.3.18.0  Δ in %
    ---------------------------------------------------------------------
    GetCurrentProcessId (no params)            2,0 µs     2,0 µs    0,2%
    GetTickCount (no params)                   1,9 µs     1,9 µs    1,1%
    GetLastError (no params)                   1,9 µs     1,9 µs    0,0%
    GetCurrentProcessId (no params)            2,0 µs     2,0 µs   -0,2%
    GetTickCount (no params)                   1,9 µs     1,9 µs   -0,7%
    GetLastError (no params)                   1,9 µs     1,9 µs   -2,0%
    Sleep(0) (1 DWORD Parameter)               3,4 µs     3,4 µs    2,3%
    SetLastError (1 DWORD Parameter)           2,8 µs     2,8 µs    1,9%
    lstrlenW (4 chars)                         3,0 µs     2,9 µs    3,2%
    lstrlenW (100 chars)                       3,0 µs     3,0 µs    2,5%
    lstrlenW (10000 chars)                     6,3 µs     6,5 µs   -3,6%
    GetSystemTime (struct SYSTEMTIME)          5,3 µs     5,3 µs   -0,3%
    GetSystemTimeAsFileTime (struct FILETIME)  3,9 µs     4,7 µs  -20,4%
    RtlMoveMemory (1 KB)                       7,0 µs     6,3 µs   10,1%
    RtlMoveMemory (1 MB)                      52,2 µs    48,1 µs    7,8%
    Alles anzeigen

    Einen wirklichen Performancesprung kann ich nicht erkennen. Die Unterschiede lassen sich nichtmal signifikant von der Messungenauigkeit trennen.
    Ich habe (da im Ticket ja steht, dass dieses bereits mit 3.3.16.1 umgesetzt wurde) auch mal die Version 3.3.14.0 dagegen gehalten.
    Aber auch da sieht das Ergebnis ziemlich gleich aus.

    Ich bin daher ziemlich ratlos was es mit der ominösen DllCall performance optimisation auf sich hat.

  • JSON-UDF

    • AspirinJunkie
    • 8. September 2025 um 13:41

    Die JSON-UDF weiß nichts darüber wie du die Daten semantisch interpretieren möchtest.
    JSON kennt nur Syntax.
    Und hier haben wir auch noch den Fall, dass JSON das Konzept von 2D-Arrays nicht kennt.
    Es kennt nur zwei Datenstrukturen als Verschachtelungsebene: Listen (quasi 1D-Arrays) und Objekte (Key-Value-Strukturen).
    Und da JSON nur die Syntax betrachtet, kann sie z.B. aus deinem verschachtelten Array-In-Array nicht ableiten, dass dort das erste Element die Headereinträge meint und danach die Zeilen einen Bezug zu diesem Header haben.
    Semantik ist nicht die Aufgabe von JSON - und damit auch nicht von der UDF.
    Daher wird es dort auch keine Funktion geben die aus einem 2D-Array eben keine verschachtelte Liste, wie man es aus dem Aufbau der Datenstruktur ableiten würde, sondern eine Liste von Objekten - denn das gibt die Syntax schlicht nicht her und eine andere Interpretation auf die Daten zu finden ist Sache des Users selbst.

    Hierfür muss man sich also was eigenes basteln. Oder wie im Falle der 2D-Tabellenstruktur Funktionen nutzen die die Arrays eben als solche Tabellen interpretiert. Ich hatte hierzu mal die TableData-UDF gebastelt. Die macht im Grunde genau das was deine _ArrayToJson() macht. Sie konvertiert ein 2D-Array so in ein 1D-Array mit Maps als Elementen, dass die erste Zeile als Key-Namen interpretiert wird und man dann das gleiche Ergebnis z.B. so erreichen würde:

    C
    #include "TableData.au3"
    #include "JSON.au3"
    
    _Main()
    
    Func _Main()
        Local $aTable[][3] = _
            [ _
                ['Feature',             'Technologie',    'Status'], _
                ['REST API - User',     'Node.js',        'Fertig'], _
                ['Datenbank-Migration', 'SQL',            'Geplant'], _
                ['Frontend Dashboard',  'React',          'Im Review'], _
                ['Unit Tests - Auth',   'Python',         'In Arbeit'], _
                ['CI/CD Pipeline',      'GitHub Actions', 'Fertig'], _
                ['Performance Tuning',  'Go',             'Offen'] _
            ]
    
    	; 2D-Array als Tabellenobjekt mit Header betrachten und in Array von Maps konvertieren
    	Local $aObjects = _td_toObjects(_td_fromArray($aTable, True))
    
    	_WriteFile('.\output\TableData-table.json', _JSON_Generate($aObjects))
    EndFunc
    
    
    Func _WriteFile($sFile, $sText)
        Local Const $iUtf8NoBom = 256
        Local Const $iOverwriteCreation = 2 + 8
        Local Const $iMode = $iUtf8NoBom + $iOverwriteCreation
    
        Local $hFile = FileOpen($sFile, $iMode)
        FileWrite($hFile, $sText)
        FileClose($hFile)
    EndFunc
    Alles anzeigen

    Das ist halt mal eine Funktion, welche die Daten semantisch genauso interpretiert wie du es auch getan hast.
    Das hat mit JSON aber erstmal nichts zu tun sondern nur wie die Daten eben aufgefasst werden.
    JSON macht dann nur die Konvertierung zwischen dem JSON-String und der internen Datenstruktur - es interpretiert nichts.

    Der einzige Punkt bei dem die UDF eine Funktionalität anbietet um Datenstrukturen in andere zu konvertieren ist bei der Konvertierung zwischen 1D und 2D-Arrays (bzw. Array-In-Arrays). Dies ist jedoch schlicht nur der Fall, da hierfür eine Notwendigkeit besteht denn JSON kann wie gesagt nichts mit mehrdimensionalen Arrays anfange.

    Bei der Liste hingegen habe ich kein Pendant.
    Die Interpretation, dass der erste Eintrag eines 1D-Array der einzige Key einer Map sein soll und alle anderen Elemente als 1D-Array als Value dieses Keys behandelt werden sollen ist schlicht halt sehr individuell und bedarf daher auch einer individuellen Behandlung.

    Kurz: Ja klar kann man Daten hin und her transformieren wie man möchte - die JSON-UDF ist dafür halt nur der falsche Ort.

  • Windows Graphics Capture API (WinRT) Fullscreen Capturing Test

    • AspirinJunkie
    • 27. August 2025 um 11:37

    Ja klappt super! :love:

    30s 1920x1080@30FPS ergibt .webp-Datei mit 1,3 MB Dateigröße!

  • Welche DLLs benutzt mein Programm.

    • AspirinJunkie
    • 25. August 2025 um 16:39
    Zitat von DOheim

    Ich hätte nicht gedacht, dass mein mickriges Testprogramm mit 18 DLLs arbeitet.

    Dein Programm selbst nicht. Aber eine AutoIt-Exe ist nichts weiter als der Quellcode deines Programmes plus dem kompletten AutoIt-Interpreter. Und der stellt alle Funktionen von AutoIt zur Verfügung - egal was davon in deinem Skript genutzt wird oder nicht.

    Und das führt halt zu der relativ großen Anzahl an Abhängigkeiten.

Spenden

Jeder Euro hilft uns, Euch zu helfen.

Download

AutoIt Tutorial
AutoIt Buch
Onlinehilfe
AutoIt Entwickler
  1. Datenschutzerklärung
  2. Impressum
  3. Shoutbox-Archiv
Community-Software: WoltLab Suite™