10

Is there a way to detect if the windows/os language changed even when my app is not in focus?
So far I was able to achieve what I wanted only if the app was focused using:

string language = "";
System.Windows.Input.InputLanguageManager.Current.InputLanguageChanged +=
new System.Windows.Input.InputLanguageEventHandler((sender, e) =>
{
    language = e.NewLanguage.DisplayName;
    MessageBox.Show(language);
});

But as you can understand, this is not exactly what I want..

I was thinking about other solution such as hooking the keys that change the language (for example alt+shift) but I wont be able to know what language is currently in use and a user can change the default hotkey...

Would appreciate your help.

Ron
  • 3,975
  • 17
  • 80
  • 130
  • 1
    You did not actually change the input language for your process. Only for whatever process was in the foreground. Normal behavior for the Language Bar. – Hans Passant Oct 28 '14 at 20:08
  • @HansPassant I didnt understand that, but what I want is to hook whenever language changes... – Ron Oct 29 '14 at 17:53
  • 1
    @Run - I'd like to confirm one thing. Am I right that it is not enough for you that InputLanguageChanged event will be generated when focus returns back to your application. – Michał Komorowski Nov 04 '14 at 19:12
  • @MichałKomorowski yes, You are correct. I want to call function whenever the input language changes, regardless if my app is in focus or not. – Ron Nov 04 '14 at 20:07
  • @Ron I think what Hans was saying is that the input language *doesn't actually change for **your** application* until it is brought into the foreground. – Mike Strobel Nov 04 '14 at 20:14
  • @MikeStrobel I know.. but there has to be a way to detect if it was changed globally.. like listening to the registry (if it is written there) or other way... I dont know else I wouldnt ask it :P – Ron Nov 04 '14 at 20:33
  • Check http://stackoverflow.com/questions/19319194/getkeyboardlayoutname-of-other-process. That guy seems to have had a similar problem. – Vojtěch Dohnal Nov 05 '14 at 07:50

1 Answers1

11

The problem you are facing is related with how WM_INPUTLANGCHANGE message works. This message is sent to programs by operating system in order to inform them about language changes. However, according to documentation this message is sent only to "to the topmost affected window". It means that you can even call a native method GetKeyboardLayout (it is used by InputLanguageManager by the way) but if an application is not active GetKeyboardLayout will always return the last known, outdated, language.

Taking this into account it might be a good idea to use the solution pointed by @VDohnal i.e. find the current topmost window and read keyboard layout for it. Here is a quick proof of concept how to do it inside WPF application. I used an additional thread that periodically finds the topmost window and ready keyboard layout for it. The code is far from being perfect but it works and it might help you to implement your own solution.

public partial class MainWindow : Window
{
    [DllImport("user32.dll")]
    static extern IntPtr GetKeyboardLayout(uint idThread);
    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);

    private CultureInfo _currentLanaguge;

    public MainWindow()
    {
        InitializeComponent();

        Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    HandleCurrentLanguage();
                    Thread.Sleep(500);
                }
            });
    }

    private static CultureInfo GetCurrentCulture()
    {
        var l = GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero));
        return new CultureInfo((short)l.ToInt64());
    }

    private void HandleCurrentLanguage()
    {
        var currentCulture = GetCurrentCulture();
        if (_currentLanaguge == null || _currentLanaguge.LCID != currentCulture.LCID)
        {
            _currentLanaguge = currentCulture;
            MessageBox.Show(_currentLanaguge.Name);
        }
    }
}
Michał Komorowski
  • 6,198
  • 1
  • 20
  • 24
  • This is more then enough. All I want to do is show a baloon box with the current language + play sound whenever language changes. I didnt give u the bounty, because as you said, there might be a better or "perfect" solution. I will give u the bounty 1 day before it ends unless there's a better solution – Ron Nov 05 '14 at 20:57
  • 2
    No sense in tying up a task pool thread indefinitely; create the task with `TaskCreationOptions.LongRunning` to use a thread detached from the pool. – Mike Strobel Nov 06 '14 at 20:04
  • @MikeStrobel How do I use it? – Ron Nov 12 '14 at 17:41
  • 1
    @Ron `Task.Factory.StartNew(() => { ... }, TaskCreationOptions.LongRunning)` – Mike Strobel Nov 12 '14 at 17:48
  • @MichalKomorowski There's a bug with you solution. sometimes, spontaneously, when I swap between windows or minimize-maximize it shows the messagebox twice, one with the previous language and one with the current language. I couldn't understand why.. – Ron Nov 16 '14 at 19:48
  • @Ron - I tried to reproduce this strange behaviour but any success. I can try to help but I need more details. Is it possible that some application on your computer is changing the language in the background. – Michał Komorowski Nov 16 '14 at 20:41
  • @MichałKomorowski Nothing changes it. I will record my screen tomorrow and upload it to youtube so you'll be able to see what I am talking about. – Ron Nov 16 '14 at 20:43
  • @Ron - I think that this film shows that your program works perfectly well. In the right bottom corner I can see a popup showing that the language is changing when you are minimizing/maximizing a window and your application is informing you about it. It is what you wanted to achieve. I agree that the observed behaviour is strange but it has nothing in common with your application. – Michał Komorowski Nov 17 '14 at 21:05
  • @MichałKomorowski I wanted to be informed when the language is changed, not when I maximize/minimize a window.. because of the bug above I changed the way I detect the change (I use global hook of all three possible hotkeys for changing language - ` ctrl+shift alt+shift) – Ron Nov 18 '14 at 05:35
  • @Ron - Your film shows that minimizing/maximizing a window also changes the language. In the right bottom corner of your screen you can observe a popup, displayed by Windows, that informs you about language changes. Did global hooks solve you problem? – Michał Komorowski Nov 18 '14 at 08:23
  • @MichałKomorowski It did solve, although I might change it from global hook to register hotkey.. I dont know if it will work or not. – Ron Nov 18 '14 at 17:22
  • Apparently this does not work for console windows (like cmd.exe). It finds correct window, but GetKeyboardLayout returns [zero](http://stackoverflow.com/questions/29217501/getkeyboardlayout-doesnt-work-properly-in-some-cases) – sms Aug 07 '16 at 18:21