comtypes icon indicating copy to clipboard operation
comtypes copied to clipboard

Asynchronous callbacks from Windows

Open reversefold opened this issue 4 years ago • 3 comments

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.

reversefold avatar Feb 14 '21 21:02 reversefold

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

QuadCorei8085 avatar Jun 24 '22 14:06 QuadCorei8085

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.

taniodev avatar Oct 21 '23 15:10 taniodev

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.

junkmd avatar Oct 24 '23 13:10 junkmd