Changing the language programmatically in WM 6.0

Localization in compact framework 2.0 is a rather poor cousin of its counterpart in the full framwork 2.0. One of the most annoying (but understandable once the reasons are understood ) issue is that CurrentThread.CurrentCulture does not exist on the Compact Framework. Effectively this means that you cannot localize a specific application. You must change the setting at the device level.

To change device level culture setting use the SetUserDefaultLCID Native call to make this change. You can also change the relevant registry entries, and reboot the system, but I usually prefer using an API if available.

What parameter are you going to pass to this function ? Obviously you need a list of supported languages to choose from. In the regular framework, this would have been CultureInfo.GetCultures, but (you guessed it), this is also not supported in Compact Framework. What is supported is a couple of rather unduely (IMHO) contorted functions - EnumUILanguages and EnumUILanguagesProc.

I didn't feel upto doing complex Native calls right after lunch, so i did some hacking (slacking ?), and found that the registry stores the available languages under the Key HKLM\MUI\Available . The name of each of the key contains the LCID for the culture in hex. Its much simpler to enumerate the available languages this way.

The last thing to do once you have allowed the user to change the language is to restart the device (soft-reset). I have left out a few crucial UI issues like prompting the user before resetting the device, and defaulting the combobox to the current Language

Here is the code for the form. Assume that the form has a ComboBox showing the list of available languages and a Menu button to select Ok and Cancel.

 

using System;

using

System.Linq;

using

System.Collections.Generic;

using

System.ComponentModel;

using

System.Data;

using

System.Drawing;

using

System.Text;

using

System.Windows.Forms;

using

System.Globalization;

using

System.Runtime.InteropServices;

using

Microsoft.Win32;

namespace

Workshop

{

public partial class Form1 : Form

{

// Control Code flags

private const uint FILE_DEVICE_UNKNOWN = 0x00000022;

private const uint FILE_DEVICE_HAL = 0x00000101;

private const uint FILE_DEVICE_CONSOLE = 0x00000102;

private const uint FILE_DEVICE_PSL = 0x00000103;

private const uint METHOD_BUFFERED = 0;

private const uint METHOD_IN_DIRECT = 1;

private const uint METHOD_OUT_DIRECT = 2;

private const uint METHOD_NEITHER = 3;

private const uint FILE_ANY_ACCESS = 0;

private const uint FILE_READ_ACCESS = 0x0001;

private const uint FILE_WRITE_ACCESS = 0x0002;

private Dictionary<string, string> LCIDCultures = new Dictionary<string, string>();

public Form1()

{

InitializeComponent();

ShowCultures();

}

private void ShowCultures()

{

//if MUI key exists

RegistryKey mui = Registry.LocalMachine.OpenSubKey("MUI");

if (null != mui)

{

// if "Available" Key exists

RegistryKey availableLanguagesKey = mui.OpenSubKey("Available");

if (null != mui)

{

//get the entry name

string[] availableLanguageLCIDEntries = availableLanguagesKey.GetValueNames();

//loop through and create a lookup for the entries

foreach (string languageLCID in availableLanguageLCIDEntries)

{

string languageName = availableLanguagesKey.GetValue(languageLCID).ToString();

LCIDCultures.Add(languageName, languageLCID);

LanguageComboBox.Items.Add(languageName);

}

}

}

}

private void menuChange_Click(object sender, EventArgs e)

{

//get the LCID from the lookup

string LCIDName = LCIDCultures[LanguageComboBox.SelectedItem.ToString()];

Int16 LCID = Int16.Parse(LCIDName, NumberStyles.AllowHexSpecifier);

//invoke the native function, and on success reset the device

if (SetUserDefaultUILanguage(LCID))

resetDevice();

}

#region

Native Calls

[

DllImport("coredll.dll")]

public static extern bool SetUserDefaultUILanguage(Int16 languageId);

[

DllImport("Coredll.dll")]

private extern static uint KernelIoControl

(

uint dwIoControlCode,

IntPtr lpInBuf,

uint nInBufSize,

IntPtr lpOutBuf,

uint nOutBufSize,

ref uint lpBytesReturned

);

#endregion

/// <summary>

/// the method needed to be called to actually reset the device.

/// </summary>

public static void resetDevice()

{

uint bytesReturned = 0;

uint IOCTL_HAL_REBOOT = CTL_CODE(FILE_DEVICE_HAL, 15, METHOD_BUFFERED, FILE_ANY_ACCESS);

KernelIoControl(IOCTL_HAL_REBOOT,

IntPtr.Zero, 0, IntPtr.Zero, 0, ref bytesReturned);

}

private static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access)

{

return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method);

}

}

}