多个识别器示例

此示例演示用于 手写 识别的 MicrosoftTablet PC Automation 应用程序编程接口 (API) 的高级功能。

包括以下各项:

  • 枚举已安装的识别器
  • 使用特定语言 识别器 创建识别器上下文
  • 使用笔划集合序列化识别结果
  • 将笔划集合组织到 InkDisp 对象中的自定义集合中
  • 墨迹 对象序列化为 ,并从 墨迹序列化格式 (ISF) 文件检索它们
  • 设置识别器输入参考线
  • 使用同步和异步识别

墨迹页眉

首先,包括适用于 Tablet PC 自动化接口的标头。 它们随 Microsoft Windows XP 平板电脑版软件开发工具包 (SDK) 一起安装。

#include <msinkaut.h>
#include <msinkaut_i.c>

EventSinks.h 文件定义 IInkEventsImpl 和 IInkRecognitionEventsImpl 接口。

#include "EventSinks.h"

枚举已安装的识别器

应用程序的 LoadMenu 方法使用可用的识别器填充“创建新笔划”菜单。 创建 InkRecognizers 。 如果 InkRecognizers 对象的 Languages 属性不为空,则识别器是文本识别器,并且其 Name 属性的值将添加到菜单中。

// Create the enumerator for the installed recognizers
hr = m_spIInkRecognizers.CoCreateInstance(CLSID_InkRecognizers);
...
    // Filter out non-language recognizers by checking for
    // the languages supported by the recognizer - there is not
    // any if it is a gesture or object recognizer.
    CComVariant vLanguages;
    if (SUCCEEDED(spIInkRecognizer->get_Languages(&vLanguages)))
    {
        if ((VT_ARRAY == (VT_ARRAY & vLanguages.vt))           // it should be an array
            && (NULL != vLanguages.parray)
            && (0 < vLanguages.parray->rgsabound[0].cElements)) // with at least one element
        {
            // This is a language recognizer. Add its name to the menu.
            CComBSTR bstrName;
            if (SUCCEEDED(spIInkRecognizer->get_Name(&bstrName)))
                ...
        }
    }

创建墨迹收集器

应用程序的 OnCreate 方法创建 InkCollector 对象,将其连接到其事件源,并启用墨迹收集。

// Create an ink collector object.
hr = m_spIInkCollector.CoCreateInstance(CLSID_InkCollector);

// Establish a connection to the collector's event source.
hr = IInkCollectorEventsImpl<CMultiRecoApp>::DispEventAdvise(m_spIInkCollector);

// Enable ink input in the m_wndInput window
hr = m_spIInkCollector->put_hWnd((long)m_wndInput.m_hWnd);
hr = m_spIInkCollector->put_Enabled(VARIANT_TRUE);

创建识别器上下文

应用程序的 CreateRecoContext 方法创建并初始化新的识别器上下文,并设置关联语言支持的参考线。 IInkRecognizer 对象的 CreateRecognizerContext 方法为语言创建 IInkRecognizerContext2 对象。 如有必要,将替换旧的识别器上下文。 上下文连接到其事件源。 最后,检查识别器上下文的 Capabilities 属性,了解识别器上下文支持的指南。

// Create a recognizer context
CComPtr<IInkRecognizerContext2> spNewContext;
if (FAILED(pIInkRecognizer2->CreateRecognizerContext(&spNewContext)))
    return false;

// Replace the current context with the new one
if (m_spIInkRecoContext != NULL)
{
    // Close the connection to the recognition events source
    IInkRecognitionEventsImpl<CMultiRecoApp>::DispEventUnadvise(m_spIInkRecoContext);
}
m_spIInkRecoContext.Attach(spNewContext.Detach());

// Establish a connection with the recognizer context's event source
if (FAILED(IInkRecognitionEventsImpl<CMultiRecoApp>::DispEventAdvise(m_spIInkRecoContext)))
    ...

// Set the guide if it's supported by the recognizer and has been created 
int cRows = 0, cColumns = 0;
InkRecognizerCapabilities dwCapabilities = IRC_DontCare;
if (SUCCEEDED(pIInkRecognizer->get_Capabilities(&dwCapabilities)))
    ...

收集笔划和显示识别结果

应用程序的 OnStroke 方法更新墨迹收集器的 InkStrokes ,取消现有的异步识别请求,并在识别器上下文中创建识别请求。

// Add the new stroke to the current collection
hr = m_spIInkStrokes->Add(pIInkStroke);

if (SUCCEEDED(hr))
{
    // Cancel the previous background recognition requests
    // which have not been processed yet
    m_spIInkRecoContext->StopBackgroundRecognition();

    // Ask the context to update the recognition results with newly added strokes
    // When the results are ready, the recognizer context returns them
    // through the corresponding event RecognitionWithAlternates
    CComVariant vCustomData;
    m_spIInkRecoContext->BackgroundRecognize(vCustomData);
}

应用程序的 OnRecognition 方法将识别请求的结果发送到输出窗口的 UpdateString 方法。

// Update the output window with the new results
m_wndResults.UpdateString(bstrRecognizedString);

删除笔划和识别结果

应用程序的 OnClear 方法从 InkDisp 对象中删除所有笔划和识别结果,并清除窗口。 将删除识别器上下文与其 InkStrokes 集合的关联。

// Detach the current stroke collection from the recognizer context and release it
if (m_spIInkRecoContext != NULL)
    m_spIInkRecoContext->putref_Strokes(NULL);

m_spIInkStrokes.Release();

// Clear the custom strokes collection
if (m_spIInkCustomStrokes != NULL)
    m_spIInkCustomStrokes->Clear();

// Delete all strokes from the Ink object
// Passing NULL as a stroke collection pointer means asking to delete all strokes
m_spIInkDisp->DeleteStrokes(NULL);

// Get a new stroke collection from the ink object
...
// Ask for an empty collection by passing an empty variant 
if (SUCCEEDED(m_spIInkDisp->CreateStrokes(v, &m_spIInkStrokes)))
{
    // Attach it to the recognizer context
    if (FAILED(m_spIInkRecoContext->putref_Strokes(m_spIInkStrokes)))
        ...
}

更改识别器上下文

当用户在“创建新笔划”菜单中选择识别器时,将调用应用程序的 OnNewStrokes 方法。 保存当前 InkStrokes 。 如果选择了其他语言识别器,则会创建新的识别器上下文。 然后,新的 InkStrokes 将附加到新的识别器上下文。

// Save the current stroke collection if there is any
if (m_spIInkRecoContext != NULL)
{
    // Cancel the previous background recognition requests
    // which have not been processed yet
    m_spIInkRecoContext->StopBackgroundRecognition();
    
    // Let the context know that there'll be no more input 
    // for the attached stroke collection
    m_spIInkRecoContext->EndInkInput();

    // Add the stroke collection to the Ink object's CustomStrokes collection
    SaveStrokeCollection();
}
...
// If a different recognizer was selected, create a new recognizer context
// Else, reuse the same recognizer context
if (wID != m_nCmdRecognizer)
{
    // Get a pointer to the recognizer object from the recognizer collection  
    CComPtr<IInkRecognizer> spIInkRecognizer;
    if ((m_spIInkRecognizers == NULL)
        || FAILED(m_spIInkRecognizers->Item(wID - ID_RECOGNIZER_FIRST,
                                             &spIInkRecognizer))
        || (false == CreateRecoContext(spIInkRecognizer)))
    {
        // restore the cursor
        ::SetCursor(hCursor);
        return 0;
    }

    // Update the status bar
    m_bstrCurRecoName.Empty();
    spIInkRecognizer->get_Name(&m_bstrCurRecoName);
    UpdateStatusBar();

    // Store the selected recognizer's command id
    m_nCmdRecognizer = wID;
}

然后,它调用 StartNewStrokeCollection,这将创建一个空 的 InkStrokes 并将其附加到识别器上下文。

保存识别器上下文的笔划集合

应用程序的 SaveStrokeCollection 方法检查现有的识别器上下文,并完成对当前笔划集合的识别。 然后将 InkStrokes 集合添加到墨迹对象的 CustomStrokes 中。

if (m_spIInkRecoContext != NULL)
{
    if (SUCCEEDED(m_spIInkStrokes->get_Count(&lCount)) && 0 != lCount)
    {
        CComPtr<IInkRecognitionResult> spIInkRecoResult;
        InkRecognitionStatus RecognitionStatus;
        if (SUCCEEDED(m_spIInkRecoContext->Recognize(&RecognitionStatus, &spIInkRecoResult)))
        {
            if (SUCCEEDED(spIInkRecoResult->SetResultOnStrokes()))
            {
                CComBSTR bstr;
                spIInkRecoResult->get_TopString(&bstr);
                m_wndResults.UpdateString(bstr);
            }
            ...
        }
    }
    // Detach the stroke collection from the old recognizer context
    m_spIInkRecoContext->putref_Strokes(NULL);
}

// Now add it to the ink's custom strokes collection
// Each item (stroke collection) of the custom strokes must be identified
// by a unique string. Here we generate a GUID for this.
if ((0 != lCount) && (m_spIInkCustomStrokes != NULL))
{
    GUID guid;
    WCHAR szGuid[40]; // format: "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
    if (SUCCEEDED(::CoCreateGuid(&guid)) 
        && (::StringFromGUID2(guid, szGuid, countof(szGuid)) != 0))
    {
        CComBSTR bstrGuid(szGuid);
        if (FAILED(m_spIInkCustomStrokes->Add(bstrGuid, m_spIInkStrokes)))
            ...