Sign in to follow this  
Followers 0
UnavailableUsername

DLL crash.. (I think) Really need help..

22 posts in this topic

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

Share this post


Link to post
Share on other sites



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

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

look into msdn. that callback has no return. 

I can't help so much because i have no midi device.

saludos

Share this post


Link to post
Share on other sites

can you provide <midifunctions.au3>?

Saludos

Share this post


Link to post
Share on other sites

#6 ·  Posted (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 by UnavailableUsername

Share this post


Link to post
Share on other sites

@danyfirex

It's a UDF by Ascend4nt: 

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

I write this. it should be a working example. (I have no midi device)

#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 by Danyfirex

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

@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?

Share this post


Link to post
Share on other sites

#11 ·  Posted (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 by binhnx

99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

@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

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

@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

;)

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

The suspense is already killing me :P Thanks!

Share this post


Link to post
Share on other sites

#19 ·  Posted (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:

#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  :party:
 
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 :thumbsup:

Edited by binhnx
2 people like this

99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

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!!

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0