comtypes
comtypes copied to clipboard
Asynchronous callbacks from Windows
I'm working with comtypes through pyWinCoreAudio. Some of the interfaces exposed there have functions for registering callbacks when something happens such as the volume changing on an audio device, see https://github.com/reversefold/pyWinCoreAudio/blob/75c93a2e7bd38094ad7e151ddeb29e1ccf701a74/pyWinCoreAudio/volume.py#L168 and https://docs.microsoft.com/en-us/windows/win32/api/endpointvolume/nf-endpointvolume-iaudioendpointvolume-registercontrolchangenotify, for example. I can call the registration function just fine but it never does anything.
I'm hoping that the issue is that I'm not running some event loop code, but I haven't been able to find any. I found PumpEvents in the docs but running that seems to have no effect.
Is there something else I'm missing that would allow callbacks like this to work? Is there something further that needs to be implemented in comtypes for this to work? Does anyone know a workaround?
Here is the code I am using to test. You will need https://pypi.org/project/pywincoreaudio/ installed and you will need to replace the "System" string for part of the name of an audio device on your machine.
from pyWinCoreAudio import device
from pyWinCoreAudio.__core_audio import endpointvolumeapi
from pyWinCoreAudio.__core_audio import iid
devenum = device.AudioDeviceEnumerator()
devices = list(devenum.endpoints)
audevs = [device.AudioDevice(dev.GetId(), devenum) for dev in devenum.endpoints]
audevs.sort(key=lambda d: d.name if d.name else d.id)
found = False
for audev in audevs:
print(f"{audev.name} - {audev.id}")
for title, endpoints in [("Capture", audev.capture_endpoints), ("Render", audev.render_endpoints)]:
if endpoints:
print(f" {title}:")
for ep in endpoints:
print(f" {ep.name} - {ep.session_manager}")
if "System" in ep.name:
found = True
break
if found:
break
if found:
break
class cb(object):
def endpoint_volume_change(e, m, c, mt):
out = f"{e} | {m} | {c} | {mt}\n"
print(out[:-1])
with open("evc.txt", "a") as f:
f.write(out)
ep.volume.register_notification_callback(cb())
from comtypes.client import PumpEvents
import time
while True:
print("PumpEvents")
PumpEvents(1)
time.sleep(1)
Run the script, wait for PumpEvents then try changing the volume of said device in the windows mixer.
Having similar issue. We have an audio device with a proprietary C++ DLL and callback events do not work.
But I accidentally noticed that If I replace:
while True:
#pythoncom.PumpMessages()
comtypes.client.PumpEvents(1);
time.sleep(1);
With
while True:
pythoncom.PumpMessages()
#comtypes.client.PumpEvents(1);
time.sleep(1);
everything works fine
I believe my problem is also related. I am trying to receive some events when generating speech with the Microsoft SAPI engine, but the comtypes.client.PumpEvents function doesn't seem to call the designated methods.
import pythoncom # from the pywin32 package
from comtypes import client
class SapiEventSink:
def _ISpeechVoiceEvents_StartStream(self, *args, **kwargs):
print('start stream')
def _ISpeechVoiceEvents_EndStream(self, *args, **kwargs):
print('end stream')
if __name__ == '__main__':
tts = client.CreateObject('SAPI.SpVoice')
event_handler = client.GetEvents(tts, SapiEventSink())
tts.Speak('Hello World!')
client.PumpEvents(5)
Replacing PumpEvents with PumpWaitingMessages works fine:
if __name__ == '__main__':
...
# client.PumpEvents(5)
pythoncom.PumpWaitingMessages()
Best regards.
test_pump_events only conducts memory leak testing.
While I use Excel in my project, I disable events. So, for me, this problem is of low priority.
However, I would welcome contributions from anyone willing to help resolve this problem.