Leaderboard
Popular Content
Showing content with the highest reputation on 12/31/2014 in all areas
-
After struggling with asm code, then here's the result. Good news, it works soooooo smoothly. My workaround is, using ASM code instead of DllCallback. So, it will run fine even with multi-thread environment. Then, the interesting part, is how to dispatch the message to the script's main thread. We know that the GUI application is single thread. It pooling windows messages from queue and process it, in main thread. Then, we can build a custom window message with all the parameters we get in the ASM callback, then send post the message to a GUI, and let it process that message in its thread It's the idea. I'm not so familiar with ASM, so implement it costs quite a lot of time. In addition, since your computer is x64, we must maintain both version, one for x86, one for x64, since the ASM cannot use the same version as our AutoIt But, now, the result is so nice. Here is the working script, modified from Danyfirex's one: #include <Z:\MIDIFunctions.au3> #include <Z:\MIDIConstants.au3> HotKeySet("{ESC}", "_Terminate") #include <WinAPI.au3> #include <WindowsConstants.au3> #include <Memory.au3> ; This is the custom window message. We need a GUIRegisterMsg to handle it Global Const $MIDI_WMCALLBACK = $WM_USER + 0x2015; ; MidiCallback params, sent as lParam of the custom message Global Const $MIDI_tagCALLBACKPARAMS = 'HANDLE midi;DWORD_PTR instance;DWORD_PTR param1;DWORD_PTR param2' #Region Memory Helper Methods, to avoid Access Violation when DEP is enabled (default enabled in x64) Func _MemVirtualProtect($pAddress, $dwSize, $dwNewProtect) Local $dwOldProtect Local $aRet = DllCall('kernel32.dll', 'BOOL', 'VirtualProtect', 'PTR', $pAddress, 'ULONG_PTR', $dwSize, 'DWORD', $dwNewProtect, 'DWORD*', $dwOldProtect) If (@error) Then Return SetError(@error, @extended, 0) If (Not $aRet[0]) Then Return SetError(1000, _WinAPI_GetLastError(), 0) Return SetExtended($aRet[4], $aRet[0]) EndFunc Func _Mem_FlushInstructionCache($hProcess, $pAddress, $dwSize) If (Not($hProcess)) Then $hProcess = _WinAPI_GetModuleHandle(0) Local $aRet = DllCall('kernel32.dll', 'BOOL', 'FlushInstructionCache', 'HANDLE', $hProcess, 'PTR', $pAddress, 'ULONG_PTR', $dwSize) If (@error) Then Return SetError(@error, @extended, 0) If (Not $aRet[0]) Then Return SetError(1000, _WinAPI_GetLastError(), 0) Return $aRet[0] EndFunc #EndRegion ; Helper method for building the opcode ; When $sOpCode is 0. it populate all stored data and return the struct variable Func __MidiFunction_OpCodePush($sOpCode, $v1 = 0x90, $v2 = 0x90, $v3 = 0x90, $v4 = 0x90, $v5 = 0x90) Static $tagOpCode = 0 Static $nBound = 100 Static $nCounter = 0 Static $aOpCode[$nBound] If (IsString($sOpCode)) Then ; Set opcode If ($nCounter = 0) Then $tagOpCode = 'align 1;' $tagOpCode &= $sOpCode Local $nBaseCounter = $nCounter $nCounter += @NumParams-1 If ($nCounter > $nBound) Then $nBound = BitShift($nBound, -1) ReDim $aOpCode[$nBound] EndIf For $i = $nBaseCounter To $nCounter Execute('__MidiFunction_ArrayElementAssign($aOpCode, ' & $i & ', $v' & $i-$nBaseCounter+1 & ')') Next Else ; Populate, reset and return Local $tOpCode = DllStructCreate($tagOpCode) Local $bError = @error If (Not $bError) Then For $i = 0 To $nCounter DllStructSetData($tOpCode, $i+1, $aOpCode[$i]) Next EndIf If ($v1) Then $nCounter = 0 If $bError Then Return SetError(1, $bError, -1) Return $tOpCode EndIf EndFunc ; We need this method since we cannot use Execute statement to assign an element of an array Func __MidiFunction_ArrayElementAssign(ByRef $aArray, $nIndex, ByRef $vValue) $aArray[$nIndex] = $vValue EndFunc ; Create and initialize ASM code for MIDI callback function. ; Return a *struct* corresponding to the ASM code, it must then be saved in a *global* variable Func _MidiFunction_CreateMidiCallback() Static $bInit = False Static $pSendMessage = _WinAPI_GetProcAddress(_WinAPI_GetModuleHandle('user32'), 'PostMessageW') If (Not $bInit) Then If @AutoItX64 Then ; Size of params struct = 8 x 4 = 32 ; __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;', 0x48, 0x83, 0xEC, 72) ;sub rsp, 72 ; Assign value for MidiParam struct variable __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;BYTE;', 0x48, 0x8B, 0x44, 0x24, 112) ;mov rax, QWORD PTR dwParam2[rsp] __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;BYTE;', 0x48, 0x89, 0x44, 0x24, 32+24) ;mov QWORD PTR params[rsp+24], rax __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;BYTE;', 0x4C, 0x89, 0x4C, 0x24, 32+16) ;mov QWORD PTR params[rsp+16], r9 __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;BYTE;', 0x4C, 0x89, 0x44, 0x24, 32+8) ;mov QWORD PTR params[rsp+8], r8 __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;BYTE;', 0x48, 0x89, 0x4C, 0x24, 32) ;mov QWORD PTR params[rsp], rcx ; Call SendMessage function __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x44, 0x8B, 0xC2) ;mov r8d, edx __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;BYTE;', 0x4C, 0x8D, 0x4C, 0x24, 32) ;lea r9, QWORD PTR params[rsp] __MidiFunction_OpCodePush('BYTE;UINT uMsg;', 0xBA, $MIDI_WMCALLBACK) ;mov edx, $MIDI_WMCALLBACK __MidiFunction_OpCodePush('BYTE;BYTE;HWND hWnd;', 0x48, 0xB9, 0) ;mov rcx, $hWnd __MidiFunction_OpCodePush('BYTE;BYTE;PTR pSendMsg;', 0x48, 0xB8, $pSendMessage) ;mov rax, USER32.SendMessageW __MidiFunction_OpCodePush('BYTE;BYTE;', 0xFF, 0xD0) ;call rax ; Finalize __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;BYTE;', 0x48, 0x83, 0xC4, 72) ;add rsp, 72 __MidiFunction_OpCodePush('BYTE', 0xC3) ;retn Else ; 'Standard' stack initialization ; params -> ebp-20 __MidiFunction_OpCodePush('BYTE;', 0x55) ;push ebp __MidiFunction_OpCodePush('BYTE;BYTE;', 0x8B, 0xEC) ;mov ebp, esp ; Subtract by size of params struct (16) + size of stack frame ptr (4) __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x83, 0xEC, 20) ;sub esp, 20 ; Assign value for MidiParam struct variable ; MidiParam params = { hMidiIn, dwInstance, dwParam1, dwParam2 }; ; hMidiIn -> ebp+8 ; dwInstance -> ebp+16 ; dwParam1 -> ebp+20 ; dwParam2 -> esp+24 ; params.hMidiIn -> ebp-20 ; params.dwInstance -> ebp-20+4 -> ebp-16 ; params.dwParam1 -> ebp-20+8 -> ebp-12 ; params.dwParam2 -> ebp-20+12 -> ebp-8 __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x8B, 0x45, 8) ;mov eax, DWORD_PTR hMidiIn[ebp] __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x89, 0x45, -20) ;mov DWORD_PTR params[ebp], eax __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x8B, 0x45, 16) ;mov eax, DWORD_PTR dwInstance[ebp] __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x89, 0x45, -16) ;mov DWORD_PTR params[ebp+4], eax __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x8B, 0x45, 20) ;mov eax, DWORD_PTR dwParam1[ebp] __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x89, 0x45, -12) ;mov DWORD_PTR params[ebp+8], eax __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x8B, 0x45, 24) ;mov eax, DWORD_PTR dwParam2[ebp] __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x89, 0x45, -8) ;mov DWORD_PTR params[ebp+12], eax ; Send message ; wMsg -> ebp+12 __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x8D, 0x45, -20) ;lea eax, DWORD PTR params[ebp] __MidiFunction_OpCodePush('BYTE;', 0x50) ;push eax __MidiFunction_OpCodePush('BYTE;BYTE;BYTE;', 0x8B, 0x4D, 12) ;mov ecx, DWORD PTR wMsg[ebp] __MidiFunction_OpCodePush('BYTE;', 0x51) ;push ecx __MidiFunction_OpCodePush('BYTE;UINT uMsg;', 0x68, $MIDI_WMCALLBACK) ;push $MIDI_WMCALLBACK __MidiFunction_OpCodePush('BYTE;HWND hWnd;', 0x68, 0) ;push $hWnd ; Call function __MidiFunction_OpCodePush('BYTE;PTR pSendMsg;', 0xB8, $pSendMessage) ;mov eax, USER32.SendMessageW __MidiFunction_OpCodePush('BYTE;BYTE;', 0xFF, 0xD0) ;call near eax ; 'Standard' finalize __MidiFunction_OpCodePush('BYTE;BYTE;', 0x8B, 0xE5) ;mov esp, ebp __MidiFunction_OpCodePush('BYTE;', 0x5D) ;pop ebp ; Restore 20 bytes subtracted when initialize stack frame __MidiFunction_OpCodePush('BYTE;WORD', 0xC2, 20) ;mov esp, ebp EndIf $bInit = True ; 2nd param set to 0 means not reset struct signature Return __MidiFunction_OpCodePush(0, 0) EndIf ; Populate Return $tagCallback EndFunc ; Register the variable returned by _MidiFunction_CreateMidiCallback() ; Return the callback pointer to pass in _midiInOpen or _midiOutOpen ; hWnd: the window handle receiving callback window message Func _MidiFunction_RegisterCallback(ByRef $tMidiCallback, $hWnd) Local $pCallback = DllStructGetPtr($tMidiCallback) DllStructSetData($tMidiCallback, 'hWnd', $hWnd) ; Set protect value to $PAGE_EXECUTE_READWRITE. ; Old value is $PAGE_READWRITE, which will raise Access Violation when DEP is enabled _MemVirtualProtect($pCallback, DllStructGetSize($tMidiCallback), $PAGE_EXECUTE_READWRITE) ; FlushInstructionCache, better safe than sorry _Mem_FlushInstructionCache(0, $pCallback, DllStructGetSize($tMidiCallback)) Return $pCallback EndFunc ; Method for extract MIDI params from window message's lparam Func _MidiFunction_DecodeCallbackParams($lParam, ByRef $hMidi, ByRef $dwInstance, ByRef $dwParam1, ByRef $dwParam2) Local $tCallbackParams = DllStructCreate($MIDI_tagCALLBACKPARAMS, $lParam) $hMidi = DllStructGetData($tCallbackParams, 1) $dwInstance = DllStructGetData($tCallbackParams, 2) $dwParam1 = DllStructGetData($tCallbackParams, 3) $dwParam2 = DllStructGetData($tCallbackParams, 4) EndFunc ; Method for get MIDI params from window message's lparam ; $sName can be midi, instance, param1, or param2 Func _MidiFunction_GetCallbackParam($lParam, $sName) Local $tCallbackParams = DllStructCreate($MIDI_tagCALLBACKPARAMS, $lParam) Return DllStructGetData($tCallbackParams, $sName) EndFunc ; Create a dummy hidden window to process callback message Global $hwndDummy = GUICreate("TEST") ; The return value of _MidiFunction_CreateMidiCallback *must* be saved in a *global* variable. ; If saved in a function's local variable then all the ASM will gone when the function return due to variable scope Global $tMidiCallback = _MidiFunction_CreateMidiCallback() ; OK, now use GUIRegisterMsg to handle the dispatched callback message GUIRegisterMsg($MIDI_WMCALLBACK, "MidiCallbackProc") Global $iKey = 0 Global $hMIDIIN = _midiInOpen(0, _MidiFunction_RegisterCallback($tMidiCallback, $hwndDummy), 0, 0x30000) If (@error) Then ConsoleWrite("ERROR: " & @error & ' ' & @extended) _midiinreset($hMIDIIN) _midiinstart($hMIDIIN) While 1 Sleep(50) WEnd Func _Terminate() ; Now as we switch to PostMessage, its now safe _MidiInClose($hMIDIIN) ConsoleWrite("Bye!") Exit EndFunc ;==>_Terminate ; This function is not executed in an another thread but in *the main application thread*, so it will not crash! Func MidiCallbackProc($hWnd, $uMsg, $wParam, $lParam) Local $hMidi, $dwInstance, $dwParam1, $dwParam2 ; Extract all the param, use _MidiFunction_DecodeCallbackParams helper function _MidiFunction_DecodeCallbackParams($lParam, $hMidi, $dwInstance, $dwParam1, $dwParam2) ; Then call the MidiIn function, all now is executed in the same thread! MidiIn($hMidi, $wParam, $dwInstance, $dwParam1, $dwParam2) EndFunc Func MidiIn($hMIDIIN222, $wMsg, $dwInstance, $wParam1, $wParam2) ;~ If $wParam1 > 255 Then ;~ $mm = $wParam1 ;~ $on = BitAND($mm, 0x000090) / 16 ;~ If $on = 9 Then ;~ $iKey = BitAND($mm, 0x00ff00) / 256 ;~ ConsoleWrite("!" & $iKey & @CRLF) ;~ EndIf ;~ EndIf Switch $wMsg Case $MIM_OPEN ConsoleWrite("$MIM_OPEN" & @CRLF) Case $MIM_CLOSE ConsoleWrite("$MIM_CLOSE" & @CRLF) $bExitFlag = True Case $MIM_DATA ConsoleWrite("$MIM_DATA, dwInstance= " & $dwInstance & " wParam1= " & $wParam1 & " wParam2= " & $wParam2 & @CRLF) Case $MIM_LONGDATA ConsoleWrite("$MIM_LONGDATA" & @CRLF) Case $MIM_ERROR ConsoleWrite("$MIM_ERROR" & @CRLF) Case $MIM_LONGERROR ConsoleWrite("$MIM_LONGERROR" & @CRLF) Case $MIM_MOREDATA ConsoleWrite("$MIM_MOREDATA" & @CRLF) Case Else ConsoleWrite("wwMsg = unknown" & @CRLF) EndSwitch EndFunc ;==>MidiIn PS: Happy new year Edit: My script has a hidden bug. :blush: The _MidiInClose($hMidi) statement does nothing since I pass null handle to it. Leaving that bug exists cause no harm to your monitoring but may cause memory-leak when your program exits. If I pass the right handle, the script hangs on exits because of the deadlock. It is because the SendMessage function is synchronized call, it waits for the GUI to process message before return, so whatever we do when we still using SendMessage, the _MidiInClose call may have a high change to be called in the middle of our ASM callback, which cause dealock! Switch to PostMessage will fix it, once and forever2 points
-
You can use FileInstall to unpack the 7za.exe in a temporary folder when needed.1 point
-
Find second highest value in a Tab: proper way
member42 reacted to johnmcloud for a topic
Local $Array[10] = [12, 27, 20, 120, 111, 215, 54, 8, 77, 114] Local $iMAX = $Array[0] Local $iMIN = $Array[0] Local $iMAX_Next = $Array[0] For $i = 0 To UBound($Array) - 1 If $Array[$i] < $iMIN Then $iMIN = $Array[$i] If $Array[$i] > $iMAX Then $iMAX_Next = $iMAX $iMAX = $Array[$i] ElseIf $Array[$i] < $iMAX And $Array[$i] > $iMAX_Next Then $iMAX_Next = $Array[$i] EndIf Next ConsoleWrite("Maximum value is : " & $iMAX & @CR) ConsoleWrite("Minimum value is : " & $iMIN & @CR) ConsoleWrite("Second maximum value is : " & $iMAX_Next & @CR)1 point -
? Local $Array[4] = [12, 1, 3, 5] Local $iMax Local $iMax2 For $i = 0 To UBound($Array) - 1 If $Array[$i] > $iMax Then $iMax2 = $iMax $iMax = $Array[$i] ElseIf $Array[$i] > $iMax2 Then $iMax2 = $Array[$i] EndIf Next ConsoleWrite("Second max value : " & $iMax2)1 point