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?
Update
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.
Update 2
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...
Update 3
...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.LoadFile()
as shown above. To my best knowledge I don't specify assembly name or version with the provider type name. – SeumasClient.Properties.Settings
class, mentioned in the exception stack trace, actually sits in the assembly (along with a host of other classes in the call chain, not shown here). – SeumasLoadFile
orLoadFrom
which is failing here. The assembly itself is found and loaded without any error message. I did as you suggested though, but Fuslogvw did not show any relevant binding failures. – SeumasLoadFile
is evil? I found contradicting information aboutLoadFrom
vsLoadFile
so far, and no clear explanation on their exact difference(s). – Seumas