
Can an AutoHotkey script be notified of volume changing and muting/unmuting?
-
- Posts: 2057
- Joined: 29 Mar 2015, 09:41
- Contact:
-
- Posts: 2057
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Can an AutoHotkey script be notified of volume changing and muting/unmuting?
lexikos' RegisterSyncCallback() solves that problem. This code works well without dll and another script:teadrinker wrote: ↑The problem is that IAudioEndpointVolumeCallback works in another thread.
Code: Select all
SetBatchLines, -1
Gui, +AlwaysOnTop
Gui, Font, s16
Gui, Add, Text, y13, Volume:
Gui, Add, Text, xp y+5 wp right, Muted:
Gui, Add, Text, x+3 y13 left, 100
Gui, Add, Text, xp y+5, 0
GuiControl,, Static3, % Round( VA_GetMasterVolume() )
GuiControl,, Static4, % VA_GetMasterMute()
Gui, Show
IAudioEndpointVolume := VA_GetAudioEndpointVolume("playback")
IAudioEndpointVolumeCallback := IAudioEndpointVolumeCallback_Create(IAudioEndpointVolume)
VA_IAudioEndpointVolume_RegisterControlChangeNotify(IAudioEndpointVolume, IAudioEndpointVolumeCallback)
OnExit( Func("Clear").Bind(IAudioEndpointVolume, IAudioEndpointVolumeCallback) )
Return
GuiClose() {
ExitApp
}
SetInfo(muted, volume) {
GuiControl,, Static3, % Round( volume*100 )
GuiControl,, Static4, % muted
}
Clear(IAudioEndpointVolume, IAudioEndpointVolumeCallback) {
VA_IAudioEndpointVolume_UnregisterControlChangeNotify(IAudioEndpointVolume, IAudioEndpointVolumeCallback)
ObjRelease(IAudioEndpointVolumeCallback)
ObjRelease(IAudioEndpointVolume)
}
IAudioEndpointVolumeCallback_Create(aev) {
static VTBL := [ "QueryInterface"
, "AddRef"
, "Release"
, "OnNotify" ]
, heapSize := A_PtrSize*10
, heapOffset := A_PtrSize*9
, flags := (HEAP_GENERATE_EXCEPTIONS := 0x4) | (HEAP_NO_SERIALIZE := 0x1)
, HEAP_ZERO_MEMORY := 0x8
hHeap := DllCall("HeapCreate", "UInt", flags, "Ptr", 0, "Ptr", 0, "Ptr")
addr := IAudioEndpointVolumeCallback := DllCall("HeapAlloc", "Ptr", hHeap, "UInt", HEAP_ZERO_MEMORY, "Ptr", heapSize, "Ptr")
addr := NumPut(addr + A_PtrSize, addr + 0)
for k, v in VTBL
addr := NumPut( RegisterSyncCallback("IAudioEndpointVolumeCallback_" . v), addr + 0 )
NumPut(hHeap, IAudioEndpointVolumeCallback + heapOffset)
Return IAudioEndpointVolumeCallback
}
IAudioEndpointVolumeCallback_QueryInterface(this, riid, ppvObject) {
Return 0 ; S_OK
}
IAudioEndpointVolumeCallback_AddRef(this) {
static refOffset := A_PtrSize*8
NumPut(refCount := NumGet(this + refOffset, "UInt") + 1, this + refOffset, "UInt")
Return refCount
}
IAudioEndpointVolumeCallback_Release(this) {
static refOffset := A_PtrSize*8
, heapOffset := A_PtrSize*9
NumPut(refCount := NumGet(this + refOffset, "UInt") - 1, this + refOffset, "UInt")
if (refCount = 0) {
hHeap := NumGet(this + heapOffset)
DllCall("HeapDestroy", "Ptr", hHeap)
}
Return refCount
}
IAudioEndpointVolumeCallback_OnNotify(this, pNotify) {
timer := Func("SetInfo").Bind(NumGet(pNotify + 16, "UInt"), NumGet(pNotify + 20, "Float"))
SetTimer, % timer, -10
Return 0
}
/*
RegisterSyncCallback
A replacement for RegisterCallback for use with APIs that will call
the callback on the wrong thread. Synchronizes with the script's main
thread via a window message.
This version tries to emulate RegisterCallback as much as possible
without using RegisterCallback, so shares most of its limitations,
and some enhancements that could be made are not.
Other differences from v1 RegisterCallback:
- Variadic mode can't be emulated exactly, so is not supported.
- A_EventInfo can't be set in v1, so is not supported.
- Fast mode is not supported (the option is ignored).
- ByRef parameters are allowed (but ByRef is ignored).
- Throws instead of returning "" on failure.
*/
RegisterSyncCallback(FunctionName, Options:="", ParamCount:="")
{
if !(fn := Func(FunctionName)) || fn.IsBuiltIn
throw Exception("Bad function", -1, FunctionName)
if (ParamCount == "")
ParamCount := fn.MinParams
if (ParamCount > fn.MaxParams && !fn.IsVariadic || ParamCount+0 < fn.MinParams)
throw Exception("Bad param count", -1, ParamCount)
static sHwnd := 0, sMsg, sSendMessageW
if !sHwnd
{
Gui RegisterSyncCallback: +Parent%A_ScriptHwnd% +hwndsHwnd
OnMessage(sMsg := 0x8000, Func("RegisterSyncCallback_Msg"))
sSendMessageW := DllCall("GetProcAddress", "ptr", DllCall("GetModuleHandle", "str", "user32.dll", "ptr"), "astr", "SendMessageW", "ptr")
}
if !(pcb := DllCall("GlobalAlloc", "uint", 0, "ptr", 96, "ptr"))
throw
DllCall("VirtualProtect", "ptr", pcb, "ptr", 96, "uint", 0x40, "uint*", 0)
p := pcb
if (A_PtrSize = 8)
{
/*
48 89 4c 24 08 ; mov [rsp+8], rcx
48 89 54'24 10 ; mov [rsp+16], rdx
4c 89 44 24 18 ; mov [rsp+24], r8
4c'89 4c 24 20 ; mov [rsp+32], r9
48 83 ec 28' ; sub rsp, 40
4c 8d 44 24 30 ; lea r8, [rsp+48] (arg 3, ¶ms)
49 b9 .. ; mov r9, .. (arg 4, operand to follow)
*/
p := NumPut(0x54894808244c8948, p+0)
p := NumPut(0x4c182444894c1024, p+0)
p := NumPut(0x28ec834820244c89, p+0)
p := NumPut( 0xb9493024448d4c, p+0) - 1
lParamPtr := p, p += 8
p := NumPut(0xba, p+0, "char") ; mov edx, nmsg
p := NumPut(sMsg, p+0, "int")
p := NumPut(0xb9, p+0, "char") ; mov ecx, hwnd
p := NumPut(sHwnd, p+0, "int")
p := NumPut(0xb848, p+0, "short") ; mov rax, SendMessageW
p := NumPut(sSendMessageW, p+0)
/*
ff d0 ; call rax
48 83 c4 28 ; add rsp, 40
c3 ; ret
*/
p := NumPut(0x00c328c48348d0ff, p+0)
}
else ;(A_PtrSize = 4)
{
p := NumPut(0x68, p+0, "char") ; push ... (lParam data)
lParamPtr := p, p += 4
p := NumPut(0x0824448d, p+0, "int") ; lea eax, [esp+8]
p := NumPut(0x50, p+0, "char") ; push eax
p := NumPut(0x68, p+0, "char") ; push nmsg
p := NumPut(sMsg, p+0, "int")
p := NumPut(0x68, p+0, "char") ; push hwnd
p := NumPut(sHwnd, p+0, "int")
p := NumPut(0xb8, p+0, "char") ; mov eax, &SendMessageW
p := NumPut(sSendMessageW, p+0, "int")
p := NumPut(0xd0ff, p+0, "short") ; call eax
p := NumPut(0xc2, p+0, "char") ; ret argsize
p := NumPut((InStr(Options, "C") ? 0 : ParamCount*4), p+0, "short")
}
NumPut(p, lParamPtr+0) ; To be passed as lParam.
p := NumPut(&fn, p+0)
p := NumPut(ParamCount, p+0, "int")
return pcb
}
RegisterSyncCallback_Msg(wParam, lParam)
{
if (A_Gui != "RegisterSyncCallback")
return
fn := Object(NumGet(lParam + 0))
paramCount := NumGet(lParam + A_PtrSize, "int")
params := []
Loop % paramCount
params.Push(NumGet(wParam + A_PtrSize * (A_Index-1)))
return %fn%(params*)
}
- JoeWinograd
- Posts: 1643
- Joined: 10 Feb 2014, 20:00
Re: Can an AutoHotkey script be notified of volume changing and muting/unmuting?
Hi teadrinker,
Thank you for your continuing efforts on this...very much appreciated! I'll give the new code a spin this weekend and will let you know how it goes. Regards, Joe
Thank you for your continuing efforts on this...very much appreciated! I'll give the new code a spin this weekend and will let you know how it goes. Regards, Joe
- JoeWinograd
- Posts: 1643
- Joined: 10 Feb 2014, 20:00
Re: Can an AutoHotkey script be notified of volume changing and muting/unmuting?
Hi teadrinker,
A quick note to let you know that your new code is working perfectly here on a W10/64-bit system with 1.1.32.00/U64...no crashes...no freezes. Next task is to integrate your code into mine. Will let you know how that goes. Thanks for your ongoing help, Joe
A quick note to let you know that your new code is working perfectly here on a W10/64-bit system with 1.1.32.00/U64...no crashes...no freezes. Next task is to integrate your code into mine. Will let you know how that goes. Thanks for your ongoing help, Joe
-
- Posts: 2057
- Joined: 29 Mar 2015, 09:41
- Contact:
- JoeWinograd
- Posts: 1643
- Joined: 10 Feb 2014, 20:00
Re: Can an AutoHotkey script be notified of volume changing and muting/unmuting?
Hi teadrinker,
Following up on my last post, I integrated your new code into my horizontal volume slider script...tested on W7 and W10...works great!
A question for you. I'm not very knowledgeable with call-backs or DLLs, both of which you use heavily. I'm wondering if anything in your code survives termination of the script. In other words, after an ExitApp, is anything that you deployed via a call-back or DLL (or anything else) still active in Windows? If so, what would I have to do before an ExitApp to close/delete/remove any remnants of the script? Thanks, Joe
Following up on my last post, I integrated your new code into my horizontal volume slider script...tested on W7 and W10...works great!

A question for you. I'm not very knowledgeable with call-backs or DLLs, both of which you use heavily. I'm wondering if anything in your code survives termination of the script. In other words, after an ExitApp, is anything that you deployed via a call-back or DLL (or anything else) still active in Windows? If so, what would I have to do before an ExitApp to close/delete/remove any remnants of the script? Thanks, Joe
-
- Posts: 2057
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Can an AutoHotkey script be notified of volume changing and muting/unmuting?
No, after the process is closed, all its resources are relised.
- JoeWinograd
- Posts: 1643
- Joined: 10 Feb 2014, 20:00
Re: Can an AutoHotkey script be notified of volume changing and muting/unmuting?
> after the process is closed, all its resources are released
Great news! Excellent!
Great news! Excellent!