Introduction
For a certain application we were developing, we needed to retrieve the names
of some of the common shell folders on the desktop like My Computer, Recycle Bin
etc. We soon discovered that by default each OS comes with some pre-defined
names depending on what Language version of the OS was used. These strings were
stored inside Shell32.dll. But the user was free to rename any of these
folders, and when he did that the new strings were stored in the registry. I do
not know, how many people will find this code useful, but in case someone ever
encounters a situation where he needs to do this, he need not spend the long
hours we had to spend before we discovered that this was simply a matter of
reading Shell32.dll and the registry. I've written some functions which
you can use in your programs directly, by including a single header file.
Typical Usage
cout << "Localized folder names\r\n";
cout << "------------------------------\r\n";
cout << GetLocalizedName(MyComputer) << "\r\n";
cout << GetLocalizedName(MyDocuments) << "\r\n";
cout << GetLocalizedName(NetworkNeighbourhood) << "\r\n";
cout << GetLocalizedName(RecycleBin) << "\r\n";
cout << GetLocalizedName(InternetExplorer) << "\r\n";
cout << "\r\n";
SetCustomName(MyComputer,"Nish's Computer");
SetCustomName(RecycleBin,"Trash Can");
cout << "User-defined folder names\r\n";
cout << "------------------------------\r\n";
cout << GetCustomName(MyComputer) << "\r\n";
cout << GetCustomName(MyDocuments) << "\r\n";
cout << GetCustomName(NetworkNeighbourhood) << "\r\n";
cout << GetCustomName(RecycleBin) << "\r\n";
cout << GetCustomName(InternetExplorer) << "\r\n";
The Functions
For all three functions I am using a custom enum
to specify the desktop
folder
enum IconFolder
{
MyComputer,
MyDocuments,
NetworkNeighbourhood,
RecycleBin,
InternetExplorer
};
GetLocalizedName
CString GetLocalizedName(IconFolder folder)
{
HMODULE sh32lib = LoadLibrary("Shell32.dll");
char buff[256];
switch(folder)
{
case MyComputer:
LoadString(sh32lib,9216,buff,255);
break;
case MyDocuments:
LoadString(sh32lib,9227,buff,255);
break;
case NetworkNeighbourhood:
LoadString(sh32lib,9217,buff,255);
break;
case RecycleBin:
LoadString(sh32lib,8964,buff,255);
break;
case InternetExplorer:
LoadString(sh32lib,9222,buff,255);
break;
default:
strcpy(buff,"Invalid Folder Request");
break;
}
FreeLibrary(sh32lib);
return CString(buff);
}
As you can see, all I do is to load strings from Shell32.dll, the
string table entry identifiers are hard coded and I have tested this on Windows
XP Home and Professional. I do believe that they will be the same for Windows
2000 versions, but I am not as sure for other OSes like Windows ME. If someone
could test it and let me know, that'd be awfully nice.
GetCustomName
CString GetCustomName(IconFolder folder)
{
HKEY hKey = 0;
CString keyname =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\";
switch(folder)
{
case MyComputer:
keyname += "{20D04FE0-3AEA-1069-A2D8-08002B30309D}";
break;
case MyDocuments:
keyname += "{450D8FBA-AD25-11D0-98A8-0800361B1103}";
break;
case NetworkNeighbourhood:
keyname += "{208D2C60-3AEA-1069-A2D7-08002B30309D}";
break;
case RecycleBin:
keyname += "{645FF040-5081-101B-9F08-00AA002F954E}";
break;
case InternetExplorer:
keyname += "{871C5380-42A0-1069-A2EA-08002B30309D}";
break;
default:
keyname = "";
break;
}
BYTE buff[256];
if(keyname.GetLength())
{
LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER,
(LPCTSTR)keyname,0,KEY_READ,&hKey);
ZeroMemory(buff,255);
DWORD sz = sizeof buff;
DWORD typ = REG_SZ;
RegQueryValueEx(hKey,"",0,&typ,buff,&sz);
RegCloseKey(hKey);
}
else
{
return CString("Invalid Folder Request");
}
if(CString(buff).GetLength())
return CString(buff);
else
return CString("No custom definition");
}
Well, this is pretty simple too; just a matter of reading some registry
entries. Basically this is exactly what the OS does too when it shows the
desktop icons, it first reads these registry values and if they are blank, then
it will extract the strings out of Shell32.dll.
SetCustomName
void SetCustomName(IconFolder folder, CString foldername)
{
HKEY hKey = 0;
CString keyname =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\";
switch(folder)
{
case MyComputer:
keyname += "{20D04FE0-3AEA-1069-A2D8-08002B30309D}";
break;
case MyDocuments:
keyname += "{450D8FBA-AD25-11D0-98A8-0800361B1103}";
break;
case NetworkNeighbourhood:
keyname += "{208D2C60-3AEA-1069-A2D7-08002B30309D}";
break;
case RecycleBin:
keyname += "{645FF040-5081-101B-9F08-00AA002F954E}";
break;
case InternetExplorer:
keyname += "{871C5380-42A0-1069-A2EA-08002B30309D}";
break;
default:
keyname = "";
break;
}
if(keyname.GetLength())
{
LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER,
(LPCTSTR)keyname,0,KEY_WRITE,&hKey);
RegSetValueEx(hKey,"",0,REG_SZ,
(const BYTE*)foldername.GetBuffer(0),foldername.GetLength());
foldername.ReleaseBuffer();
RegCloseKey(hKey);
LPITEMIDLIST pidl;
SHGetSpecialFolderLocation(NULL,CSIDL_DESKTOP,&pidl);
SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_IDLIST,pidl,0);
}
}
Here, we simply update the registry entries with the new custom strings. If
you save a NULL
string, then the OS will use the language specific
strings from Shell32.dll. There is some interesting bit of code in the
end that I use to force a refresh of the desktop icon names. I tried everything
from invalidating the desktop, sending WM_PAINT
messages to sending
F5 key messages, but none of them worked. This was the only code that worked for
me.
Refreshing the desktop trick (save this one)
LPITEMIDLIST pidl;
SHGetSpecialFolderLocation(NULL,CSIDL_DESKTOP,&pidl);
SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_IDLIST,pidl,0);
Conclusion
The basic problem with using these type of registry reads and DLL string
reads is that you never know when Microsoft will change the string identifiers
or the registry locations in their next OS versions. But that's a risk you have
to live with, and whenever they make a change, you'll have to be prepared to do
version checks and do the required stuff depending on the IS versions detected.
I do hope at least a few people would have benefited from this articles. Feel
free to submit your feedback through the forum at the bottom of this article.