Sunshine's Desktop Symbol Utility

Version: 1.0

Download: <here>

Download Source: <here>

Description:
This utility change the text color and the text background color of the symbols on the desktop. Read below for more details.

Requirements:
- Windows XP with Admin Rights

What's this?

In Windows XP, I always missed a feature concerning the desktop symbols. You have 2 choices: First, you can choose a background color which is also used to draw the text background (screenshot 1). But this looks quite ugly with most wallpapers. Second possibility is the enable transparent backgrounds (under Control Panel -> bla bla...). Unfortunately does Windows paint also a shadow under the text which makes it difficult to read and in my opinion does not look very good.
That's the reason I decided to write a tool to solve this 'misbehaviour' (screenshot 3).


Screenshot 1

Screenshot 2

What it should be...

Features:

- Use the color of your choice for text and background (or transparency of course).
- When activated, you can completely close the utlity and restart later to deactivate it.
- Dll which does the main work is just 4 KB.
- Autostart option: the utility closes itself after activation; you will notice no difference to normal windows start!

Enjoy!


Information for Coders

Here come some notes on developing this utility... maybe they are interesting for the coders outside :-)
So how to start? At first, I had to find out how the symbols are drawn. So I used Spy++ which comes with Visual Studio to find the desktop window and I figured out following hierachie:

- "Program Manager" Progman
   - "" SHELLDLL_DefView
      - "FolderView" SysListView32


Pretty interesting... Our desktop window is a normal listview control, well known from the windows common controls :-) So finding it in our code is quite simple, following three lines will do the job:


HWND hProgman = FindWindow("Progman", 0);
HWND hDesktop = FindWindowEx(hProgman, 0, "SHELLDLL_DefView", 0);
HWND hListView = FindWindowEx(hDesktop, 0, "SysListView32", 0);


A bit diving in the Windows API make clear that you can use the messages LVM_SETTEXTCOLOR and LVM_SETTEXTBKCOLOR to modify the text (background) colors. And that worked fine! With just 5 lines of code, I changed the desktop symbol text colors... but two problems faces us:

Problem 1: When the transparent desktop symbol mode is activated, it does NOT work.
Solution:
Deactivate it ;-) Ok, more preciser: I used Regmon from Sysinternals to find out which values are changed in registry when I (de)activate this mode. In fact, just one key handles it: it's called ListviewShadow and you can find it under HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced. 1 means enabled, 0 means disabled. Ok, that was easy :-)


Problem 2: Whenever the desktop window is repainted/updated, our changes are lost. As workaround, you could use a timer and send the two messages periodically, but that's not a good solution cause too long periods will result in some flickering when for a short time the original colors are seen and too short periods waste system power.
Solution: We should try to catch when desktop is updated and apply our colors. I did it by injecting my own dll into the process where the listview window is belonging to (which is, in fact, explorer.exe). The CreateRemoteThread/WriteProcessMemory works fine (for more information about it see [1]) Cause this tool will only be useful under WinXP, it's no problem to use API functions which are only under XP available. After injecting, we can sublass the corresponding window procedure, catch the WM_PAINT message and set our colors. (to make it clear: you can just subclass a window procedure in your own process (!), so that's the only reason we inject the dll in explorer.exe!)
I coded a first version which worked really fine. But another problem came up.


Inter-Process Communication:

In our main application, we choose the colors, but our dll has to set it. So we have to send data from the utility to the dll. My first approach didn't worked: I wanted to use WM_COPYDATA for sending the color values because by already catching WM_PAINT it would be just a few lines of code to process the WM_COPYDATA message in our dll. But my message never arrived in the dll file; in fact even sending the message in the main app to the listview window failed... and I don't know why :-(
So searching for another method, I came across the 'shared data segement'-approach in C++. This is normally used if you have more than one instance of a dll file but want to use just one instance of a specific variable. All variables in the shared data segment are stored in an extra section in the PE file which is (as the name says it) shared between all instances. So I thought I can use this method too to share variables in my app and in my dll :-)
To got it running, some things have to be looked at: the variables in the shared data segment in the dll as to be declared again as extern in a header file which must be included in the main app. To get it linked in MSVC, both projects must be in the same solution and you have to define that both projects are tied together by right-clicking the main project and check the dependancy to the project of the dll file. (Took me some time to find that out and finally got it linked...).

But suddenly I got some errors when starting my little app... well let's think... due to the shared segment thing, the dll is now loaded with my app (check the import table!). Well, that seems logic cause the dll must be in our address space to modify the variables in the shared section. As soon as the dll is loaded to some address space, it tries to subclass the window procedure of the desktop listview... but that's works just if it's in the explorer process and not in our process. That's why I inserted a new variable called bActivate which is only set to true by my app when the dll is about to be injected in the explorer.exe and the dll just try to subclass when bActivate is true.
Another thing to mention is the fact that I save the values of some variables to file when closing my app without unjecting the dll. That's needed to unload the dll later when the app is executed again; cause for unloading the dll, some values are needed we got from the injecting function. Otherwise we had no opportunity the unload our dll, we had always to kill the explorer process to deactivate it.
I think that were the most important notes, the rest of code should be easy to understand. Hope you read something new and my code will help you sometime.

Sunshine, August 2k6

References:

[1] CodeProject.com ("Three Ways to Inject Your Code into Another Process")


This site is part of Sunshine's Homepage