by Michael S. Kaplan, published on 2012/08/15 07:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2012/08/15/10339749.aspx
The latest question that passed through the gauntlet was:
I’m working with .NET and I need to be able to map a set of two-letter country codes (e.g. US, IE) into the appropriate RegionInfo object. I’ve got this working correctly for all regions defined in .NET, but we’ve recently discovered that there are a number of regions which are not present in .NET including Andorra and Antigua and Barbuda.
I realise we can overcome this problem by creating custom cultures, but this seems like it might be a maintainability issue.
In case it’s relevant, my team is using .NET 3.5 due to various limitations for now.
What a nightmare to create that many custom cultures, none of which would likely have useful data..
All without getting reviewed the way we review data now! :-(
I think the wider list can be picked up with a simple p/invoke call to EnumSystemGeoID instead.
GEO stuff has limitations (as I mentioned in the past), especially around time zones and such, but for names it should work pretty well!
Say code like the folllowing:
using System;
using System.Text;
using System.Runtime.InteropServices;
class Program {
private static string stTarget;
static void Main(string[] args) {
if(args.Length > 0) {
stTarget = args[0];
}
else {
stTarget = string.Empty;
}
EnumSystemGeoID(GEOCLASS_NATION, 0, GeoInfoProc);
}
private static bool GeoInfoProc(Int32 geoID) {
int len = GetGeoInfo(geoID, GEO_FRIENDLYNAME, null, 0, 0);
if(len > 0) {
StringBuilder data = new StringBuilder(len);
len = GetGeoInfo(geoID, GEO_FRIENDLYNAME, data, len, 0);
if(len > 0) {
string stIsoCode = data.ToString(0, len - 1);
bool fFound = string.Compare(stIsoCode,
stTarget, StringComparison.OrdinalIgnoreCase) == 0;
if(stTarget.Length == 0 || fFound) {
Console.WriteLine(string.Format("{0} = {1}", stIsoCode, geoID));
}
return !fFound;
}
}
return false;
}
// Geo Type
private const uint GEO_NATION = 0x0001;
private const uint GEO_LATITUDE = 0x0002;
private const uint GEO_LONGITUDE = 0x0003;
private const uint GEO_ISO2 = 0x0004;
private const uint GEO_ISO3 = 0x0005;
private const uint GEO_RFC1766 = 0x0006;
private const uint GEO_LCID = 0x0007;
private const uint GEO_FRIENDLYNAME = 0x0008;
private const uint GEO_OFFICIALNAME = 0x0009;
private const uint GEO_TIMEZONES = 0x000A;
private const uint GEO_OFFICIALLANGUAGES = 0x000B;
// Geo Class
private const uint GEOCLASS_NATION = 16;
private const uint GEOCLASS_REGION = 14;
private delegate bool EnumGeoInfoProc(int geoID);
[DllImport("kernel32.dll")]
private static extern bool EnumSystemGeoID(uint geoClass, int parentGeoID, EnumGeoInfoProc enumGeoInfoProc);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint ="GetGeoInfoW")]
private static extern int GetGeoInfo(int geoID, uint geoType, StringBuilder geoData, int size, int langID);
}
It will build a nice list that happens to include all of the various names requested....
This code or code like it should work just fine with 3.5 or 4.0 or even 4.5.
So screw R56egionInfo! You can get your GEO on, instead....