Using the COM Library

The Tablet PC managed library reference can now be found in the regular Windows Vista SDK Class Library reference section. It provides an object model for Microsoft Visual C++. The majority of the objects in the COM Library are identical to those found in the Tablet PC Managed API.

However, the COM API contains some members in addition to those found in the Managed API because of differences between the standard Microsoft Win32 environment and the Microsoft .NET Frameworksoftware development kit (SDK) environment. For example, the InkRectangle and InkTransform objects are used in COM, but the FrameworkSDK provides native implementation for InkRectangle Class and InkTransform Class that eliminates the need for these objects in the Tablet PC platform Managed API.


The objects in the COM API and the ink controls are not designed for use in Active Server Pages (ASP).


Working with Collections

If you pass in a NULL value as the index to any of the collection objects in the COM Library, you receive the first item in the collection because these argument values are coerced to 0 when the call is made.

The _NewEnum property is marked restricted in the Interface Definition Language (IDL) definition for the collection interfaces.

In C++, use a For... loop to iterate through a collection by first obtaining the collection's length. The example below shows how to iterate through the strokes of an InkDisp object, pInk.

IInkStrokes* pStrokes;
HRESULT result = pInk->get_Strokes(&pStrokes);
if (SUCCEEDED(result))
    // Loop over strokes
    long nStrokes;
    result = pStrokes->get_Count(&nStrokes);
    if (SUCCEEDED(result))
        for (int i =0; i < nStrokes; i++)
            IInkStrokeDisp* pStroke;
            result = pStrokes->Item(i, &pStroke);
            if (SUCCEEDED(result))
              // Code that uses pStroke
              // ...


If you pass in VT_EMPTY or VT_NULL as the index to any of the collection objects in the COM Library, you receive the first item in the collection because these argument values are coerced to 0 when the call is made.

Using IDispatch

To increase performance, the Tablet PC Platform COM application programming interface (API) does not support calling IDispatchImpl::Invoke with a DISPPARAMS structure with named arguments. The IDispatchImpl::GetIDsOfNames is also not supported. Instead, call Invoke with the DISPIDs supplied in the SDK headers.

Waiting for Events

The Tablet PC environment is multithreaded. Refer to COM documentation for multi-threading.

Support for Aggregation

Aggregation has been tested for only the InkEdit control, the InkPicture control, the InkDisp object, and the InkOverlay object. Aggregation has not been tested for other controls and objects in the library.


Using the Tablet PC SDK in C++ requires the use of some COM concepts, such as VARIANT, SAFEARRAY, and BSTR. This section describes how to use them.


The VARIANT structure is used for communication between COM objects. Essentially, the VARIANT structure is a container for a large union that carries many types of data.

The value in the first member of the structure, vt, describes which of the union members is valid. When you receive information in a VARIANT structure, check the vt member to find out which member contains valid data. Similarly, when you send information using a VARIANT structure, always set vt to reflect the union member that contains the information.

Before using the structure, initialize it by calling the VariantInit COM function. When finished with the structure, clear it before the memory that contains the VARIANT is freed by calling VariantClear.

For more information on the VARIANT structure, see VARIANT and VARIANTARG Data Types.

The SAFEARRAY structure is provided as a way to safely work with arrays in COM. The VARIANT's parray field is a pointer to a SAFEARRAY. Use functions such as SafeArrayCreateVector, SafeArrayAccessData, and SafeArrayUnaccessData to create and fill a SAFEARRAY in a VARIANT.

For more information on the SAFEARRAY data type, see SafeArray Data Type.

This C++ example creates an IInkStrokeDisp , pInkStrokeDisp, in an InkDisp object, pInk, from an array of point data.

VARIANT   var, varPK;
LONG*   plongArray=NULL;
POINT   ptArray[2]={0};
long   lSize=0;
IInkStrokeDisp* pInkStrokeDisp;

IInkDisp*   pInk;   // the object should be created correctly 
                    // elsewhere and assigned here.

ptArray[0].x = 20;
ptArray[0].y = 100;
ptArray[1].x = 30;
ptArray[1].y = 110;
lSize = 2;   // two points

VariantInit( &var );
VariantInit( &varPK );
SAFEARRAY* psa = SafeArrayCreateVector( VT_I4, 0, lSize*2 );
if( psa )
  if( SUCCEEDED( hr = SafeArrayAccessData( psa, (void**)&plongArray) ))
      for( long i = 0; i < lSize; i++ ) 
         plongArray[2*i] = ptArray[i].x;
         plongArray[2*i+1] = ptArray[i].y;
      hr = SafeArrayUnaccessData( psa );

      if ( SUCCEEDED( hr ) ) 
         var.vt     = VT_ARRAY | VT_I4;
         var.parray = psa;

        // varPK (packet description) is currently reserved, so it is
        // just empty variant for now.
         pInk->CreateStroke( var, varPK, &pInkStrokeDisp );   
VariantClear( &var );
VariantClear( &varPK );


The supported string format for COM is BSTR. A BSTR has a pointer to a zero-terminated string, but it also contains the length of the string (in bytes, not counting the terminator), which is stored in the 4 bytes immediately preceding the first character of the string.

For more information on BSTR, see BSTR Data Type.

This C++ sample shows how to set the factoid on an InkRecognizerContext using a BSTR.

IInkRecognizerContext* pRecognizerContext = NULL;
result = CoCreateInstance(CLSID_InkRecognizerContext, 
    (void **) &pRecognizerContext);
if SUCCEEDED(result)
    BSTR bstrFactoid = SysAllocString(FACTOID_DATE);
    result = pRecognizerContext->put_Factoid(bstrFactoid);
    if SUCCEEDED(result)
      // Use recognizer context...
      // ...