global $iAudioSwitched 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 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 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 Func _UnregisterEndpointNotificationCallback() $o_MMDeviceEnumerator.UnregisterEndpointNotificationCallback($p_IMMNotificationClient) EndFunc FUNC outputChanged() if $iAudioSwitched and TimerDiff($iAudioSwitched) > 250 then $iAudioSwitched = 0 RETURN 1 ELSE RETURN 0 endif EndFunc