Introduction
This article discusses a simple class that can be used for assigning an icon to a folder that displays in Windows Explorer.
Background
In the course of a discussion I was having with a UI Engineer I work with, he was relating that he discovered how to create an icon for a folder in Windows Explorer (Select Folder: Properties: Customize Tab: Change Icon...), and I got to thinking, "There must be a way to do that programmatically".
It turns out that not only is there a way to do it, but it's also a fairly easy task to handle. The discussion we were having about this topic was one part of a larger endeavor, and that will be the source of a future article. However, to accomplish the final goal, the first problem to solve is that of assigning an icon to a folder - and that seemed to be an interesting enough topic in itself to write an article about.
Using the code
If you wish to just try out the sample application, simply open the FolderIcons.sln solution file and build or run it within the Visual Studio IDE.
Discussion
There are basically two steps involved in assigning an icon to a folder (or possibly three steps, if you count creating the folder):
- Create a desktop.ini file inside the folder for which to create the icon (the "Target Folder").
- Set the Target Folder's attribute to "System".
To accomplish these steps, I created two classes. The first is called FolderIcon
, which orchestrates the steps. The second one is called IniWriter
, which just has one static method called WriteValue()
that wraps the WritePrivateProfileString()
Win32 API function (for simplicity's sake, of course - if you wish to rewrite the method so that it's pure managed code routines, then be my guest; this was mostly a proof-of-concept project from my perspective, though I probably would re-write the method for production).
As you may have noticed from the screenshot, there is a small dialog which serves as a simple user interface that enables browsing or entering a folder path (the folder is created if it does not currently exist), selecting of an icon (the icon file must exist), and entering a description which will appear in the InfoTip that appears when you hover your mouse pointer over the folder in Windows Explorer. When the user clicks on the "Create Icon" button, a FolderIcon
object is created, passing the folder path into its constructor, after which its CreateFolderIcon()
method is invoked:
private void btnCreateIcon_Click(object sender, System.EventArgs e)
{
...
FolderIcon myFolderIcon = new FolderIcon(txtFolderPath.Text);
myFolderIcon.CreateFolderIcon(txtIconPath.Text, txtInfoTip.Text);
myFolderIcon = null;
...
}
The CreateFolderIcon Method
Drilling down into the CreateFolderIcon()
method:
public void CreateFolderIcon( string iconFilePath, string infoTip )
{
if ( CreateFolder() )
{
CreateDesktopIniFile(iconFilePath, infoTip);
SetIniFileAttributes();
SetFolderAttributes();
}
}
First, CreateFolderIcon()
attempts to create the folder, then it calls the methods to create the desktop.ini file, seta the .ini file's attributes to Hidden and System, and then sets the Target Folder's attributes to System. The individual methods are described in the following sections.
The CreateDesktopIniFile Method
There are two forms of the CreateDesktopIniFile()
method. The one called by CreateFolderIcon
above has two parameters: the path to the icon and the description of the folder to be displayed in an InfoTip (a.k.a. ToolTip). The simple version of CreateDesktopIniFile()
calls another form of CreateDesktopIniFile()
with a slightly more verbose signature:
private bool CreateDesktopIniFile( string iconFilePath,
bool getIconFromDLL, int iconIndex, string infoTip )
{
...
this.IniPath = this.FolderPath + "desktop.ini";
IniWriter.WriteValue(".ShellClassInfo", "IconFile",
iconFilePath, this.IniPath);
IniWriter.WriteValue(".ShellClassInfo", "IconIndex",
iconIndex.ToString(), this.IniPath);
IniWriter.WriteValue(".ShellClassInfo", "InfoTip",
infoTip, this.IniPath);
return true;
}
CreateDesktopIniFile()
uses the IniWriter
class' WriteValue()
to create the desktop.ini file in the Target Folder, and since the WriteValu()
method is static, it is used without having to instantiate the IniWriter
class. The WriteValue()
method is basically just a wrapper around the WritePrivateProfileString()
Win32 API function, and the parameters correspond directly. All you have to do to use it is to pass the section name, the key name, the value for the key, and the path to the desktop.ini file.
To display an icon for a folder, the desktop.ini must contain a section named ".ShellClassInfo
" that contains two keys: "IconFile
" - the path to the .ico file you wish to assign to this folder, and "IconIndex
" - which should be set to zero for icon files. InfoTip
is optional, but it's an easy way to create a fuller description of the folder's purpose.
After the above three WriteValue()
lines are called, you will end up with something similar to the following:
[.ShellClassInfo]
IconFile=C:\Graphics\Icons\MyIcon.ico
IconIndex=0
InfoTip=Test is the greatest folder ever!
The settings listed above are the ones required to display the icon. You can probably deduce from the "IconIndex
" key in the .ini file (as well as the corresponding iconIndex
parameter on the CreateDesktopIniFile()
method) that the method supports setting up the .ini file to work with icons embedded in DLLs and EXEs. You would set IconFile
to point to the DLL in question and the IconIndex
to reference the index of the icon in the DLL. To see how this works, you can play around with the "Change Icon..." functionality in Windows, and then open the resultant desktop.ini file. What this means for your application is that you could provide a set of icons embedded in a DLL which you could assign to folders to indicate various purposes for the folders, which is similar to what Windows does for some of its directories.
The SetIniFileAttributes Method
The next step is to set the attributes of the desktop.ini file to Hidden and System:
private bool SetIniFileAttributes()
{
...
if ((File.GetAttributes(this.IniPath) & FileAttributes.Hidden)
!= FileAttributes.Hidden)
{
File.SetAttributes(this.IniPath, File.GetAttributes(this.IniPath)
| FileAttributes.Hidden);
}
if ((File.GetAttributes(this.IniPath) & FileAttributes.System)
!= FileAttributes.System)
{
File.SetAttributes(this.IniPath, File.GetAttributes(this.IniPath)
| FileAttributes.System);
}
return true;
}
As a side note, it is my understanding from preliminary testing I've performed that this step is not entirely necessary - you can create a desktop.ini file without fiddling with its file attributes, and it will work just fine. However, if you follow the steps via the Windows Explorer folder properties, it sets the attributes to Hidden and System, so I just thought it would be good to be consistent with Windows' functionality.
The SetFolderAttributes Method
Not only does the desktop.ini's attributes need to be set to System, but the Target Folder's need to be set as well:
private bool SetFolderAttributes()
{
...
if ((File.GetAttributes(this.FolderPath) & FileAttributes.System)
!= FileAttributes.System)
{
File.SetAttributes(this.FolderPath, File.GetAttributes
(this.FolderPath) | FileAttributes.System);
}
return true;
}
Unlike the settings for the desktop.ini file, the System setting for the folder is necessary. It may be interesting to you to note that the class and methods one uses for getting and setting the attributes of the folder are identical to that of a file (i.e., with the File
class' static methods GetAttributes()
and SetAttributes()
). Honestly, it threw me for a bit when I was researching this part of the functionality, since it seemed more natural and straightforward to me to have the SetAttributes()
method in the Directory
class.
Points of Interest
As I mentioned earlier in the article, this project just encapsulates (no pun intended) the first part of a two-part series which will show a semi-practical application of the folder icon assignment functionality. Next time, I'll start from this point and expound a bit on how this can be a very useful feature when you play with some settings in Microsoft Office. Until then, I'll keep it a secret.
References
The following references proved useful in my creation of this project and article, so I thought it appropriate to give them proper recognition:
History
- January 15, 2005 - v1.0 by Evan Stone.