TSF ITfTextEditSink::OnEndEdit observes direct ASCII edits in Packaged Notepad, but not in Win32 EDIT or RichEdit — is this expected behavior? Details

soso 40 Reputation points
2026-09-18T13:30:53.8233333+00:00

Hello,

I am developing a Windows text input support application using the Text Services Framework (TSF), and I would like to clarify the expected behavior of ITfTextEditSink::OnEndEdit / ITfEditRecord for direct ASCII input.

At this stage, the implementation is read-only for diagnostics. It does not modify text.

The purpose of this test is to detect a shortcut such as:


when the user types it directly from the hardware keyboard, without creating an IME composition.

The user should be able to keep Microsoft IME as the normal input method. We do not want to require the user to switch input profiles just to use the shortcut feature.

Test environment

  • Windows 11 x64
  • TSF text service
  • ITfTextEditSink
  • ITfEditRecord
  • No SetText
  • No clipboard operations
  • No SendInput
  • No keyboard hook
  • No simulated Backspace/retyping
  • No polling-based text monitoring

The text service is successfully activated and the document context / text edit sink is attached before the test.

Test procedure

The initial text is:


The caret is placed immediately after 。 and before the ASCII space.

The user then types the following directly from the keyboard:


The expected final text is:


Result 1: Packaged Notepad

With the current Windows Notepad application, the TSF diagnostic path successfully observes the direct ASCII edits.

We receive text edit observations corresponding to:


After the final l, we can read the text immediately before the caret and reconstruct the exact shortcut range /mail.

No IME composition is involved.

So in Packaged Notepad, this read-only detection method works.

Result 2: Standard Win32 EDIT control

We created a minimal Win32 test application containing a standard Unicode EDIT control.

The following conditions are confirmed:

  • the TSF text service is activated;
  • the host is accepted by the diagnostic text service;
  • the context is attached;
  • the text edit sink setup succeeds;
  • the control has focus;
  • the final text is correctly changed by the user's keyboard input to:

However, during the direct ASCII /mail input interval, our OnEndEdit / ITfEditRecord diagnostic path produces no committed-text edit observation.

The text is visibly inserted into the control, but the edit is not observable through the same TSF path that works in Packaged Notepad.

Result 3: RichEdit

We repeated the same test using:


The result is the same as with the standard EDIT control:

  • TSF service activation succeeds;
  • context/sink attachment succeeds;
  • focus remains on the RichEdit control;
  • the final text is correctly:

but the direct ASCII insertion produces no committed-text edit observation through our current ITfTextEditSink / ITfEditRecord diagnostic path.

Questions

Is this host-dependent behavior expected by TSF design?

In particular, is a TSF text service guaranteed to receive ITfTextEditSink::OnEndEdit notifications for direct committed ASCII edits in standard Win32 EDIT and RichEdit controls when no TSF composition is active?

  1. If such notifications are not guaranteed, what is the Microsoft-recommended supported architecture for observing direct keyboard text input such as /mail across Windows edit controls?
  2. Is there a supported TSF architecture that allows this while the user continues to use Microsoft IME as the active input method, without requiring the user to manually switch to another input profile?

We specifically want to avoid unsupported or intrusive approaches such as keyboard hooks, clipboard monitoring, SendInput, or simulated deletion/retyping.

At this point, we are trying to determine whether the difference between Packaged Notepad and standard EDIT/RichEdit is:

  • expected TSF behavior;
  • a limitation of this observation approach; or
  • an indication that our TSF integration for these controls is incomplete.

Any clarification on the expected TSF behavior, or guidance toward the appropriate supported architecture, would be greatly appreciated.

Thank you.Hello,

I am developing a Windows text input support application using the Text Services Framework (TSF), and I would like to clarify the expected behavior of ITfTextEditSink::OnEndEdit / ITfEditRecord for direct ASCII input.

At this stage, the implementation is read-only for diagnostics. It does not modify text.

The purpose of this test is to detect a shortcut such as:


when the user types it directly from the hardware keyboard, without creating an IME composition.

The user should be able to keep Microsoft IME as the normal input method. We do not want to require the user to switch input profiles just to use the shortcut feature.

Test environment

  • Windows 11 x64
  • TSF text service
  • ITfTextEditSink
  • ITfEditRecord
  • No SetText
  • No clipboard operations
  • No SendInput
  • No keyboard hook
  • No simulated Backspace/retyping
  • No polling-based text monitoring

The text service is successfully activated and the document context / text edit sink is attached before the test.

Test procedure

The initial text is:


The caret is placed immediately after 。 and before the ASCII space.

The user then types the following directly from the keyboard:


The expected final text is:


Result 1: Packaged Notepad

With the current Windows Notepad application, the TSF diagnostic path successfully observes the direct ASCII edits.

We receive text edit observations corresponding to:


After the final l, we can read the text immediately before the caret and reconstruct the exact shortcut range /mail.

No IME composition is involved.

So in Packaged Notepad, this read-only detection method works.

Result 2: Standard Win32 EDIT control

We created a minimal Win32 test application containing a standard Unicode EDIT control.

The following conditions are confirmed:

  • the TSF text service is activated;
  • the host is accepted by the diagnostic text service;
  • the context is attached;
  • the text edit sink setup succeeds;
  • the control has focus;
  • the final text is correctly changed by the user's keyboard input to:

However, during the direct ASCII /mail input interval, our OnEndEdit / ITfEditRecord diagnostic path produces no committed-text edit observation.

The text is visibly inserted into the control, but the edit is not observable through the same TSF path that works in Packaged Notepad.

Result 3: RichEdit

We repeated the same test using:


The result is the same as with the standard EDIT control:

  • TSF service activation succeeds;
  • context/sink attachment succeeds;
  • focus remains on the RichEdit control;
  • the final text is correctly:

but the direct ASCII insertion produces no committed-text edit observation through our current ITfTextEditSink / ITfEditRecord diagnostic path.

Questions

Is this host-dependent behavior expected by TSF design?

In particular, is a TSF text service guaranteed to receive ITfTextEditSink::OnEndEdit notifications for direct committed ASCII edits in standard Win32 EDIT and RichEdit controls when no TSF composition is active?

  1. If such notifications are not guaranteed, what is the Microsoft-recommended supported architecture for observing direct keyboard text input such as /mail across Windows edit controls?
  2. Is there a supported TSF architecture that allows this while the user continues to use Microsoft IME as the active input method, without requiring the user to manually switch to another input profile?

We specifically want to avoid unsupported or intrusive approaches such as keyboard hooks, clipboard monitoring, SendInput, or simulated deletion/retyping.

At this point, we are trying to determine whether the difference between Packaged Notepad and standard EDIT/RichEdit is:

  • expected TSF behavior;
  • a limitation of this observation approach; or
  • an indication that our TSF integration for these controls is incomplete.

Any clarification on the expected TSF behavior, or guidance toward the appropriate supported architecture, would be greatly appreciated.

Thank you.

Windows development | Windows API - Win32
0 comments No comments

Answer accepted by question author
Taki Ly (WICLOUD CORPORATION) 5,630 Reputation points Microsoft External Staff Moderator
2026-09-21T02:11:12.94+00:00

Hello @soso ,

I was able to reproduce your three results with a minimal read-only text service, and the outcome points to the host, not to your TSF integration.

What I tested (Windows 11 x64)

A minimal TIP that only advises ITfThreadMgrEventSink and, for the focused document manager, ITfTextEditSink. In OnEndEdit it logs ITfEditRecord::GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT), the selection, the 12 characters before the caret and ITfContext::GetStatus. No SetText, no hooks, no clipboard. Initial text こんにちは。 with the caret after 。, then /mail typed through the normal keyboard input path.

Results:

  • Standard Unicode EDIT: sink attach succeeded, but the context reports TF_STATUS.dwStaticFlags = TS_SS_TRANSITORY. The text was inserted correctly, and OnEndEdit fired zero times during /mail.
  • RICHEDIT50W with the default edit style (EM_GETEDITSTYLE returns 0): same transitory context, zero OnEndEdit.
  • RICHEDIT50W after EM_SETEDITSTYLE(SES_USECTF, SES_USECTF): regular context (not transitory), five OnEndEdit notifications carrying / m a i l at ACP 6..10, and the text before the caret reconstructs こんにちは。/mail.
  • Packaged Notepad (editor window class RichEditD2DPT): regular context, five notifications, identical to the previous item.

So the same RichEdit engine and the same TIP go from "nothing observable" to "every character observable" by flipping one host-side flag. That rules out a missing piece in your text service.

Why this happens

A text service never talks to the control directly; everything goes through the TSF manager (Architecture). For the manager to know about an edit made by the application itself (direct typing handled by the control), the host has to implement a text store (ITextStoreACP) and call ITextStoreACPSink::OnTextChange; the advise sink "receives notifications when the text store is modified by something other than the manager, such as user input to the application" (Text Stores, OnTextChange). Only then does the manager surface the change to text services as ITfTextEditSink::OnEndEdit with a read-only cookie (OnEndEdit).

  • The classic EDIT control has no TSF text store. TSF reaches it through the IMM32 compatibility layer, which gives your TIP a transitory context (TS_SS_TRANSITORY, "expected to have a short usage cycle") that only holds composition text. Direct WM_CHAR input is inserted by the control itself and is never reported to TSF, which is why your sink setup succeeds but no committed-text notification arrives.
  • RichEdit does have TSF support, but it is off by default: SES_USECTF "Turns on TSF support. (default: 0)" (EM_GETEDITSTYLE, EM_SETEDITSTYLE). Without it, RichEdit behaves like EDIT from a TIP's point of view. Notepad's editor is RichEdit with TSF enabled, which matches what you saw.

Your questions

  1. Is it expected? Yes, this is host-dependent by design, and it is also a limitation of the observation approach rather than a gap in your integration. TSF does not guarantee OnEndEdit for direct edits in a host that does not expose a text store, and neither EDIT nor a default RichEdit does.
  2. Supported architecture across Windows edit controls: within TSF there is none for TSF-unaware hosts, because the data simply does not reach the manager. What I can suggest: in hosts you control, use RichEdit with SES_USECTF or implement ITextStoreACP; in your TIP, check ITfContext::GetStatus for TS_SS_TRANSITORY to detect a host where the document is not observable and degrade gracefully. The only TSF-level data available in such hosts is keystrokes via ITfKeyEventSink, which requires your TIP to be the active keyboard TIP. For read-only observation in third-party apps you could also evaluate UI Automation text change events, but I have not verified that path.
  3. Coexisting with Microsoft IME: only one keyboard-category TIP is active per language, so a keyboard TIP cannot run alongside Microsoft IME. This does not change the result above though: whether or not your service is the active keyboard TIP, a host without a text store still reports nothing. In my test the observer TIP was the active keyboard TIP for en-US while Microsoft Pinyin stayed active for zh-CN, and the host results were exactly as listed.

One more thing, the code blocks in your question render empty on my side (initial text, typed sequence, observations, RichEdit class name). If any detail differs from what I assumed above, please re-add them and I will take another look.

Hope these information help. If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide for your confirmation.

Thank you.

Was this answer helpful?

1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.