65.9K
CodeProject 正在变化。 阅读更多。
Home

让您的浏览器开口说话

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (3投票s)

2008年4月21日

CPOL

2分钟阅读

viewsIcon

33068

downloadIcon

373

使用语音 SDK 让 Internet Explorer 读取文档或文档的部分内容给用户。

1_small.JPG

2.jpg

要求

  • Microsoft Speech SDK 5.1

http://msdn2.microsoft.com/en-us/library/bb250489.aspx (使用 VS2005 构建 BHO) 和
http://msdn2.microsoft.com/en-us/library/bb735853(VS.85).aspx#IEAddOnsMenus_topic2 (为 IE 创建菜单) 是我创建此插件时使用的两个网站。 我将跳过如何创建浏览器助手对象并将其添加到 Internet Explorer 工具菜单的过程,直接进入有趣的部分。 speakThread 中实际进行语音朗读的大部分代码都来自 MSDN 文档中关于 Speech API 的说明。

语音线程

#include <sapi.h >//we need this
...
DWORD WINAPI speakThread(LPVOID bstr)
{
    if (FAILED(::CoInitialize(NULL)))
        return -1;
    ISpVoice * pVoice = NULL;
    HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, 
                                 (void **)&pVoice);
    if( SUCCEEDED( hr ) )
    {
        hr = pVoice->Speak((BSTR)bstr,0,NULL);
        pVoice->WaitUntilDone(INFINITE);
        pVoice->Release();
        pVoice = NULL;
        ::CoUninitialize();
        return hr;
    }
    ::CoUninitialize();
    return hr;
}

这里发生的事情是,我们将一个 BSTR 作为 LPVOID 传递给线程。 我们初始化 COM 并创建一个类型为 ISpVoice 的接口。 我们调用此接口的 Speak 例程,并为其提供接收到的 string 。 接下来,我们等待该例程完成,然后进行清理。

STDMETHODIMP CCIESpeechBHO::Exec(const GUID *pguidCmdGroup, 
  DWORD nCmdID, DWORD nCmdExecOpt, 
  VARIANTARG *pvaIn, VARIANTARG *pvaOut) {

    CComPtr spDoc;
    HRESULT hr  = m_pBrowser->get_Document(&spDoc);

    if(SUCCEEDED(hr))
    {
        CComQIPtr spHTMLDoc = spDoc;

        if(NULL != spHTMLDoc)
        {
            IHTMLElement* pHtmlElement = NULL;
            IHTMLSelectionObject* pHtmlSelectionObject;
            spHTMLDoc->get_selection(&pHtmlSelectionObject);
            BSTR bstr;
            pHtmlSelectionObject->get_type(&bstr);
            if (_tcscmp(_tcsupr(bstr),L"TEXT")!=0)
            {
                spHTMLDoc->get_body(&pHtmlElement);
                pHtmlElement->get_innerText(&bstr);
            }
            else
            {
                IHTMLTxtRange* pHtmlTxtRange;
                pHtmlSelectionObject->createRange((IDispatch**)&pHtmlTxtRange);
                pHtmlTxtRange->get_text(&bstr);
            }
            DWORD dwThreadID=0;
            CreateThread(NULL,0,speakThread,(LPVOID)bstr,0,&dwThreadID);
        }
    }

    return hr;
}

Exec 函数中,浏览器在选择菜单项时会调用该函数,执行几项操作。 首先,我们要求浏览器提供 HTML 的副本。 接下来,我们创建一个 IHTMLSelectionObject 接口,并检查是否选择了任何文本。 如果选择了文本,则我们创建一个范围并获取文本;否则,我们获取正文并从 HTML 的内部正文中获取文本。 最后,我们创建一个线程,并将从我们的选择或 HTML 正文接收到的文本传递给它。

注释

  • 必须创建一个线程,否则 Internet Explorer 将会锁定,直到文本朗读完成。
  • 如果页面上有隐藏文本,它将读取所有隐藏文本,例如 MSDN 网站上的链接。
  • 此方法允许您查看其他页面,而无需等待文本朗读完成,但是 API 中没有实现停止机制,因此用户必须等待文本朗读完成,然后才能进行新的选择并朗读。

我希望看到的功能

  • Adobe Acrobat 版本
  • Microsoft Word 版本
  • 添加了跳过和重放段落的方法

历史

  • 2008 年 4 月 21 日:初始发布
© . All rights reserved.