;aligncomment = 60
#AutoIt3Wrapper_UseX64=y


#include <APIFilesConstants.au3>
#include <APIProcConstants.au3>
#include <Array.au3>
#include <AutoItConstants.au3>
#include <ComboConstants.au3>
#include <Date.au3>
#include <EditConstants.au3>
#include <File.au3>
#include <FileConstants.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <GuiEdit.au3>
#include <Memory.au3>
#include <MemoryConstants.au3>
#include <SendMessage.au3>
#include <StaticConstants.au3>
#include <StringConstants.au3>
#include <WinAPIConv.au3>
#include <WinAPIFiles.au3>
#include <WinAPIHObj.au3>
#include <WinAPIInternals.au3>
#include <WinAPIMem.au3>
#include <WinAPIProc.au3>
#include <WinAPISys.au3>
#include <WinAPISysInternals.au3>
#include <WindowsConstants.au3>


#Region *** Optionen ***
Opt('GUIOnEventMode', 1)
Opt('MustDeclareVars', 1)
Opt('GUICloseOnESC', 0)
#EndRegion *** Optionen ***

_WinAPI_SetPriorityClass($HIGH_PRIORITY_CLASS)
_GDIPlus_Startup()
OnAutoItExitRegister('_OnExit')

#Region *** Konstanten deklarieren ***
Global Const $DBT_DEVICEARRIVAL = 0x8000                     ; A device or piece of media has been inserted and is now available.
Global Const $DBT_DEVICEREMOVECOMPLETE = 0x8004              ; A device or piece of media has been removed.
Global Const $tagDEV_BROADCAST_HDR = 'dword dbch_size        ; dword dbch_devicetype; dword dbch_reserved'
Global Const $tagDEV_BROADCAST_VOLUME = 'dword dbcv_size     ; dword dbcv_devicetype; dword dbcv_reserved; dword dbcv_unitmask; dword dbcv_flags'
Global Const $DBT_DEVTYP_VOLUME = 0x00000002
Global Const $sAppTitle = 'Flash-Test'
Global Const $sAppVersion = '1.0.1.0'
Global Const $sAppDate = '11.12.2018'
Global Const $sAppAuthor = 'Gorathan / Oscar'
Global Const $sAppAuthorEMail = 'mailto:admin@technik-hobby.de'
Global Const $sAppURL = 'https://forum.technik-hobby.de'
Global Const $iAppDPI = RegRead('HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics', 'AppliedDPI')
#EndRegion *** Konstanten deklarieren ***


;64Bit

#cs CheckBuffer64
    Use64                                                    ; 64Bit Modus!

    push rdi                                                 ;nichtflüchtige register retten
    push rsi
    push rbx
    push rbp

    ;bei fastCall werden die ersten 4 Parameter in Registern rcx, rdx, r8 und r9 übergeben...
    mov rsi,rcx                                              ;buffer
    mov rdx,rdx                                              ;Anzahl
    shr rdx,1                                                ;Anzahl/2 da 64 Bit (2*32)
    mov rax,r8                                               ;startwert
    xor rcx,rcx                                              ;counter

    @loop:                                                   ; Hauptschleife
    mov rbx,qword[rsi+rcx*8]                                 ; den Wert aus dem Buffer holen und in ebx speichern
    cmp rbx,rax                                              ; vergleichen, ob eax und ebx gleich sind
    jne @end                                                 ; wenn Wert ungleich, dann Routine verlassen
    xor rax,0xffffffffffffffff                               ; eax invertieren
    inc rcx                                                  ; counter++
    mov rbx,qword[rsi+rcx*8]                                 ; den Wert aus dem Buffer holen und in ebx speichern
    cmp rbx,rax                                              ; vergleichen, ob eax und ebx gleich sind
    jne @end                                                 ; wenn Wert ungleich, dann Routine verlassen
    xor rax,0xffffffffffffffff                               ; eax wieder zurueck invertieren
    inc rax                                                  ; eax++
    inc rcx                                                  ; counter++
    cmp rcx,rdx                                              ; Vergleich Counter mit Anzahl
    jb @loop                                                 ; wenn kleiner, dann Schleife @loop weiter ausfuehren
    pop rbp
    pop rbx
    pop rsi
    pop rdi
    mov rax,0                                                ; kein Fehler, also 0 in eax laden (Rueckgabe an AutoIt)
    ret
    @end:
    mov rax,rcx                                              ; den Counter nach eax (Rueckgabe an AutoIt)

    pop rbp
    pop rbx
    pop rsi
    pop rdi



    ret
#ce CheckBuffer64


#cs FillBuffer64
    Use64                                                    ; 64Bit Modus!

    push rdi                                                 ;nichtflüchtige register retten
    push rsi
    push rbx
    push rbp


    ;~     mov esi,[esp+4]                                     ; esi = Buffer (Pointer)
    ;~     mov edx,dword[esp+8]                                ; edx = Anzahl
    ;~     mov eax,dword[esp+12]                               ; eax = Startwert

    ;bei fastCall werden die ersten 4 Parameter in Registern rcx, rdx, r8 und r9 übergeben...
    mov rsi,rcx
    mov rdx,rdx
    shr rdx,1
    mov rax,r8

    xor rcx,rcx                                              ; ecx = counter (auf 0 setzen)
    @loop:                                                   ; Hauptschleife
    mov qword[rsi+rcx*8],rax                                 ; den Wert von eax in den Buffer schreiben
    xor rax,0xffffffffffffffff                               ; eax invertieren
    inc rcx                                                  ; counter++
    mov qword[rsi+rcx*8],rax                                 ; den invertierten Wert von eax in den Buffer schreiben
    xor rax,0xffffffffffffffff                               ; eax wieder zurueck invertieren
    inc rax                                                  ; eax++
    inc rcx                                                  ; counter++
    cmp rcx,rdx                                              ; Vergleich Counter mit Anzahl
    jb @loop                                                 ; wenn kleiner, dann Schleife @loop weiter ausfuehren


    pop rbp
    pop rbx
    pop rsi
    pop rdi

    ret
#ce FillBuffer64


Global $__g_bASMCode
Global $__g_bASMCode2


If @AutoItX64 Then  ;64Bit
    $__g_bASMCode = "0x575653554889CE4889D248D1EA4C89C04831C9488904CE4883F0FF48FFC1488904CE4883F0FF48FFC048FFC14839D172E25D5B5E5FC3" ; FillBuffer-ASM-Code als Binaercode
    $__g_bASMCode2 = "0x575653554889CE4889D248D1EA4C89C04831C9488B1CCE4839C3752B4883F0FF48FFC1488B1CCE4839C3751B4883F0FF48FFC048FFC14839D172D85D5B5E5F48C7C000000000C34889C85D5B5E5FC3" ; CheckBuffer-ASM-Code als Binaercode
Else
    $__g_bASMCode = '0x8B7424048B5424088B44240C31C989048E83F0FF4189048E83F0FF404139D172EDC3' ; FillBuffer-ASM-Code als Binaercode
    $__g_bASMCode2 = '0x8B7424048B5424088B44240C31C98B1C8E39C3751A83F0FF418B1C8E39C3750F83F0FF404139D172E5B800000000C389C8C3' ; CheckBuffer-ASM-Code als Binaercode
EndIf






#Region ASM-Binaercode in den Speicher schreiben
Global Const $__g_iMemSize = StringLen($__g_bASMCode) / 2 - 1 ; Codelaenge ermitteln
Global Const $__g_pMem = _MemVirtualAlloc(0, $__g_iMemSize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE) ; Virtuellen Speicher reservieren
If $__g_pMem = 0 Then Exit MsgBox(16, 'FillBuffer-Error!', "Can't allocate virtual memory!")
Global $__g_tASMCode = DllStructCreate('byte[' & $__g_iMemSize & ']', $__g_pMem) ; Structur fuer den Binaercode erstellen
DllStructSetData($__g_tASMCode, 1, $__g_bASMCode)            ; den Binaercode in die Structur schreiben
Global $__g_pASMCode = DllStructGetPtr($__g_tASMCode)        ; den Pointer der Structur holen
ConsoleWrite(StringFormat('FillBuffer-ASM-Code-Size: %i Bytes\r\n', $__g_iMemSize))

Global Const $__g_iMemSize2 = StringLen($__g_bASMCode2) / 2 - 1 ; Codelaenge ermitteln
Global Const $__g_pMem2 = _MemVirtualAlloc(0, $__g_iMemSize2, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE) ; Virtuellen Speicher reservieren
If $__g_pMem2 = 0 Then Exit MsgBox(16, 'CheckBuffer-Error!', "Can't allocate virtual memory!")
Global $__g_tASMCode2 = DllStructCreate('byte[' & $__g_iMemSize2 & ']', $__g_pMem2) ; Structur fuer den Binaercode erstellen
DllStructSetData($__g_tASMCode2, 1, $__g_bASMCode2)          ; den Binaercode in die Structur schreiben
Global $__g_pASMCode2 = DllStructGetPtr($__g_tASMCode2)      ; den Pointer der Structur holen
ConsoleWrite(StringFormat('CheckBuffer-ASM-Code-Size: %i Bytes\r\n', $__g_iMemSize2))
#EndRegion ASM-Binaercode in den Speicher schreiben

#Region globale Variablen
Global $g_sFlashDrive, $g_sDisplay, $g_sWriteData, $g_sReadData, $g_bStop = False, $g_bStart = False, $g_iBytesDiff, $g_bReadWrite = True
Global Const $__g_iIOBufferSize = 0x4000000                  ; 64 MB I/O Buffer
Global Const $__g_pIOBuffer = _MemVirtualAlloc(0, $__g_iIOBufferSize, $MEM_COMMIT, $PAGE_READWRITE)
If $__g_pIOBuffer = 0 Then Exit MsgBox(16, 'IOBuffer-Error!', "Can't allocate virtual memory!")
Global $__g_tIOBuffer = DllStructCreate('dword[' & $__g_iIOBufferSize / 4 & ']', $__g_pIOBuffer)
ConsoleWrite(StringFormat('IOBuffer-Size: %s\r\n\r\n', _WinAPI_StrFormatByteSize($__g_iIOBufferSize)))
#EndRegion globale Variablen




#Region *** Hauptfenster erstellen ***
Global $hMainGui = GUICreate(StringFormat('%s   v%3.3s (beta)', $sAppTitle, $sAppVersion), 1020, 500)
GUISetOnEvent($GUI_EVENT_CLOSE, '_CloseMainGui')
GUISetBkColor(0xD0D0D0)
GUISetFont(11 / $iAppDPI * 96, 400, 0, 'Verdana')

GUICtrlCreateGroup('Test-Laufwerk auswählen', 5, 10, 240, 70)
GUICtrlSetFont(-1, 10)
Global $idFlashDrive = GUICtrlCreateCombo('', 15, 35, 220, 20, $CBS_DROPDOWNLIST)
GUICtrlSetFont(-1, 14 / $iAppDPI * 96, 400, 0, 'Courier New')
GUICtrlSetData(-1, _ReadFlashDrives())
GUICtrlSetOnEvent(-1, '_SwitchDrive')
GUICtrlCreateGroup('', -99, -99, 1, 1)

GUICtrlCreateGroup('Test-Speicherplatz', 5, 90, 240, 90)
GUICtrlSetFont(-1, 10)
Global $idTestSpaceAll = GUICtrlCreateRadio('gesamter freier Speicher', 15, 115, 220, 20)
GUICtrlSetState(-1, $GUI_CHECKED)
Global $idTestSpaceSel = GUICtrlCreateRadio('nur', 15, 145, 45, 20)
GUICtrlSetOnEvent(-1, '_SelectSpace')
Global $idTestSpaceInp = GUICtrlCreateInput('', 70, 143, 80, 22, BitOR($ES_NUMBER, $ES_RIGHT))
Global $idTestSpaceLabel = GUICtrlCreateLabel('GB', 155, 145, 50, 20)
GUICtrlCreateGroup('', -99, -99, 1, 1)
_CtrlSetState($idTestSpaceAll, $idTestSpaceLabel, $GUI_DISABLE)

GUICtrlCreateGroup('Laufwerksinformationen', 260, 10, 480, 180)
GUICtrlSetFont(-1, 10 / $iAppDPI * 96)
GUICtrlCreateLabel('Bezeichnung:', 280, 35, 120, 20)
GUICtrlSetFont(-1, 12 / $iAppDPI * 96, 400, 2, 'Verdana')
Global $idDriveLabel = GUICtrlCreateLabel('', 410, 35, 300, 20)
GUICtrlCreateLabel('Typ:', 280, 65, 120, 20)
GUICtrlSetFont(-1, 12 / $iAppDPI * 96, 400, 2, 'Verdana')
Global $idDriveTyp = GUICtrlCreateLabel('', 410, 65, 300, 20)
GUICtrlCreateLabel('Anschluß:', 280, 95, 120, 20)
GUICtrlSetFont(-1, 12 / $iAppDPI * 96, 400, 2, 'Verdana')
Global $idBusTyp = GUICtrlCreateLabel('', 410, 95, 300, 20)
GUICtrlCreateLabel('Dateisystem:', 280, 125, 120, 20)
GUICtrlSetFont(-1, 12 / $iAppDPI * 96, 400, 2, 'Verdana')
Global $idFileSystem = GUICtrlCreateLabel('', 410, 125, 300, 20)
GUICtrlCreateLabel('Speicherplatz:', 280, 155, 120, 20)
GUICtrlSetFont(-1, 12 / $iAppDPI * 96, 400, 2, 'Verdana')
Global $idDriveSpace = GUICtrlCreateLabel('', 410, 155, 300, 20)
GUICtrlCreateGroup('', -99, -99, 1, 1)

Global $idProgressGroup = GUICtrlCreateGroup('Fortschritt', 5, 190, 240, 50)
GUICtrlSetFont(-1, 10 / $iAppDPI * 96)
Global $idProgress = GUICtrlCreateLabel('', 15, 212, 90, 20, $SS_RIGHT)
GUICtrlSetFont(-1, 11 / $iAppDPI * 96, 400, 0, 'Verdana')
Global $idProgressAll = GUICtrlCreateLabel('', 110, 212, 120, 20)
GUICtrlSetFont(-1, 11 / $iAppDPI * 96, 400, 0, 'Verdana')
GUICtrlCreateGroup('', -99, -99, 1, 1)

Global $idTestStartRW = GUICtrlCreateButton('Schreib-/Lesetest', 280, 200, 200, 26)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlSetOnEvent(-1, '_StartTest')
Global $idTestStartR = GUICtrlCreateButton('Nur Lesetest', 280, 227, 200, 26)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlSetOnEvent(-1, '_StartTest')
Global $idTestCancel = GUICtrlCreateButton('abbrechen', 520, 200, 200, 53)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlSetOnEvent(-1, '_StopTest')

Global $idTestProgress = GUICtrlCreateGroup('Testverlauf (schreiben)', 5, 250, 735, 245)
GUICtrlSetFont(-1, 10)
Global $idStartTime = GUICtrlCreateLabel('Start:', 140, 270, 140, 20)
GUICtrlSetFont(-1, 11 / $iAppDPI * 96, 400, 0, 'Verdana')
Global $idEndTime = GUICtrlCreateLabel('Ende:', 437, 270, 140, 20, $SS_RIGHT)
GUICtrlSetFont(-1, 11 / $iAppDPI * 96, 400, 0, 'Verdana')
Global $idTestDisplay = GUICtrlCreatePic('', 100, 290, 512, 160)
Global $aidTestLabel[7]
For $i = 0 To UBound($aidTestLabel) - 1
    $aidTestLabel[$i] = GUICtrlCreateLabel('0 MB/s', 15, 440 - $i * 26, 70, 16, $SS_RIGHT)
    GUICtrlSetFont(-1, 9 / $iAppDPI * 96, 400, 0, 'Verdana')
Next
Global $aidTestTime[11]
For $i = 0 To 8
    $aidTestTime[$i] = GUICtrlCreateLabel($i * 2, 85 + $i * 64, 460, 32, 16, $SS_CENTER)
    GUICtrlSetFont(-1, 9 / $iAppDPI * 96, 400, 0, 'Verdana')
Next
For $i = 0 To 512 Step 8
    GUICtrlCreateLabel('', 100 + $i, 450, 1, 5 + (Mod($i, 16) ? 0 : 5), $SS_BLACKFRAME)
Next
GUICtrlCreateLabel('Speicherplatz (in GigaByte)', 280, 474, 200, 16)
GUICtrlSetFont(-1, 9 / $iAppDPI * 96, 400, 0, 'Verdana')

Global $idSpeedMax = GUICtrlCreateLabel('< 0.0 MB/s', 625, 296, 100, 20)
GUICtrlSetFont(-1, 9 / $iAppDPI * 96, 400, 0, 'Verdana')
Global $idSpeedAvg = GUICtrlCreateLabel('Ø 0.0 MB/s', 625, 360, 100, 20)
GUICtrlSetFont(-1, 9 / $iAppDPI * 96, 400, 0, 'Verdana')
Global $idSpeedMin = GUICtrlCreateLabel('> 0.0 MB/s', 625, 426, 100, 20)
GUICtrlSetFont(-1, 9 / $iAppDPI * 96, 400, 0, 'Verdana')
GUICtrlCreateGroup('', -99, -99, 1, 1)

GUICtrlCreateGroup('Testbericht', 750, 10, 265, 485)
GUICtrlSetFont(-1, 10 / $iAppDPI * 96)
Global $idConsole = GUICtrlCreateEdit('', 760, 30, 245, 455, BitOR($ES_READONLY, $ES_WANTRETURN, $WS_VSCROLL, $ES_AUTOVSCROLL))
GUICtrlSetFont(-1, 9 / $iAppDPI * 96, 400, 0, 'Verdana')
GUICtrlSetBkColor(-1, 0xFFFFFF)
GUICtrlCreateGroup('', -99, -99, 1, 1)
#EndRegion *** Hauptfenster erstellen ***

GUISetState(@SW_SHOW, $hMainGui)
GUIRegisterMsg($WM_COMMAND, '_WM_COMMAND')
GUIRegisterMsg($WM_DEVICECHANGE, '_WM_DEVICECHANGE')
HotKeySet('{ESC}', '_StopTest')
_DrawPath($idTestDisplay, '')
While Sleep(50)
    If $g_bStart Then _RunTest()
WEnd

Func _OnExit()
    If $__g_pMem > 0 Then _MemVirtualFree($__g_pMem, $__g_iMemSize, $MEM_DECOMMIT)
    If $__g_pMem2 > 0 Then _MemVirtualFree($__g_pMem2, $__g_iMemSize, $MEM_DECOMMIT)
    If $__g_pIOBuffer > 0 Then _MemVirtualFree($__g_pIOBuffer, $__g_iIOBufferSize, $MEM_DECOMMIT)
    _GDIPlus_Shutdown()
EndFunc                                                      ;==>_OnExit

Func _CloseMainGui()
    GUIDelete($hMainGui)
    Exit
EndFunc                                                      ;==>_CloseMainGui

Func _SelectSpace()
    GUICtrlSetState($idTestSpaceInp, $GUI_FOCUS)
EndFunc                                                      ;==>_SelectSpace

Func _CtrlSetState($iStart, $iEnd, $iState)
    For $iCtrl = $iStart To $iEnd
        GUICtrlSetState($iCtrl, $iState)
    Next
EndFunc                                                      ;==>_CtrlSetState

Func _StopTest()
    GUICtrlSetState($idTestCancel, $GUI_DISABLE)
    $g_bStop = True
EndFunc                                                      ;==>_StopTest

Func _StartTest()
    $g_bReadWrite = (@GUI_CtrlId == $idTestStartRW)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $g_bReadWrite = ' & $g_bReadWrite & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    $g_bStart = True
EndFunc                                                      ;==>_StartTest

Func _ConsoleWrite($sMsg)
    _GUICtrlEdit_AppendText($idConsole, $sMsg)
EndFunc                                                      ;==>_ConsoleWrite

Func _SaveTestData()
    Local $hFile, $sFilename
    $sFilename = StringFormat('%s\\results\\%04i_%02i_%02i__%02i_%02i_%02i.ft', @ScriptDir, @YEAR, @MON, @MDAY, @HOUR, @MIN, @SEC)
    $hFile = FileOpen($sFilename, BitOR($FO_UTF8, $FO_CREATEPATH, $FO_OVERWRITE))
    If $hFile = -1 Then Return
    FileWrite($hFile, GUICtrlRead($idConsole))
    FileWriteLine($hFile, 'Daten vom Schreibtest:')
    FileWriteLine($hFile, $g_sWriteData)
    FileWriteLine($hFile, 'Daten vom Lesetest:')
    FileWriteLine($hFile, $g_sReadData)
    FileClose($hFile)
EndFunc                                                      ;==>_SaveTestData

Func _RunTest()
    Local $aSpace, $iTestCount, $sTestFile, $sTestDir, $iMillis, $hFile, $nBytes, $iBytesDiff, $iDiff, $aRet, $iError, $iTimer, $iTimer1, $iTimer2
    Local $tBuffer, $pBuffer, $iBufferSize
    $sTestDir = $g_sFlashDrive & '\FlashTest\'
    If Not FileExists($sTestDir) Then                        ; wenn Verzeichnis nicht vorhanden, dann erstellen
        If Not DirCreate($sTestDir) Then                     ; wenn das erstellen nicht geklappt hat, dann...
            _ConsoleWrite(StringFormat('Fehler: "Konnte Verzeichnis nicht erstellen!"\r\n'))
            Return
        EndIf
    EndIf
    GUICtrlSetState($idTestStartRW, $GUI_DISABLE)
    GUICtrlSetState($idTestStartR, $GUI_DISABLE)
    GUICtrlSetData($idConsole, '')
    _ConsoleWrite(StringFormat('%s v%3.3s (beta)\r\n', $sAppTitle, $sAppVersion))
    _ConsoleWrite(StringFormat('Starte Test mit Laufwerk "%s"\r\n', $g_sFlashDrive))
    _ConsoleWrite(StringFormat('Anschluss: %s\r\n', GUICtrlRead($idBusTyp)))
    _ConsoleWrite(StringFormat('Dateisystem: %s\r\n', GUICtrlRead($idFileSystem)))
    _ConsoleWrite(StringFormat('Speicher: %s\r\n', GUICtrlRead($idDriveSpace)))
    _DrawPath($idTestDisplay, '')
    $g_bStart = False
    $g_sWriteData = ''
    $g_sReadData = ''
    $g_iBytesDiff = 0
    GUICtrlSetState($idTestCancel, $GUI_ENABLE)
    GUICtrlSetData($idStartTime, 'Start: ' & StringRight(_NowCalc(), 8))
    GUICtrlSetData($idEndTime, 'Ende:')
    GUICtrlSetData($idProgress, _WinAPI_StrFormatByteSize(0))
    If $g_bReadWrite Then
        GUICtrlSetData($idProgressGroup, 'Fortschritt (schreiben)')
        GUICtrlSetData($idTestProgress, 'Testverlauf (schreiben)')
        $aSpace = _WinAPI_GetDiskFreeSpaceEx($g_sFlashDrive)
        If BitAND(GUICtrlRead($idTestSpaceSel), $GUI_CHECKED) Then
            $iTestCount = GUICtrlRead($idTestSpaceInp)
            If $iTestCount > Int($aSpace[2] / 1073741824) Then $iTestCount = Int($aSpace[2] / 1073741824)
        Else
            $iTestCount = Int($aSpace[2] / 1073741824)       ; Anzahl der vollen 1-GB-Dateien
        EndIf
    Else
        Local $aFiles = _FileListToArray($sTestDir, '*.ft', $FLTA_FILES)
        If @error Then Return
        $iTestCount = $aFiles[0]
    EndIf
    GUICtrlSetData($idProgressAll, ' von ' & _WinAPI_StrFormatByteSize($iTestCount * 1073741824))
    _ConsoleWrite(StringFormat('Zum Test ausgewählt: %s\r\n', _WinAPI_StrFormatByteSize($iTestCount * 1073741824)))
    $iBufferSize = $__g_iIOBufferSize
    #Region Testdaten schreiben
    If $g_bReadWrite Then
        $iTimer = _WinAPI_GetTickCount64()
        $iTimer1 = $iTimer
        For $iCount = 1 To $iTestCount                       ; Anzahl der 1 GB grossen Testdateien
            $sTestFile = StringFormat('%s%06i.ft', $sTestDir, $iCount)
            $hFile = _WinAPI_CreateFileEx($sTestFile, $CREATE_ALWAYS, $GENERIC_WRITE, 0, $FILE_FLAG_NO_BUFFERING)
            If $hFile = 0 Then
                _ConsoleWrite(StringFormat('Fehler: "Konnte Datei "%s" nicht öffnen!"\r\n', $sTestFile))
                ContinueLoop
            Else
                $iTimer2 = _WinAPI_GetTickCount64()
                For $i = 0 To Int((0x40000000 - $iBufferSize) / $iBufferSize) ; 1024 MB (1 GB)
                    $aRet = DllCallAddress('uint_ptr:cdecl', $__g_pASMCode, 'uint_ptr', $__g_pIOBuffer, 'uint_ptr', $iBufferSize / 4, 'uint_ptr', $iBufferSize * $i) ; ASM-Routine fuellt den Buffer


                    If Not _WinAPI_WriteFile($hFile, $__g_pIOBuffer, $iBufferSize, $nBytes) Then ; der Buffer wird in die Datei geschrieben
                        _ConsoleWrite(StringFormat('Fehler: "Ein Teil (%s) der Datei konnte nicht geschrieben werden!"\r\n', Hex($iBufferSize * $i, 8)))
                        _WinAPI_CloseHandle($hFile)
                        ExitLoop 2
                    EndIf
                    $g_iBytesDiff += $nBytes
                    $iMillis = _WinAPI_GetTickCount64()
                    $iDiff = $iMillis - $iTimer2
                    $g_sWriteData &= ($nBytes / 1048576) / ($iDiff / 1000) & '|'
                    If $iMillis - $iTimer1 > 500 Then
                        $iTimer1 = $iMillis
                        GUICtrlSetData($idProgress, _WinAPI_StrFormatByteSize($g_iBytesDiff))
                        GUICtrlSetData($idSpeedAvg, StringFormat('Ø %.1f MB/s', ($g_iBytesDiff / 1048576) / (($iMillis - $iTimer) / 1000)))
                    EndIf
                    _DrawPath($idTestDisplay, $g_sWriteData, 0xFF6622)
                    $iTimer2 = $iMillis
                    If $g_bStop Then
                        _WinAPI_CloseHandle($hFile)
                        ExitLoop 2
                    EndIf
                Next
                _WinAPI_FlushFileBuffers($hFile)
                _WinAPI_CloseHandle($hFile)
            EndIf
        Next
        If $g_bStop Then
            _ConsoleWrite(StringFormat('Schreibtest abgebrochen.\r\n'))
            GUICtrlSetData($idEndTime, 'Ende: ' & StringRight(_NowCalc(), 8))
            $g_bStop = False
            $tBuffer = 0
            MsgBox(0, $sAppTitle, 'Schreibtest abgebrochen!', 0, $hMainGui)
            GUICtrlSetState($idTestStartRW, $GUI_ENABLE)
            GUICtrlSetState($idTestStartR, FileExists($g_sFlashDrive & '\FlashTest\000001.ft') ? $GUI_ENABLE : $GUI_DISABLE)
            Return
        EndIf
        Local $iTestSize = $iTestCount * 1073741824, $iTestMillis = _WinAPI_GetTickCount64() - $iTimer
        _ConsoleWrite(StringFormat('Zeit %s\r\nSchreibrate %s\r\n', _Ticks2Time($iTestMillis), GUICtrlRead($idSpeedAvg)))
        GUICtrlSetData($idProgress, _WinAPI_StrFormatByteSize($iTestSize))
        MsgBox(0, $sAppTitle, 'Schreibtest beendet!' & @CRLF & 'Mit Lesetest beginnen...', 3, $hMainGui)
        #EndRegion Testdaten schreiben
    EndIf
    #Region Testdaten lesen
    _DrawPath($idTestDisplay, '')
    GUICtrlSetData($idProgressGroup, 'Fortschritt (lesen)')
    GUICtrlSetData($idTestProgress, 'Testverlauf (lesen)')
    GUICtrlSetData($idProgress, _WinAPI_StrFormatByteSize(0))
    $g_iBytesDiff = 0
    $iTimer = _WinAPI_GetTickCount64()
    $iTimer1 = $iTimer
    For $iCount = 1 To $iTestCount                           ; Anzahl der 1 GB grossen Testdateien
        $sTestFile = StringFormat('%s%06i.ft', $sTestDir, $iCount)
        $hFile = _WinAPI_CreateFileEx($sTestFile, $OPEN_EXISTING, $GENERIC_READ, 0, $FILE_FLAG_NO_BUFFERING)
        If $hFile = 0 Then
            _ConsoleWrite(StringFormat('Fehler: "Konnte Datei "%s" nicht öffnen!"\r\n', $sTestFile))
            ContinueLoop
        Else
            $iTimer2 = _WinAPI_GetTickCount64()
            $iBytesDiff = 0
            For $i = 0 To (0x40000000 - $iBufferSize) / $iBufferSize ; 1024 MB (1 GB)
                If Not _WinAPI_ReadFile($hFile, $__g_pIOBuffer, $iBufferSize, $nBytes) Then ; ein Teil wird aus der Datei gelesen
                    _ConsoleWrite(StringFormat('Fehler: "Ein Teil (%s) der Datei konnte nicht gelesen werden!"\r\n', Hex($iBufferSize * $i, 8)))
                    _WinAPI_CloseHandle($hFile)
                    ExitLoop 2
                EndIf


                $aRet = DllCallAddress('UINT_PTR:cdecl', $__g_pASMCode2, 'UINT_PTR', $__g_pIOBuffer, 'UINT_PTR', $iBufferSize / 4, 'UINT_PTR', $iBufferSize * $i) ; ASM-Routine testet den Buffer

                If $aRet[0] Then                             ; die Rueckgabe der ASM-Routine enthaelt das fehlerhafte DWORD oder "0" fuer kein Fehler
                    _ConsoleWrite(StringFormat('Fehler: "Fehlerhaftes DWORD an Adresse: %s"\r\n', Hex($iBufferSize * $i + $aRet[0] * 4, 8)))
                    $iError += 1
                EndIf
                $g_iBytesDiff += $nBytes
                $iMillis = _WinAPI_GetTickCount64()
                $iDiff = $iMillis - $iTimer2
                $g_sReadData &= ($nBytes / 1048576) / ($iDiff / 1000) & '|'
                If $iMillis - $iTimer1 > 500 Then
                    $iTimer1 = $iMillis
                    GUICtrlSetData($idProgress, _WinAPI_StrFormatByteSize($g_iBytesDiff))
                    GUICtrlSetData($idSpeedAvg, StringFormat('Ø %.1f MB/s', ($g_iBytesDiff / 1048576) / (($iMillis - $iTimer) / 1000)))
                EndIf
                _DrawPath($idTestDisplay, $g_sReadData, 0x2266FF)
                $iTimer2 = $iMillis
                If $g_bStop Then
                    _WinAPI_CloseHandle($hFile)
                    ExitLoop 2
                EndIf
            Next
            _WinAPI_CloseHandle($hFile)
            If $iError Then
                _ConsoleWrite(StringFormat('Achtung! Das deutet auf einen defekten Datenträger hin!\r\n'))
                $iError = 0
            EndIf
        EndIf
    Next
    #EndRegion Testdaten lesen
    If $g_bStop Then
        _ConsoleWrite(StringFormat('Lesetest abgebrochen.\r\n'))
        GUICtrlSetData($idEndTime, 'Ende: ' & StringRight(_NowCalc(), 8))
        $g_bStop = False
        $tBuffer = 0
        MsgBox(0, $sAppTitle, 'Lesetest abgebrochen!', 0, $hMainGui)
        GUICtrlSetState($idTestStartRW, $GUI_ENABLE)
        GUICtrlSetState($idTestStartR, FileExists($g_sFlashDrive & '\FlashTest\000001.ft') ? $GUI_ENABLE : $GUI_DISABLE)
        Return
    EndIf
    Local $iTestSize = $iTestCount * 1073741824, $iTestMillis = _WinAPI_GetTickCount64() - $iTimer
    _ConsoleWrite(StringFormat('Zeit %s\r\nLeserate %s\r\n', _Ticks2Time($iTestMillis), GUICtrlRead($idSpeedAvg)))
    _ConsoleWrite(StringFormat('Test beendet.\r\n'))
    GUICtrlSetData($idProgress, _WinAPI_StrFormatByteSize($iTestSize))
    GUICtrlSetData($idEndTime, 'Ende: ' & StringRight(_NowCalc(), 8))
    _SaveTestData()
    GUICtrlSetState($idTestStartRW, $GUI_ENABLE)
    GUICtrlSetState($idTestStartR, FileExists($g_sFlashDrive & '\FlashTest\000001.ft') ? $GUI_ENABLE : $GUI_DISABLE)
    GUICtrlSetState($idTestCancel, $GUI_DISABLE)
    $g_bStop = False
    $tBuffer = 0
EndFunc                                                      ;==>_RunTest

Func _Ticks2Time($iTicks)                                    ; diese Funktion, wandelt die Millisekunden (Ticks) in "hh:mm:ss.ms" um
    Local $iH, $iM, $iS, $aTime
    $iH = Int($iTicks / 3600000)                             ; Integerwert (Ganzzahl) der Division durch 3600000 entspricht den Stunden
    $iTicks = Mod($iTicks, 3600000)                          ; Modulo (Restwert) der Division durch 3600000 sind die restlichen Millisekunden
    $iM = Int($iTicks / 60000)                               ; Integerwert (Ganzzahl) der Division durch 60000 entspricht den Minuten
    $iTicks = Mod($iTicks, 60000)                            ; Modulo (Restwert) der Division durch 60000 sind die restlichen Millisekunden
    $iS = Int($iTicks / 1000)                                ; Integerwert (Ganzzahl) der Division durch 1000 entspricht den Sekunden
    $iTicks = Mod($iTicks, 1000)                             ; Modulo (Restwert) der Division durch 1000 sind die restlichen Millisekunden
    Return StringFormat('%02i:%02i:%02i.%03i', $iH, $iM, $iS, $iTicks)
EndFunc                                                      ;==>_Ticks2Time

Func _DrawPath($iCtrl, $sData, $iColor = 0x4488EE)
    Local Static $iYValOld = 0, $iMaxOld, $iMinOld, $iTimeOld = 8
    If $sData = '' Then
        For $i = 0 To UBound($aidTestTime) - 1
            GUICtrlSetData($aidTestTime[$i], $i * 4)
        Next
        $iTimeOld = 8
    EndIf
    Local $aData, $iCount, $iMax, $iMin, $iStart = Ceiling(($iTimeOld - 8) * 64), $aPos, $iY, $iYVal, $hBitmap, $hGraphic, $hPen, $hPenGrey, $hBMP, $hPrevImage
    $aPos = ControlGetPos($hMainGui, '', $iCtrl)
    $hBitmap = _GDIPlus_BitmapCreateFromScan0($aPos[2], $aPos[3])
    $hGraphic = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsClear($hGraphic, 0xFFFFFFFF)
    $hPen = _GDIPlus_PenCreate(0xFF000000 + $iColor, 2)
    $hPenGrey = _GDIPlus_PenCreate(0xFFAAAAAA, 1)
    $aData = StringSplit(StringTrimRight($sData, 1), '|', $STR_NOCOUNT)
    $iCount = UBound($aData)
    If Ceiling($iCount / 64) > $iTimeOld Then
        $iTimeOld = Ceiling($iCount / 64)
        For $i = 0 To UBound($aidTestTime) - 1
            GUICtrlSetData($aidTestTime[$i], ($iTimeOld - 8 + $i) * 4)
        Next
        $iCount -= 64
    EndIf
    $iCount -= $iStart
    $iMax = _ArrayMax($aData, 1)
    If $iMax <> $iMaxOld Then
        $iMaxOld = $iMax
        GUICtrlSetData($idSpeedMax, StringFormat('< %.1f MB/s', $iMax))
    EndIf
    $iMin = _ArrayMin($aData, 1)
    If $iMin <> $iMinOld Then
        $iMinOld = $iMin
        GUICtrlSetData($idSpeedMin, StringFormat('> %.1f MB/s', $iMin))
    EndIf
    $iY = $aPos[3]
    $iYVal = $iY / $iMax
    If $iYVal <> $iYValOld Then
        $iYValOld = $iYVal
        For $i = 0 To UBound($aidTestLabel) - 1
            GUICtrlSetData($aidTestLabel[$i], Ceiling($iMax / 6 * $i) & ' MB/s')
        Next
    EndIf
    For $iX = 0 To $iCount - 1
        _GDIPlus_GraphicsDrawLine($hGraphic, $iX, $iY, $iX, $iY - Int($iYVal * $aData[$iStart + $iX]), $hPen)
    Next
    For $i = 1 To UBound($aidTestLabel) - 2
        _GDIPlus_GraphicsDrawLine($hGraphic, 0, $i * 26, $aPos[2], $i * 26, $hPenGrey)
    Next
    $hBMP = _GDIPlus_BitmapCreateDIBFromBitmap($hBitmap)
    $hPrevImage = _SendMessage(GUICtrlGetHandle($iCtrl), $STM_SETIMAGE, $IMAGE_BITMAP, $hBMP)
    If $hPrevImage Then _WinAPI_DeleteObject($hPrevImage)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_PenDispose($hPenGrey)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_BitmapDispose($hBitmap)
EndFunc                                                      ;==>_DrawPath

Func _SwitchDrive()
    Local Const $aBusType = StringSplit('UNKNOWN,SCSI,ATAPI,ATA,1394,SSA,FIBRE,USB,RAID,ISCSI,SAS,SATA,SD,MMC', ',', $STR_NOCOUNT)
    Local $sDrive, $aDriveNr, $sLabel, $sTyp, $aSpace, $sTotal, $sFree, $iBus
    $sDrive = StringLeft(GUICtrlRead($idFlashDrive), 2)
    If $sDrive = '' Then Return
    _CtrlSetState($idTestSpaceAll, $idTestSpaceLabel, $GUI_ENABLE)
    $g_sFlashDrive = $sDrive
    $aDriveNr = _WinAPI_GetDriveNumber($sDrive)
    $sLabel = DriveGetLabel($g_sFlashDrive)
    GUICtrlSetData($idDriveLabel, StringFormat('"%s"', $sLabel = '' ? '<keine>' : $sLabel))
    Switch DriveGetType($g_sFlashDrive)
        Case 'Fixed'
            $sTyp = 'Lokaler Datenträger'
        Case 'Removable'
            $sTyp = 'Wechseldatenträger'
    EndSwitch
    GUICtrlSetData($idDriveTyp, StringFormat('%s', $sTyp))
    GUICtrlSetData($idFileSystem, StringFormat('%s', DriveGetFileSystem($g_sFlashDrive)))
    $aSpace = _WinAPI_GetDiskFreeSpaceEx($g_sFlashDrive)
    $sTotal = _WinAPI_StrFormatByteSize($aSpace[1])
    $sFree = _WinAPI_StrFormatByteSize($aSpace[2])
    GUICtrlSetData($idDriveSpace, StringFormat('%s von %s frei', $sFree, $sTotal))
    $iBus = _WinAPI_GetDriveBusType($g_sFlashDrive)
    GUICtrlSetData($idBusTyp, $aBusType[$iBus])
    GUICtrlSetState($idTestStartRW, $GUI_ENABLE)
    GUICtrlSetState($idTestStartR, FileExists($g_sFlashDrive & '\FlashTest\000001.ft') ? $GUI_ENABLE : $GUI_DISABLE)
EndFunc                                                      ;==>_SwitchDrive

Func _ReadFlashDrives()
    Local $aDrives = DriveGetDrive($DT_ALL), $sDrives, $aSpace, $sTotal
    For $i = 1 To $aDrives[0]
        Switch DriveGetType($aDrives[$i])
            Case 'Fixed', 'Removable'
                If DriveStatus($aDrives[$i]) = 'READY' Then
                    $aSpace = _WinAPI_GetDiskFreeSpaceEx($aDrives[$i])
                    $sTotal = _WinAPI_StrFormatByteSize($aSpace[1])
                    $sDrives &= StringFormat('%s   %12s|', StringUpper($aDrives[$i]), $sTotal)
                EndIf
        EndSwitch
    Next
    Return StringTrimRight($sDrives, 1)
EndFunc                                                      ;==>_ReadFlashDrives

Func _WM_COMMAND($hWnd, $Msg, $wParam, $lParam)
    #forceref $hWnd, $Msg, $lParam
    Local $iIDFrom, $iCode
    $iIDFrom = _WinAPI_LoWord($wParam)
    $iCode = _WinAPI_HiWord($wParam)
    Switch $iIDFrom
        Case $idTestSpaceInp
            Switch $iCode
                Case $EN_SETFOCUS
                    GUICtrlSetState($idTestSpaceSel, $GUI_CHECKED)
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc                                                      ;==>_WM_COMMAND

Func _WM_DEVICECHANGE($hWnd, $Msg, $wParam, $lParam)
    #forceref $hWnd, $Msg, $lParam
    Local $tDEV_BROADCAST_HDR, $tDEV_BROADCAST_VOLUME, $iBitmask, $sDrive
    Switch $wParam
        Case $DBT_DEVICEARRIVAL
            $tDEV_BROADCAST_HDR = DllStructCreate($tagDEV_BROADCAST_HDR, $lParam)
            Switch DllStructGetData($tDEV_BROADCAST_HDR, 'dbch_devicetype')
                Case $DBT_DEVTYP_VOLUME
                    $tDEV_BROADCAST_VOLUME = DllStructCreate($tagDEV_BROADCAST_VOLUME, $lParam)
                    $iBitmask = DllStructGetData($tDEV_BROADCAST_VOLUME, 'dbcv_unitmask')
                    $sDrive = _BitMaskToDriveLetter($iBitmask)
                    GUICtrlSetData($idFlashDrive, '')
                    GUICtrlSetData($idFlashDrive, _ReadFlashDrives())
            EndSwitch
        Case $DBT_DEVICEREMOVECOMPLETE
            $tDEV_BROADCAST_HDR = DllStructCreate($tagDEV_BROADCAST_HDR, $lParam)
            Switch DllStructGetData($tDEV_BROADCAST_HDR, 'dbch_devicetype')
                Case $DBT_DEVTYP_VOLUME
                    $tDEV_BROADCAST_VOLUME = DllStructCreate($tagDEV_BROADCAST_VOLUME, $lParam)
                    $iBitmask = DllStructGetData($tDEV_BROADCAST_VOLUME, 'dbcv_unitmask')
                    $sDrive = _BitMaskToDriveLetter($iBitmask)
                    GUICtrlSetData($idFlashDrive, '')
                    GUICtrlSetData($idFlashDrive, _ReadFlashDrives())
                    If DriveStatus($g_sFlashDrive) <> 'READY' Then
                        $g_sFlashDrive = ''
                        GUICtrlSetState($idTestStartRW, $GUI_DISABLE)
                        _CtrlSetState($idTestSpaceAll, $idTestSpaceLabel, $GUI_DISABLE)
                        GUICtrlSetData($idDriveLabel, '')
                        GUICtrlSetData($idDriveTyp, '')
                        GUICtrlSetData($idFileSystem, '')
                        GUICtrlSetData($idDriveSpace, '')
                        GUICtrlSetData($idBusTyp, '')
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc                                                      ;==>_WM_DEVICECHANGE

Func _BitMaskToDriveLetter($iBitmask)
    For $i = 0 To 25
        If BitAND($iBitmask, 2 ^ $i) Then Return Chr(97 + $i) & ':'
    Next
    Return
EndFunc                                                      ;==>_BitMaskToDriveLetter


