BUG: Multithreaded Tablet PC Applications May Quit Because of Memory Corruption (827638)



The information in this article applies to:

  • Microsoft Windows XP Tablet PC Edition Software Development Kit (SDK) 1.1
  • Microsoft Windows XP Tablet PC Edition Software Development Kit (SDK) 1.5

SYMPTOMS

Under certain conditions, a class that derives from or that uses either the Microsoft.Ink.Ink class (Managed library) or the InkDisp component object model (COM) object in the Tablet PC functions may experience a memory corruption. The result of this corruption may be that the application quits.

CAUSE

This problem occurs because an internal component of the Ink object may not be correctly reference counted when the internal component is used in a multithreaded application.

WORKAROUND

To work around this problem, you (the developer) can create a dummy Ink object at application load-time and then destroy the dummy Ink object when the application shuts down.

Important The dummy Ink object must be maintained throughout the lifetime of the application; otherwise the heap corruption may still occur.

RESOLUTION

The following Microsoft Visual C++ code shows how to create and to destroy a dummy Ink object.
 // WorkAround.cpp 
 //
    CComPtr<IInkDisp> CreateDummyInk()
{
    // The ISF data for the dummy Ink object must be as follows:
    static const BYTE isf [] = 
    {
	    0x00, 0xCC, 0x01, 0x00, 0x00, 0x00, 0xC0, 0xB8,
	    0x02, 0xB0, 0xEA, 0x01, 0x03, 0xBD, 0x01, 0x48,

	    0x01, 0x49, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00,
	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

	    0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00,
	    0x38, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00,

	    0x3B, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00,
	    0x00, 0x00, 0x41, 0x00, 0x00, 0x3F, 0x00, 0x00,

	    0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x42,
	    0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x43,

	    0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00,
	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	    0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x32, 0x00,

	    0x00, 0x00, 0x00, 0x52, 0x01, 0x08, 0x00, 0x44,
	    0x32, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	    0x00, 0x00, 0x45, 0x32, 0x3D, 0x00, 0x00, 0x00,

	    0x00, 0x40, 0x00, 0x00, 0x47, 0x01, 0x35, 0x00,
	    0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x36, 0x00,

	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
	    0x00, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x46,

	    0x32, 0x57, 0x09, 0x00, 0x00, 0x00, 0x53, 0x00,
	    0x00, 0x00, 0x00, 0x4B, 0x01, 0x08, 0x00, 0x56,

	    0x00, 0x00, 0x00, 0x00, 0x51, 0x32, 0x50, 0x32,
	    0x4A, 0x01, 0x08, 0x00, 0x0A, 0x01, 0x00,

    };

    // The following GUIDs are provided as an example of how to add
    // custom extended property GUIDs into the Ink objects.
    static const GUID guidExtProps [] =
    {
        // {B2B38458-097B-41a1-B5B3-C46345DCB28B}
        { 0xb2b38458, 0x097b, 0x41a1, { 0xb5, 0xb3, 0xc4, 0x63, 0x45, 0xdc, 0xb2, 0x8b } },

        // {D5BABAB9-8FBE-4581-B4F5-379EF8C82E91}
        { 0xd5babab9, 0x8fbe, 0x4581, { 0xb4, 0xf5, 0x37, 0x9e, 0xf8, 0xc8, 0x2e, 0x91 } },
    };

    CComVariant data;
    BYTE *pBuff = NULL;

    // The variant is a one dimensional array of BYTEs.
    data.vt = VT_ARRAY | VT_UI1;
    SAFEARRAYBOUND sab;
    sab.lLbound = 0;
    sab.cElements = sizeof(isf);

    // Allocate memory for the SafeArray.
    data.parray = SafeArrayCreate(VT_UI1, 1, &sab);

    // Copy ISF into the SafeArray.
    SafeArrayAccessData(data.parray, (VOID **)(&pBuff));
    memcpy(pBuff, isf, sizeof(isf));
    SafeArrayUnaccessData(data.parray);

    CComPtr<IInkDisp> spInk;

    // Create an Ink object.
    HRESULT hr = spInk.CoCreateInstance(CLSID_InkDisp);

    // Load the Ink data.
    if(SUCCEEDED(hr))
        	hr = spInk->Load(data);

    CComPtr<IInkDrawingAttributes> spDrawAttrs;
    CComPtr<IInkStrokeDisp> spStroke;
    if(SUCCEEDED(hr))
    {
        	// Get DrawingAttributes object of the first stroke.
        	// You do not have to check for stroke count, because
        	// the ISF above contains one stroke.
        	CComPtr<IInkStrokes> spStrokes;
        	hr = spInk->get_Strokes(&spStrokes);
        	if(SUCCEEDED(hr))
        	{
            		hr = spStrokes->Item(0, &spStroke);
            		if(SUCCEEDED(hr))
            		{			
                // Get the DrawingAttributes object from the stroke.
                			hr = spStroke->get_DrawingAttributes(&spDrawAttrs);
            			}
        		}
    	}

    // Add custom properties to DrawingAttributes object.
    if(SUCCEEDED(hr))
    {
        	// Get the extended properties from DrawingAttributes object.
        	CComPtr<IInkExtendedProperties> spExtProperties;
        	hr = spDrawAttrs->get_ExtendedProperties(&spExtProperties);

        	// Enumerate all the properties in the static array.
        	ULONG cExtPropCount = sizeof(guidExtProps)/sizeof(GUID);

        	// Add enumerated properties to the extended property list.
        	for(ULONG i = 0; SUCCEEDED(hr) && (i < cExtPropCount); ++i)
        	{
            		CComVariant vtData;
            		vtData.vt = VT_UI1 | VT_ARRAY;

            		// The data size for extended properties can differ from 
            		// the actual size that would be used in the application.
            		vtData.parray = SafeArrayCreateVector(VT_UI1, 0, sizeof(DWORD));
            		BYTE * pData = NULL;
            		if(NULL != vtData.parray)
            		{
                			hr = SafeArrayAccessData(vtData.parray, (void **)&pData);
            		}
            		else
                			hr = E_OUTOFMEMORY;
            		if(SUCCEEDED(hr))
            		{
                			memset(pData, 0, sizeof(DWORD));
                			hr = SafeArrayUnaccessData(vtData.parray);
            		}
            		if(SUCCEEDED(hr))
            		{
                			CComPtr<IInkExtendedProperty> spExtProp;
                			hr = spExtProperties->Add(CComBSTR(guidExtProps[i]), vtData, &spExtProp);
            		}
        	}

        	// Add DrawingAttributes back to the stroke object
        	// so that the extended properties stick to the 
        	// DrawingAttributes object.
        	if(SUCCEEDED(hr))
            		hr = spStroke->putref_DrawingAttributes(spDrawAttrs);
    	}

    // If anything went wrong, unref the Ink object.
    if(FAILED(hr))
        		spInk = NULL;

    return spInk;
}

MORE INFORMATION

In the sample code in the "Resolution" section, the data size for a custom extended property may differ from the actual size that is used in the application. The sizes may be different because the operating system does not force the data size of a custom extended property to be the same size as the property that the application uses. Therefore, it is acceptable to create the same custom extended property with a different data size.

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

REFERENCES

For additional information, visit the following Microsoft Web sites:

Modification Type:MinorLast Reviewed:2/11/2004
Keywords:kbDSXGlobal2003Swept kbbug KB827638 kbAudDeveloper