; _IMMNotificationClient ; By KaFu, based on this example by trancexx ; http://www.autoitscript.com/forum/topic/151474-looking-to-capture-immnotificationclientondevicestatechanged-events/#entry1084193 Global $iAudioSwitched = 0 Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") Func _ErrFunc($oError) ConsoleWrite("COM Error, ScriptLine(" & $oError.scriptline & ") : Number 0x" & Hex($oError.number, 8) & " - " & $oError.windescription & @CRLF) EndFunc ; Global Const $sIID_IMMNotificationClient = "{7991EEC9-7E89-4D85-8390-6C703CEC60C0}" Global Const $tagIMMNotificationClient = "OnDeviceStateChanged hresult(wstr;dword);" & _ "OnDeviceAdded hresult(wstr);" & _ "OnDeviceRemoved hresult(wstr);" & _ "OnDefaultDeviceChanged hresult(dword;dword;wstr);" & _ "OnPropertyValueChanged hresult(wstr;int64);" ; last param type is improvisation because AutoIt lacks proper type Func _IMMNotificationClient_OnDeviceStateChanged($hresult, $wstr, $dword) #forceref $hresult Return 0 ; S_OK EndFunc Func _IMMNotificationClient_OnDeviceAdded($hresult, $wstr) #forceref $hresult Return 0 ; S_OK EndFunc Func _IMMNotificationClient_OnDeviceRemoved($hresult, $wstr) #forceref $hresult Return 0 ; S_OK EndFunc Func _IMMNotificationClient_OnDefaultDeviceChanged($hresult, $dword1, $dword2, $wstr) #forceref $hresult $iAudioSwitched = TimerInit() Return 0 ; S_OK EndFunc Func _IMMNotificationClient_OnPropertyValueChanged($hresult, $wstr, $int64) #forceref $hresult Return 0 ; S_OK EndFunc #cs Global Const $sIID_IUnknown = "{00000000-0000-0000-C000-000000000046}" Global Const $tagMyInterface = "FirstMethod hresult(wstr);" & _ "SecondMethod hresult(int;wstr);" #ce Global $t_IMMNotificationClient Global $o_IMMNotificationClient = ObjectFromDtag("_IMMNotificationClient_", $tagIMMNotificationClient, $t_IMMNotificationClient) ; Global $p_IMMNotificationClient = ptr($o_IMMNotificationClient()) Global $p_IMMNotificationClient = $o_IMMNotificationClient() #cs ; Is object get? ConsoleWrite("!!! IsObj($oMyObject) = " & IsObj($o_IMMNotificationClient) & @CRLF) $o_IMMNotificationClient.OnDeviceRemoved("Test") ; Get object pointer: ConsoleWrite("+>>> Object pointer = " & $o_IMMNotificationClient() & @CRLF) #ce Func ObjectFromDtag($sFunctionPrefix, $tagInterface, ByRef $tInterface) Local Const $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _ "AddRef dword();" & _ "Release dword();" ; Adding IUnknown methods $tagInterface = $tagIUnknown & $tagInterface Local Const $PTR_SIZE = DllStructGetSize(DllStructCreate("ptr")) ; Below line really simple even though it looks super complex. It's just written weird to fit one line, not to steal your eyes Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace($tagInterface,"\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "variant*", "ptr"), "hresult", "long"),"bstr", "ptr"), @LF, 3) Local $iUbound = UBound($aMethods) Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams ; Allocation. Read http://msdn.microsoft.com/en-us/library/ms810466.aspx to see why like this (object + methods): $tInterface = DllStructCreate("ptr[" & $iUbound + 1 & "]") If @error Then Return SetError(1, 0, 0) For $i = 0 To $iUbound - 1 $aSplit = StringSplit($aMethods[$i], "|", 2) If UBound($aSplit) <> 2 Then ReDim $aSplit[2] $sNamePart = $aSplit[0] $sTagPart = $aSplit[1] $sMethod = $sFunctionPrefix & $sNamePart $aTagPart = StringSplit($sTagPart, ";", 2) $sRet = $aTagPart[0] $sParams = StringReplace($sTagPart, $sRet, "", 1) $sParams = "ptr" & $sParams DllStructSetData($tInterface, 1, DllCallbackGetPtr(DllCallbackRegister($sMethod, $sRet, $sParams)), $i + 2) ; Freeing is left to AutoIt. Next DllStructSetData($tInterface, 1, DllStructGetPtr($tInterface) + $PTR_SIZE) ; Interface method pointers are actually pointer size away Return ObjCreateInterface(DllStructGetPtr($tInterface), "", $tagInterface, False) ; and first pointer is object pointer that's wrapped EndFunc Global Const $sCLSID_MMDeviceEnumerator = "{BCDE0395-E52F-467C-8E3D-C4579291692E}" Global Const $sIID_IMMDeviceEnumerator = "{A95664D2-9614-4F35-A746-DE8DB63617E6}" Global Const $tagIMMDeviceEnumerator = "EnumAudioEndpoints hresult(dword;dword;ptr*);" & _ "GetDefaultAudioEndpoint hresult(dword;dword;ptr*);" & _ "GetDevice hresult(wstr;ptr*);" & _ "RegisterEndpointNotificationCallback hresult(ptr);" & _ "UnregisterEndpointNotificationCallback hresult(ptr);" Global $o_MMDeviceEnumerator = ObjCreateInterface($sCLSID_MMDeviceEnumerator, $sIID_IMMDeviceEnumerator, $tagIMMDeviceEnumerator) $o_MMDeviceEnumerator.RegisterEndpointNotificationCallback($p_IMMNotificationClient) OnAutoItExitRegister("_UnregisterEndpointNotificationCallback") Func _UnregisterEndpointNotificationCallback() $o_MMDeviceEnumerator.UnregisterEndpointNotificationCallback($p_IMMNotificationClient) EndFunc ; =================================================================================== ; Main Loop FUNC outputChanged() if $iAudioSwitched and TimerDiff($iAudioSwitched) > 250 then $iAudioSwitched = 0 RETURN 1 ELSE $iAudioSwitched = 0 RETURN 0 endif EndFunc