You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于pycaw、ctypes、comtypes的Windows音频检测程序崩溃排查

音频播放检测程序崩溃问题修复

问题背景

Windows 11系统、Python 3.11.3环境下,编写的音频播放检测程序可正常运行数次迭代,但1-2分钟内会静默崩溃。尝试添加CoInitialize和CoUninitialize初始化/反初始化函数无效果,控制台无错误输出,但事件查看器记录了访问违规错误。

原程序代码

import time
import logging

from pycaw.pycaw import AudioUtilities, IAudioSessionControl2
from comtypes import POINTER, CoInitialize, CoUninitialize
from ctypes import cast

# Setup logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# Define the AudioSessionState enumeration
class AudioSessionState:
    AudioSessionStateInactive = 0
    AudioSessionStateActive = 1
    AudioSessionStateExpired = 2

def is_audio_playing():
    CoInitialize()
    try:
        sessions = AudioUtilities.GetAllSessions()
        for session in sessions:
            try:
                audio_control = cast(session._ctl, POINTER(IAudioSessionControl2))
                state = audio_control.GetState()
                if state == AudioSessionState.AudioSessionStateActive:
                    return True
            except Exception as e:
                logging.error(f"Exception: {e}")
                raise e
    except Exception as e:
        logging.critical(f"Critical error in is_audio_playing: {e}")
        raise e
    finally:
        CoUninitialize()
    return False

iteration = 0
while True:
    try:
        iteration += 1
        logging.info(f"Iteration {iteration}: {is_audio_playing()}")
        time.sleep(1)
    except Exception as e:
        logging.error(f"Exception: {e}")
        raise e

事件查看器崩溃报告

- System 

  - Provider 

   [ Name]  Application Error 
   [ Guid]  {a0e9b465-b939-57d7-b27d-95d8e925ff57} 
 
   EventID 1000 
 
   Version 0 
 
   Level 2 
 
   Task 100 
 
   Opcode 0 
 
   Keywords 0x8000000000000000 
 
  - TimeCreated 

   [ SystemTime]  2023-12-13T00:25:26.3818329Z 
 
   EventRecordID 26243 
 
   Correlation 
 
  - Execution 

   [ ProcessID]  32952 
   [ ThreadID]  11272 
 
   Channel Application 
 
   Computer Ikon 
 
  - Security 

   [ UserID]  S-1-5-21-3641799435-1895349376-3777817403-1001 
 

- EventData 

  AppName python.exe 
  AppVersion 3.11.3150.1013 
  AppTimeStamp 642cb853 
  ModuleName _ctypes.pyd 
  ModuleVersion 3.11.3150.1013 
  ModuleTimeStamp 642cb828 
  ExceptionCode c0000005 
  FaultingOffset 00000000000074b1 
  ProcessId 0x5eb4 
  ProcessCreationTime 0x1da2d5ab79efffc 
  AppPath C:\Python311\python.exe 
  ModulePath C:\Python311\DLLs\_ctypes.pyd 
  IntegratorReportId a15a3e44-164e-4d8e-a0fa-bdb530c0771e 
  PackageFullName  
  PackageRelativeAppId  

调试日志片段

2023-12-12 19:42:37,098 - DEBUG - CoInitializeEx(None, 2)
2023-12-12 19:42:37,104 - DEBUG - Release <POINTER(IMMDeviceEnumerator) ptr=0x22630b64a90 at 22630a26950>
2023-12-12 19:42:37,106 - DEBUG - Release <POINTER(IMMDevice) ptr=0x226301d77d0 at 22632a9bbd0>
2023-12-12 19:42:37,106 - DEBUG - Release <POINTER(IUnknown) ptr=0x22630af5f00 at 22630a26950>
2023-12-12 19:42:37,107 - DEBUG - Release <POINTER(IAudioSessionControl) ptr=0x22630af3830 at 22632a9bbd0>
2023-12-12 19:42:37,107 - DEBUG - Release <POINTER(IAudioSessionManager2) ptr=0x22630af5f00 at 22632a9acd0>
2023-12-12 19:42:37,107 - DEBUG - Release <POINTER(IAudioSessionEnumerator) ptr=0x22632ce8040 at 22630a26950>
2023-12-12 19:42:37,107 - DEBUG - Release <POINTER(IAudioSessionControl) ptr=0x22632cfc620 at 22632a9bcd0>
2023-12-12 19:42:37,108 - DEBUG - Release <POINTER(IAudioSessionControl2) ptr=0x22630af3838 at 22630a26950>
2023-12-12 19:42:37,108 - DEBUG - CoUninitialize()
2023-12-12 19:42:37,108 - DEBUG - Release <POINTER(IAudioSessionControl2) ptr=0x22632cfc628 at 22632a9bcd0>
2023-12-12 19:42:37,109 - INFO - Iteration 25: False
2023-12-12 19:42:38,109 - DEBUG - CoInitializeEx(None, 2)
2023-12-12 19:42:38,119 - DEBUG - Release <POINTER(IMMDeviceEnumerator) ptr=0x22630b64a90 at 22630a26950>
2023-12-12 19:42:38,119 - DEBUG - Release <POINTER(IMMDevice) ptr=0x22630af48d0 at 22632a9bcd0>
2023-12-12 19:42:38,119 - DEBUG - Release <POINTER(IUnknown) ptr=0x22630af5f00 at 22630a26950>
2023-12-12 19:42:38,120 - DEBUG - Release <POINTER(IAudioSessionControl2) ptr=0x22630af39a8 at 22632a9a450>

问题分析

  • 崩溃错误码c0000005是访问违规,说明程序尝试访问了无效的内存地址,根源是COM对象生命周期管理混乱
  • 每次调用is_audio_playing都执行CoInitialize/CoUninitialize,与pycaw内部的COM初始化逻辑冲突,导致COM引用计数紊乱
  • 手动用cast转换COM接口后未主动释放,加上提前return True跳过后续清理,导致残留的COM引用变为野指针
  • 异常直接raise会终止整个程序,没有容错机制

修复方案及代码

以下是修复后的稳定版本代码:

import time
import logging
from pycaw.pycaw import AudioUtilities, IAudioSessionControl2
from comtypes import POINTER, CoInitialize, CoUninitialize
from comtypes.client import QueryInterface

# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# 音频会话状态枚举
class AudioSessionState:
    AudioSessionStateInactive = 0
    AudioSessionStateActive = 1
    AudioSessionStateExpired = 2

def is_audio_playing():
    try:
        sessions = AudioUtilities.GetAllSessions()
        for session in sessions:
            try:
                # 使用QueryInterface安全获取IAudioSessionControl2接口,符合COM规范
                audio_control = QueryInterface(session._ctl, POINTER(IAudioSessionControl2))
                state = audio_control.GetState()
                # 手动释放COM接口引用,避免内存泄漏
                audio_control.Release()
                if state == AudioSessionState.AudioSessionStateActive:
                    return True
            except Exception as e:
                logging.error(f"处理音频会话出错: {e}")
                # 单个会话出错不影响全局,继续处理下一个
                continue
    except Exception as e:
        logging.critical(f"音频检测逻辑出错: {e}")
    return False

if __name__ == "__main__":
    # 程序启动时仅初始化一次COM,无需每次调用函数重复操作
    CoInitialize()
    iteration = 0
    try:
        while True:
            iteration += 1
            try:
                playing_status = is_audio_playing()
                logging.info(f"Iteration {iteration}: {playing_status}")
                time.sleep(1)
            except Exception as e:
                logging.error(f"迭代执行出错: {e}")
                # 出错后继续循环,不终止程序
                time.sleep(1)
    except KeyboardInterrupt:
        # 捕获Ctrl+C,实现优雅终止
        logging.info("程序被手动终止")
    finally:
        # 程序退出时统一反初始化COM
        CoUninitialize()

关键修复点

  1. 全局COM初始化:仅在程序启动时执行一次CoInitialize,退出时执行CoUninitialize,避免重复初始化导致的冲突
  2. 安全获取COM接口:用QueryInterface替代ctypes.cast,符合COM接口查询规范,减少内存错误风险
  3. 主动释放COM引用:每次获取IAudioSessionControl2接口后调用Release(),确保资源及时回收
  4. 容错机制优化:单个会话处理出错时continue而非raise,避免局部错误导致整个程序崩溃
  5. 优雅终止支持:捕获KeyboardInterrupt信号,方便手动终止程序时清理资源

内容的提问来源于stack exchange,提问作者David Hite

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 09:55:53