共用方式為


多個辨識器範例

此範例示範 MicrosoftTablet PC Automation 應用程式開發介面 (API) 用於 手寫 辨識的進階功能。

其中包含下列各項:

  • 列舉已安裝的辨識器
  • 使用特定語言辨識器建立辨識器內容
  • 使用筆劃集合序列化辨識結果
  • 將筆劃集合組織成 InkDisp 物件內的自訂集合
  • 筆跡 物件序列化至 ISF) 檔案 (,並從筆跡序列化格式 擷取物件
  • 設定辨識器輸入輔助線
  • 使用同步和非同步辨識

筆跡標頭

首先,包含平板電腦自動化介面的標頭。 這些會與 Microsoft Windows XP Tablet PC Edition 軟體發展工具組 (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物件。 如有必要,會取代舊的辨識器內容。 內容會連線到其事件來源。 最後,會檢查辨識器內容 的功能屬性, 以參考辨識器內容所支援的內容。

// 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)))
            ...