Sidebar gadgets are one of the gems of the Windows Vista operating system.
For developers, they open some innovating, exciting possibilities, and provide one
more reason why being a developer is fun. But wouldn't you know it?
Microsoft left a few important features out of version 1, and this article will
solve one of them: the ability to share locally-stored data between gadget instances.
In this article we'll look at a solution to that problem that uses the time-tested
.ini file format first used in Windows 1.0 back in 1985.
Now, some people are aghast at the thought of using anything other than
to save settings in a file. I did in fact consider using
but ultimately decided to go with the
.ini format for a few key reasons:
- Simplicity. The majority of gadgets will have relatively modest needs, and
will likely need to store string values.
XML's main advantage
is its ability to handle more complex storage requirements, but at the expense of
simplicity (at least when compared to an
- User-editable. Let's face it: manually editing an
XML file is
a place where only geeks will go.
.ini files, on the other hand,
are simple to understand and edit - even for a newbie.
.ini files are much easier and quicker to read, and
inserting comments is simple and painless.
- Version-proof code. To parse the
.ini file, the code in this
article uses basic
RegExp notations and string manipulations, things
that will likely execute unchanged many years from now, whereas
Plainly speaking, an
.ini file is just right for the job at hand, and
although the same thing can be accomplished using
XML, it was my sense
XML would be overkill.
Lastly, as you'll see below, Windows Vista itself uses the
format for storing global gadget settings, so by doing so ourselves, we are remaining
consistent with the operating system.
Note: It also would have been possible to store settings in the
Windows registry, but that would limit the portability and ease-of-use of the settings,
so it was quickly ruled out.
Gadgets behave differently than regular programs, and in fact are not programs in
the traditional sense. The only program to speak of is the Sidebar application
itself, which by default is set to execute when Windows boots. The Sidebar
is basically a kind of Web browser, and the gadgets are Web pages that are open
and displayed simultaneously.
When you add a gadget to the Sidebar, the Sidebar application creates persistent
storage for that gadget in a file located in the current user's personal folder.
The exact path of the Sidebar storage file is
In the Sidebar's
Settings.ini file, the newly-added gadget is assigned
an integer identifying number (for example, "5"), and all the settings for that
gadget are stored in a group using that number (for example,
Within each group, settings are stored as key/value pairs in typical
The Sidebar saves some basic settings for each gadget currently being displayed,
such as the gadget's path, its docked/undocked status, the opacity setting, etc.
The key names for all of the Sidebar-managed settings start with the string,
For example, the opacity setting is stored using the key name,
As a developer, you can add settings to the
.ini file too, using the
Table 1: Windows Vista built-in gadget settings model
Read the stored setting that corresponds to
to guess the correct data type to convert the value to. Because this can
rounding errors with numeric values, as well as other data conversion
readString() should generally be used instead of this
Read the stored setting that corresponds to
the value as a character string. In most cases, this method should be
rather than the
Convert the value
anyValue to a string, and then
save it under
the key name
strKey. To avoid the errors that can
write() whenever possible.
Save the string value
strValue under the key name
In most cases, this method should be used, rather than the
As indicated in Table 1,
should almost always be favored over the
methods. (The word "almost" is used in case there is an instance where read
or write should be used, but I haven't thought of one yet.)
If you try to store an
Object data type, the word "object" will be stored,
enumerated contents of the object. Therefore, in order to store an
data type it needs to be converted to the
JSON string representation, and to
that I'd recommend using the public domain json.js library, developed by Douglas
It is beyond the scope of this article to get into any great depth with json.js,
but suffice to say that you can easily store an Object data type using the following
Limitations of the built-in gadget settings
As you would expect, there are inherent limitations involved with storing settings
using the built-in methods, but the limitations may be more restrictive than you
would initially guess.
- There is a limit of 2,048 characters for each setting. I could not find any
documentation of this fact; I figured it out through trial and error (and through
some painful debugging sessions). Normally this does not pose a serious
but if you try to store complex objects using the technique described above, you
may run into this limitation, as I did.
- When you close a gadget (i.e., remove it from the Sidebar by clicking the "X" icon),
the Sidebar removes all of the settings for that gadget from the Sidebar's
file. There is no way for settings to survive a closed gadget.
- Because each gadget instance stores its settings in a separate section in the
Settings.ini file, there is no way to share settings between gadget
- There is no built-in method of "packaging" default settings along with a gadget,
or to ship different versions of a gadget with different default settings.
With these limitations in mind, we arrive at the subject of this article: the Gadget
Gadget Settings Manager
The solution to the limitations imposed by the Sidebar
the creation of a
Settings.ini file located in the Gadget's root folder,
combined with a small
Settings.ini file created by
SettingsManager is structured
like the Windows
.ini files you are probably accustomed to using.
In fact, while researching the topic during development of this library, I dusted
off my copy of The Waite Group's Windows API Bible and made sure
it would be compatible. However, I utterly refused to call the settings retrieval
For those unacquainted with
.ini file structure, it is a basic ASCII
text file containing key/value pairs organized into sections, called "groups".
Any text that starts with a semi-colon (
;) or pound sign (
is considered a comment, and is ignored. There is no multi-line comment syntax.
Listing 1: Structure of an .ini file
You can find more information about the
.ini file format at
are always surrounded by double-quotes, which is the same behavior as the Sidebar's
Settings.ini string values. However, the surrounding double-quotes
are not included as part of the returned value.
To get started with the
SettingsManager, a download link for the
file can be found below in the Downloads section below.
The file should be placed into whichever folder you are placing "core"
files for your gadget, because it needs to be accessible to every page that needs
to read and/or write settings, and it is language-independent. Then, in the
<head /> section of each page that needs to access settings,
<script /> tag referencing the
core/js/ with the actual folder name in which
resides. As with all src references in Sidebar gadgets, do not make the path
absolute by inserting a leading slash character. The path should be relative
to the root folder.
SettingsManager library will not interfere or conflict with any other
code in the gadget because it is entirely encapsulated in one globally-scoped object,
SettingsManager. It maintains storage of all the in-memory
settings within that object, as well as all the methods for accessing and changing
Working with SettingsManager
Before accessing any settings, you first need to instruct
all the settings from the gadget's
Settings.ini file into memory.
That is done using the
loadFile() method. (If you don't first
load the setting, then you run the risk of over-writing any settings currently in
Once the settings are loaded, you can retrieve any of the settings; you can add,
update, and delete settings; and you can get lists of group names and key names.
See the method reference table below for documentation of each method.
All settings are manipulated in memory, not on disk. Settings are not saved
back to the
Settings.ini file until the
is called. This allows you full control over how and when file access is performed.
Listing 2 contains a typical interaction with
is a hypothetical function in the gadget to save the current position and size of
an element in the
Listing 2: Typical SettingsManager usage
Using the example function in Listing 2, let's call the function with sample arguments
and see what the resulting
Settings.ini file would look like.
Sample function call:
Contents added to the
Property and Method Reference
The following tables list all of the available properties and methods of the
library. Remember, property and method names are case-sensitive.
Table 2: Properties available in the SettingsManager library
Name of the file used to store the settings. Does not include any path
but does include the extension (
.ini). The default value is
"Settings.ini". If working with more than one settings
property controls which settings file you are currently loading or
Changing this property has no direct effect on the settings in memory.
The sub-folder where the settings file is to be found/stored. Needs to be
expressed relative to the gadget root. For example, if the settings are
be stored as
LocalFolder should be set to
default value is an empty string (
""), which stores the
the gadget root.
Table 3: Methods available in the SettingsManager library
Usage and Description
Constructs and returns the full file system path of the settings
drive letter, gadget path,
Loads all the settings from the
.ini file into
memory. This is
normally the first method called before adding, changing, and
Saves all settings in memory to the
the existing file.
Retrieves the value of one setting from memory. If the
keyName is not found, then
defaultValue is returned,
or an empty string (
"") is returned if
Sets one setting's value in memory. The value is not save to the
file on disk until
saveFile() is called. If
does not exist, it is created. If
the group it is overwritten; otherwise it is created.
be a string.
Deletes one setting from memory. The value is not deleted from
file on disk until
saveFile() is called. If the
group is empty
after deleting the value, the group itself will be deleted if
Deletes one group from memory, and all values stored in that
group. The changes
are not saved to the
.ini file on disk until
Returns the number of keys [i.e., settings] stored within the
specified group in
Returns an array of strings containing all of the key names
within the specified
group in memory.
Returns the number of groups currently stored in memory.
Returns an array of strings containing all of the group names
currently stored in
name is a valid group
or key name.
(Group and key names have the same criteria for validity: at
least one character
in length, must start and end with a letter, number, or
underscore, and in between
can be almost any combination of letters, numbers, spaces, and
Generates and returns all of the settings in memory as a string,
file format. The return value is exactly the same as the contents
.ini file would be. In fact, this method is used
to generate the contents of the
.ini file that is
method is exposed since it may have additional value apart from
saving to disk.
One at a time, please!
Under most circumstances,
SettingsManager will be used infrequently - for
example, when the user clicks a Save button to persist the current settings.
that means we don't need to worry about more than one gadget instance trying to
save changes to the
.ini file at the same time. Thus, the
was purposely designed to ignore that possibility. Rather than adding the
bulk and complication of a proprietary locking scheme I decided to keep the library
size smaller and place the onus of a locking mechanism on the small percentage of
programs that require them.
In others words, why increase the size and complexity of 100% of the gadgets, if
only 5% will require it?
However, there are some situations in which a locking mechanism is critical. For
example, there may be a case where several gadget instances are active simultaneously,
and the instances use
SettingsManager to communicate with each other. In
case it is important that only one instance of the gadget be allowed to write to
.ini file at any one time.
In this example, a gadget instance would use a locking mechanism in the following
- Obtain a lock,
- Load the settings from disk, change the setting(s) in memory, and then immediately
save them back to disk, and,
- Release the lock.
The most important design aspects of any such locking mechanism are:
- The locking mechanism must be globally available to all gadget instances, and,
- The program code must obtain (or deny) the lock in one single operation -
within one line of code - so that there is no chance that two instances could
simultaneously obtain a lock.
Building an actual locking mechanism is beyond the scope of this article, but I
did build a generic
LockManager module, which I may include in a future
(If there is interest, please leave me a comment!)
How it works
So now that we know how to use
SettingsManager, let's see how it works.
developers tend to be most interested in the under-the-cover details, I'll start
inside and work my way out.
One of the nice aspects of Windows Vista gadget development is that you don't need
to worry about which browser you're developing for, because everything runs on IE7
in the Sidebar. Another helpful thing is that the Sidebar has a high level
of trust of the installed gadgets, so permissions are rarely an issue. Both
of these helped a great deal in developing
could be used with confidence. It is a liberating experience to code an HTML
page without fear of browser incompatibility!
ActiveX objects are used in the
methods, to create a
Scripting.FileSystemObject. Once a
is created, reading and writing data to disk is straight-forward. For example,
saveFile() method consists of the following logic.
Listing 3: saveFile() program logic
To provide control over when file access takes place,
the file system during
All other operations, such as reading and writing individual settings, take place
Storage in memory
Storing the settings in memory is accomplished using arrays and objects. One
SettingsManager._groups, is used to store all settings.
(In my coding standards, private properties and methods start with an underscore.)
members, accessing members
private from your program code is strongly discouraged. The
property is considered private for all the reasons one would make a private property
in a class: the internal structures can be changed without affecting programs that
rely on them, and the built-in methods provide all the functionality necessary under
_groups is an array of
array representing one group of settings within the
(As explained in Gadget Settings Manager above, a group is all
the settings that come after the
[Group Name] notation in the file.)
Each element of the
_groups array, called a "group object", is defined
As you can see, each group object has two properties -
Values property is an array of Key/Value
objects, used to store all the settings within the group.
Apart from simple assignment statements, the arrays are manipulated in the
methods using the
splice() function. This handy
can be used to delete elements, insert elements, or both at the same time.
loadFile() method is used to fill the
with the settings stored in the
.ini file. I have not done actual
measurements and comparisons of different loading methods, but the techniques used
loadFile() should produce very quick load times.
Recognizing that I/O is normally the most restrictive bottleneck in an application,
all of the data is read at once using the
function. I have found in real-world usage that all-at-once I/O functions
ReadAll() can greatly improve performance, just as any "bursting"
After the file contents are in memory, a simple
call puts all the non-blank, non-comment lines into an array, one element per line.
Then, it is a matter of iterating through the elements and building the
array. Again, regular expressions are employed to analyze the text on each
First, a line is tested to see if it contains a group name, in which case it creates
a new element in the
_groups array. Or, if not, the line is tested
to see if it contains a Key/Value pair. If so, it adds the Key/Value pair
Values array in the last group added to the
The group and key/value tests are defined as follows. (
is defined as a
private property so that the group and key naming
easily be changed in one place.)
Each method in
SettingsManager is fully commented, using Microsoft's
format. This will allow a developer using the "Orcas" version of Visual Studio
to get full
IntelliSense support with their
if you type
SettingsManager. the full list of available properties
and methods in
SettingsManager will appear. Then, if you select
the list of that method's arguments and their types will be displayed.
I believe once developers start seeing the tremendous value of
SettingsManager is implemented as one large
variable, which is a common technique employed by third party library makers.
It is similar to declaring a class in
C# and other object-oriented
The most important reason for using this technique is because only the module name
itself (in this case
SettingsManager) is exposed to the global namespace.
Thus, assuming your
in the global namespace, the module can be integrated without fear of naming conflicts.
SettingsManager fills a gap in the Windows Vista Sidebar gadget object
ability to share settings between gadgets, as well as to "package" a gadget with
default settings. The module reads and writes the standard Windows
file format, making it simple to directly edit settings while maintaining compatibility
with Sidebar gadget standards.
SettingsManager is accomplished with the simple addition of a
/> tag at the top of any page that needs access to settings.
SettingsManager should not introduce any naming conflicts, because its
code is encapsulated in a single object called
SettingsManager does not have any built-on locking mechanism, which is fine
95% of its uses, but for the small percentage of applications that require a locking
mechanism, that code will have to be supplied and managed external to
(Don't forget to leave me a comment if you are interested in having me include
LockManager in a future article!)
When reading and writing settings, the most important thing to remember is that
settings are not saved to disk until the
saveFile() method is called.
Keeping that concept in the forefront of your thoughts may save a great deal of
debugging time later.
I am interested in hearing from you! Please take a moment to leave a comment
to let me know if this article was useful for you, and what you have used
for in your gadget applications.
About the author
Todd Northrop is the owner and creator of Lottery
Post, the Internet's largest lottery community. He founded his company,
Speednet Group, to perform consulting, web development, and web hosting, working
primarily with Microsoft .NET technologies.
Sorry, no bio is available
This author has published 2 articles on DotNetSlackers. View other articles or the complete profile here.
Please login to rate or to leave a comment.