1

OK, I have been at this for a while ...

I am trying to track when user changes input languages from Language Bar.

I have a Text Service DLL - modeled from MSDN and WinSDK samples - that registers fine, and I can use the interfaces ITfActiveLanguageProfileNotifySink & ITfLanguageProfileNotifySink and see those events just fine.

I also have finally realized that when I change languages these events occur for the application/process that currently has focus.

What I need to do is to simply have these events able to callback to my own application, when it has the focus. I know I am missing something.

Any help here is appreciated.

Thanks.

  • Which operating system is this? On XP through Windows 7, the language changes are per process; on Windows 8, language changes are global. – Eric Brown Jul 28 '13 at 05:42
  • Eric - This is on Windows 7. How can the application receive these callbacks for a registered text service? My text service object derives from those two sinks, as well as a custom interface. I get the thread manager in my app, but after that it seems no matter what I do I can not see those events in my app. For example, if I pass a window handle to the text object to have it call back, this handle does not seem to be valid, even when my app has focus. – user2621234 Jul 28 '13 at 18:52
  • 1
    BTW, great blog! That has been the most helpful resource I have found yet. – user2621234 Jul 28 '13 at 18:59
  • I'm not quite sure I understand the question yet. The text services get the language change notifications, and (often) use that to decide whether to be on or not. I *believe* that applications can also set up notification sinks, but creating a thread manager implies that the application will also create a text store, which may be more than you want to do. Your text service could also communicate with your app, either via COM, shared memory, or the IPC mechanism of your choice. – Eric Brown Jul 28 '13 at 20:00
  • Eric - yes, I simply want my app to be notified when the text service receives language change notificattions. For exmpale, let's say we take the BasicText Service from MSDN example and have that class derive from ITfLanguageProfileNotifySink and ITfActiveLanguageProfileNotifySink. class CTextService : public ITfTextInputProcessor, public ITfLanguageProfileNotifySink, public ITfActiveLanguageProfileNotifySink – user2621234 Jul 29 '13 at 18:33
  • In the case of ITfActiveLanguageProfileNotifySink, I see its OnActivated method being invoked. But what do I need to do in an app to simply have that notification occur (also) there? I agree I may be making this too difficult, because it seems that there should be some COM or IPC mechanism to accomplish this. In my app if I do CoGetClassObject(c_clsidTextService, CLSCTX_INPROC_SERVER, ...) what next? – user2621234 Jul 29 '13 at 18:45

1 Answers1

1

I did some double-checking, and you should be able to create a thread manager object without implementing ITextStoreACP so long as you don't call ITfThreadMgr::Activate.

So, the code should look like:

HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
    ITfThreadMgr* pThreadMgr(NULL);
    hr = CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (LPVOID*) &pThreadMgr);
    if (SUCCEEDED(hr))
    {
        ITfSource *pSource;
        hr = pThreadMgr->QueryInterface(IID_ITfSource, (LPVOID*)&pSource);
        if(SUCCEEDED(hr))
        {
            hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, 
                (ITfActiveLanguageProfileNotifySink*)this,
                &m_dwCookie);

            pSource->Release();
        }
    }
}

Alternatively, you can use ITfLanguageProfileNotifySink - this interface is driven from the ItfInputProcessorProfiles object instead of ItfThreadMgr. There's a sample of how to set it up on the MSDN page for ItfLanguageProfileNotifySink.

For both objects, you need to keep the source object (ITfThreadMgr or ITfInputProcessorProfiles) as well as the sink object (what you implement) alive until your application exits.

Before your application exits, you need to remove the sink from the source object using ITfSource::UnadviseSink, and then release the source object (using Release). (You don't need to keep the ItfSource interface alive for the life of your application, though.)

Eric Brown
  • 13,774
  • 7
  • 30
  • 71
  • Eric - thanks, I got this to work in an app WITHOUT having my text service registered. The ITfLanguageProfileNotifySink interface does seem to be better suited, as then I get the LANGID when changing input language (from Language Bar). But here is an issue that the customer has noticed: why do these notifications not come in when changing Chinese (sub-)languages, eg ChangJie or New Phonetic? Thanks again! – user2621234 Jul 31 '13 at 02:02
  • 1
    @user2621234 - Those aren't sublanguages, those are Input Methods. One can have multiple input methods for the same language. To track input methods, you'll have to use [ITfActiveLanguageProfileNotifySink](http://msdn.microsoft.com/en-us/library/ms538491.aspx) and figure out how to interpret the CLSIDs for the various input methods. – Eric Brown Jul 31 '13 at 05:40
  • CLSIDs are documented here: https://learn.microsoft.com/windows-hardware/manufacture/desktop/windows-language-pack-default-values#input-method-editors – DJm00n May 24 '22 at 11:45