SCU Specification
Version 1.1
Last Edit: 11-April-2002
Introduction
The Software Configuration Utility consists of a single COM object which can be used to access a hierarchical store of configuration information (such as preferences) for software, and a two-function API in a dynamic library. The following text describes the interface to SCU. All API and method calls are stdcall. See the binary distribution of SCU for header files for C++ and Delphi.
For Windows implementations, SCU operates on the SCU database file located in the Windows directory on your system disk, by default. This is determined as being the parent directory of the System directory (or the System directory itself, if it is the root directory).
The SCU database is organized as an arbitrarily deep hierarchy of nodes. Each node has a specific data type and name. Keys simply serve to hold other keys or values. All other data types are terminal nodes. These terminal nodes (also called Values) have a type, a name, and data associated with them. Keys have no data associated with them. Each node is uniquely identifiable by a path which indicates the parent node name(s) and terminal value name, separated with backslashes (\), with the highest-level node to the left.
Another way of looking at it is as if the database were a hierarchical file system. In this example, Keys are directories (or sub-directories) and Values are files, which contain data (the associated data). The full path and filename uniquely identify the Value. For instance, "\Users\George\Path" would indicate a Key or Value named "Path", under the Key named "George", which is under the top-level key named "Users".
Note that no item can exceed 2 Gb in size. Names may consist of any ASCII values except asterisk (*), question mark (?), and ellipse (...) which are all reserved for wildcard operations. Also, "." and ".." are reserved for use in future versions of SCU. However, it is generally a good idea to limit the names to displayable characters in the range 33-126 to avoid confusion on the part of the user.
SCU implementations must be multi-process safe. Specifically, multiple processes must be able to access the SCU database simultaneously without data corruption, or abnormal program termination. The mechanism for this is implementation-specific, but may include locking, semaphores, or services.
Special Values
There are some top-level keys which are always defined by SCU and therefore not available for terminal values. They are:
Key name | Description |
Alarms | Values under this key denote alarms that have been set on other values. |
Groups | Values under this key denote groups. Each key under this key is the name of a group which has been defined via an SCU editor (the node is terminal at initialization time). Under each group name key is a "Groups" key, under which are zero or more other group names. |
Network | Values under this key provide information about networks. Specifically, the SCU_Path value (a string) contains the network path name of the SCU database that applies to the network. |
Users | Values under this key denote user-specific values. Each key under this key is the name of a user who has been defined via an SCU editor (the node is terminal at initialization time). Under each user is a key called "Groups", which contains zero or more keys whose names match groups in the top-level Group key. A value stored under the user's key, "SCU Path" indicates (if present) the SCU database to use for user-specific values. |
Users
An SCU user is a named sub-domain of the SCU database. Specifically, this is the mechanism that provides for different users having different configurations. It is up to the calling program to provide the username, which usually comes from the operating system. When a value is set on behalf of a user, it is created and/or set under the \Users\<name> path (where "<name>" is the user name). If the value is set without a a username being provided, it is created in the root of the SCU database. For instance the value named "Test\Bin" can appear in "\Test\Bin" for no user or in "\Users\Joe\Test\Bin" for user "Joe". When an existing value is read, the user's sub-domain is checked first. If the value is found, it is returned. otherwise, the value is looked for elsewhere (see following description). Each user can have a value called "SCU Path" which, if present, specifies a separate SCU database file for that user. System Administrators can then set the system-wide SCU database as a read-only file for most users, and the user-specific database file can be writable by the user. Further security can be implemented via alarms.
The search performed for a value looks in the following places, until it is found or all possible locations have been exhausted. For user and path, the search is:
1. Look in <user-specific SCU database file>\<path>. This step is skipped if user is not specified.
2. Look in <SCU database>\Users\<user>\<path>. This step is skipped if user is not specified.
3. Iterate through <SCU database>\Users\<user>\Groups\* using the found group names to look in \Groups\<group>\<path>. This step is skipped if User is not specified.
4. Iterate through the groups from step 2, looking for parent groups (mentioned via <SCU database>\Groups\<group>\Groups\*). This step is skipped if step 2 is not skipped.
5. Look in <SCU database>\<path>
6. If <SCU database>\Network\SCU_Path is defined, use the value as the name of the SCU database file and goto step 2.
As an example, assume the following SCU database layout:
Groups | |||||
Accounting | |||||
Groups | |||||
Office | |||||
Software | |||||
Millisoft Inc | |||||
Editors | |||||
Last File | |||||
Office | |||||
Software | |||||
Millisoft | |||||
Editors | |||||
Line Wrap | |||||
Software | |||||
Millisoft Inc | |||||
Editors | |||||
Line Wrap | |||||
Users | |||||
Joe | |||||
Groups | |||||
Accounting | |||||
Software | |||||
Millisoft Inc | |||||
Editors | |||||
Line Wrap | |||||
Zoe | |||||
Groups | |||||
Accounting |
If SCU was asked to lookup "Software\Millisoft Inc\Editors\Line Wrap" for user Joe, SCU would first look in "\Users\Joe\Software\Millisoft Inc\Editors\Line Wrap" and would find the value there. If asked to lookup the same value without a user specified, it would find "\Software\Millisoft Inc\Editors\Line Wrap". If asked to lookup the same value for user David, who doesn't exist (or has no matching path in his sub-domain", SCU would find "\Software\Millisoft Inc\Editors\Line Wrap". If SCU is asked to lookup "Software\Millisoft Inc\Editors\Last File" for user Joe, it would look in the Joe sub-domain and not find it. It would then look in the "\Groups\Accounting\Software\Millisoft Inc\Editors\Last File" and find it there. It looks in the accounting group since "Accounting" is one of the key values found in "\Users\Joe|Groups". If SCU was asked to look for "Software\Millisoft Inc\Editors\Line Wrap" for user Zoe, it would not find the value in the Zoe sub-domain. It would then look in the Accounting group sub-domain (since "\Users\Zoe\Groups" contains "Accounting") and not find it there either. It would then find it in "\Groups\Office\Software\Millisoft\Editors\Line Wrap". It finds it there because "\Groups\Accounting\Groups" contains the "Office" key).
To describe the process another way: SCU allows you to define values at numerous levels and then override them at specific lower levels. So, you could set default values at a network level, but override them with a default values at a system-level for certain systems, and/or at a user-level for certain users, and/or at a group-level for certain groups. For instance, a popular word processor could have default settings set network-wide. But individual users can alter their preferences without changing the network setting.
Alarms
Alarms provide protection against changes to an SCU database. An alarm is set on a specific, non-Key, value. The alarm can protect against any of four possible types of access. If an attempt is made to access a value which has an alarm set for that type of access, a dialog is shown notifying the current user of the operation and allowing them to disallow it. Alarms cannot be set on alarms, except via the Control alarm. Alarms are stored under the top-level Alarms key. The path of the value to protect is used as a path under the top-level Alarms key. The terminal value is a string containing one or more alarm specifiers (see below). For instance, the value "\Users\Joe\SCU Path" would have an alarm in "\Alarms\Users\Joe\SCU Path". If no alarm is set for a value, any access is allowed.
Alarms
Type | Alarm specifier | Description |
Control | C | Prevents the alarm, itself, from being deleted or changed without user authorization. |
Delete | D | Prevents the value from being deleted or renamed without user authorization. |
Read | R | Prevents the value from being read without user authorization. This type of alarm is not normally recommended, but can be used to detect whether or not a specific SCU value is being read. |
Write | W | Prevents the value from being changed without user authorization. |
Groups
Groups provide a way of grouping configuration settings for several users, such that changing the setting in one place will change it for all users of the group, but no one else (assuming individual users haven't overriden the setting themselves). Each group has a name which serves as a key in the \Groups path. Under that key are settings that apply to that group and also the "Groups" key which contains a list of parent groups of this group. Thus, groups themselves can be members of other groups.
Alternate Implementations
Any implementation of SCU can differ significantly from another. It is not assumed that data is compatible between any two implementations. However, the SCU interface provided must match the following defintions. The remaining issue is: how does one transport data between two different SCU implementations? This is done by providing an export and import utility. So to transfer data, the user simply exports from the source and imports to the destination. Note that import and export features are not provided in the TSCU methods or in the API calls. An inport/export utility must be provided with SCU (usually in the form of an SCU editor application).
The import/export files are text files which only contain characters within the ASCII range of 32 to 126, inclusive. The exception being that ASCII 10 and 13 (LF and CR) are used to delimit lines. Each line should end with a CRLF combination. The file consists of "records" which are one or more lines of text, each. File layout:
Header
Value record
.
.
.
The Header record is a single line with the following text:
SCU 1.0
Following the header is one or more value records. Each value record consists of at least two lines: the first is the full path name of the value. The second is the length of the data a space (ASCII 32) and a number corresponding to the data type. Keys always have a length of 0. For other data types, the description of the data is on line 3 (or starts on line 3 for large values). All values are written to the file as a series of two-digit hexadecimal values. Integers and Floats are written in little-endian form. Each hexadecimal value is delimited from the others by a space. No data lines may exceed 254 bytes, total. Generally, lines should be left at 128, or even 80, bytes. Thus, a 256-byte-long value would take 768 bytes (two bytes for each Hexadecimal value, plus one space = 3 bytes * 256 bytes = 768 byes). At 129 characters per line, the data would take six lines. The idea here is ease of data interchange, not efficiency of storage space.
Data types
Each item in the SCU database has a data type. The following SCU data types are defined:
Value | Mnuemonic | Description |
0 | SCUDT_Undefined | Type not defined. |
1 | SCUDT_Key | A key. |
2 | SCUDT_String | Arbitrary text. This is not null-terminated - nulls can be embedded in the text. |
3 | SCUDT_Integer | 4-byte signed integer. |
4 | SCUDT_Boolean | True or False. |
5 | SCUDT_Float | IEEE 754 Double-precision floating point number. |
6 | SCUDT_Binary | Arbitrary binary data. |
-1 | SCUDT_All | All types. |
Errors
SCU makes use of UEH, although it will operate without UEH32.DLL. SCU has a facility code of 22. The following are SCU error codes:
Code | Mnuemonic | Description |
0 | SCUErr_Success | Operation suceeded - no errors. |
1 | SCUErr_Value_Not_Found | Reference was made to a value that doesn't exist. |
2 | SCUErr_Invalid_Size | Data size specified was invalid (too large for data type or < 0). |
3 | SCUErr_Exists | Key or value already exists. |
4 | SCUErr_Alarm_Failure | Operation caused an alarm which user used to abort operation. |
API
SCU32.DLL provides two sets of API functions: COM-based, and non-COM-based. The non-COM-based are only found in Version 1.1 and later. The COM-based API calls are:
TSCU Create_SCU_Interface() ;
Creates and returns a TSCU object.
void Free_SCU_Interface( TSCU ) ;
Frees a TSCU object created with Create_SCU_Interface.
The Non-COM-based API calls are:
Integer Version();
Returns the version of the SCU interface. For version 1.1 this would return 11.
String SCU_Get_Message();
This function returns the text associated with the last error that occured in a SCU function call
void SCU_Set_Path( String Path );
This function defines the SCU database file path and filename to be used by subsequent calls to SCU32 non-COM-based routines. The default is to use C:\Windows\SCU.DAT.
Integer SCU_Delete_Value( String Name) ;
This function deletes the specified value from the SCU database. It returns 0 if the value was successfully deleted, and -1 otherwise. In the case of an error, call SCU_Get_Message to retrieve the text of the error.
Integer SCU_Set_Value( String Name, Integer Type, String Value, Integer Size );
This function sets a value in the SCU database. If the value does not exist, it is created, including the path if neccessary.
Name is the name of the value.
Type is the datatype of the value.
Value is what to assign to the value (ignored for Keys since keys have no values).
Size is the size of the value. For strings and binary this is the length of th data. For all other types, this is ignored. Size may be -1 for strings that end in a null - in that case the size will be automatically calculated.If the function succeeds, the return value is 0, otherwise it is -1. If it fails, use SCU_Get_Message to retrieve the error text.
String SCU_Get_Value( String Name, Integer& Type, Integer& Size );
This function retrieves a value and returns the contents of that value. Name is the name of the value. On call the Type and Size are ignored. On return they contain the type and size of the data. Type is the data type.
TSCU methods
The TSCU object has the following methods. Parameter and result types:
Type | C++ equivalent | Delphi equivalent | Comments |
Integer | int | Integer | 4-byte signed. |
String | char* | PChar | Strings returned by TSCU methods are only guaranteed to be valid until the next call to a TSCU method, and are read-only. |
Boolean | bool | Boolean | |
Pointer | void* | Pointer |
TUEC Initialize() ;
This method must be called first to initialize the object after it has been returned by Create_SCU_Interface.
void Terminate() ;
This method is called when you are finished with the TSCU object and must be called just prior to calling Free_SCU_Interface.
TUEC Init( String Name ) ;
This method is used to access a different SCU.DAT file than the default. Name is the complete path and filename.
Integer Version ();
This method returns the version of SCU. For instance, a value of 10 indicates version 1.0.
The following functions are intended for use by SCU editors (such as ConfigEd) and generally are not used by other applications, since they do not handle users automatically.
TUEC Write( String Path, Pointer Data, Integer Size );
This method writes data to the specified path. Data points to the data to write. Size is the size of the data, in bytes. Note that the data must match the data type of the specified path.
TUEC Read( String Path, Pointer Data, Integer Size ) ;
This method reads data from the specified path into the memory pointed to by Data. Up to Size bytes are read.
Integer Read_Size( String Path ) ;
This method returns the size of the data at the specified path.
Integer Read_Type( String Path ) ;
This method returns the data type of the specified path.
Boolean Exists( String Path ) ;
This function returns True if the specified path exists.
Boolean Key_Exists( String Path ) ;
This function returns True if the specified key exists.
String Lookup( String Path, Integer DT ) ;
This function is used to lookup value names. DT is the data type to match: SCUDT_Key matches keys and SCUDT_All matches keys and values. On the initial call, Path contains the specification to lookup. On subsequent calls, Path has a length of 0 and the next matching value is returned. If the result is length 0, then no matches (or no more matches) were found. The Path may contain wildcards. Certain characters have special meanings:
Wildcard specification Meaning * Match zero or more characters. For instance: Z*
this matches any value name starting with "Z".
? Match a single character. For instance: A?
this matches any value name starting with "A" that is two characters long.
... Search all subdirectories. For instance: A\...\*
this will find any values in any subkeys under the top-level A key.
TUEC Delete( String Path ) ;
This method causes the specified value or key to be permanently deleted.
TUEC Create( String Path, Integer DT ) ;
This method creates a value with the given name and type. This method fails if the path already exists.
TUEC Create_And_Write( String Path, Integer DT, Pointer Data, Integer Size ) ;
This method creates a value name and assigns it a value. Path is the path and name of the value to create. DT is the data type. Data is a pointer to the data to write, and Size is the size of the data, in bytes. This method fails if the path already exists.
TUEC Rename( String Old, String New ) ;
This method renames a value or key from the Old name to the New name.
The following functions are the ones most applications will use:
TUEC Set_Value( String User, String Path, Pointer Data, Integer Size ) ;
This method sets an existing value, specified by Path, for the specified user. Data points to the data to write and Size is the size of the data, in bytes.
TUEC Get_Value( String User, String Path, Pointer Data, Integer Size ) ;
This method obtains the value of the specified path, for the specified user. The data is read into the memory pointed to by Data, up to Size bytes.
Integer Value_Size( String User, String Path ) ;
This method returns the size of the specified value for the specified user, in bytes. If the value is not found, the return value is -1.
Integer Value_Type( String User, String Path ) ;
This method returns the data type of the specified value for the specified user. If the value doesn't exist, the method returns SCUDT_Undefined.
Boolean Value_Exists( String User, String Path ) ;
This method returns True if the specified value exists for the specified user.
TUEC Delete_Value( String User, String Path ) ;
This method deletes the specified value for the specified user.
TUEC Create_Value( String User, String Path, Integer DT ) ;
This method creates the specified value, of the specified data type, for the specified user. If the value already exists, an error is returned.
TUEC Verify();
This method verifies the internal consistency of the current data file.
The following only applies to Version 1.1 and later:
TUEC Read_Value( String User, String Path, Integer& DT, Integer& Size, Pointer& Buffer );
This method reads the specified value for the specified user. DT, Size, and Buffer are ignored on call. If the result indicates a success, DT contains the data type of the data on return, Size contains the size of the data, in bytes, and Buffer points to an internal buffer containing the data of the value. The buffer is only valid until the next method call to the object.
TUEC Init_And_Extend( String Name, Integer Size);
This method initializes the object for access to the specified SCU database file. If the file does not exist, it is created and pre-extended to the specified size (in bytes). If Size is 0 or negative, it is ignored. Note: Not all implementations are required to pre-extend the database.
TUEC Delete_With_Callback( String Path, TLogger* Logger );
This method does the same function as the Delete() method, but allows the passing of a logger object, through which the names of deleted values is returned to the calling code. If Logger is NULL, no logging occurs. TLogger is a COM object.