Forum:  LLBLGen Pro Runtime Framework

Thread:  ApplicationConfiguration class is very unflexible


worldspawn (User)   Posted on: 09-Mar-2007 00:52:25.
The ApplicationConfiguration class is annoying. It reads settings using AppSettingsReader so the settings MUST come from the currently executing executables configuration file. So for a visual studio add-in the settings must be added to devenv.exe.config!! It then goes on to look for driver definitions in a folder relative to the executing executables startup path.

Basically meaning unless the ApplicationConfiguration class is instantiated from an application launched from the LLBLGen Pro installation folder it will fail.

Code:

private void LoadDatabaseDriverDefinitions()
{
     string text1 = Path.Combine(Application.StartupPath, this._databaseDriversRootFolder);
     string[] textArray1 = Directory.GetDirectories(text1);
...
}


It needs a static method or something it can create an instance of itself with a provided configuration reader as well as a provided path to look for driver definitions.

In the meantime i'm just going to call the CliRefresher tool as a separate process. Dissapointed
worldspawn (User)   Posted on: 09-Mar-2007 04:14:57.
I'm now trying to figure out how to generate for a single entity and i see that Generator.Start requires an instance of ApplicationConfiguration... *sigh*.

I suppose I'm going to have to use the CommandLineGenerator from the SDK... Dissapointed


Otis (LLBLGen Pro Team)   Posted on: 09-Mar-2007 11:29:11.
worldspawn wrote:
The ApplicationConfiguration class is annoying. It reads settings using AppSettingsReader so the settings MUST come from the currently executing executables configuration file. So for a visual studio add-in the settings must be added to devenv.exe.config!! It then goes on to look for driver definitions in a folder relative to the executing executables startup path.

Yup, but it's not that annoying. The purpose is that it's used with an llblgen pro install, as the usage of that code needs a license.

I understand what you're doing but the code at the moment isn't designed to be used as a vs.net add-in.

Alex Davidson (User)   Posted on: 13-Mar-2007 12:53:30.
This is a problem I needed to solve some time ago.

http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=7785

Eventually, I solved it by using some rather dodgy reflection techniques to poke around inside the ConfigurationManager and hook the required configuration sections.

Code:

public static class ReflectionUtils
    {
        public static void SetAppDomainBaseDirectory(string dir)
        {
            (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
            try
            {
                AppDomainSetup ads = typeof (AppDomain).InvokeMember("FusionStore", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty, null, AppDomain.CurrentDomain, new object[] {}) as AppDomainSetup;
                ads.ApplicationBase = dir;
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
        }
    }
    
    /// <summary>
    /// A substitute for ConfigurationManager's backing object. Acts an an adapter to
    /// allow ConfigurationManager to use a Configuration object.
    /// </summary>
    public class ReplacementConfigSystem : IInternalConfigSystem
    {
        private Configuration _base;
        private IInternalConfigSystem _original;
        private string _appPath;

        /// <summary>
        /// Create an object that wraps a Configuration object and can plug into
        /// ConfigurationManager.
        /// </summary>
        /// <param name="appPath">Full path to the application's executable</param>
        public ReplacementConfigSystem(string appPath)
        {
            _appPath = appPath;
            _base = ConfigurationManager.OpenExeConfiguration(appPath);
        }
        
        #region Install/Uninstall hook
        /// <summary>
        /// Substitute the current app's configuration data for this one.
        /// Uses reflection to replace the ConfigurationManager's settings and change
        /// Application.StartupPath. Currently only replaces AppSettings.
        /// </summary>
        public void Install()
        {
            if (_original == null)
            {
                string appDir = Path.GetDirectoryName(_appPath);
                (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
                try
                {
                    BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
                    Type configManType = typeof(ConfigurationManager);
                    Type appObjType = typeof(Application);

                    // Force config system initialisation to complete.
                    configManType.InvokeMember("EnsureConfigurationSystem", flags | BindingFlags.InvokeMethod, null, null, new object[] { });

                    // Get the object upon which init locking is performed and make the swap.
                    object lockObj = configManType.InvokeMember("s_initLock", flags | BindingFlags.GetField, null, null, new object[] { });
                    lock (lockObj)
                    {
                        _original = configManType.InvokeMember("s_configSystem", flags | BindingFlags.GetField, null, null, new object[] { }) as IInternalConfigSystem;
                        if (_original == null)
                        {
                            throw new OperationCanceledException("Configuration initialisation was incomplete. Unable to install hook.");
                        }
                        else
                        {
                            try
                            {
                                // Install the hook.
                                configManType.InvokeMember("s_configSystem", flags | BindingFlags.SetField, null, null, new object[] { this });
                                appObjType.InvokeMember("startupPath", flags | BindingFlags.SetField, null, null, new object[] { appDir });
                            }
                            catch
                            {
                                // Attempt to roll back.
                                Uninstall();
                                throw;
                            }
                        }
                    }
                }
                catch
                {
                    _original = null;
                    throw;
                }
                finally
                {
                    CodeAccessPermission.RevertAssert();
                }
            }
            else
            {
                throw new Exception("Hook already installed. Cannot reinstall.");
            }
        }

        /// <summary>
        /// Restores ConfigurationManager and Application.StartupPath.
        /// </summary>
        public void Uninstall()
        {
            if (_original != null)
            {
                (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
                try
                {
                    BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
                    Type configManType = typeof(ConfigurationManager);
                    Type appObjType = typeof(Application);
                    object lockObj = configManType.InvokeMember("s_initLock", flags | BindingFlags.GetField, null, null, new object[] { });
                    lock (lockObj)
                    {
                        configManType.InvokeMember("s_configSystem", flags | BindingFlags.SetField, null, null, new object[] { _original });
                        appObjType.InvokeMember("startupPath", flags | BindingFlags.SetField, null, null, new object[] { null });
                        _original = null;
                    }
                }
                finally
                {
                    CodeAccessPermission.RevertAssert();
                }
            }
            else
            {
                throw new Exception("Hook not installed. Cannot uninstall.");
            }
        }
        #endregion

        #region IInternalConfigSystem Members
        /// <summary>
        /// Return the requested Section from the Configuration object.
        /// Sections are handled differently by ConfigurationManager, so
        /// this method reconstructs the Section in an appropriate manner.
        /// </summary>
        /// <param name="configKey">Key name</param>
        /// <returns></returns>
        public object GetSection(string configKey)
        {
            if (_base != null)
            {
                NameValueCollection properties = new NameValueCollection();
                ConfigurationSection section = _base.GetSection(configKey);
                if (section is AppSettingsSection)
                {
                    foreach (KeyValueConfigurationElement element in (section as AppSettingsSection).Settings)
                    {
                        properties.Add(element.Key, element.Value);
                    }
                    return properties;
                }
            }
            return _original.GetSection(configKey);
        }

        public void RefreshConfig(string sectionName)
        {
            _original.RefreshConfig(sectionName);
        }

        public bool SupportsUserConfig
        {
            get
            {
                return true;
            }
        }
        #endregion
    }


Quite nasty and it may need revising if the internals of ConfigurationManager ever change. Also, it only supports the <appSettings> section at present.

Use it like so:

Code:

string llblgenExe = /* full path to LLBLGen executable */;
ReplacementConfigSystem llblgenConfig = new ReplacementConfigSystem(llblgenExe);
llblgenConfig.Install();
// do LLBLGen-y stuff that uses ApplicationConfiguration.
llblgenConfig.Uninstall();



Aurelien (Support Team)   Posted on: 13-Mar-2007 14:49:17.
thanks for your contribution !
worldspawn (User)   Posted on: 19-Apr-2007 07:51:15.
Thanks Alex, I'll give that a try Regular Smiley