Friday, October 28, 2011

Convert a Locale Name to LCID in C++

I've spent most of the afternoon today figuring out how to convert a locale name, such as "en-US", into a locale identifier (LCID). Locale names are also called RFC 1766 language codes, or ISO 639 language names. As it turns out, this is easy to with the function LocaleNameToLCID(), but this function does not exist in Windows XP.

Microsoft provides a redistributable library that adds this functionality for XP, but it's 2MB and yet another module to install. Since my entire installation is 4MB, this isn't an option for me. You can read about that library in the article Mapping Locale Data on Downlevel Systems. In that article, they discuss the function DownlevelLocaleNameToLCID().


For handy reference, Microsoft provides a list of Language Identifier Constants and Strings on MSDN.

If you then need to convert the LCID to a CODEPAGEID, you can use this call to retrieve the code page for a particular LCID:

GetLocaleInfo(lcid, LOCALE_IDEFAULTCODEPAGE, ...)

Additional information on this topic can be found on The Old New Thing blog, including a discussion of using the MLang library.

// This is a version of LocaleNameToLCID() that works on Windows XP. 
// On Windows Vista or later, it automatically delegates to the 
// built-in version of LocaleNameToLCID(). 
// This function is not case-sensitive. 

// Tested with Unicode builds only, not tested under ANSI builds, but should be pretty close. 
// Example value for szLocale is "en-US" 
LCID CompatibleLocaleNameToLCID(LPCSTR szLocale)
{
CString strValue(szLocale);
CStringW wstrValue(strValue);

static  bool  bDone;
LCID (WINAPI * pfnLocaleNameToLCID)(LPCWSTR, DWORD);
if  (!bDone)
{
bDone = true ;
*(FARPROC*)&pfnLocaleNameToLCID = ::GetProcAddress(GetModuleHandleA("Kernel32" ), "LocaleNameToLCID" );
}

if  (pfnLocaleNameToLCID)
return  LocaleNameToLCID(wstrValue, 0);
 

CComPtr<IMultiLanguage> iLang;
HRESULT hr = iLang.CoCreateInstance(CLSID_CMultiLanguage);
LCID lcid;
if  (SUCCEEDED(hr) && SUCCEEDED(iLang->GetLcidFromRfc1766(&lcid, (BSTR)wstrValue.GetString()))) {
return  lcid;
}
 

return LOCALE_USER_DEFAULT;
}
 


Tuesday, October 25, 2011

Reading XML from C++

Reading XML from a URL is easy with by MSXML::IXMLHttpRequest.

Sample code is available at:
http://msdn.microsoft.com/en-us/library/ms992615(v=exchg.65).aspx

However, there are some disadvantages to this interface, which does not give you access to the low level handles if you need to tweak anything. For example, some servers require you to authenticate with a certificate and IXMLHttpRequest provides no way to do this. This interface will also display errors to the user and prompt for required information, so it's not appropriate for an application that will run silently.

Therefore, Microsoft provides a second interface called IServerXMLHTTPRequesthttp://msdn.microsoft.com/en-us/library/windows/desktop/ms762278(v=VS.85).aspx. This interface inherits from IXMLHttpRequest, but it does not use WinInet and so is appropriate for use in servers. It also allows you to explicitly provide a certificate using SXH_OPTION_SELECT_CLIENT_SSL_CERT.