void CMarkTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec)
{
    ITfRange *pRangeComposition;
    ITfContext *pContext;
    ITfProperty *pDisplayAttributeProperty;

    // we need a range and the context it lives in
    if (_pComposition->GetRange(&pRangeComposition) != S_OK)
        return;

    if (pRangeComposition->GetContext(&pContext) != S_OK)
    {
        pContext = NULL;
        goto Exit;
    }

    // get our the display attribute property
    if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
        goto Exit;

    // clear the value over the range
    pDisplayAttributeProperty->Clear(ec, pRangeComposition);

    pDisplayAttributeProperty->Release();

Exit:
    pRangeComposition->Release();
    SafeRelease(pContext);
}
BOOL CMarkTextService::_SetCompositionDisplayAttributes(TfEditCookie ec)
{
    ITfRange *pRangeComposition;
    ITfContext *pContext;
    ITfProperty *pDisplayAttributeProperty;
    VARIANT var;
    HRESULT hr;

    // we need a range and the context it lives in
    if (_pComposition->GetRange(&pRangeComposition) != S_OK)
        return FALSE;

    hr = E_FAIL;

    if (pRangeComposition->GetContext(&pContext) != S_OK)
    {
        pContext = NULL;
        goto Exit;
    }

    // get our the display attribute property
    if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
        goto Exit;

    // set the value over the range
    // the application will use this guid atom to lookup the acutal rendering information
    var.vt = VT_I4; // we're going to set a TfGuidAtom
    var.lVal = _gaDisplayAttribute; // our cached guid atom for c_guidMarkDisplayAttribute

    hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var);

    pDisplayAttributeProperty->Release();

Exit:
    pRangeComposition->Release();
    SafeRelease(pContext);
    return (hr == S_OK);
}
STDAPI CMarkTextService::OnEndEdit(ITfContext *pContext, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord)
{
    ITfRange *pRangeComposition;
    IEnumTfRanges *pEnumRanges;
    ITfRange *pRange;
    ITfContext *pCompositionContext;
    TF_SELECTION tfSelection;
    BOOL fResult;
    BOOL fCancelComposition;
    ULONG cFetched;

    if (_pComposition == NULL)
        return S_OK;

    // are we responsible for the edit?
    if (pContext->InWriteSession(_tfClientId, &fResult) == S_OK && fResult)
        return S_OK;

    // is this the context our composition lives in?
    if (_pComposition->GetRange(&pRangeComposition) != S_OK)
        return S_OK;

    if (pRangeComposition->GetContext(&pCompositionContext) != S_OK)
        goto Exit;

    fResult = IsEqualUnknown(pCompositionContext, pContext);

    pCompositionContext->Release();

    if (!fResult)
        goto Exit; // different context

    fCancelComposition = FALSE;

    // we're composing in this context, cancel the composition if anything suspicious happened

    // did the selection move outside the composition?
    if (pEditRecord->GetSelectionStatus(&fResult) == S_OK && fResult)
    {
        if (pContext->GetSelection(ecReadOnly, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) == S_OK &&
            cFetched == 1)
        {
            if (_pComposition->GetRange(&pRangeComposition) == S_OK)
            {
                fResult = IsRangeCovered(ecReadOnly, tfSelection.range, pRangeComposition);

                pRangeComposition->Release();

                if (!fResult)
                {
                    fCancelComposition = TRUE;
                }
            }
            tfSelection.range->Release();
        }
    }

    if (fCancelComposition)
        goto CancelComposition;

    // did someone else edit the document text?
    if (pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0, &pEnumRanges) == S_OK)
    {
        // is the enumerator empty?
        if (pEnumRanges->Next(1, &pRange, NULL) == S_OK)
        {
            pRange->Release();
            fCancelComposition = TRUE;
        }
        pEnumRanges->Release();
    }

    if (fCancelComposition)
    {
CancelComposition:
        // we need a write edit session to cancel the composition
        _TerminateCompositionInContext(pContext);
    }

Exit:
    pRangeComposition->Release();
    return S_OK;
}
STDAPI CCompositionEditSession::DoEditSession(TfEditCookie ec)
{
    ITfInsertAtSelection *pInsertAtSelection;
    ITfContextComposition *pContextComposition;
    ITfComposition *pComposition;
    ITfRange *pRangeComposition;
    ITfRange *pRangeInsert;
    ITfContext *pCompositionContext;
    HRESULT hr;
    BOOL fEqualContexts;

    // get an interface on the context we can use to deal with compositions
    if (_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition) != S_OK)
        return E_FAIL;

    hr = E_FAIL;

    pInsertAtSelection = NULL;

    if (_pMark->_IsComposing())
    {
        // we have a composition, let's terminate it
        
        // it's possible our current composition is in another context...let's find out
        fEqualContexts = TRUE;
        if (_pMark->_GetComposition()->GetRange(&pRangeComposition) == S_OK)
        {
            if (pRangeComposition->GetContext(&pCompositionContext) == S_OK)
            {
                fEqualContexts = IsEqualUnknown(pCompositionContext, _pContext);
                if (!fEqualContexts)
                {
                    // need an edit session in the composition context
                    _pMark->_TerminateCompositionInContext(pCompositionContext);
                }
                pCompositionContext->Release();
            }
            pRangeComposition->Release();
        }

        // if the composition is in pContext, we already have an edit cookie
        if (fEqualContexts)
        {
            _pMark->_TerminateComposition(ec);
        }
    }
    else
    {
        // let's start a new composition over the current selection
        // this is totally contrived, a real text service would have
        // some meaningful logic to trigger this

        // first, test where a keystroke would go in the document if we did an insert
        // we need a special interface to insert text at the selection
        if (_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection) != S_OK)
        {
            pInsertAtSelection = NULL;
            goto Exit;
        }

        if (pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert) != S_OK)
            goto Exit;

        // start the composition
        if (pContextComposition->StartComposition(ec, pRangeInsert, _pMark, &pComposition) != S_OK)
        {
            pComposition = NULL;
        }

        pRangeInsert->Release();

        // _pComposition may be NULL even if StartComposition return S_OK, this mean the application
        // rejected the composition

        if (pComposition != NULL)
        {
            _pMark->_SetComposition(pComposition);
            // underline the composition text to give the user some feedback UI
            _pMark->_SetCompositionDisplayAttributes(ec);
        }
    }

    // if we make it here, we've succeeded
    hr = S_OK;

Exit:
    SafeRelease(pInsertAtSelection);
    pContextComposition->Release();

    return hr;
}