I have become stuck while trying to use SystemParametersInfo. I use the Dvorak keyboard, so I am trying to make an exe that I can double click whenever I want to change the keyboard layout. I am using SystemParametersInfo with C#. I can retrieve the current input language just fine. But when I try to set the input language I get a return value of false, and the input language does not change.
Thank you in advance for any help.
Here is the code I am using:
class KeyboardSwitcher
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref uint pvParam, uint fWinIni);
private const uint SPI_GETDEFAULTINPUTLANG = 0x0059;
private const uint SPI_SETDEFAULTINPUTLANG = 0x005A;
private const uint SPIF_UPDATEINIFILE = 0x01;
private const uint SPIF_SENDWININICHANGE = 0x02;
private const uint DVORAK = 0xF0020409;
private const uint QWERTY = 0x04090409;
static void Main(string[] args)
{
uint currentLayout = 0;
uint newLayout = 0;
bool result = false;
int error = 0;
SystemParametersInfo(SPI_GETDEFAULTINPUTLANG, 0, ref currentLayout, 0);
if(currentLayout != DVORAK) {
newLayout = DVORAK;
/*result = SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, ref newLayout, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);*/
result = SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, ref newLayout, SPIF_SENDWININICHANGE);
error = Marshal.GetLastWin32Error();
}
else {
newLayout = QWERTY;
/*result = SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, ref newLayout, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);*/
result = SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, ref newLayout, SPIF_SENDWININICHANGE);
error = Marshal.GetLastWin32Error();
}
Console.WriteLine(">>> Current Layout <<<");
Console.WriteLine("0x" +currentLayout.ToString("X"));
Console.WriteLine(">>> New Layout <<<");
Console.WriteLine("0x" +newLayout.ToString("X"));
Console.WriteLine("Change Successful >>> " +result);
Console.WriteLine(error);
Console.WriteLine(">>> Final Layout <<<");
uint finalLayout = 0;
SystemParametersInfo(SPI_GETDEFAULTINPUTLANG, 0, ref finalLayout, 0);
Console.WriteLine("0x" +finalLayout.ToString("X"));
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
}
}
UPDATE:
I threw in Console.WriteLine(Marshal.GetLastWin32Error()); with the rest of the print statements and it is returning 0. Before reading the comments, I also tried GetLastError() by importing Kernel32.dll and it also returned 0. Thank you for you responses.
UPDATE 2: I have changed my code to show my usage of GetLastWin32Error().
Here is the output when I have Dvorak set as the default input language.
>>> Current Layout <<<
0xF0020409
>>> New Layout <<<
0x4090409
Change Successful >>> True
Error Code >>> 0
>>> Final Layout <<<
0x4090409
Press any key to continue...
Here is the output when I have Qwerty set as the default input language.
>>> Current Layout <<<
0x4090409
>>> New Layout <<<
0xF0020409
Change Successful >>> True
Error Code >>> 0
>>> Final Layout <<<
0xF0020409
Press any key to continue...
I got the hex values for the layouts be using SPI_GETDEFAULTINPUTLANG when I had each layout set as default. Do you think the value that it returns might be different than the value that I need to send?
I have done some research on the InputLanguage Class and I do not believe it is suitable for this situation because the DefaultInputLanguage property does not have a setter and there are no other methods to set the default input language.
UPDATE 3: When I change the fourth parameter of SystemParametersInfo to just SPIF_SENDWININICHANGE it becomes much closer to working. The result of the SET is True and and when I run the program back to back it switches the language back and forth. So this means that something in Windows is indeed being changed. However, the change is new being reflected in the Control Panel, and the keyboard does not actually change. I feel like I do need the SPIF_UPDATEINIFILE to push the change through. The value that I am using is correct, isn't it? I have also updated the code and the output so that the output is a bit more verbose.
To inform an application that the language has changed without restarting it you need to send the application window a WM_INPUTLANGCHANGEREQUEST followed by a WM_INPUTLANGCHANGE message where lParam is the locale identifier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With