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

Delphi应用调用新版SAPI语音(如Microsoft Mark)的问题求助

解决方案:Delphi枚举OneCore语音失败问题修复

问题根源分析

你的代码存在三个核心问题:

  1. 硬编码注册表路径:直接使用注册表路径而非SAPI预定义的类别GUID,导致兼容性问题
  2. 未处理32/64位注册表重定向:32位程序在64位Windows下默认访问Wow6432Node下的注册表项,而OneCore语音的配置不在该路径中
  3. 未正确释放内存:Token.GetId返回的PWideChar未调用CoTaskMemFree释放,存在内存泄漏风险

修正后的枚举代码

procedure TMainForm.EnumerateVoices;
const
  // SAPI预定义的语音类别GUID(OneCore语音也归属于此类别)
  SPCAT_VOICES = '{4EEC79F4-7423-48EB-9889-079F74F64754}';
  // 强制访问64位注册表(适配32位程序在64位系统运行)
  SPOBJTOKENCAT_FLAG_64BIT = $00000002;
var
  Category: ISpObjectTokenCategory;
  EnumTokens: IEnumSpObjectTokens;
  Token: ISpObjectToken;
  TokenCount: Cardinal;
  TokenID: PWideChar;
  EnumResult: HResult;
  DisplayName: WideString;
begin
  try
    ListBox.Clear;
    Category := CoSpObjectTokenCategory.Create;
    
    // 设置语音类别,指定访问64位注册表解决重定向问题
    if Succeeded(Category.SetId(SPCAT_VOICES, SPOBJTOKENCAT_FLAG_64BIT)) then
    begin
      if Succeeded(Category.EnumTokens(nil, nil, EnumTokens)) then
      begin
        EnumResult := EnumTokens.Next(1, Token, TokenCount);
        while EnumResult = S_OK do
        begin
          if Succeeded(Token.GetId(TokenID)) then
          begin
            // 获取语音友好名称(如"Microsoft Mark")
            if Succeeded(Token.GetStringValue('SpokenName', DisplayName)) then
              ListBox.Items.Add(Format('%s (%s)', [DisplayName, TokenID]))
            else
              ListBox.Items.Add(TokenID);
            // 释放GetId返回的内存
            CoTaskMemFree(TokenID);
          end;
          
          Token := nil;
          EnumResult := EnumTokens.Next(1, Token, TokenCount);
        end;
      end
      else
        ShowMessage('Category.EnumTokens failed');
    end
    else
      ShowMessage('Category.SetId failed');
      
    ShowMessage(Format('EnumerateVoices found %d voices', [ListBox.Count]));
  except
    on E: Exception do
      ShowMessage(Format('EnumerateVoices failed. Error: %s', [E.Message]));
  end;
end;

关键修复点说明

  1. 使用官方类别GUID:SPCAT_VOICES是SAPI标准的语音类别标识符,OneCore语音也注册在此类别下,避免硬编码路径带来的版本兼容性问题
  2. 注册表重定向处理:SPOBJTOKENCAT_FLAG_64BIT强制组件访问64位注册表路径,解决32位程序无法读取OneCore语音配置的问题;如果是64位程序,可以移除该标志
  3. 内存管理:调用CoTaskMemFree释放Token.GetId返回的字符串指针,符合COM组件的内存管理规则
  4. 友好名称显示:通过Token.GetStringValue('SpokenName')获取用户可见的语音名称,提升体验

额外注意事项

  • 确保生成的SpeechLib_TLB.pas是从C:\Windows\System32\Speech_OneCore\common\sapi_onecore.dll正确生成,且适配程序位数(32位程序需用32位版本的TLibImp.exe)
  • 程序运行时需具备读取HKLM注册表项的权限(普通用户权限即可满足)
  • 若仍无法枚举,可尝试在EnumTokens的第二个参数传入语言筛选(如'Language=409'表示英语),缩小枚举范围

内容的提问来源于stack exchange,提问作者Brad Kilmer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 12:42:03