Overview
| Format and usage | Creation
| Members reference
ConfigFile class is a
component that allows you to read/write Universal Structured Data (UDS)
from/to different medias/formats. Structured configurations is a synonym
of UDS when it is used in a format and for purpose of configuration.
UDS data are trees of data separated in sections (which can be nested). Component loads/creates them into collections (using VarDictionary
objects), the application is able to fill/change them and save the changed
data. ConfigFile
defines abstract structure and supports different formats and medias.
The created/loaded in memory data tree can be stored to different
targets and formats. Current version of ConfigFile component supports
internally 3 formats: text (see Universal
Data Structure), windows registry and binary
streams. Of
course the binary format is the most powerful and supports all
the features but if you need something more human readable or
compatibility with other applications the other formats are at
hand
Structured configurations are very powerful tool and they can be
used for various purposes - from configuration and data exchange to a DB replacement in case of small amount of data. These data
structures are the same structures used by the structured data
classes in our C++ libraries. Their origin is Jacked-Objects library.
The standard comes not from a file format, but from the internal
architecture standards implemented by the C++ (and Jacked) classes in
our libraries. Its capabilities cover not only configurations but also
network and inter-process data transfers, flexible and portable object
persistence mechanisms, data hierarchy common for our applications.
Historically the first usage of these data classes was in 1996 and
overlaps with the years of XML growth. Our architecture was developed
because of the same reasons and thus it has many similarities with the
XML, but don't forget it was born as an abstract data scheme and not
over a document format specification. Also it is oriented to the programmer
and thus supports value types from the very beginning. The class in this
DLL is for simple usage, in general the structured data classes and
the standard are the backbone of our applications. Read more detailed
definitions here: Universal
Data Structure.
How the ConfigFile represents the data in memory
ConfigFile class uses trees of collections/dictionaries to represent the
UDS in memory (Logical Data Structure). VarDictionary objects are used for that
purpose, their Info
property holds additional object - NodeInfo.
This object helps you to determine the node meaning (record or
section) and holds the class info (see UDS
for the logical data structure definition). Collections in the data tree are of two
types: Sections
and Records. In
short the rules about them are:
- Sections - may contain other sections
(sub-sections) and records as items.
- Records - may contain only values and no
sections or other records.
Result of the Read, ReadFromRegistry or ReadFromBinaryStream methods strictly follows the above rules, but if
you are creating the data to be saved later through the one of the
Write methods you can use one compromise: To simplify the usage of the
collections it is allowed to add values
in the sections. During the write operation a record will be
created for all the values with the same names found in the section
and the values will be assumed as unnamed. Probably this sounds a bit
confusing at a first look, but it makes the usage of the collections
intuitive and simple (take a look at the examples). Syntax for
accessing a value stored in the section collection equals the
syntax for accessing an unnamed value in a record thus you don't need
to create record for every new value you want to add to the data
structure. However the correct usage will require you to use the
CreateSection and CreateRecord methods to create the elements of the
tree and then attach them to the node where you want them. So, to
create a simple tree like this (see the text format description in UDS):
{ A:
(int)V1=123
{ B:
(string)V2=Blah
(string)V2=Blah Blah
} B;
} A;
You will need code like this:
Set cf = Server.CreateObject("newObjects.utilctls.ConfigFile")
Set uds = cf.CreateSection
uds.Add "A", cf.CreateSection
uds("A").Add "V1", cf.CreateRecord
uds("A")("V1").Add "", CLng(123)
uds("A").Add "B", cf.CreateSection
uds("A")("B").Add "V2", cf.CreateRecord
uds("A")("B")("V2").Add "","Blah"
uds("A")("B")("V2").Add "","Blah Blah"
You can save it to a text file like this:
cf.Write "myfile.txt", uds
Or to a binary stream (lets use a file in the sample):
Set sf = Server.CreateObject("newObjects.utilctls.SFMain")
Set file = sf.CreateFile(Server.MapPath("myfile.bin"))
cf.WriteToBinaryStream file, uds
As we mentioned above the nodes of the data tree are represented by
VarDictionary objects configured to behave as follows:
Sections are represented by VarDictionary
object with behavior
properties set to:
firstItemAsRoot = false
itemsAssignmentAllowed = true
enumItems = false
allowUnnamedValues = true
allowDuplicateNames = true
extractValues = false
Records are represented by VarDictionary
object with behavior
properties set to:
firstItemAsRoot = true
itemsAssignmentAllowed = true
enumItems = false
allowUnnamedValues = true
allowDuplicateNames = true
extractValues = true
Note that you should not store any
objects as values and you should use CreateRecord and CreateSection
(see below) methods to create the sections and the records. If you
violate the structure requirements the Write methods will fail to
store it. However if you are not going to save the structure (for
example you read something that is initial configuration or something
like it) you can do whatever you want - attach other objects, values
of not supported types and so on. This may help you create quite
complex and flexible application, but you should always be aware of
the fact that this will make the structure unsaveable.
Another recommendation is to use the
VBScript conversion functions (or other ways to do so) when setting
values. The implicit value conversions may produce unexpected results.
For example the string "123" will implicitly be saved
as string, but you may want to create integer value, so using the CLng
will guarantee that the type of the value will be exactly what you
want.
Default (registered as Both) - i.e. the object is created in the
same COM apartment.
- ProgID: newObjects.utilctls.ConfigFile
- CLSID: {262DE78D-982E-4B0F-8230-D99D079EA7FA}
Freethreaded registration.
- ProgID: newObjects.utilctls.ConfigFile.free
- CLSID: {FCF4F139-90E0-47a9-8DC0-62B1B216CC41}
Read/Write from/to text file
Read
Set variable = object.Read(fileName)
Attempts to read the file pointed by the string passed as fileName
parameter and returns a tree of collections that represents the
data from the file.
If the file was not found, can not be read or format is wrong
error will be generated.
Write
object.Write(fileName,dataTree)
Attempts to save the collections in the dataTree
parameter to the file specified by the fileName string
parameter. dataTree must point to a collection that
represents Section. Records can not be saved if they
are not part of a section.
Read/Write from/to registry
ReadFromRegistry
Set variable = object.ReadFromRegistry(branch,path)
Attempts to read the registry branch specified:
branch is a number that specifies the root registry branch:
0 |
HKEY_CLASSES_ROOT |
1 |
HKEY_CURRENT_USER |
2 |
HKEY_LOCAL_MACHINE |
3 |
HKEY_USERS |
4 |
HKEY_PERFORMANCE_DATA |
5 |
HKEY_CURRENT_CONFIG |
6 |
HKEY_DYN_DATA |
Note that except the first 3 branches the rest are supported by
some Windows versions only - use them with care!
path - is the full key name. For example if you want to
load the branch containing the settings for your program this will
be something like: "SOFTWARE\MyCompany\MyApplication".
Note: The class names are saved/read as the default values of the
keys.
WriteToRegistry
object.WriteToRegistry(branch,path,dataTree)
Attempts to save the collections in the registry branch - see
the parameters for the ReadFromRegistry for details.
Read/Write from/to binary stream
ReadFromBinaryStream
Set variable = object.ReadFromBinaryStream(strm)
Attempts to read structured data from a stream. The strm
parameter can be a SFStream
object or another object that supports IStream interface (this is
most useful for VB and C++ programmers, script programmers may
need something else than SFStream only if some external objects
are used).
See the Storages
and Files on how you can access files, memory blocks, OLE
files as streams. In theory you can use any stream to read/read
structured data from/to it. Even COM components supplied by other
vendors can be used if they support the IStream (standard Win32
interface) interface. This allows the data to be transferred or
stored in almost unlimited medias and the usual file system files
are only one of the many possible storages.
The binary format is not described in this article but detailed
information on it will be added to the documentation as separate
chapter in order to allow other developers to write format
compatible solutions (on other OS-es for example).
The binary format supports all the structured data features and
data types.
WriteToBinaryStream
object.WriteToBinaryStream(strm, dataTree)
Attempts to save the collections in the binary stream - see
the ReadFromBinaryStream method for more information.
Helper methods
To avoid need to configure manually the VarDictionary objects you
prepare for the data trees these methods shall be used to create
new nodes. The result returned by each of these methods is a VarDictionary
object with NodeInfo
object attached and configured. Although you can add new records
with only one value by assigning values to the sections it is
recommended to create new records, add them to the sections and then
add the value(s) to the record. The values stored in the sections
are in fact rule violations and support for them is provided
only as efforts-saving feature that can be used by experienced
developers to save some coding work. This support is a syntax based
trick - e.g. if you have section "A" and a record
"V" with one value in it the syntax:
o("A")("V") will be the same as in case the
value is stored in the section directly (without record) but this
happens because of the automatic resolution of the default values of
the objects (the record's default value is the first value stored in
it). Therefore this feature may lead to confusions in some cases
(especially if you are using records with multiple values) and we
strongly recommend to avoid it in production environments.
CreateSection
Set variable = object.CreateSection()
Creates new empty section collection. ClassName
in the Node info (accessible
through the Info
property of the collection object) is set to empty string -
you can set this value after obtaining the newly created
collection.
See above how the TextConfig class represents sections and records.
CreateRecord
Set variable = object.CreateRecord()
Creates new empty record collection. See above how the
TextConfig class represents sections and records.
Configuration properties
PreserveStringsWide
object.PreserveStringsWide = value
variable = object.PreserveStringsWide
Boolean value that specifies how the strings will be preserved
when the data is written. This is suitable for the binary format
only. The text and registry format depend on the media limitations
and currently ASNI code page is used to convert them.
When this value is set to True the string values written to a
binary stream are saved as wide strings (UNICODE).
Default is False.
PreserveUnsignedInt
object.PreserveUnsignedInt = value
variable = object.PreserveUnsignedInt
Boolean value that specifies whether the unsigned integer values
will be
preserved when the data is written. This is suitable for the
binary format only (WriteToBinaryStream). In the earlier versions of UDS no unsigned
values have been supported. This property is intended to allow the
developers to control the behavior.
What will happen if you create data for a program that uses an
earlier version with this property set to True. No fatal error
will occur however the program will not be able to read any
unsigned integer values from the data. Note that scripts very
often rely on implicit type conversions. This means that if the
property is False all the integer values will be converted to
signed integers, when it is set to True the unsigned integers will
be preserved. Therefore if you are not using explicitly type
conversion routines when setting values in the data tree the
result may be different according to the property value.
Default is False.
ErrorMode
object.ErrorMode = value
variable = object.ErrorMode
Error mode can be currently 0, 1 or 2
0 - default. Pass the parameters "as is". If the value
of one or more of the configuration properties is not compatible
with the write/read method used an error will occur only if the
operations performed concern it. For example if the write method
does not allow wide strings an error will not occur as long as no
strings are saved.
1 - Adjust options. The values of the configuration
properties incompatible with the write/read method used will be
automatically discarded/adjusted if possible. Perhaps this mode is
the most convenient one. It allows you to avoid frequent changes of
the configuration properties while using different write/read
methods.
2 - Check options. When a value of a configuration property is
incompatible with the write/read method used an error occurs before
any work is done.
|