I've been using custom config files with my assemblies for some time now. I like to keep the assemblies in separate folders so that I can manage our applications a little easier as we have a diverse set of them. That means I typically just have to add the <loadFromRemoteSources enabled="true"/> to the <runtime> element in the acad.exe.config and I'm good to go. To ensure my config is treated as being a part of the dll, I use reflection and the ConfigurationManager.OpenMappedExeConfiguration(). Now as I'm upgrading from AutoCAD 2012 to 2015, this no longer working. Does anyone know why or have a solution as to working around it? It still works if both the dll and its config are copied to the install directory, but I don't want to dump all our assemblies to the install directory to work around this. This also screams that it has something to do with the assembly binding process and the exception with the fusion log data (using the sample code shown below) shows the following which looks typical for failed probing to me:
System.IO.FileNotFoundException was caught
_HResult=-2147024894
_message=Could not load file or assembly 'TestAutoCADApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
HResult=-2147024894
IsTransient=false
Message=Could not load file or assembly 'TestAutoCADApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Source=System.Configuration
FileName=TestAutoCADApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
FusionLog==== Pre-bind state information ===
LOG: DisplayName = TestAutoCADApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
(Fully-specified)
LOG: Appbase = file:///C:/Program Files/Autodesk/AutoCAD 2015/
LOG: Initial PrivatePath = NULL
Calling assembly : System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Program Files\Autodesk\AutoCAD 2015\acad.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2015/TestAutoCADApp.DLL.
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2015/TestAutoCADApp/TestAutoCADApp.DLL.
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2015/TestAutoCADApp.EXE.
LOG: Attempting download of new URL file:///C:/Program Files/Autodesk/AutoCAD 2015/TestAutoCADApp/TestAutoCADApp.EXE.
StackTrace:
at System.Configuration.TypeUtil.GetTypeWithReflectionPermission(IInternalConfigHost host, String typeString, Boolean throwOnError)
at System.Configuration.MgmtConfigurationRecord.CreateSectionGroupFactory(FactoryRecord factoryRecord)
at System.Configuration.MgmtConfigurationRecord.EnsureSectionGroupFactory(FactoryRecord factoryRecord)
at System.Configuration.MgmtConfigurationRecord.GetSectionGroup(String configKey)
at System.Configuration.Configuration.GetSectionGroup(String sectionGroupName)
at TestAutoCADApp.Commands.TestConfigSettingsAccess(Action`1 writeAction) in c:\Development-TVA\Visual Studio 2013\TestCode\TestAutoCADApp\TestAutoCADApp\Commands.cs:line 49
InnerException:
Here's a very generic example if you would like to replicate this yourself:
The command...
[CommandMethod("TestConfig")] public void TestConfig() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; TestConfigSettingsAccess(new Action<string>((string target) => ed.WriteMessage(target))); } public void TestConfigSettingsAccess(Action<string> writeAction) { try { TestAutoCADApp.Properties.Settings.Default.SamplePath = DateTime.Now.ToString(); writeAction(TestAutoCADApp.Properties.Settings.Default.SamplePath); Uri uriCodeBase = new Uri(Assembly.GetExecutingAssembly().CodeBase); FileInfo appfilePath = new FileInfo(uriCodeBase.LocalPath + ".config"); ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = appfilePath.FullName; Configuration configuration = null; //Retrieve the Configuration file data and return configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); MainGroup mainGroup = configuration.GetSectionGroup("mainGroup") as MainGroup; MainSection mainSection = mainGroup.Sections["mainSection"] as MainSection; SomeElement someElement = mainSection.SomeElement; writeAction(someElement.Name); } catch(SystemException ex) { //exception code } }
Here is the element, section, and group code:
class MainGroup : ConfigurationSectionGroup { [ConfigurationProperty("mainSection", IsRequired = true, IsKey = false)] internal MainSection MainSection { get { return (MainSection)base.Sections["mainSection"]; } } }
class MainSection : ConfigurationSection { [ConfigurationProperty("someElement", IsRequired = false, Options = ConfigurationPropertyOptions.None)] internal SomeElement SomeElement { get { return (SomeElement)this["someElement"]; } set { this["someElement"] = value; } } }
class SomeElement : ConfigurationElement { private static ConfigurationPropertyCollection _properties = new ConfigurationPropertyCollection(); protected override ConfigurationPropertyCollection Properties { get { return _properties; } } private static ConfigurationProperty _name = new ConfigurationProperty("name", typeof(string), @"some name", null, null, ConfigurationPropertyOptions.IsRequired); internal string Name { get { return (string)base[_name]; } private set { base[_name] = value; } } public SomeElement() { this.Properties.Add(_name); } }
and now the configuration file (note the namespaces and the fully qualified assembly names need to be correct). You can ignore the <userSettings> element as I was playing around with that to see if it affected it any.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <section name="TestAutoCADApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> </sectionGroup> <sectionGroup name="mainGroup" type="TestAutoCADApp.MainGroup, TestAutoCADApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <section name="mainSection" type="TestAutoCADApp.MainSection, TestAutoCADApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> </sectionGroup> </configSections> <userSettings> <TestAutoCADApp.Properties.Settings> <setting name="SamplePath" serializeAs="String"> <value>C:\</value> </setting> </TestAutoCADApp.Properties.Settings> </userSettings> <mainGroup> <mainSection> <someElement name="Test from Custom Config"/> </mainSection> </mainGroup> </configuration>
Solved! Go to Solution.
Solved by moogalm. Go to Solution.
Hi ,
Can you please provide sample project to test at my end ,I'm curious to investigate, I'm not sure if this the similar problem we faced earlier where primary project is referring to some other references which are not available in acad install folder and causing assembly resolve issues.
http://adndevblog.typepad.com/autocad/Madhukar-Moogala/
Sure, no problem. Attached is a project zipped. I look forward to your response as this is making troubleshooting migration issues difficult. I used VS 2013.
Hi , this the same issue I was talking to you earlier , you need to subscribe to assembly event and resolve by your self , autocad no longer resolves.
namespace CustomConfigIssue { public class Commands { [CommandMethod("TestConfig")] public void TestConfig() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; TestConfigSettingsAccess(new Action<string>((string target) => ed.WriteMessage(target))); } public void TestConfigSettingsAccess(Action<string> writeAction) { /* * You need to hook to assembly resolve event * Autocad 2015 will not solve assemblies ,if assemblies are not in acad install folder * user need to subscribe to assembly loading event and send the assembly */ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; try { Uri uriCodeBase = new Uri(Assembly.GetExecutingAssembly().CodeBase); FileInfo appfilePath = new FileInfo(uriCodeBase.LocalPath + ".config"); ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = appfilePath.FullName; Configuration configuration = null; //Retrieve the Configuration file data and return configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); MainGroup mainGroup = configuration.GetSectionGroup("mainGroup") as MainGroup; MainSection mainSection = mainGroup.Sections["mainSection"] as MainSection; SomeElement someElement = mainSection.SomeElement; writeAction(someElement.Name); } catch (SystemException ex) { //exception code } } private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { Assembly assembly = null; Assembly[] assems = AppDomain.CurrentDomain.GetAssemblies(); String name; if (args.Name.IndexOf(",") > -1) name = args.Name.Substring(0, args.Name.IndexOf(",")); else name = args.Name; foreach (Assembly assem in assems) { if(assem.GetName().Name == name) { return assem; } } return assembly; } } }