Using without registering [the source code]

by Michael S. Kaplan, published on 2007/01/08 03:01 -05:00, original URI:

(Boy, take out the bracketed piece of the title and it sounds almost sordid!) 

I believe I have mentioned the CultureAndRegionInfoBuilder class before. It is the programmatic way to create custom cultures in the >= 2.0 version of the .NET Framework, and also custom locales in Vista.

It has a very cool Register method that will create the binary file containing that culture/locale information, put it in the right location, and add the registry keys so that it will be used.

However, this was not a good answer for Microsoft Locale Builder, which has to create install packages for people to be able to put their custom locales on other people's machines. They needed a way to create the file so they could register it on some other machine later (sometimes much later), in a setup that was not running managed code.

Now obviously the tool could have been written to call the Register method, copy the file, and then quickly call the Unregister method, but this is pretty unwieldy, and also would require the user to be an administrator on the machine to even make use of the tool. All things considered, this is pretty ugly.

So the wise decision was made to have it use reflection to call an internal method to create the file in an arbitrary location for packaging. And because it would quite obnoxious to do this without telling potential tool writers who wanted to create their own "Locale Builder" type tools, so how to do it was described in a documented topic that Shawn pointed to several months ago entitled How to Save Custom Cultures Without Administrative Privileges.

The biggest problem with the MSDN topic is that it isn't an example or a snippet or a sample; it is just a description of a technique. That's good enough for the parts that say "write this registry key" and such, but not so much when it is about using reflection to call internal methods....

I talked to Tarek about it and he agreed there was something missing here. It's not like reflection is ever easy without some assistance....

So anyway, here is a sample of how one can do the heard part of this -- creating the binary file that goes in the globalization subdirectory. Note that as the MSDN topic points out, if you don't add the registry keys it won't completely work right. But creating the registry keys, that part is easy, right? :-)

using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Globalization;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

class CaribReflection
    private static void CreateNlpFile()
            CultureAndRegionInfoBuilder carib                 = new CultureAndRegionInfoBuilder("en-US", CultureAndRegionModifiers.Replacement);
            Assembly                    assembly              = Assembly.GetAssembly(carib.GetType());
            Type                        cultureDefinitionType = assembly.GetType("System.Globalization.CultureDefinition");

            if (cultureDefinitionType == null) {

            BindingFlags    bf                        = BindingFlags.NonPublic | 
                                                        BindingFlags.Static | 
                                                        BindingFlags.Instance | 
            ConstructorInfo cultureDefinitionCtr      = cultureDefinitionType.GetConstructor((bf & ~BindingFlags.Static), null, new Type[0], null);
            object          cultureDefinitionObj      = cultureDefinitionCtr.Invoke(bf, null, null, CultureInfo.InvariantCulture);
            MethodInfo      getDataFromCaribMethod    = cultureDefinitionType.GetMethod("GetDataFromCultureAndRegionInfoBuilder", bf);

            if (getDataFromCaribMethod == null) {
                Console.WriteLine("Couldn't get the method GetDataFromCultureAndRegionInfoBuilder from CultureDefinition class");
            Object [] parameters = new Object[1] { carib };
            getDataFromCaribMethod.Invoke(cultureDefinitionObj, bf, null, parameters, CultureInfo.InvariantCulture);

            MethodInfo buildBinaryFileMethod = cultureDefinitionType.GetMethod("BuildBinaryFile", bf);
            if (buildBinaryFileMethod == null) {
                Console.WriteLine("Couldn't get the method BuildBinaryFile from CultureDefinition class");

            string     tempfile = Path.GetTempFileName();
            FileStream fs       = new FileStream(tempfile, 

            parameters = new Object[1] { fs };
            buildBinaryFileMethod.Invoke(cultureDefinitionObj, bf, null, parameters, CultureInfo.InvariantCulture);


            Console.WriteLine("The created Nlp file is: "+ tempfile);
        } catch (Exception e) {

    static void Main(string[] args) {

Now like I said, don't forget those registry keys. And Shawn's post has some other really good advice you'll want to read.

Otherwise, that's all you need.

Though if you are writing your own "Locale Builder" tool, then I am a member of a team that would love to hear about it (some of us might even might love to try it out?). :-)


This post brought to you by  (U+189e, a.k.a. MONGOLIAN LETTER MANCHU ALI GALI TTA)

no comments

Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

referenced by

2007/05/17 A private CultureAndRegionInfoBuilder does not imply a private CultureInfo

go to newer or older post, or back to index or month or day