Basic Recognition Sample

Basic Recognition Sample

This application demonstrates how you can build a simple handwriting recognition application.

This program creates an InkCollector object to ink-enable the window and a default recognizer context object. Upon receiving the "Recognize!" command, fired from the application's menu, the collected ink strokes are passed to the recognizer context. The best result string is presented in a message box.

The interfaces used are: IInkCollector, IInkDisp, IInkDrawingAttributes, IInkStrokes, IInkRecognizerContext, and IInkRecognitionResult.

Creating the RecognizerContext Object

In the WndProc procedure for the application, when the WM_CREATE message is received on startup, a new recognizer context that uses the default recognizer is created. This context is used for all recognition in the application.

case WM_CREATE:
{
    HRESULT hr;
    hr = CoCreateInstance(CLSID_InkRecognizerContext,
             NULL, CLSCTX_INPROC_SERVER, IID_IInkRecognizerContext,
             (void **) &g_pIInkRecoContext);
    if (FAILED(hr))
    {
        ::MessageBox(NULL, TEXT("There are no handwriting recognizers installed.\n"
            "You need to have at least one in order to run this sample.\nExiting."),
            gc_szAppName, MB_ICONERROR);
        return -1;
    }
  //...

Recognizing the Strokes

The recognize command is received when the user clicks the Recognize! menu item. The code gets a pointer to the ink InkStrokes (pIInkStrokes) off of the InkDisp object, and then passes the InkStrokes to the recognizer context using a call to putref_Strokes.

 case WM_COMMAND:
  //...
  else if (wParam == ID_RECOGNIZE)
  {
      // change cursor to the system's Hourglass
      HCURSOR hCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
      // Get a pointer to the ink stroke collection
      // This collection is a snapshot of the entire ink object
      IInkStrokes* pIInkStrokes = NULL;
      HRESULT hr = g_pIInkDisp->get_Strokes(&pIInkStrokes);
      if (SUCCEEDED(hr))
      {
          // Pass the stroke collection to the recognizer context
          hr = g_pIInkRecoContext->putref_Strokes(pIInkStrokes);
          if (SUCCEEDED(hr))
          {

The code then calls the Recognize method of the InkRecognizerContext object, passing in a pointer to a IInkRecognitionResult object to hold the results.

              // Recognize
              IInkRecognitionResult* pIInkRecoResult = NULL;
              hr = g_pIInkRecoContext->Recognize(&pIInkRecoResult);
              if (SUCCEEDED(hr))
              {

Finally, the code uses the IInkRecognitionResult object's TopString property retrieve the top recognition result into a string variable, releases the IInkRecognitionResult object, and displays the string in a message box.

                  // Get the best result of the recognition
                  BSTR bstrBestResult = NULL;
                  hr = pIInkRecoResult->get_TopString(&bstrBestResult);
                  pIInkRecoResult->Release();
                  pIInkRecoResult = NULL;

                  // Show the result string
                  if (SUCCEEDED(hr) && bstrBestResult)
                  {
                      MessageBoxW(hwnd, bstrBestResult,
                                  L"Recognition Results", MB_OK);
                      SysFreeString(bstrBestResult);
                  }
        <p>Be sure to reset the recognizer context between usages.
        </p>
        
      <snippet><![CDATA[              // Reset the recognizer context
              g_pIInkRecoContext->putref_Strokes(NULL);
          }
          pIInkStrokes->Release();
      }
      // restore the cursor
      ::SetCursor(hCursor);
  }