UnavailableUsername Posted December 30, 2014 Share Posted December 30, 2014 Hello, I have posted a question before about handling midi input. I'm a bit further now thanks to a script by "Uwe" . It works now, but always crashes. I have a feeling I'm doing something wrong.. Here's an example: #include <midifunctions.au3> Global $n = 0 Global $cb = DLLCallbackRegister ("MIDIIn", "long", "ptr;int;dword;dword;dword") Global $midi_in = _midiInOpen (0,DllCallbackGetPtr($cb),0,0x30000) _midiinreset($midi_in) _midiinstart($midi_in) While 1 if $n > 0 Then Tooltip("Key number: "& $n,200,200) EndIf sleep(50) Wend Func MidiIn($midi, $msg,$instance,$Param1,$Param2) If $param1> 255 then $mm=$Param1 $on=BitAND($mm,0x000090)/16 If $on = 9 then $n=BitAND($mm,0x00ff00)/256 return($n) Endif EndIf EndFunc It crashes at random times, with random error codes. Sometimes it's just exit code 0. Sometimes it's suddenly missing a variable, and sometimes a red error with negative number (can't recreate that right now) If it's allowed on this forum, i'd like to giveĀ 30 bucks through paypal for the golden solution. (yes I am very desperate.) Thank you, Robin Link to comment Share on other sites More sharing options...
computergroove Posted December 30, 2014 Share Posted December 30, 2014 Does it crash right away or is it running for some time before it crashes (memory leak)? Can you reproduce any error consistently? What exactly does this script do? Get Scite to add a popup when you use a 3rd party UDF -> http://www.autoitscript.com/autoit3/scite/docs/SciTE4AutoIt3/user-calltip-manager.html Link to comment Share on other sites More sharing options...
binhnx Posted December 30, 2014 Share Posted December 30, 2014 I don't have a device to test, but any crash with AutoIt usually is a result of terrible UDF which carelesslyĀ change the type of parameter specified with DllCall (i.e, PTR to INT, HANDLE to LONG, etc...). All those UDF work with x86 Windows version, but hardly crash when switch to Windows x64.Ā If you are using x64 Windows version, so that is the most possible reason makes your script crash. To fix: I don't know what functions in that UDF you are going to use. Assume that you use 3 above functions, then open the UDF, navigate to each function, and change it like this: _midiInOpen (Line 633). Change line 635, 637, 638 from $struct = DllStructCreate("udword") $ret = Dllcall("winmm.dll","long","midiInOpen","ptr",DllStructGetPtr($struct),"int",$devid,"long",$callback,"long",$instance,"long",$flags) if not @error Then toĀ $struct = DllStructCreate("handle") $ret = Dllcall("winmm.dll","long","midiInOpen","ptr",DllStructGetPtr($struct),"uint",$devid,"dword_ptr",$callback,"dword_ptr",$instance,"dword",$flags) If (Not @error And $ret[0] = 0) Then Line 643 to If @error Then Return SetError(@error, 0, -1) Return $ret[0] -Ā _midiinreset (Line 687) andĀ _MidiInStart (Line 698) Change the last parameter type from 'long' to 'handle' 99 little bugs in the code 99 little bugs! Take one down, patch it around 117 little bugs in the code! Link to comment Share on other sites More sharing options...
Danyfirex Posted December 30, 2014 Share Posted December 30, 2014 look into msdn. that callback has no return.Ā I can't help so much because i have no midi device. saludos Ā Danysys.comĀ Ā Ā Ā Ā AutoIt... Ā UDFs:Ā VirusTotal API 2.0 UDFĀ -Ā libZPlay UDFĀ - Apps:Ā Guitar Tab TesterĀ -Ā VirusTotal Hash Checker Examples:Ā Text-to-Speech ISpVoice InterfaceĀ -Ā Get installed applicationsĀ -Ā Enable/Disable Network connectionĀ Ā PrintHookProcĀ -Ā WINTRUSTĀ -Ā Mute Microphone LevelĀ -Ā Get Connected NetWorksĀ -Ā Create NetWork Connection ShortCut Ā Ā Link to comment Share on other sites More sharing options...
Danyfirex Posted December 30, 2014 Share Posted December 30, 2014 can you provideĀ <midifunctions.au3>? Saludos Ā Danysys.comĀ Ā Ā Ā Ā AutoIt... Ā UDFs:Ā VirusTotal API 2.0 UDFĀ -Ā libZPlay UDFĀ - Apps:Ā Guitar Tab TesterĀ -Ā VirusTotal Hash Checker Examples:Ā Text-to-Speech ISpVoice InterfaceĀ -Ā Get installed applicationsĀ -Ā Enable/Disable Network connectionĀ Ā PrintHookProcĀ -Ā WINTRUSTĀ -Ā Mute Microphone LevelĀ -Ā Get Connected NetWorksĀ -Ā Create NetWork Connection ShortCut Ā Ā Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 30, 2014 Author Share Posted December 30, 2014 (edited) @binhnx Thanks a lot for helping me. I see that you used the original midiudf for your fix, no problem, switched to that one. But it still crashes. I indeed use an x64 version of windows. The most frustrating bit is that I can't see where it goes wrong. Here's a small "log". $mm=$Param1 $mm=^ ERROR ->18:44:25 AutoIT3.exe ended.rc:1 >Exit code: 1 Time: 141.322 +>18:51:51 AutoIT3.exe ended.rc:0 >Exit code: 0 Time: 158.625 +>18:52:17 AutoIT3.exe ended.rc:0 >Exit code: 0 Time: 7.523 +>18:55:23 AutoIT3.exe ended.rc:0 >Exit code: 0 Time: 4.516 +>18:56:00 AutoIT3.exe ended.rc:0 >Exit code: 0 Time: 18.120 !>19:00:55 AutoIT3.exe ended.rc:-1073741819 >Exit code: -1073741819 Time: 282.731 +>19:10:31 AutoIT3.exe ended.rc:0 >Exit code: 0 Time: 55.326 Random times, random errors..Ā Do you need any extra info? Thanks. Edited December 30, 2014 by UnavailableUsername Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 30, 2014 Author Share Posted December 30, 2014 @danyfirex It's a UDF by Ascend4nt:Ā Link to comment Share on other sites More sharing options...
Danyfirex Posted December 30, 2014 Share Posted December 30, 2014 (edited) I write this. it should be a working example. (I have no midi device) expandcollapse popup#include "MIDIFunctions.au3" #include "MIDIConstants.au3" HotKeySet("{ESC}", "_Terminate") Global $iKey = 0 Global $hMidiInProc = DllCallbackRegister("MIDIIn", "none", "handle;uint;dword_ptr;dword_ptr;dword_ptr") Global $hMIDIIN = _midiInOpen(0, DllCallbackGetPtr($hMidiInProc), 0, 0x30000) _midiinreset($hMIDIIN) _midiinstart($hMIDIIN) While 1 If $iKey > 0 Then ToolTip("Key number: " & $iKey, 200, 200) EndIf Sleep(50) WEnd Func _Terminate() _MidiInClose($hMIDIIN) DllCallbackFree($hMidiInProc) Exit EndFunc ;==>_Terminate Func MidiIn($hMIDIIN, $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) Case $MIM_DATA ConsoleWrite("$MIM_DATA, dwInstance= " & $dwInstance & " wParam1= " & $wParam2 & " 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 Saludos Edited December 30, 2014 by Danyfirex Ā Danysys.comĀ Ā Ā Ā Ā AutoIt... Ā UDFs:Ā VirusTotal API 2.0 UDFĀ -Ā libZPlay UDFĀ - Apps:Ā Guitar Tab TesterĀ -Ā VirusTotal Hash Checker Examples:Ā Text-to-Speech ISpVoice InterfaceĀ -Ā Get installed applicationsĀ -Ā Enable/Disable Network connectionĀ Ā PrintHookProcĀ -Ā WINTRUSTĀ -Ā Mute Microphone LevelĀ -Ā Get Connected NetWorksĀ -Ā Create NetWork Connection ShortCut Ā Ā Link to comment Share on other sites More sharing options...
binhnx Posted December 30, 2014 Share Posted December 30, 2014 Yes, definitely is the carelessly changing the type, but now it seems not the UDF creator, but you who made mistakes. The post by @Danyfirex should resolve your problem. 99 little bugs in the code 99 little bugs! Take one down, patch it around 117 little bugs in the code! Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 30, 2014 Author Share Posted December 30, 2014 @danyfirex Thank you for the effort. It works, but it also exits.. Ā $MIM_DATA, dwInstance= 0 wParam1= 45380 wParam2= 45380 $MIM_DATA, dwInstance= 0 wParam1= 45382 wParam2= 45382 $MIM_DATA, dwInstance= 0 wParam1= 45512 wParam2= 45512 $MIM_DATA, dwInstance= 0 wParam1= 45583 wParam2= 45583 $MIM_DATA, dwInstance= 0 wParam1= 45594 wParam2= 45594 $MIM_DATA, dwInstance= 0 wParam1= 45751 wParam2= 45751 $MIM_DATA, dwInstance= 0 wParam1= 45769 wParam2= 45769 $MIM_DATA, dwInstance= 0 wParam1= 45800 wParam2= 45800 $MIM_DATA, dwInstance= 0 wParam1= 45819 wParam2= 45819 $MIM_DATA, dwInstance= 0 wParam1= 45825 wParam2= 45825 $MIM_DATA, dwInstance= 0 wParam1= 46044 wParam2= 46044 $MIM_DATA, dwInstance= 0 wParam1= 46086 wParam2= 46086 $MIM_DATA, dwInstance= 0 wParam1= 46126 wParam2= 46126 $MIM_DATA, dwInstance= 0 wParam1= 46173 wParam2= 46173 $MIM_DATA, dwInstance= 0 wParam1= 46206 wParam2= 46206 $MIM_DATA, dwInstance= 0 wParam1= 46270 wParam2= 46270 $MIM_DATA, dwInstance= 0 wParam1= 46274 wParam2= 46274 $MIM_DATA, dwInstance= 0 wParam1= 46281 wParam2= 46281 $MIM_DATA, dwInstance= 0 wParam1= 46368 wParam2= 46368 $MIM_DATA, dwInstance= 0 wParam1= 46467 wParam2= 46467 $MIM_DATA, dwInstance= 0 wParam1= 46495 wParam2= 46495 $MIM_DATA, dwInstance= 0 wParam1= 46626 wParam2= 46626 $MIM_DATA, dwInstance= 0 wParam1= 46630 wParam2= 46630 $MIM_DATA, dwInstance= 0 wParam1= 46705 wParam2= 46705 $MIM_DATA, dwInstance= 0 wParam1= 46722 wParam2= 46722 $MIM_DATA, dwInstance= 0 wParam1= 46753 wParam2= 46753 $MIM_DATA, dwInstance= 0 wParam1= 46773 wParam2= 46773 $MIM_DATA, dwInstance= 0 wParam1= 46801 wParam2= 46801 $MIM_DATA, dwInstance= 0 wParam1= 46934 wParam2= 46934 $MIM_DATA, dwInstance= 0 wParam1= 46947 wParam2= 46947 $MIM_DATA, dwInstance= 0 wParam1= 46972 wParam2= 46972 $MIM_DATA, dwInstance= 0 wParam1= 47174 wParam2= 47174 +>20:23:57 AutoIT3.exe ended.rc:0 >Exit code: 0 Time: 48.277 I have absolutely no clue on how dll calls work.. So sorry if I'm the one who's messing this up. Any more ideas? Link to comment Share on other sites More sharing options...
binhnx Posted December 30, 2014 Share Posted December 30, 2014 (edited) The script by @danyfirex terminate if you press Esc key. Did you accidentallyĀ press it ? Its strange if that script still exits. If you've modified danyfirex's script, plz post your current one. Edited December 30, 2014 by binhnx 99 little bugs in the code 99 little bugs! Take one down, patch it around 117 little bugs in the code! Link to comment Share on other sites More sharing options...
Danyfirex Posted December 30, 2014 Share Posted December 30, 2014 I'll try again later. maybe Autoit is the problem. I'll try a C++ example later when I download a virtual midi device. Ā Saludos Ā Danysys.comĀ Ā Ā Ā Ā AutoIt... Ā UDFs:Ā VirusTotal API 2.0 UDFĀ -Ā libZPlay UDFĀ - Apps:Ā Guitar Tab TesterĀ -Ā VirusTotal Hash Checker Examples:Ā Text-to-Speech ISpVoice InterfaceĀ -Ā Get installed applicationsĀ -Ā Enable/Disable Network connectionĀ Ā PrintHookProcĀ -Ā WINTRUSTĀ -Ā Mute Microphone LevelĀ -Ā Get Connected NetWorksĀ -Ā Create NetWork Connection ShortCut Ā Ā Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 31, 2014 Author Share Posted December 31, 2014 @Danyfirex Thanks again. But I've been testing and thinking. And it seems that the function MidiIn is called an awful lot. Is it possible that the function calls are too fast? Now it is called when a note is on or off, but also when no key is pressed. If it only gets called when a note is on (or off), it should be a lot easier for autoit to process (I think). Is this possible? Robin Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 31, 2014 Author Share Posted December 31, 2014 Oh and another error that occured.. $MIM_DATA, dwInstance= 0 wParam1= 3142 wParam2= 3142 $MIM_DATA, dwInstance= 0 wParam1= 3148 wParam2= 3148 $MIM_DATA, dwInstance= 0 wParam1= 3256 wParam2= 3256 $MIM_DATA, dwInstance= 0 wParam1= 3482 wParam2= 3482 $MIM_DATA, dwInstance= 0 wParam1= 3705 wParam2= 3705 $MIM_OPEN !>13:40:49 AutoIT3.exe ended.rc:-1073741819 >Exit code: -1073741819 Time: 7.989 I've seen this twice now. It suddenly writes $MIM_OPEN again and crashes. Link to comment Share on other sites More sharing options...
binhnx Posted December 31, 2014 Share Posted December 31, 2014 Thank for very useful response, UnavailableUsername, your details at describe the problem helps much. My testing- C program run smoothly. I also find out that, although I set a sleep to make the callback run all-time, IĀ still be able to interact with the GUI.Ā Addition with the remark in MSDN say that "Applications should not call any multimedia functions from inside the callback function, as doing so can cause a deadlock"Ā Then I think the callback is called fromĀ different thread than the main application's thread.Ā A quick test confirm this. Since AutoIt does not support multi-threading, this cause unexpected behaviourĀ (sometimes nothing happens, sometimes crash with normal exit code (0), sometimes crash with interpreted error code (1), and interesting, sometimes crash with Access Violation code -1073741819) @UnavailableUsername Now we have a good news and a bad news. The good news is we found the reason of all these annoy things. The bad news is, we cannot fix it with AutoIt, since AutoIt don't and will never support multi-threading. It's a pain in the ass, but, from theĀ optimistic view, it may be a good time to start learning a new language I think .NET is quite easy to learn.Ā But if your application is simple enough, even C is simpler than .NET So take a deep breath, use your 30 bucks to buys some good drinks and cookies then start from scratch.Ā 99 little bugs in the code 99 little bugs! Take one down, patch it around 117 little bugs in the code! Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 31, 2014 Author Share Posted December 31, 2014 @binhnx Thank you for the explanation. I've already written the rest of the code. It's 3000 lines, and all is working perfectly except for this bit. I understand that autoit does not have all the possibilities I need, but I really enjoyed using it for the past years. Since this is probably the only script I will be using my keyboard with, it seems a bit extreme to learn a new language just to monitor my input. I'm not really a hardcore coder, I just sometimes like to create a litte piece of software when I need it and it doesn't exist. Maybe I can find a software that can monitor my keys and than ctrlread it from that. That way multithreading can be preventend by just reading the control in the while loop. I've been looking for a software to do just that, but most software presents it in a way in wich I cannot read it (or I just don't know how to read it).Ā Of course, using only autoit would be best. But if this can't be solved I will have to find a way around.. #include <MsgBoxConstants.au3> $money = 1000 $problem = "unsolved" If $problem = "solved" Then $money = $money -30 If $money > 29 Then MsgBox($MB_SYSTEMMODAL, "Cookies", "You can still buy drinks and cookies!", 10) EndIf Else MsgBox($MB_SYSTEMMODAL, "I'm sorry", "No time for drinks and cookies!", 10) EndIf Link to comment Share on other sites More sharing options...
binhnx Posted December 31, 2014 Share Posted December 31, 2014 Haha, I suddenly think about a promissing workaround. Just wait, I will test it. Hope this will become a new year's gift for you 99 little bugs in the code 99 little bugs! Take one down, patch it around 117 little bugs in the code! Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 31, 2014 Author Share Posted December 31, 2014 The suspense is already killing me Thanks! Link to comment Share on other sites More sharing options...
Solution binhnx Posted December 31, 2014 Solution Share Posted December 31, 2014 (edited) 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: expandcollapse popup#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 foreverĀ Edited January 1, 2015 by binhnx Danyfirex and UnavailableUsername 2 99 little bugs in the code 99 little bugs! Take one down, patch it around 117 little bugs in the code! Link to comment Share on other sites More sharing options...
UnavailableUsername Posted December 31, 2014 Author Share Posted December 31, 2014 Oh my god. You've put a lot of of work into this!! You're the best. I'm with my family now to drink beer and stuff, so I'll check this out first thing in the morning (or afternoon). Thanks so much, can't wait to try it out. Happy new year!! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now