Professional Documents
Culture Documents
Configure Your .Net Application - The Other INI
Configure Your .Net Application - The Other INI
Configure Your .Net Application - The Other INI
CP5462 This course is intended for anyone developing .Net applications for AutoCAD that would like to
learn how to make their applications more dynamic with configuration files. There’s a lot of value in storing
information that can be read at startup or saved during use without the requirement of a database! Using
configuration files, developers can save user-specific preferences such as window size, last opened directory, or
application settings saved by the user. Application parameters can be updated without recoding and recompiling.
Historically, INI files have been a popular tool for the accomplished programmer. The .Net framework offers a
more integrated solution using its configuration classes in conjunction with the xml-based configuration files. In
this class we will cover the basics of the .Net configuration classes, how to integrate their use into your .Net
application for AutoCAD, and how to customize configuration elements specifically suited for your application.
Learning Objectives
At the end of this class, you will be able to:
• Understand the basic mechanics and fundamental elements of the Microsoft configuration system.
• Understand how to create/modify your application’s configuration file.
• Understand how to use the .Net configuration classes to interface with the configuration file.
• Understand how to use the .Net framework and its configuration classes to create and handle custom
configuration elements.
• Understand how to persist custom object data in a configuration file.
Email: bjhuffine@tva.gov
Configure Your .Net Application - The Other INI
INTRODUCTION
Integrating the power of the AutoCAD .Net API with the flexibility and power behind the Microsoft .Net
Framework can yield some amazing results. Just as many of us have found the Autodesk products and
interfaces to be complex, deep, and well thought out, the .Net Framework provides a literal framework
of classes and objects at a mind-numbing depth and complexity all intended to provide developers with
a controlled toolset beneficial to accomplishing specified tasks. It should be no surprise that Microsoft
has provided objects for use in creating and interacting with configuration files. This document outlines
these objects, identifies how to use them and progresses toward total customization of them.
So what’s in it for me? Easy... configuration files can be used to store integral application settings
information. The hierarchical structure of the configuration system allows developers to control and
store settings data via both manual and programmatic updates without requiring additional code! Need
a few reasons? Here’s a few that could give some idea as to when it may be beneficial to use
configuration files:
• In lieu of an INI file. INI files require P/Invoke or custom parsing code to which the rules may
not always be the same from one to the next.
• When a small data store is needed. Most applications require storage of some data. If the
amount of data needed to be stored is too small to involve all the overhead of maintaining a
database and a more streamlined and less clunky approach that streamreaders/writers with
custom parsing code is desired, then a configuration file may be an ideal solution.
• When configuration data needs to be shared between applications.
• When user preferences need to be stored somewhere for a custom look and feel to the
application.
• In lieu of hard coding settings or program constants. Being able to update a configuration file’s
settings information via any text or xml editor means being able to update program constants,
calculation parameters, etc, without recoding, recompiling, and redeploying!
• When strongly-typed data needs to be read, modified, and saved in a simple, easy-to-use
system.
• When using a predefined .Net configuration element can simplify application development and
maintenance or add control. For example, when connecting with an external database, defining
supporting CLR versions, needing reference versioning control, specifying trace and debug
settings data, specifying how the .Net Framework is to connect to the Internet and handle web
addresses, specifying cryptography settings, ... okay, okay, okay... I think you get the point now.
The point is... there are many predefined configuration file elements that can provide a quick
and efficient means of application control and storage of critical, but variable, settings data such
as the <ConnectionStrings> element collection for storing database connection string data or
the <dependentAssembly> element that can be used to identify a specific reference for
versioning control or the <cryptographySettings> element to store encryption settings data.
2
Configure Your .Net Application - The Other INI
This topic is incredibly simple for those wanting a simple solution, but can be incredibly deep for those
wanting to perform complex operations using the technology. In fact, there are industry experts who
have been considering writing books on the topic to more clearly document the technology. With that in
mind, consider that this document provides a lot of information from beginning to advanced levels in a
short amount of space. So unfortunately, there may be specific details, predefined elements, or other
more specific, related topics not covered in this document. No worries though, added are some
references at the end to assist, and if all else fails, feel free to contact me and maybe between the two
of us we can figure it out.
Assumptions/Givens
Obviously, discussion of such a topic without writing volumes of information requires some assumptions
and givens. Hopefully these will assist the reader more clearly identify the scope of this document.
• Configuration hierarchies and classes can be applied in both an application (exe) and web
context. This document will only consider the application context.
• This document focuses on the Microsoft .Net Framework configuration file technology and how
to leverage it with AutoCAD applications developed using the AutoCAD .Net API. Therefore it is
assumed that the reader already understands the concepts of setting up a basic project for
AutoCAD automation, which API references to use, how to interact with the drawing database,
etc.
• The reference code is based on projects developed with the 4.0 Framework, however, they
were tested with the 3.5 Framework as well.
• Though there are quite a few pages to this document, a large portion of it is illustrated code.
This makes including examples in both C# and VB difficult to do without making a book out of it.
So where there are the greatest differences, both will be shown. Otherwise, C# examples will
be used. There are some fantastic free translator tools online that can assist if you are a
diehard VB’er (if that’s even a word) or again, feel free to contact me and samples in VB can be
provided.
• To keep this document concise and to-the-point, there are some assumptions with some of the
technology, such as knowing how to work with an INI or the components of an xml file. For
those who are familiar with this, no problems, for those who are not, I’ve included appendices of
supplemental information. This way hopefully there will be no one left behind.
THE FUNDAMENTALS
Background
Application “initialization” files, aka the ever popular INI files, have been in use for MANY, MANY years.
In fact, Initialization File Mapping was introduced with Windows NT and Windows 95 to aid developer
migration of data stored in classic INI files to (at that time) the new Windows Registry. Even so, many
continue to use them today. INI files have the *.ini extension and, as illustrated below, its structure can
be typified as a simple, plain-text file with section tags surrounded by square brackets, property
3
Configure Your .Net Application - The Other INI
elements in each section with assigned values, and semicolons prefixing lines for commenting. INI files
can be read with Notepad or any other plain-text editor.
For the most part, reading INI files is accomplished with one of two approaches:
Hopefully, the information to follow will address all these and then some by using the predefined
Microsoft .Net Configuration classes.
Configuration files are xml-based files whose naming convention appends .config to the name of its
corresponding executable. For example, if an AutoCAD .Net application executable is titled
MyCADApp.dll, the configuration file would be aptly named MyCADApp.dll.config. Because these files
are xml-based, they can be viewed in a variety of ways such as Notepad, Visual Studios, Internet
Explorer (great way to view as read-only and allow expand/collapse of sections without accidently
modifying the file), and other third-party xml editors. There are five types of configuration files in the
application context. The following identifies and briefly explains each:
4
Configure Your .Net Application - The Other INI
This is a higher-level, user configuration file that is typically used to identify the user-specific
settings that can be accessed on different machines connected to the same network when
Windows roaming profiles are enabled. Though it fits within the configuration hierarchical
architecture, using the roaming profile will be a bit beyond the scope of this document. The
location of these roaming configuration files are %UserProfile%\Application Data\[Company
Name]\[Application Name]_[Evidence Type]_[Evidence Hash]\[Version]\user.config where
the Company Name, Application Name, and Version values are as identified by the
application assembly.
This is probably the most specific configuration file in the configuration hierarchy of the
application context. This is where user-specific settings information can both be stored and
written to programmatically. Its default settings values are defined in the application
configuration file while the actual values are stored in the user.config file located in
%UserProfile%\Local Settings\Application Data\[Company Name]\[Application Name]_
[Evidence Type]_[Evidence Hash]\[Version]\user.config where the Company Name,
Application Name, and Version values are as identified by the application assembly. These
values can be modified from the Application tab in the Visual Studio project properties
designer window. These values are automatically applied in the AssemblyInfo.cs or
AssemblyInfo.vb project files as assembly attributes.
5
Configure Your .Net Application - The Other INI
Not only can a hierarchical network of elements be utilized within a configuration file, but the entire .Net
configuration architecture is hierarchical. Consider the following illustration of this concept regarding
the previously outlined configuration files. The hierarchy is machine → application → roaming-user →
local-user, with local-user being the most specific. This merging concept is also documented in several
other online articles and in MSDN. A very thorough and detailed outline of this concept and the
configuration architecture can be found in a series of online articles titled Unraveling the Mysteries of
.Net 2.0 Configuration. See the References section of this document for more about this article.
6
Configure Your .Net Application - The Other INI
Roaming-User
Configuration:
User.config
Local-User
Configuration:
User.config
When an application loads its configuration data, the .Net CLR will automatically combine all these
configuration settings into a single merged view. If a setting is duplicated, the more specific setting will
override the least specific. In other words, if the setting in the local-user configuration file is duplicated
in the roaming-user, the local-user will override and replace the roaming-user setting.
Before diving straight into creating and modifying a configuration file, let’s get a better handle on its
structure. Configuration elements can be nested, creating a hierarchy of section groups, sections,
basic data elements, element collections, etc. Element tags are used for each. These are illustrated
below using a series of custom elements.
<!--Configuration Group-->
<example.group>
<BasicElement
dateTimeValue="7/7/2011"
intValue="100"
/>
<!--Element Collections-->
<myElementCollection>
<add type="cake" flavor="carrot"/>
<add type="pie" flavor="coconut"/>
<add type="cheesecake" flavor="chocolate"/>
</myElementCollection>
</example.section>
</example.group>
The configuration file schema includes a slew of predefined elements that are included in the .Net
Framework’s configuration schema. These predefined elements can be leveraged to specify trace
listeners that collect, store, and route trace/debug messages, determine how the CLR handles garbage
collection for the specified application, determine the version of an assembly to use, specify
cryptography mapping schema, define database connection strings, and much more. As previously
mentioned, the network of configuration classes is incredibly deep, so with exception of a few helpful
predefined elements, the rest will be considered out of scope.
Developers can also create custom elements to store settings information as well. This can be
extremely beneficial when attempting to more clearly organize the structure of a large configuration file.
In either case, whether using standard, predefined, or custom elements, most (if not all) will be nested
within the root <configuration> element.
Without further ado, let’s begin building our first configuration file. There are multiple ways we can
accomplish this, but let’s start with the most basic approach and keep things simple.
Step-1. Using Visual Studios, start and setup a class library project for an AutoCAD, .Net
application including the acdbmgd.dll and acmgd.dll references.
Step-2. From the Project menu, select the project properties menu item. Then select the
Settings tab. If using the C# language, Visual Studios will provide a link in the center
of the Settings tab requesting the necessary approval to create a Settings class
within the project. Click this link.
8
Configure Your .Net Application - The Other INI
Step-3. Type in the setting Name, select the Type, and enter a default Value. Try creating
multiple settings using a variety of types.
Browse... is a Type selection option that can be very handy. As seen below, any
referenced types (in our cases the acdbmgd.dll and acmgd.dll types) can be seen for
selection. This is great as it allows us to use the custom types in the settings derived
in AutoCAD’s .Net API. Also, after adding the references, you may need to ensure
the start external program has the acad.exe supplied on the Debug settings tab in
the project properties and a quick build is done. Evidently Visual Studios will not see
the AutoCAD API reference objects without first having an internal reference to its
parent exe.
A little word of caution... this feature is finicky. It seems that Visual Studios caches
reference information for performance reasons and there may be times when this
cache doesn’t update correctly and the references can’t be seen. This can be
frustrating, but there are some back door workarounds through manual modification
of the Settings.settings and Settings.Designer.cs files if absolutely necessary. If the
issue is not seeing the AutoCAD API reference types, try resetting start external
program setting, save, and rebuild.
9
Configure Your .Net Application - The Other INI
Step-4. Now select a Scope for each. You’ll notice that there are two: Application and User.
User-scoped settings are those that will be persisted in the local-user, user.config
file. These can be modified and saved during runtime. The default values and
section declaration will remain in the application-level configuration file though.
Step-5. Select Save, view the VS Solution Explorer, and notice that a new file called
app.config was added.
Taking a look at the results of what Visual Studios has created for us in the app.config file, we see the
following:
10
Configure Your .Net Application - The Other INI
<sectionGroup name="applicationSettings"
type="System.Configuration.ApplicationSettingsGroup, System,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" >
<section name="DocumentConfigSample.Properties.Settings"
type="System.Configuration.ClientSettingsSection, System,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</sectionGroup>
</configSections>
If developing in VB, there will be an additional group element called <system.diagnostics>. Simply
collapse or delete this. This section is a predefined element that is intended to allow the developer to
incorporate trace capabilities via the system Event Log. If chosen to ignore or delete this, there is no
harm as the code generator for VB is simply trying to add some helpful code when initializing a
configuration file.
Creating the configuration file was extremely simple! And believe it or not, so is interacting with it.
Reading and writing settings values is accomplished slightly different with C# than with VB. Accessing
the values with VB means using the Settings property in the My namespace, while with C#, using the
Settings.Default property of the application’s Properties namespace:
11
Configure Your .Net Application - The Other INI
VB:
'Read the stored Settings (both Application- and User-scoped)
Dim acadColor As Autodesk.AutoCAD.Colors.Color = My.Settings.myColor
C#:
//Read the stored Settings (both Application- and User-scoped)
Autodesk.AutoCAD.Colors.Color acadColor =
MyCADApp.Properties.Settings.Default.myColor;
Reading and writing settings values seems pretty straightforward. However, what happens after the
updated values is saved? Where will we find these? How can we verify that the application is doing
what we asked it to? Since application-scoped settings are not intended to be programmatically
updated during runtime, these are persisted in the application’s configuration file, i.e. the
MyCADApp.dll.config file. So this is really no big deal.
As for the user-scoped settings, the application configuration file (i.e. MyCADApp.dll.config) only shows
the default settings’ values and not the persisted ones. Not to worry though, they’re not out in the great
beyond where we’ll never find them again. You may remember that we stated the local-user
configurations are stored in the %UserProfile%\Local Settings\Application Data\[Company
Name]\[Application Name]_[Evidence Type]_[Evidence Hash]\[Version]\user.config directory. The
problem here is that if you look in that directory associated with your specific application’s [Company
Name] you will not find anything. What gives? It’s there, just not where you think. Since any AutoCAD
.Net automation are developed as class libraries, *.dll, they do not run within their own process space.
Instead, they are loaded within the ACAD.exe process space, making AutoCAD the host application.
Now let’s take another look but with Autodesk, Inc as the [Company Name], and aula... there lies a
user.config file. Open the file and you’ll find all the persisted user-scoped settings values for your
application.
12
Configure Your .Net Application - The Other INI
Consider that a class library executable has been developed for use in AutoCAD and has its own
configuration file associated with itself. Now consider that the developer wishes to extend this
configuration with a file independent from the application and accessible to other applications. Follow
these steps to create an auxiliary configuration file:
Step-1. Using Visual Studios, open and create the desired project intended to maintain the
auxiliary configuration file.
Step-2. In Solution Explorer, right-click the project title and select Add/New Item...
13
Configure Your .Net Application - The Other INI
Step-3. From the language category selected, select Application Configuration File. Since
this configuration file is intended to be maintained outside of the application context,
there’s no harm renaming this one. For the sample code to follow, this one has been
named “Auxiliary.config”... Okay, okay... I’m an engineer and creativity is not my best
suit. Now select Add.
Step-4. This is a start, but we need some data to work with. The <appSettings> element is
one that is intended to work with a key/value collection of settings elements. So let’s
type: <appSettings></appSettings> in between the <configuration> element to
define the configuration section boundary to which these settings will be placed.
Then type <add key="SomeUniqueKey" value="SettingValue"/> for each
setting in between the <appSettings> element. Here’s an example:
14
Configure Your .Net Application - The Other INI
Now if you’ve wondered where it is we’re supposed to define the data type, then
you’re asking a good question. You don’t. Unfortunately, the <appSettings> element
is not strongly-typed. To ensure a strongly-typed environment, you’ll have to
incorporate that into your handling of the values. More on this later.
So far, creating an external configuration file has been a breeze. Believe it or not, so is interacting with
one. The only trick here is that working with one requires an understanding of how to open and
encapsulate an external configuration file. The reason for this not simply being a “Step 1, 2, 3”
approach is because of context. Earlier we showed that it is easy to use the Settings tab from the
Project Designer in Visual Studios to both create and interact with configuration file settings. These
settings operate in the context of our MyCADApp.dll executable and the CLR assumes the two are in
the same directory. This time, the settings are in a separate and independent file, unassociated with
the executable. To better explain this process, let’s begin with opening an external configuration file.
MSDN and many other resources will illustrate opening an external configuration file by associating it to
the application’s configuration. The problem with this is that the application configuration (i.e. that
regarding the official application context) is not your MyCADApp.dll, it’s the parent host application,
acad.exe. So be aware that these resources may create some really confusing results. No worries
though, there are some methods available for opening an external configuration file.
First, let’s discuss opening the Auxiliary.config file we created earlier. Since this is an independent and
unassociated file, we can use the OpenMappedExeConfiguration() method of the
ConfigurationManager class which returns a Configuration class reference associated to the specified
configuration file. Now that’s a mouthful! The ConfigurationManager class in the System.Configuration
namespace is a sealed, static class that provides access to configuration files. The Configuration class
object returned represents the merged view of the configuration settings from all the configuration files
that apply to a specific entity (computer, application, or web site). The ConfigurationManager actually
provides several Open...Configuration() methods for opening various levels of configuration files, but for
this example the OpenMappedExeConfiguration() applies best for opening an external one. Below is
an example of how this method can be used to open an external file.
Now let’s jazz things up a bit and open the acad.exe.config file that should be located in the same
directory as acad.exe. Ahhhhh yes... I did go there! There is a whole host of reasons for using the
acad.exe.config file. For example, one could use it to store AutoCAD system variable settings’ values
intended to initialize the drawing environment, store settings associated with automation developed
around specific AutoCAD features such as publishing or plotting, or to modify configuration properties
that affect how AutoCAD loads and operates our custom .Net applications.
15
Configure Your .Net Application - The Other INI
Since the application configuration is that associated with acad.exe (i.e. the host application), opening it
is even simpler than for an unassociated and independent external file:
1.] None: Gets the Configuration that applies to the application context. For AutoCAD .Net
development, this would mean the acad.exe.config since acad.exe is the host application.
2.] PerUserRoaming: Gets the Configuration associated with the Roaming-User, user.config, as
associated with the host application.
3.] PerUserRoamingAndLocal: Gets the Configuration associated with the Local-User, user.config,
as associated with the host application.
Below are a couple of examples that illustrate reading the settings information from the <appSettings>
element. Don’t forget to add a reference to System.Configuration! This will apply to all examples in this
document.
Notice that the value returned is a string value. Again, this is because the <appSettings> element is not
based on a strongly-typed system. This must be accomplished in the handling of the value once
retrieved. Don’t let this discourage you from using it though, because most system data types that are
derived from System.Object will have predefined methods. If you wish to store and persist custom
AutoCAD object data, try creating a conversion class of your own. To keep things clean and simple in
this document, I illustrate this in Appendix C: Type Conversion Examples for those interested.
Writing values is really not that different from reading them. The biggest difference will be inclusion of
the Configuration.Save() and ConfigurationManager.RefreshSection() methods. The Save() method is
fairly obvious, but the RefreshSection() method is not. The RefreshSection() method refreshes the
16
Configure Your .Net Application - The Other INI
named section so that it will be re-read from disk the next time it is retrieved. In this example I add a
new <appSettings> element item in the list, remove another, and modify a third.
There are two ways to create the MyCADApp.dll.config file we need. The first is to use the VS Project
Designer and create as many Application- and User-scoped settings as necessary, which has already
been outlined in Basic Configuration Files section of this document. The App.config development
template file will then be created automatically. Or if there are no settings of this kind to include and we
plan to go straight to using custom configuration elements, follow these steps:
Step-1. Using Visual Studios, start and setup a class library project for an AutoCAD, .Net
application including the acdbmgd.dll and acmgd.dll references.
Step-2. In Solution Explorer, right-click the project title and select Add/New Item...
Step-3. From the language category selected, select Application Configuration File template.
DO NOT change the name. Now select Add.
17
Configure Your .Net Application - The Other INI
The App.config file is created and should look something like this:
App.config is a template configuration file for your particular application. This file is
not the official build version configuration file. When building, the compiler will copy
this file into the appropriate bin directory along with the executable using the required
naming convention, MyCADApp.dll.config. So...
Debug/Build/Run Setup
Now that we have the App.config created, we can build the application. Upon doing so we will find the
application’s dll and its built configuration file, *.dll.config, in the solution’s Debug/Release directory.
Due mostly to the nature of how the configuration classes operate in the .Net Framework, extension of
the application context, and the overall AutoCAD .Net application loading process it is necessary for us
to consider a few additional factors.
With custom configuration elements, the configuration handler code is located in the .Net application
developed, while the framework configuration classes associate with the acad.exe host application.
Also, since the debug version is being processed from a directory other than the AutoCAD install folder,
AutoCAD must be made aware of this. The fix is a two-stage process:
Stage-1.] Enter two post-build event commands from the Build Events tab in the Visual
Studios Project Designer. Each command should copy the latest build version of
the executable and application configuration file to the AutoCAD install directory.
For example:
18
Configure Your .Net Application - The Other INI
Stage-2.] Edit the acad.exe.config file so that it can associate to the application’s debug
version assembly. To do this, add the following within the <runtime> element of the
acad.exe.config file.
<runtime>
...
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyCADApp" />
<codeBase version="1.0.0.0"
href="FILE://C:/.../Debug/MyCADApp.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Note that neither of these steps is necessary for “in-production” deployment of the application if it is
copied to the AutoCAD install directory. These steps are really only necessary for debugging.
Deploying applications where custom elements are of concern is simple. There’s really only one rule,
copy the compiled release version *.dll and *.dll.config files to the AutoCAD install directory together.
They should not be separated in the directory. ADN Support actually recommends that developers
consider deploying their .Net applications to the install directory anyways, especially when using the
CUI or Ribbon API, exposing .Net classes to COM, etc.
19
Configure Your .Net Application - The Other INI
Custom configuration elements can add organization and a lot of additional control. However we must
keep in mind that the custom elements, sections, and groups we use are not recognized by the system,
so we must develop the handler code for each one used. This section of the document will outline the
configuration file declaration and usage as well as any associated handler code necessary to support
custom elements, sections, and groups.
Configuration Elements
It is the configuration elements and their specified attributes that will be used to store settings
information in our configuration files. Below is an example:.
Creating the custom configuration element handler is actually very easy. To do so, add a class that
inherits from ConfigurationElement, then add properties to represent each of the element’s attributes
intended to store settings data. Before examples are provided, let’s first discuss the approach. There
are two models for doing this, programmatic and declarative. The programmatic approach allows for
finer control but requires a little more code. The declarative model is a lot simpler in that the element
attributes (class properties) are decorated with .Net attributes that instruct the configuration system
about the property types via reflection. Though this model is simpler to create, it may not provide as
much control as a developer may wish for in some situations. Below are examples of both, but keep in
mind that these models can be used for other configuration handlers as well. So in future examples,
you’ll see one or the other to keep things interesting.
Programmatic Model:
using System.Configuration;
#region Properties
20
Configure Your .Net Application - The Other INI
*/
#endregion
#region Constructors
public ConfigElement_P()
{
//Add the element attribute properties to the
//element properties collection
_Properties.Add(_myColor);
#endregion
}
Declarative Model:
using System.Drawing;
using System.Configuration;
Sometimes there will be situations where a collection of elements are useful. For example, a list of
block names and file paths could be beneficial in a program where specific blocks containing specific
properties need to be used in an application.
Before defining how to create the configuration element collection handlers, there are two basic types
of collections we need to understand:
This is the default element collection type. Collections of this type can be merged in the
hierarchy of configuration files. With these collection types the basic <add/>, <remove/>, and
<clear/> element commands can be used across multiple levels of configuration files to control
the collection. These commands can also be renamed in the configuration element collection
handler as well if desired.
The Basic Map collection type is different from the ARC Map in that it is more specific, in fact
additive in nature (once added cannot be removed by a more specific configuration file).
Collections of this type apply to the configuration level to which they are specified. Also, unlike
the ARC Map, the collection cannot be cleared with the <clear/> element.
Step-1. Develop the configuration element handler that represents each element in the
collection as described in the section of this document titled Customized
Configuration Files / Developing Custom Configuration Handlers / Configuration
Elements.
Step-2. For the configuration element collection handler, create a class that inherits
ConfigurationElementCollection from the System.Configuration namespace. Also
apply the ConfigurationCollection() attribute to the class declaration where the
22
Configure Your .Net Application - The Other INI
collection type and item element is defined. Depending upon the collection type, this
attribute can provide an opportunity to rename the <add/>, <remove/>, and <clear/>
elements.
using System.Configuration;
namespace adskCustomConfiguration
{
[ConfigurationCollection(typeof(ConfigCollectedElement),
/*This attribute can also be used to redefine the add/remove/clear
* element names as indicated below. Otherwise, the basic
* <add />, <remove />, and <clear/> elements are used in the config.
* Redefining element names here does NOT apply to the Basic Map
* collection type.*/
AddItemName="ElementNameForAdding",
ClearItemsName="ElementNameForClearing",
RemoveItemName="ElementNameForRemoving",
CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)]
public class ConfigElementCollection_ARCMap:ConfigurationElementCollection
{}
}
1.] Properties - Used to apply any element attributes for the collection element
as well as improve performance.
2.] CollectionType - Override the default value and return the appropriate
collection type.
3.] ElementName - This property is really only necessary for the Basic Map
collection type when desiring to rename the <add/> element.
#region Properties
/// <summary>
/// Override configuration property collection to apply any necessary attributes
/// and improve performance
/// </summary>
protected override ConfigurationPropertyCollection Properties
{
get { return _Properties; }
}
/// <summary>
/// Define the collection type as being an ARC Map by overriding
/// </summary>
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
23
Configure Your .Net Application - The Other INI
/// <summary>
/// Added particularly for the Basic Map approach
/// Gets the name used to identify this collection of elements
/// in the configuration file when overridden.
/// </summary>
protected override string ElementName
{
get { return "myCollectedElement"; }
}
#endregion
#endregion
/// <summary>
/// Creates new custom element for collection
/// </summary>
protected override ConfigurationElement CreateNewElement()
{
return new ConfigCollectedElement();
}
/// <summary>
/// Identifies the key attribute for the collection
/// </summary>
24
Configure Your .Net Application - The Other INI
/// <returns>Key</returns>
protected override object GetElementKey(ConfigurationElement element)
{
return (element as ConfigCollectedElement).Name;
}
#endregion
Step-6. Finally, add some useful methods for programmatic interaction with the collection.
#endregion
25
Configure Your .Net Application - The Other INI
Sections/SectionGroups
Sections and SectionGroups are elements typically used to better organize the configuration file and to
contain the custom elements. At a minimum, Sections are required for custom configuration, however
SectionGroups are not. If SectionGroups are used, the Sections will be nested within them. Below is a
very generic illustration of this concept:
<customSectionGroup>
<customSection>
<customElement name="myName" path="C:\...\..." description="A generic description"/>
</customSection>
</customSectionGroup>
Every Section and SectionGroup must be defined within the configuration file’s <configSections>
element. This is necessary so that the system can identify the appropriate custom handlers. The
example below defines a custom Section and SectionGroup:
<configSections>
<sectionGroup
name="MyApplicationGroup"
type="adskCustomConfiguration.ConfigGroup,adskCustomConfiguration">
<section
name="MyApplicationSection"
type="adskCustomConfiguration.ConfigSection,adskCustomConfiguration"/>
</sectionGroup>
</configSections>
Each Section used in a specified SectionGroup is defined within the appropriate <sectionGroup>
declaration element. The name attribute defines the name of the Section or SectionGroup. The type
attribute defines the class and namespace of the Section or SectionGroup handler. Typically the type
attribute format will be:
Since most of the basic AutoCAD .Net applications will not be signed, versioning is not being
maintained in the AutoCAD install directory, and culture can be defaulted, the format can be simplified
to:
The configuration section handler is just as simple to create as the configuration element handler.
Though the examples below don’t illustrate this, a configuration Section or SectionGroup can also
contain attributes like any other element. To create a Section handler, derive a class that inherits from
ConfigurationSection in the System.Configuration namespace and then define each attribute just as
was done for the basic configuration element handler. Of course the point of a custom Section is to
contain custom elements. So add a property decorated with the ConfigurationProperty attribute
(assuming the declarative method) for each element represented in the section. Below is an example
26
Configure Your .Net Application - The Other INI
configuration section handler that represents a section that contains two custom elements and two
custom element collections.
using System.Configuration;
namespace adskCustomConfiguration
{
public sealed class ConfigSection:ConfigurationSection
{
[ConfigurationProperty("myProgElement")]
public ConfigElement_P ProgElement
{
get { return (ConfigElement_P)this["myProgElement"]; }
set { this["myProgElement"] = value; }
}
[ConfigurationProperty("myDeclElement")]
public ConfigElement_D DeclElement
{
get { return (ConfigElement_D)this["myDeclElement"]; }
set { this["myDeclElement"] = value; }
}
#endregion
#endregion
}
}
27
Configure Your .Net Application - The Other INI
Developing SectionGroup handlers is even easier. Simply identify the Sections nested in the
SectionGroups as properties similar to what has already been presented.
using System.Configuration;
namespace adskCustomConfiguration
{
public class ConfigGroup:ConfigurationSectionGroup
{
[ConfigurationProperty("MyApplicationSection")]
public ConfigSection MyConfigSection
{
get { return (ConfigSection)base.Sections["MyApplicationSection"]; }
}
#endregion
}
}
The ConfigurationManager class and supporting methods have already been defined in this document
and will not be reiterated here. However, the basic concepts for opening, modifying, and saving custom
configuration settings will be illustrated here.
To open the customized configuration file, use the OpenExeConfiguration() method of the
ConfigurationManager class, specifying the string file path of the AutoCAD .Net application (e.g.
C:\...\MyCADApp.dll). This allows the OpenExeConfiguration() method to return a Configuration class
representation of the application configuration file as associated to the .Net application instead of the
host acad.exe. Since the method parameter supplied is the path to the application executable (*.dll)
and not the application configuration file (*.dll.config), the configuration file is required to be in the same
location as the executable.
Assuming SectionGroups were used, first create an instance of the SectionGroup handler with the
Configuration.SectionGroups.Get(“SectionGroup Element Name”) or Configuration.GetSectionGroup
(“SectionGroup Element Name”) methods. Then, from the SectionGroup reference object, use the
ConfigurationSectionGroup.Sections collection to retrieve the desired Section. Upon obtaining the
Section instance, each nested element can then be retrieved along with each of its attribute values.
If a SectionGroup was not used, the Section instance can be directly obtained with the
Configuration.Sections.Get(“Section Element Name”) or Configuration.GetSection (“Section Element
Name”). Below is a short example. Note that it uses reflection and the application’s codebase to
supply the executable file path.
<?xml version="1.0"?>
<configuration>
<!‐‐
Sections and SectionGroups have to be defined in the
<configSections> element so that the configuration classes
can find the configuration handlers
‐‐>
<configSections>
<sectionGroup name="MyApplicationGroup"
type="adskCustomConfiguration.ConfigGroup,adskCustomConfiguration">
<section name="MyApplicationSection"
type="adskCustomConfiguration.ConfigSection,adskCustomConfiguration"/>
</sectionGroup>
</configSections>
<MyApplicationGroup>
<MyApplicationSection>
</MyApplicationSection>
</MyApplicationGroup>
<startup><supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.0"/></startup></configuration>
29
Configure Your .Net Application - The Other INI
if (custGroup != null)
{
//Get custom section in group
ConfigSection custSection = (ConfigSection)custGroup.Sections["MyApplicationSection"];
if (custSection != null)
{
//Get color attribute value from one of the custom nested elements
Color myColor = (Color)custSection.ProgElement.ColorProperty;
Since we used the set keyword when defining our attribute properties, modifying and saving settings
data is also very simple. Simply update the value and save.
30
Configure Your .Net Application - The Other INI
{
Name = "ARCMapItem#" + NextItem.ToString(),
Path = @"C:\Test\ARCMap\Item" + NextItem.ToString()
};
custSection.CustomElementCollectionARC.Add(newElement);
//Save results
dllConfig.Save(ConfigurationSaveMode.Full);
Without spending a lot of time on the subject, let’s briefly consider validation and type safety. These
topics are not required when developing custom configuration files, but can be extremely handy in
certain situations. So it’s definitely worth a little of our time to discuss. The following will simply outline
the use of configuration validators and type converters. For custom development of these, see
Appendices D and E.
Validators
The System.Configuration namespace comes with several predefined validators and accompanying
validator attributes that can be used to ensure data correctness of element attribute values entered in
the configuration file. These validators add more control to the applicable scope of data values to be
accepted and will cause the system to throw an ArgumentException error when invalidated instead of a
generic one whose message may not be clear. Applying these are simple and depends upon whether
the element handler is defined using the programmatic or declarative approach. With the programmatic
approach, the validator is supplied as another parameter in the attributes’ field declaration. With the
declarative approach, the attribute property is decorated with a validator Attribute. To identify all the
available predefined Configuration validators and their accompanying attributes, see the MSDN article
on the System.Configuration namespace. Below is an example for each method:
Programmatic Approach:
Declarative Approach:
31
Configure Your .Net Application - The Other INI
Converters
Type conversion is typically handled by the configuration system in the .Net Framework automatically
during the serialization/deserialization processes. However, there may be times when the developer
desires to persist custom data. This would be especially true when persisting objects from the
AutoCAD .Net API as many are non-serializable, sealed classes. System.Configuration contains many
predefined converter classes that are available for the more common uses and can be found in the
MSDN article on the namespace. As with validators, the method of applying the converter is dependent
upon the approach for defining the element attributes. The following illustrates application of a custom
converter called DBConverter for each approach.
Programmatic Approach:
Declarative Approach:
[ConfigurationProperty("blockDB")]
[TypeConverter(typeof(DBConverter))]
public Database BlockDB
{
get { return (Database)this["blockDB"]; }
set { this["blockDB"] = value; }
}
CONCLUSION
Wow! We’ve seen how simple it is to create and interact with configuration files. We’ve also seen how
much more integrated, consistent, flexible, and robust this system is. And we’ve also covered usage of
this technology from a basic level to full customization. Hopefully you’re now envisioning many
opportunities for its use in your own applications. With use of this configuration technology, user
preferences, AutoCAD process settings data, custom AutoCAD .Net application settings data, and .Net
application support and directive data can be easily stored and leveraged to add a more interactive and
dynamic level to the application without requiring additional coding, recompiling, and all the overhead
that accompanies a database!
REFERENCES
Though mostly on the Internet, there are many wonderful resources out there. Many of which I couldn’t
have written this white paper without. So for those interested in learning more, here are a few of my
favorites:
32
Configure Your .Net Application - The Other INI
• MSDN: http://www.MSDN2.com
There is some great information all in the MSDN Library regarding the various base
classes. To see information regarding the various predefined elements try searching for
the “Configuration File Schema” based on the specified Framework version. For v4.0,
the MSDN contents path is MSDN Library/.NET Development/.NET Framework
4/General Reference for the .Net Framework/Configuration File Schema. From here the
higher level elements are illustrated and once any are clicked on can be drilled down
much further.
• Wikipedia: http://www.Wikipedia.org
Of course who can for such a great resource for clarifying our understanding based on
the experience and knowledge of others?
• PInvoke.net: http://www.pinvoke.net:
This is a great little tool for those interested in using P/Invoke. It is essentially a wiki of
P/Invoke signatures and data types.
Please note that this document is the result of personal practical application, bench top development,
compilation, and study of information that was derived from several sources. So there’s no one
statement intentionally pulled from any one source. So hopefully I’ve properly identified those
resources that have had the most impact for me in writing this and maybe they can be of great benefit
for others as well.
33
Configure Your .Net Application - The Other INI
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace INI_With_PInvoke
{
class INIFile
{
#region Properties
#endregion
/********************************************************************
* For more information on these and other COM methods to P/Invoke, *
* see www.PInvoke.net *
* ******************************************************************/
//Declare the Read and Write PInvoke methods based on the kernel32.dll
//COM library of the Win32 API
#endregion
34
Configure Your .Net Application - The Other INI
#region Constructor
/// <summary>
/// INIFile Constructor
/// </summary>
public INIFile(string FileName)
{
//initialize properties
_FileName = FileName;
}
#endregion
/// <summary>
/// Reads data from the initialized ini file.
/// </summary>
/// <returns>String representation of Key's assigned value.</returns>
public string Read(string Section, string KeyName)
{
StringBuilder sbReturn = new StringBuilder(255);
return sbReturn.ToString();
}
/// <summary>
/// Writes data to the initialized ini file.
/// If the file is not found, it will be created in C:\Windows
/// </summary>
public void Write(string Section, string KeyName, string Value)
{
//Call the PInvoked method to write information
WritePrivateProfileString(Section, KeyName, Value, this.FileName);
}
#endregion
}
}
35
Configure Your .Net Application - The Other INI
Class Usage: This console code example illustrates how the INIFile class can be used.
static void Main(string[] args)
{
//Let's use the MyApplication.ini that is copied to the same
//directory as this *.exe. Use reflection to obtain this *.exe's
//directory and include the ini file name in the full file path.
string MyAssemblyCodeBase = Assembly.GetExecutingAssembly().CodeBase;
Uri uriCodeBase = new Uri(MyAssemblyCodeBase);
FileInfo fiDirectory = new FileInfo(uriCodeBase.LocalPath);
string FileName = Path.Combine(fiDirectory.Directory.ToString(),
"MyApplication.ini");
Sample INI File: The MyApplication.ini file data used in this example.
36
Configure Your .Net Application - The Other INI
1.] <MyElement/>
2.] <MyIntegerElement>100</MyIntegerElement>
For discussion’s sake I’ll refer to option 1.] as an empty-element tag method and 2.] as a start-end tag
method.
A rule of thumb that’s nice in helping to remember how to format the element tags is that the forward
slash character (/) indicates the closing character (i.e. / = “close”). Therefore, (<) open element, (/>)
close element. If using the empty-element tag approach, then the “close” character goes at the end as
one would assume. If the element formatting is enclosing (start-end tag method), the “close” character
goes at the beginning of the end tag to indicate that it is closing that element instance.
Enclosing elements usually surround some form of data. Since many of us are familiar with
programming terms and variables, consider this correlation:
<MyIntegerElement>100</MyIntegerElement>
While elements describe the data, attributes are like properties of an element. In the following
example, the element CourseCertificate contains four attributes (properties): certType, ceu, size, and
units.
<CourseCertificate
certType="CourseCompletion"
ceu="1.5"
size="8.5x11"
units="inches">Configure Your .Net Applicaiton - The Other INI
</CourseCertificate>
Notice also that just as in any C-based language development, whitespaces do not matter. So this
could’ve just as easily been written as one long line.
Comments are indicated using the <!-- and --> wrappers. For example:
37
Configure Your .Net Application - The Other INI
Line myLine;
acLineStringConverter.TryParse(config.AppSettings.Settings["myLine"].Value,
out myLine);
where...
#region Properties
#endregion
if (TypeData[0] == "Line")
{
double X = 0, Y = 0, Z = 0;
X = 0; Y = 0; Z = 0;
38
Configure Your .Net Application - The Other INI
39
Configure Your .Net Application - The Other INI
#region Properties
#endregion
#region Constructors
#endregion
if (objValue<this.MinDate || objValue>this.MaxDate)
throw new ArgumentException("Value should be between " + this.MinDate +
" and " + this.MaxDate + "!");
}
#endregion
40
Configure Your .Net Application - The Other INI
Creating the accompanying Validator Attribute is just as simple. Add a new class and set to derive from
the ConfigurationValidatorAttribute class. Then override the ValidatorInstance() method.
#region Properties
#endregion
#region Constructors
DateTime dttMinDate;
DateTime dttMaxDate;
DateTime.TryParse(maxDate, out dttMaxDate);
DateTime.TryParse(minDate, out dttMinDate);
this.MinDate = dttMinDate;
this.MaxDate = dttMaxDate;
#endregion
#endregion
}
41
Configure Your .Net Application - The Other INI
return _DxfPath;
}
}
return _DxfInLogPath;
}
}
#endregion
/// <summary>
/// Converts string representation of the Drawing DXF into a Database object
/// </summary>
42
Configure Your .Net Application - The Other INI
File.WriteAllBytes(DxfPath, dxfBytes);
return dbDXF;
}
else
return null;
}
/// <summary>
/// Converts AutoCAD Database object into a string representation of the Drawing
/// </summary>
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value,
Type destinationType)
{
return DXFContents;
}
else
return null;
}
#endregion
}
43