I recently had two people ask me the same question:
"Why can't I insert more than one character into a composition on Notepad?"
It's actually a bit more complicated than that, since this behavior only appears to happen on Windows XP with a US English text service. (Japanese text services appear to work correctly.)
I just had a chance to figure out what was going on here, and, since it doesn't seem to be documented anywhere else, I thought I'd post the answer so that the next poor sod doesn't have to spend eternity scratching his head wondering why he can't get it to work.
After a longish debugging bout, and a bunch of searching through the XP source code, I have the answer.
The answer is that there's a Text Event Sink attached to the TSF-unaware context, and when the context changes, this event sink looks to see if the changed text has the GUID_PROP_COMPOSING property attached to it. If it doesn't, it terminates the composition.
So, if you want to have your text services insert more than one character into a composition, you need to make sure that your text has the GUID_PROP_COMPOSING property set to 1.
I had previously written some code to manage this property. However, you cannot explicitly set the GUID_PROP_COMPOSING property -
ITfProperty::SetValue() will return TF_E_READONLY if you do.
What you do need to do is use the ITfComposition::ShiftStart and ITfComposition::ShiftEnd methods to move the start and ending points of your composition to cover the text. These methods will update the GUID_PROP_COMPOSING property directly.
The code to do that would look like this: BOOL CTextService::_SetCompositionComposing(TfEditCookie ec, ITfContext *pContext)
// the composition requires a range and the context it lives in
if (_pComposition->GetRange(&pRangeComposition) != S_OK)
hr = E_FAIL;
// get our the display attribute property
if (pContext->GetProperty(GUID_PROP_COMPOSING, &pComposingProperty) == S_OK)
// set the value over the range
var.vt = VT_I4;
var.lVal = 1;
hr = pComposingProperty->SetValue(ec, pRangeComposition, &var);
return (hr == S_OK);
When you're ready to finalize your composition, you should clear the property over the composed text by calling ITfProperty::Clear().
Note that none of the sample text services do this.