多個辨識器範例
此範例示範 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)))
...