<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>TSF Aware : Text Services</title><link>http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx</link><description>Tags: Text Services</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Why you need to actually test instead of just looking at the source code</title><link>http://blogs.msdn.com/tsfaware/archive/2009/05/15/why-you-need-to-actually-test-instead-of-just-looking-at-the-source-code.aspx</link><pubDate>Fri, 15 May 2009 21:43:12 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9619132</guid><dc:creator>Eric Brown</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/9619132.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=9619132</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=9619132</wfw:comment><description>&lt;p&gt;In a previous &lt;a href="http://blogs.msdn.com/tsfaware/archive/2007/12/22/things-the-documentation-left-out-part-n.aspx"&gt;post&lt;/a&gt;, I mentioned that you need to set the GUID_PROP_COMPOSING property across text in a composition, or else Windows XP will terminate the composition.&lt;/p&gt;  &lt;p&gt;That’s true.&lt;/p&gt;  &lt;p&gt;I then provided a code snippet to set that property. &lt;/p&gt;  &lt;p&gt;That snippet is wrong.&lt;/p&gt;  &lt;p&gt;It turns out that you can’t set the GUID_PROP_COMPOSING property via ITfProperty::SetValue.&amp;#160; If you try, you’ll get a &lt;a href="http://msdn.microsoft.com/en-us/library/ms629001(vs.85).aspx"&gt;TF_E_READONLY&lt;/a&gt; error (even if you are in a read-write edit session), which can cause great consternation.&lt;/p&gt;  &lt;p&gt;Instead, you need to call &lt;a href="http://msdn.microsoft.com/en-us/library/ms538689(VS.85).aspx"&gt;ITfComposition::ShiftStart&lt;/a&gt; and/or &lt;a href="http://msdn.microsoft.com/en-us/library/ms538684(VS.85).aspx"&gt;ITfComposition::ShiftEnd&lt;/a&gt; to set the bounds of the composition after changing the text in your composition.&lt;/p&gt;  &lt;p&gt;These calls update the GUID_PROP_COMPOSING property internally.&lt;/p&gt;  &lt;p&gt;Sorry for the confusion.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9619132" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Where are the TSF Samples?</title><link>http://blogs.msdn.com/tsfaware/archive/2008/10/22/where-are-the-tsf-samples.aspx</link><pubDate>Wed, 22 Oct 2008 23:57:41 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9011695</guid><dc:creator>Eric Brown</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/9011695.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=9011695</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=9011695</wfw:comment><description>&lt;p&gt;Many people have emailed me asking about the TSF samples on MSDN.&amp;#160; They’re supposed to be on MSDN code gallery, although they don’t appear to be there.&lt;/p&gt;  &lt;p&gt;They are, however, part of the &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=F26B1AA4-741A-433A-9BE5-FA919850BDBF&amp;amp;displaylang=en"&gt;Windows SDK&lt;/a&gt;. After installation, you'll find them in %programfiles%\Microsoft SDKs\Windows\v6.1\Samples\winui\Input\tsf.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;There are actually more samples in the Windows SDK than were on MSDN, including some examples of how to write a text store, as well as how to interact with TSF in UILess mode.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9011695" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/tsf/default.aspx">tsf</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Applications/default.aspx">Applications</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/TSF+Manager/default.aspx">TSF Manager</category></item><item><title>Categories</title><link>http://blogs.msdn.com/tsfaware/archive/2008/03/14/categories.aspx</link><pubDate>Fri, 14 Mar 2008 17:00:36 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8186114</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/8186114.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=8186114</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=8186114</wfw:comment><description>&lt;p&gt;In an earlier post on &lt;a href="http://blogs.msdn.com/tsfaware/archive/2007/05/30/what-is-a-keyboard.aspx"&gt;keyboards&lt;/a&gt;, I talked briefly about text service categories.&amp;#160; I'd like to talk more about categories.&lt;/p&gt;  &lt;p&gt;TSF will make sure that at most one text service in any category is enabled at any given time.&lt;/p&gt;  &lt;p&gt;So, for example, you can enable one text service with &lt;b&gt;GUID_TFCAT_TIP_KEYBOARD&lt;/b&gt;, one text service with &lt;b&gt;GUID_TFCAT_TIP_SPEECH&lt;/b&gt;, and one text service with &lt;strong&gt;GUID_TFCAT_TIP_HANDWRITING&lt;/strong&gt;, and all three text services will be active at the same time.&lt;/p&gt;  &lt;p&gt;But if you &lt;a href="http://msdn2.microsoft.com/en-us/library/aa381942.aspx"&gt;enable another text service&lt;/a&gt; with &lt;b&gt;GUID_TFCAT_TIP_KEYBOARD&lt;/b&gt;, the first one will be deactivated.&amp;#160; &lt;/p&gt;  &lt;p&gt;If you don't want this to happen, then you should call &lt;a href="http://msdn2.microsoft.com/en-us/library/ms538584(VS.85).aspx"&gt;RegisterCategory&lt;/a&gt; with your own category GUID.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8186114" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/tsf/default.aspx">tsf</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Visual Studio 2008 issues</title><link>http://blogs.msdn.com/tsfaware/archive/2008/01/17/visual-studio-2008-issues.aspx</link><pubDate>Fri, 18 Jan 2008 01:52:49 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7144329</guid><dc:creator>Eric Brown</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/7144329.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=7144329</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=7144329</wfw:comment><description>&lt;p&gt;If you're building a text service DLL, you almost certainly don't want to use Visual Studio 2008's compiler.&amp;#160; The problem is that Visual Studio 2008 uses a new C Runtime Library, and if you build your text service with Visual Studio, your text service likely won't load in all applications.&amp;#160; (Plus, you would have to redistribute the C Runtime Library.)&lt;/p&gt;  &lt;p&gt;What to do instead?&lt;/p&gt;  &lt;p&gt;Well, I would recommend installing the Vista (or XP) DDK and use the &lt;a href="http://ddkwizard.assarbad.net/"&gt;DDKWizard&lt;/a&gt; instead.&amp;#160; The DDK comes with its own C/C++ compiler that uses the C Runtime Library that ships with the OS (and won't cause problems with other applications), and the DDKWizard will let you use all of Visual Studio's awesome capabilities.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7144329" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Things the Documentation left out, part N</title><link>http://blogs.msdn.com/tsfaware/archive/2007/12/22/things-the-documentation-left-out-part-n.aspx</link><pubDate>Sat, 22 Dec 2007 18:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6831043</guid><dc:creator>Eric Brown</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/6831043.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=6831043</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=6831043</wfw:comment><description>&lt;P&gt;I recently had two people ask me the same question:&lt;/P&gt;
&lt;P&gt;"Why can't I insert more than one character into a composition on Notepad?"&lt;/P&gt;
&lt;P&gt;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.&amp;nbsp; (Japanese text services appear to work correctly.)&lt;/P&gt;
&lt;P&gt;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.&lt;/P&gt;
&lt;P&gt;After a longish debugging bout, and a bunch of searching through the XP source code, I have the answer.&lt;/P&gt;
&lt;P&gt;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.&amp;nbsp; If it doesn't, it terminates the composition.&lt;/P&gt;
&lt;P&gt;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.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;I had previously written some code to manage this property.&amp;nbsp; However, you cannot explicitly set the GUID_PROP_COMPOSING property -&lt;/P&gt;
&lt;P&gt;ITfProperty::SetValue() will return TF_E_READONLY if you do.&lt;/P&gt;
&lt;P&gt;What you &lt;STRONG&gt;do&lt;/STRONG&gt; 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.&amp;nbsp; These methods will update the GUID_PROP_COMPOSING property directly.&lt;/P&gt;
&lt;P&gt;&lt;STRIKE&gt;The code to do that would look like this:&lt;/STRIKE&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&lt;STRIKE&gt;&lt;/STRIKE&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRIKE&gt;BOOL CTextService::_SetCompositionComposing(TfEditCookie ec, ITfContext *pContext)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ITfRange *pRangeComposition;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ITfProperty *pComposingProperty;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HRESULT hr; &lt;/STRIKE&gt;
&lt;P&gt;&lt;STRIKE&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // the composition requires a range and the context it lives in&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_pComposition-&amp;gt;GetRange(&amp;amp;pRangeComposition) != S_OK)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return FALSE; &lt;/STRIKE&gt;
&lt;P&gt;&lt;STRIKE&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; hr = E_FAIL; &lt;/STRIKE&gt;
&lt;P&gt;&lt;STRIKE&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // get our the display attribute property&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (pContext-&amp;gt;GetProperty(GUID_PROP_COMPOSING, &amp;amp;pComposingProperty) == S_OK)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; VARIANT var;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // set the value over the range&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var.vt = VT_I4; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var.lVal = 1; &lt;/STRIKE&gt;
&lt;P&gt;&lt;STRIKE&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hr = pComposingProperty-&amp;gt;SetValue(ec, pRangeComposition, &amp;amp;var); &lt;/STRIKE&gt;
&lt;P&gt;&lt;STRIKE&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pComposingProperty-&amp;gt;Release();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/STRIKE&gt;
&lt;P&gt;&lt;STRIKE&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pRangeComposition-&amp;gt;Release();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (hr == S_OK);&lt;BR&gt;} &lt;/STRIKE&gt;
&lt;P&gt;&lt;STRIKE&gt;When you're ready to finalize your composition, you should clear the property over the composed text by calling ITfProperty::Clear(). &lt;/STRIKE&gt;
&lt;P&gt;Note that none of the sample text services do this.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6831043" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Applications/default.aspx">Applications</category></item><item><title>ITfCompartmentEventSink::OnChange means what it says...</title><link>http://blogs.msdn.com/tsfaware/archive/2007/11/05/itfcompartmenteventsink-onchange-means-what-it-says.aspx</link><pubDate>Mon, 05 Nov 2007 18:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5822820</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/5822820.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=5822820</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=5822820</wfw:comment><description>&lt;P&gt;I&amp;nbsp;got caught by this recently.&amp;nbsp; &lt;A href="http://msdn2.microsoft.com/en-us/library/ms538625.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms538625.aspx"&gt;ITfCompartmentEventSink::OnChange&lt;/A&gt; means what it says.&amp;nbsp; If you repeatedly store the same value into a global compartment, the event sinks will not fire.&amp;nbsp; If you store a different value into the compartment, the event sinks fire just fine.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5822820" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/tsf/default.aspx">tsf</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Compartment Values</title><link>http://blogs.msdn.com/tsfaware/archive/2007/11/02/compartment-values.aspx</link><pubDate>Fri, 02 Nov 2007 17:00:05 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5818008</guid><dc:creator>Eric Brown</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/5818008.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=5818008</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=5818008</wfw:comment><description>&lt;p&gt;I've been working with compartments recently, and I've run across a few 'features' that tripped me up.&amp;nbsp; I figure if I've run across them, others have too.&lt;/p&gt; &lt;p&gt;Although &lt;a href="http://msdn2.microsoft.com/en-us/library/ms538650.aspx"&gt;MSDN&lt;/a&gt; says that you can put integers, BSTRs, and interface pointers into a compartment, you can not&amp;nbsp;store interface pointers or strings into a global compartment (that you get from &lt;a href="http://msdn2.microsoft.com/en-us/library/ms628987.aspx"&gt;ITfThreadMgr::GetGlobalCompartment&lt;/a&gt;).&amp;nbsp; You can only store integers and empty variants.&lt;/p&gt; &lt;p&gt;Side note:&amp;nbsp; I would be&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;em&gt;very&lt;/em&gt; cautious about storing interface pointers into a (local) compartment, as well.&amp;nbsp; If an application has multiple UI threads (e.g., Explorer), TSF will load multiple instances of the text service (one instance per thread).&amp;nbsp; When each instance retrieves the interface pointer from an event sink&amp;nbsp;and tries to call it, the interface pointer is properly marshalled across the apartment boundaries (COM marshals the IUnknown in the variant returned from ITfCompartment::GetValue), but you do have a proxy in the way, and calls across the interface pointer will be quite slow.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5818008" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/tsf/default.aspx">tsf</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>The usual path</title><link>http://blogs.msdn.com/tsfaware/archive/2007/10/08/the-usual-path.aspx</link><pubDate>Mon, 08 Oct 2007 17:00:01 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5276716</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/5276716.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=5276716</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=5276716</wfw:comment><description>&lt;p&gt;Text Services Framework assumes that your text service follows a particular processing path.&amp;nbsp;&amp;nbsp;If your text service doesn't conform to these assumptions, then your programming job will be more complicated.&amp;nbsp; (Not impossible, just more complex.)&amp;nbsp; The text service samples on MSDN also follow these assumptions, but they aren't explicitly stated anywhere (that I know of).&amp;nbsp;&amp;nbsp; I've mentioned some of these assumptions in previous articles, but I thought I'd bring them together in one post.&lt;/p&gt; &lt;p&gt;Text Services makes the following assumptions:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Your service &lt;strong&gt;must&lt;/strong&gt; perform all changes to a context or range object within an edit session.&amp;nbsp; Text Services Framework enforces this assumption through the use of edit cookies.  &lt;li&gt;Your service should not assume that it is possible to request a synchronous edit session.&amp;nbsp; (I discussed this &lt;a href="http://blogs.msdn.com/tsfaware/archive/2007/05/17/rules-of-text-services.aspx"&gt;here&lt;/a&gt;.)  &lt;li&gt;Your service should track focus changes between applications and between controls within an application.&amp;nbsp; This means that your text service must install event sinks for &lt;a href="http://msdn2.microsoft.com/library/ms628969.aspx"&gt;ITfThreadFocusSink&lt;/a&gt;&amp;nbsp;and &lt;a href="http://msdn2.microsoft.com/library/ms628973.aspx"&gt;ITfThreadMgrEventSink&lt;/a&gt;.  &lt;li&gt;Your text service should use &lt;a href="http://msdn2.microsoft.com/library/ms538061.aspx"&gt;compositions&lt;/a&gt; to handle partially formed input.&amp;nbsp; &lt;/li&gt;&lt;/ol&gt; &lt;p&gt;This last assumption is the big one.&amp;nbsp; It can cause problems for text services that aren't keyboard-related (speech, for example).&lt;/p&gt; &lt;p&gt;The problem is that TSF handles the (admittedly, very difficult) job of interacting with non-TSF aware applications entirely through compositions.&amp;nbsp; Once you close the composition, TSF assumes that you're completely finished with that piece of input.&lt;/p&gt; &lt;p&gt;Unfortunately, it's hard to tell beforehand when you're done with a piece of dictation.&amp;nbsp; SAPI will tell you when it's recognized a piece of text, obviously, but, ideally, once you've dictated some text, you would like to be able to correct it.&amp;nbsp; That requires that you leave the composition open.&lt;/p&gt; &lt;p&gt;In an application that isn't TSF-aware, though, you need to close that composition as soon as you can (it's bad form to have large open compositions; most IMEs have compositions that are a few characters in size).&lt;/p&gt; &lt;p&gt;So there's a tradeoff here.&amp;nbsp; Dictation in Windows Vista currently closes the composition as soon as the text is recognized.&amp;nbsp; (In fact, it doesn't use compositions at all.)&amp;nbsp;&amp;nbsp; That works fine for TSF-aware applications, but causes problems with TSF-unaware applications.&amp;nbsp; In particular, once you've dictated some text, you can't correct it by voice.&amp;nbsp; That's why Windows Speech recognition makes you confirm every dictation into a TSF-unaware application.&amp;nbsp; &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5276716" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/dictation/default.aspx">dictation</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>What to do when you push a key</title><link>http://blogs.msdn.com/tsfaware/archive/2007/10/05/what-to-do-when-you-push-a-key.aspx</link><pubDate>Fri, 05 Oct 2007 17:00:01 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5276438</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/5276438.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=5276438</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=5276438</wfw:comment><description>&lt;p&gt;I received an interesting email the other day asking about how to get the character code from the parameters passed to the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms628603.aspx"&gt;ITfKeyEventSink::OnKeyDown&lt;/a&gt; method.&lt;/p&gt; &lt;p&gt;The answer is that most keyboard related text services only work with a particular keyboard layout, and the text service manages the mapping from virtual key codes to character codes.&lt;/p&gt; &lt;p&gt;It is actually surprisingly difficult to write a keyboard-related text service that is keyboard-layout agnostic, as there isn't a public API that exposes the details of the keyboard layout.&lt;/p&gt; &lt;p&gt;"What about &lt;a href="http://msdn2.microsoft.com/en-us/library/ms646316.aspx"&gt;ToAscii&lt;/a&gt;/&lt;a href="http://msdn2.microsoft.com/en-us/library/ms646320.aspx"&gt;ToUnicode&lt;/a&gt; (and the related APIs)", you say?&amp;nbsp; Well, unfortunately, those APIs don't actually work across all languages.&amp;nbsp; In particular, they don't deal well with dead keys and obscure shift states (AltGr, SGCAPS, etc.), and, of course, fail utterly with languages like Japanese, Chinese, and Korean.&amp;nbsp; Michael Kaplan has an extensive set of articles about using ToUnicode (summarized &lt;a href="http://blogs.msdn.com/michkap/archive/2006/04/22/581107.aspx"&gt;here&lt;/a&gt;).&lt;/p&gt; &lt;p&gt;If you want to write a text service that is completely agnostic as to keyboard layouts, then, most likely, you probably don't want to use &lt;a href="http://msdn2.microsoft.com/en-us/library/ms628601.aspx"&gt;ITfKeyEventSink&lt;/a&gt;.&amp;nbsp; What &lt;strong&gt;can&lt;/strong&gt; you use to monitor keyboard changes?&lt;/p&gt; &lt;p&gt;So far, the best recommendation I have is to use &lt;a href="http://msdn2.microsoft.com/en-us/library/ms628963.aspx"&gt;ITfTextEditSink::OnEndEdit&lt;/a&gt;, and&amp;nbsp;if either of the following two conditions are true, then you can conclude that&amp;nbsp;a keyboard made the changes:&amp;nbsp; &lt;/p&gt; &lt;p&gt;1) Call&amp;nbsp;&lt;a href="http://msdn2.microsoft.com/en-us/library/ms538905.aspx"&gt;ITfEditRecord::GetTextAndPropertyUpdates&lt;/a&gt; to see if it contains a range&amp;nbsp;with the property&amp;nbsp;&lt;a href="http://msdn2.microsoft.com/en-us/library/ms629017.aspx"&gt;GUID_PROP_COMPOSING&lt;/a&gt;. TSF will set this property on text that is part of a composition.  &lt;p&gt;2) Check if the context that you get from OnEndEdit has one or more ITfCompositionView objects within it.&amp;nbsp; Here's some sample code to demonstrate:  &lt;blockquote&gt; &lt;p&gt;hr = pContext-&amp;gt;QueryInterface(IID_ITfContextComposition,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (void **)&amp;amp;pContextComposition);&lt;br&gt;if (hr == S_OK)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IEnumITfCompositionView *pEnumCompositionView;  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; hr = pContextComposition-&amp;gt;EnumCompositions(&amp;amp;pEnumCompositionView);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (hr == S_OK)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ITfCompositionView *pCompositionView;  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (pEnumCompositionView-&amp;gt;Next(1, &amp;amp;pCompositionView, NULL) == S_OK)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ITfRange *pRange;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hr = pCompositionView-&amp;gt;GetRange(&amp;amp;pRange);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (hr == S_OK)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Do Stuff Here&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pRange-&amp;gt;Release();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pCompositionView-&amp;gt;Release();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pEnumCompositionView-&amp;gt;Release();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pContextComposition-&amp;gt;Release();&lt;br&gt;} &lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5276438" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/tsf/default.aspx">tsf</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Input Scopes</title><link>http://blogs.msdn.com/tsfaware/archive/2007/07/10/input-scopes.aspx</link><pubDate>Tue, 10 Jul 2007 17:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:3787583</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/3787583.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=3787583</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=3787583</wfw:comment><description>&lt;P&gt;Another useful, but underutilized, group of functions in TSF are those relating to Input Scopes.&amp;nbsp;&amp;nbsp; Input Scopes allow an application to define the sorts of things that are expected in this document (edit control, etc.).&amp;nbsp; For example, the Internet Explorer 7 address bar has an input scope of IS_URL | IS_DEFAULT | IS_ENUMSTRING - meaning that the address bar expects the input to be either a URL, one of the entries in its enumeration, or just plain anything (IE will default to searching for the string you typed).&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Input scopes were originally created for the Tablet PC, and a lot of the MSDN documentation is specifically related to the Tablet PC.&amp;nbsp; However, other input handlers (for example, speech) use input scopes as well to improve recognition.&amp;nbsp;&amp;nbsp; For example, Windows Speech Recognition has a custom URL language model that is used when a control specifies the IS_URL input scope.&amp;nbsp; This significantly improves recognitions on URLs that are part of the language model.&lt;/P&gt;
&lt;P&gt;The functions are very easy to use, if you have an hwnd for your control.&amp;nbsp; If you only want to set a single input scope (for example, IS_DIGITS for a numeric control), then &lt;A href="http://msdn2.microsoft.com/en-us/library/ms629025.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms629025.aspx"&gt;SetInputScope&lt;/A&gt;&amp;nbsp;is the function for you.&amp;nbsp; If you want to set multiple input scopes, or you want to use one of the more exotic input scopes like IS_PHRASELIST or IS_REGULAREXPRESSION (which return application-defined strings), then you will want to use &lt;A href="http://msdn2.microsoft.com/en-us/library/ms629026.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms629026.aspx"&gt;SetInputScopes&lt;/A&gt;, which allows you to pass an array of input scopes, along with other data.&amp;nbsp; Finally, Windows Vista adds &lt;A href="http://msdn2.microsoft.com/en-us/library/aa383417.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/aa383417.aspx"&gt;SetInputScopes2&lt;/A&gt;, which is a variant of the SetInputScopes function, but which takes a string enumerator object instead of an array of strings.&lt;/P&gt;
&lt;P&gt;One thing to take note is that if you call &lt;STRONG&gt;SetInputScope&lt;/STRONG&gt; (or one of its siblings), you need to call SetInputScope(hwnd, IS_DEFAULT) before the window is destroyed.&amp;nbsp; MSDN mentions this in the remarks; I want to emphasize it.&lt;/P&gt;
&lt;P&gt;If your control doesn't have an hwnd, or if you want to have multiple input scopes per document, the situation is a bit more complicated.&amp;nbsp;&amp;nbsp; MSDN&amp;nbsp;isn't very clear about how to make input scopes work in TSF-aware applications.&amp;nbsp; In the&amp;nbsp;description of the &lt;A href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx"&gt;ITfInputScope&lt;/A&gt; interface, MSDN&amp;nbsp;says&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;A TSF-aware application does not call &lt;B&gt;SetInputScope&lt;/B&gt; directly, but rather implements either &lt;A href="http://msdn2.microsoft.com/ms538384.aspx" mce_href="http://msdn2.microsoft.com/ms538384.aspx"&gt;ITextStoreACP&lt;/A&gt; or &lt;A href="http://msdn2.microsoft.com/ms538731.aspx" mce_href="http://msdn2.microsoft.com/ms538731.aspx"&gt;ITfContextOwner&lt;/A&gt; to get a pointer to &lt;B&gt;&lt;A href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx"&gt;ITfInputScope&lt;/A&gt;&lt;/B&gt;.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;What this bit means is that TSF-aware applications need to implement input scopes via&amp;nbsp;an application property.&amp;nbsp; In particular, you need to implement &lt;A href="http://msdn2.microsoft.com/en-us/library/ms538448.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms538448.aspx"&gt;ITextStoreACP::RequestSupportedAttrs&lt;/A&gt;, and &lt;A href="http://msdn2.microsoft.com/en-us/library/ms538449.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms538449.aspx"&gt;ITextStoreACP::RetrieveRequestedAttrs&lt;/A&gt;.&amp;nbsp; If you want to have multiple input scopes per document, then you also need to implement &lt;A href="http://msdn2.microsoft.com/en-us/library/ms538445.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms538445.aspx"&gt;ITextStoreACP::RequestAttrsAtPosition&lt;/A&gt;.&amp;nbsp; Note that the documentation refers to &lt;A href="http://msdn2.microsoft.com/en-us/library/ms629205.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms629205.aspx"&gt;TS_ATTRIDs&lt;/A&gt;; these are typedefs for GUIDs!&amp;nbsp; The TS_ATTRID that you need to support for input scopes is GUID_PROP_INPUTSCOPE&amp;nbsp; (you may, of course, add support for other TS_ATTRIDs).&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Retrieving input scopes is fairly straightforward.&amp;nbsp;&amp;nbsp;MSDN has a&amp;nbsp;nice code snippet describing how to retrieve the &lt;A href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx"&gt;ITfInputScope&lt;/A&gt; interface here; once you have it, use &lt;A href="http://msdn2.microsoft.com/en-us/library/ms628585.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms628585.aspx"&gt;ITfInputScope::GetInputScopes&lt;/A&gt; to retrieve the list of input scopes, and the other methods return auxiliary data for exotic input scopes.&lt;/P&gt;
&lt;P&gt;If you&amp;nbsp;need the&amp;nbsp;string enumerator&amp;nbsp;(i.e., you want to support IS_ENUMSTRING), then you need to get the &lt;A href="http://msdn2.microsoft.com/en-us/library/aa382054.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/aa382054.aspx"&gt;ITfInputScope2&lt;/A&gt; interface, instead of&amp;nbsp;&lt;A href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx"&gt;ITfInputScope&lt;/A&gt;; all you need to do is QI the application property for IID_ITfInputScope2&amp;nbsp;instead of IID_ITfInputScope.&amp;nbsp; &amp;nbsp;The MSDN documentation for &lt;A href="http://msdn2.microsoft.com/en-us/library/aa382054.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/aa382054.aspx"&gt;ITfInputScope2&lt;/A&gt; is incorrect; ITfInputScope2 extends &lt;A href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms628582.aspx"&gt;ITfInputScope&lt;/A&gt;, not IUnknown (and it's only available on Windows Vista).&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/P&gt;
&lt;P&gt;The &lt;A href="http://msdn2.microsoft.com/en-us/library/ms538181.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms538181.aspx"&gt;Input Scope enumerations&lt;/A&gt; are defined &lt;A href="http://msdn2.microsoft.com/en-us/library/ms538181.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms538181.aspx"&gt;here&lt;/A&gt;.&amp;nbsp; &lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3787583" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Applications/default.aspx">Applications</category></item><item><title>Fun with Contexts</title><link>http://blogs.msdn.com/tsfaware/archive/2007/06/27/fun-with-contexts.aspx</link><pubDate>Thu, 28 Jun 2007 02:08:53 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:3571954</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/3571954.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=3571954</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=3571954</wfw:comment><description>&lt;p&gt;One interface that I hadn't paid much attention to in TSF is &lt;a href="http://msdn2.microsoft.com/library/ms538718.aspx"&gt;ITfContextKeyEventSink&lt;/a&gt;.&amp;nbsp; What does this let you do?&amp;nbsp; Why, it lets you inspect keyboard input for a &lt;em&gt;particular&lt;/em&gt; context (or document).&amp;nbsp; This lets you do some really fun things, like redirect keyboard input from the current control and put it into another control.&amp;nbsp; An example of this can be seen in the &lt;a href="http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/samples/internet/TextServicesFrameWork/CandidateList/default.asp"&gt;sample text service with candidate list&lt;/a&gt;, which, while it doesn't really show how to use the ITfCandidateList interface (boo!), &lt;em&gt;does&lt;/em&gt; show how to redirect keystrokes from the main document to the candidate window.&amp;nbsp; Most of the fun is in CandidateList.cpp.&lt;/p&gt; &lt;p&gt;In the function CCandidateList::_StartCandidateList, the service creates a second context for the document, pushes it onto the context stack, then installs a &lt;a href="http://msdn2.microsoft.com/library/ms538718.aspx"&gt;ITfContextKeyEventSink&lt;/a&gt;&amp;nbsp;on the new context (and a layout sink on the original context, so that the candidate window can track layout changes).&lt;/p&gt; &lt;p&gt;Then, in the functions CCandidateList::OnTestKeyDown and CCandidateList::OnTestKeyUp, the service intercepts the incoming keystrokes, and the functions CCandidateList::OnKeyDown and CCandidateList::OnKeyUp redirect the keystrokes to the candidate window.&amp;nbsp; &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3571954" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Debugging a Text Service (and Visual Studio problems)</title><link>http://blogs.msdn.com/tsfaware/archive/2007/06/19/debugging-a-text-service-and-visual-studio-problems.aspx</link><pubDate>Tue, 19 Jun 2007 21:09:12 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:3409188</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/3409188.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=3409188</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=3409188</wfw:comment><description>&lt;p&gt;I've received a few emails asking about debugging Text Services.&amp;nbsp; I figured if two people actually went to the trouble of sending an email, that there's enough demand for a post.&lt;/p&gt; &lt;p&gt;But first, a little diversion:&amp;nbsp; It turns out that Visual Studio 2005 SP1 ships with an updated version of the C Runtime library, and, by default, adds an entry to the application manifest to use that new library.&amp;nbsp;&amp;nbsp;Unfortunately, this can cause serious problems for text services - in particular, the text service DLL often won't load.&amp;nbsp; I work around this problem by turning off manifest generation in the linker - either on the command line, with &lt;a href="http://msdn2.microsoft.com/en-us/library/f2c0w594(VS.80).aspx"&gt;/manifest:no&lt;/a&gt;, or by&amp;nbsp;going to the project property page, then selecting Configuration Properties/Linker/Manifest File in the tree view, and setting 'Generate Manifest' to 'No'.&amp;nbsp;&amp;nbsp; In general, you should minimize your use of the C Runtime library anyway, as it's more stuff that gets loaded into every process.&lt;/p&gt; &lt;p&gt;Now, on to debugging.&amp;nbsp; There are two options:&amp;nbsp; tracing, and real debugging.&amp;nbsp; You can use OutputDebugString and DBMon in your text service to trace the flow of execution without any problems, but for serious problems, like hangs, or crashes, you'll need to use a debugger.&amp;nbsp; I've never been able to debug a text service running on the same machine as the debugger, mostly because the TSF Manager needs to use your (stopped) text service in order to give keyboard focus back to the debugger.&amp;nbsp; Instead, you will either need a second machine, or&amp;nbsp;a virtual machine like &lt;a href="http://www.microsoft.com/windows/products/winfamily/virtualpc/default.mspx"&gt;Virtual PC&lt;/a&gt;.&amp;nbsp; (Microsoft gives this away for free, and also publishes trial virtual disks that you can use for &lt;a href="http://www.microsoft.com/technet/try/vhd/default.mspx"&gt;evaluation&lt;/a&gt;.)&lt;/p&gt; &lt;p&gt;Once you have installed your new text service on your other machine, you can use remote debugging to debug problems in your text service.&amp;nbsp; I've used the visual studio debugger with great success, but it can have some problems, particularly when you need to debug an application that needs to run elevated on Windows Vista.&amp;nbsp; In those situations, I use &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx"&gt;cdb and windbg&lt;/a&gt;&amp;nbsp;(cdb on the test machine, and windbg on the local machine).&amp;nbsp;&amp;nbsp; &lt;/p&gt; &lt;p&gt;To be honest, I've gotten used to the power of windbg (and cdb), and I use that almost exclusively.&amp;nbsp; But windbg isn't the friendliest debugger in the world, so I want people to know that visual studio works just fine.&lt;/p&gt; &lt;p&gt;Once you have your debugger set up, you can launch a process on your other machine (e.g., wordpad, notepad, Microsoft Word, etc.), and set a breakpoint at your dll entry point (or at the entry point of your choice, for example, CMyTextService::Activate).&amp;nbsp;&amp;nbsp; Visual studio's syntax for setting a &lt;a href="http://msdn2.microsoft.com/en-us/library/wztycb7f(VS.80).aspx"&gt;breakpoint in a specific DLL&lt;/a&gt; is a little baroque (e.g., {,,mydll}MyFunction)&amp;nbsp;, and I find windbg's syntax (e.g., mydll!MyFunction) a little easier to use.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3409188" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>What's a Keyboard?</title><link>http://blogs.msdn.com/tsfaware/archive/2007/05/30/what-is-a-keyboard.aspx</link><pubDate>Wed, 30 May 2007 17:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2975873</guid><dc:creator>Eric Brown</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/2975873.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=2975873</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=2975873</wfw:comment><description>&lt;P&gt;The Text Services Framework makes a number of assumptions when you register your text service as a keyboard text service (i.e., your text service calls RegisterCategory(&amp;lt;clsid of your text service&amp;gt;, GUID_TFCAT_TIP_KEYBOARD, &amp;lt;clsid of your text service).&lt;/P&gt;
&lt;P&gt;First, TSF assumes that exactly one keyboard text service can be active at a time.&amp;nbsp; If the user activates one keyboard text service, all other keyboard text services will be disabled.&lt;/P&gt;
&lt;P&gt;Second, TSF assumes that keyboard text services &lt;STRONG&gt;must&lt;/STRONG&gt; monitor two compartments:&lt;/P&gt;
&lt;P&gt;GUID_COMPARTMENT_KEYBOARD_DISABLED, and GUID_COMPARTMENT_KEYBOARD_OPENCLOSE;&lt;/P&gt;
&lt;P&gt;and that keyboard text services&amp;nbsp;&lt;STRONG&gt;should&lt;/STRONG&gt; monitor GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION and GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE.&lt;/P&gt;
&lt;P&gt;The &lt;A href="http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/samples/internet/TextServicesFrameWork/Keyboard/default.asp" mce_href="http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/samples/internet/TextServicesFrameWork/Keyboard/default.asp"&gt;sample&lt;/A&gt;&amp;nbsp;text service tells you to monitor these; unfortunately, it doesn't really describe why you need to monitor those compartments, nor does it tell you what these compartments really mean.&lt;/P&gt;
&lt;P&gt;The input mode compartments (GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION and GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE) contain conversion flags that your text service can use to control the mappings from keystrokes to characters.&amp;nbsp; The other two compartments control when the text service should interpret the incoming keystrokes.&lt;/P&gt;
&lt;P&gt;When GUID_COMPARTMENT_KEYBOARD_DISABLED is non-zero, it means that all keyboard processing should be disabled, and all keystrokes should be passed through without alteration.&amp;nbsp; This compartment&amp;nbsp;often has a non-zero value&amp;nbsp;when a text service wishes to display its own candidate UI.&lt;/P&gt;
&lt;P&gt;When GUID_COMPARTMENT_KEYBOARD_OPENCLOSE is non-zero, it means that the keyboard is 'open' and that keystrokes should be processed normally.&amp;nbsp; If the compartment value is zero, it means that the keyboard is 'closed', and that all keystrokes should be passed through without alteration.&lt;/P&gt;
&lt;P&gt;The main difference between these two compartments is that GUID_COMPARTMENT_KEYBOARD_DISABLED applies to a context, and GUID_COMPARTMENT_KEYBOARD_OPENCLOSE applies to an entire thread (and therefore to all the documents and contexts owned by that thread).&lt;/P&gt;
&lt;P&gt;Since GUID_COMPARTMENT_KEYBOARD_OPENCLOSE applies to an entire thread, many text services choose to set up an compartment event sink on this compartment in order to cache the compartment value.&amp;nbsp; That way, you can avoid querying the thread compartment inside your keyboard processing routines.&amp;nbsp; You could also set up a sink on GUID_COMPARTMENT_KEYBOARD_DISABLED, but you would have to disconnect and reconnect the sink when the document or context changes (including focus changes).&amp;nbsp; The cost of manipulating the sinks may outweigh the cost of caching the value.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2975873" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Generating Candidates from a Text Service</title><link>http://blogs.msdn.com/tsfaware/archive/2007/05/29/generating-candidates-from-the-text-service.aspx</link><pubDate>Tue, 29 May 2007 17:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2881684</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/2881684.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=2881684</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=2881684</wfw:comment><description>&lt;P&gt;So how do you create the candidates from within the text service?&amp;nbsp; Well, the first thing you need&amp;nbsp;is the original input, and you need to know what text was created from the original input.&amp;nbsp; Almost all text services (there might be a few that don't do this) place the created text in the document, and attach the original input as&amp;nbsp;a property associated with the created text.&lt;/P&gt;
&lt;P&gt;Now, when you want to create the candidates for a given range, you get your property data from the range, look at the original input, and generate candidates based on the original input.&amp;nbsp; Of course, you need to be in an edit session to do this.&lt;/P&gt;
&lt;P&gt;Once you've finished creating the candidates, you can show them (outside an edit session) in some sort of modeless dialog or popup.&amp;nbsp; Once you're done, you can replace the old text with the new text.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2881684" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/tsf/default.aspx">tsf</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category></item><item><title>Generating Candidates from an Application</title><link>http://blogs.msdn.com/tsfaware/archive/2007/05/25/generating-candidates-from-an-application.aspx</link><pubDate>Fri, 25 May 2007 21:43:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2878099</guid><dc:creator>Eric Brown</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/tsfaware/comments/2878099.aspx</comments><wfw:commentRss>http://blogs.msdn.com/tsfaware/commentrss.aspx?PostID=2878099</wfw:commentRss><wfw:comment>http://blogs.msdn.com/tsfaware/rsscomments.aspx?PostID=2878099</wfw:comment><description>&lt;P&gt;Kirby left a comment to my post on &lt;A href="http://blogs.msdn.com/tsfaware/archive/2007/05/14/text-services-candidates.aspx" mce_href="http://blogs.msdn.com/tsfaware/archive/2007/05/14/text-services-candidates.aspx"&gt;candidates&lt;/A&gt;&amp;nbsp;asking when a text service should create an ITfCandidateList.&lt;/P&gt;
&lt;P&gt;The answer is that when the &lt;EM&gt;text service&lt;/EM&gt; wants to show candidates (via a &lt;A href="http://msdn2.microsoft.com/library/ms628684.aspx" mce_href="http://msdn2.microsoft.com/library/ms628684.aspx"&gt;preserved key&lt;/A&gt; or other mechanism), it should show its modal UI, and quite possibly may wish to push a new context so that the modal UI can be keyboard enabled (the &lt;A href="http://msdn.microsoft.com/archive/en-us/samples/internet/TextServicesFrameWork/CandidateList/default.asp" mce_href="http://msdn.microsoft.com/archive/en-us/samples/internet/TextServicesFrameWork/CandidateList/default.asp"&gt;sample&lt;/A&gt; does this).&amp;nbsp; &lt;/P&gt;
&lt;P&gt;The thing to note here is that ITfCandidateList is not involved here.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;So when &lt;EM&gt;does&lt;/EM&gt; the text service use ITfCandidateList?&amp;nbsp; When the &lt;STRONG&gt;application&lt;/STRONG&gt; wants to show candidates.&lt;/P&gt;
&lt;P&gt;How does this work?&amp;nbsp; Well, it's pretty complicated, but I'll try to outline the steps here.&amp;nbsp; (Get ready, it's going to get a little wild...)&lt;/P&gt;
&lt;H2&gt;The big blocks &lt;/H2&gt;
&lt;P&gt;1)&amp;nbsp; Application gets &lt;A href="http://msdn2.microsoft.com/library/ms538970.aspx" mce_href="http://msdn2.microsoft.com/library/ms538970.aspx"&gt;ITfFnReconversion&lt;/A&gt; interface.&lt;/P&gt;
&lt;P&gt;2)&amp;nbsp; Application gets&amp;nbsp;target range (&lt;A href="http://msdn2.microsoft.com/library/ms538388.aspx" mce_href="http://msdn2.microsoft.com/library/ms538388.aspx"&gt;ITextStoreACPServices::CreateRange&lt;/A&gt; comes in very handy here).&lt;/P&gt;
&lt;P&gt;3)&amp;nbsp; Application calls &lt;A href="http://msdn2.microsoft.com/library/ms538973.aspx" mce_href="http://msdn2.microsoft.com/library/ms538973.aspx"&gt;ITfFnReconversion::QueryRange&lt;/A&gt; to transform the target range.&lt;/P&gt;
&lt;P&gt;4)&amp;nbsp; Application calls &lt;A href="http://msdn2.microsoft.com/library/ms538972.aspx" mce_href="http://msdn2.microsoft.com/library/ms538972.aspx"&gt;ITfFnReconversion::GetReconversion&lt;/A&gt; to get the list of candidates.&lt;/P&gt;
&lt;P&gt;5)&amp;nbsp; Application displays candidate list (e.g., in context menu, dialog, what have you).&lt;/P&gt;
&lt;P&gt;6)&amp;nbsp; Application calls &lt;A href="http://msdn2.microsoft.com/library/ms538496.aspx" mce_href="http://msdn2.microsoft.com/library/ms538496.aspx"&gt;ITfCandidateList::SetResult&lt;/A&gt;() when the selection is selected.&lt;/P&gt;
&lt;P&gt;7)&amp;nbsp; Application releases candidate list, reconversion interface, and does other cleanup.&lt;/P&gt;
&lt;P&gt;Alternatively, if the application wants to let the text service manage the UI, it can call &lt;A href="http://msdn2.microsoft.com/library/ms538975.aspx" mce_href="http://msdn2.microsoft.com/library/ms538975.aspx"&gt;ITfFnReconversion::Reconvert&lt;/A&gt; at step 4 and step out of the way.&amp;nbsp;&amp;nbsp; (Note that not all text services implement this, as this function must not wait for the UI to be dismissed before returning.)&lt;/P&gt;
&lt;P&gt;An excellent way to test this is with Microsoft Word.&amp;nbsp; Word supports reconversion, and will show the candidates in the context menu when you right-click on a word.&lt;/P&gt;
&lt;H2&gt;Getting ITfFnReconversion&lt;/H2&gt;
&lt;P&gt;This is a two-step process.&amp;nbsp; First, you get the system function provider, and then you get the reconversion pointer (error checking is omitted):&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;ITfFunctionProvider* ipProvider;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;hr = ITfThreadMgr::GetFunctionProvider(GUID_SYSTEM_FUNCTIONPROVIDER, &amp;amp;ipProvider);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;IUnknown* ipConv;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;hr = ipProvider-&amp;gt;GetFunction(GUID_NULL, IID_ITfFnReconversion, &amp;amp;ipConv);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;ITfFnReconversion *ipConversion;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;hr&amp;nbsp;= ipConv-&amp;gt;QueryInterface(IID_ITfFnReconversion, &amp;amp;ipConversion);&lt;/FONT&gt;&lt;/P&gt;
&lt;H2&gt;What should QueryRange do?&lt;/H2&gt;
&lt;P&gt;&lt;A href="http://msdn2.microsoft.com/library/ms538973.aspx" mce_href="http://msdn2.microsoft.com/library/ms538973.aspx"&gt;ITfFnReconversion::QueryRange&lt;/A&gt; needs to modify the range to cover the entire range of text contained&amp;nbsp;by the&amp;nbsp;conversion token.&amp;nbsp; An example is useful here, as that last sentence didn't even make sense to me.&amp;nbsp; The Speech text service does correction by words, and all words must be part of a single recognition.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Therefore, the Speech text services's implementation of QueryRange alters the range of text to contain entire words that are part of a single recognition.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;If the range starts in the middle of a word with recognition data, QueryRange shifts the start of the range to the start of a word.&amp;nbsp; If the range starts in text that doesn't have recognition data associated with it, it moves the start point to a point where it does have recognition data, or to the end of the initial range.&amp;nbsp; If it runs off the end, it sets pfConvertable to FALSE, and returns NULL.&lt;/P&gt;
&lt;P&gt;If it does find some recognition data for the start of the range, it adjusts the end of the range in the same manner.&lt;/P&gt;
&lt;P&gt;But wait, you say.&amp;nbsp; How can QueryRange do all that when it doesn't have an edit cookie?&amp;nbsp; Well, it gets one in the usual manner - it (say it with me) &lt;EM&gt;schedules an edit session&lt;/EM&gt; to do all that work.&amp;nbsp; OK, you say, how can I&amp;nbsp;get my data back from an&amp;nbsp;asynchronous edit session?&amp;nbsp; Well, you're in luck - you're &lt;EM&gt;guaranteed&lt;/EM&gt; to be able to schedule a &lt;STRONG&gt;synchronous&lt;/STRONG&gt; edit session from &lt;A href="http://msdn2.microsoft.com/library/ms538973.aspx" mce_href="http://msdn2.microsoft.com/library/ms538973.aspx"&gt;ITfFnReconversion::QueryRange&lt;/A&gt;.&amp;nbsp; &lt;/P&gt;
&lt;H2&gt;What should GetReconversion do?&lt;/H2&gt;
&lt;P&gt;&lt;A href="http://msdn2.microsoft.com/library/ms538972.aspx" mce_href="http://msdn2.microsoft.com/library/ms538972.aspx"&gt;ITfFnReconversion::GetReconversion&lt;/A&gt; needs to compute the candidates from a target range (that should be aligned with a conversion token).&amp;nbsp; Like QueryRange, most of the work will need to be done from an edit session, and also like QueryRange, you are guaranteed to be able to schedule a synchronous edit session from within GetReconversion.&amp;nbsp; How you go about computing the candidates is discussed in my next post.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2878099" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/tsfaware/archive/tags/tsf/default.aspx">tsf</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/dictation/default.aspx">dictation</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Text+Services/default.aspx">Text Services</category><category domain="http://blogs.msdn.com/tsfaware/archive/tags/Applications/default.aspx">Applications</category></item></channel></rss>