Following this question, I successfully created my custom settings provider in the legacy C# app we are developing. It is referenced via the SettingsProvider
attribute:
public sealed class MySettings : SettingsProvider
{
...
}
[SettingsProvider(typeof(MySettings))]
internal sealed partial class Settings {}
However, now I ran into another problem.
Our client app includes an autoupdate facility, and it is implemented so that the bulk of the client - including the classes above - is built into a DLL (let's call it client.dll here), which is then used by an EXE. The EXE first checks for updates and downloads the latest one from the update server if needed, replacing all DLLs etc. with their newer version (including client.dll). In order to be able to replace DLLs at runtime, it can't link to them statically. So after the update, it loads client.dll and runs it like this:
Assembly assy = Assembly.LoadFile(
AppDomain.CurrentDomain.BaseDirectory + "client.dll");
object frm = assy.CreateInstance("Client.Forms.MainForm");
Application.Run((Form)frm);
The unfortunate consequence of this is that the framework can't find my custom settings provider class inside the dynamically loaded assembly. I tried to use LoadFrom
instead of LoadFile
above but it didn't help. The only working solution I found so far is to implement a proxy class in the loader exe with the same name as the real settings provider, which is found by the framework all right. The proxy then instantiates the real settings provider from the client assembly and delegates all calls to it.
This seems to work but I am not happy with it. Is there a way to help the framework find my class inside a dynamically loaded assembly directly?
The error message I get:
System.Configuration.ConfigurationErrorsException: Failed to load provider type: Client.Properties.MySettings, Client, Version=4.0.1341.0, Culture=neutral, PublicKeyToken=null.
at System.Configuration.ApplicationSettingsBase.get_Initializer()
at System.Configuration.ApplicationSettingsBase.CreateSetting(PropertyInfo propInfo)
at System.Configuration.ApplicationSettingsBase.EnsureInitialized()
at System.Configuration.ApplicationSettingsBase.get_Properties()
at System.Configuration.SettingsBase.GetPropertyValueByName(String propertyName)
at System.Configuration.SettingsBase.get_Item(String propertyName)
at System.Configuration.ApplicationSettingsBase.GetPropertyValue(String propertyName)
at System.Configuration.ApplicationSettingsBase.get_Item(String propertyName)
at Client.Properties.Settings.get_SomeConfigSetting()
at ...
Via debugging and log messages I determined that the initializer method of the class is never called, so the class is never instantiated. In other words, there seems to be no hidden initialization error behind the above exception.
One more potentially important bit: the client is currently running on .NET 2.0 and there are no plans to upgrade in the foreseeable future.
I started to investigate the AppDomain as suggested by @jwddixon's answer. First I wanted to check whether Client.dll really ends up in a different app domain than that of the caller EXE. So I listed the assemblies in the current app domain, and saw that Client is in fact there. But to my surprise, I noticed that there are actually two assemblies named Client in the list - both the EXE and the DLL have the same assembly name, but different version (4.0.0.0 for the EXE, 4.0.1352.0 for the DLL at present). I was not fully aware of this so far, and this may be important. I will try changing the EXE assembly name next...
...and that actually fixed the problem! Aaargh... Towards my unknown predecessors who invented this contorted scheme for who knows why, I have very unfond thoughts at this moment... but kudos to all of you guys for contributing questions and ideas which eventually lead to the solution!
Assembly. It defines an assembly, which is a reusable, versionable and self describing building block of a Common Language Runtime.
Assembly Class. Assembly: Mscorlib.dll. Namespace: System.Reflection. Summary. Defines an Assembly, which is a reusable, versionable, and self-describing building block of a common language runtime application.
An assembly is a file that is automatically generated by the compiler upon successful compilation of every . NET application. It can be either a Dynamic Link Library or an executable file. It is generated only once for an application and upon each subsequent compilation the assembly gets updated.
An assembly is a collection of types and resources that are built to work together and form a logical unit of functionality. Assemblies take the form of executable (.exe) or dynamic link library (. dll) files, and are the building blocks of . NET applications.
I haven't tried this but how about a something like:
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
AppDomain domain = AppDomain.CreateDomain("myAppDomain", null, setup)
setup.ApplicationBase = file;
Assembly assy = domain.Load(AssemblyName.GetAssemblyName(
AppDomain.CurrentDomain.BaseDirectory +
"client.dll"));
object frm = assy.CreateInstance("Client.Forms.MainForm");
Application.Run((Form)frm);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With