Click here to Skip to main content
16,016,391 members
Articles / Desktop Programming

Draw Behind Desktop Icons in Windows 8+

Rate me:
Please Sign up or sign in to vote.
4.97/5 (59 votes)
22 Dec 2014CPOL6 min read 205.9K   4K   71   87
Draw or render a Windows Form directly over the Wallpaper, behind the Desktop Icons in Windows 8+10

Introduction

Those who read this article probably know DreamScene, that Windows Vista feature, that allows to render video sequences (in .dream format) as desktop background. There is also a tool called rainmeter, that allows you to place widgets/gadgets/whatever on your desktop in a top most, top and bottom position. Also there is winamp, the first program, where I saw directx rendering in action behind the desktop icons.

These tools have one thing in common, they cannot do it in Windows 8, at least the "draw under the desktop icons" part they don't.

EDIT:

The approach described in this article also works for Windows 10. 

How It Used To Work (Windows XP, Vista, 7)

There is the window tree. This tree contains all windows that are currently displayed/or hidden on the current desktop and then there is a tool called Spy++ (Visual Studio -> Tools -> Spy++), that can be used to display and navigate that tree. That tool is part of Visual Studio.

The last leaf of this tree is the Program Manager. This window represents the whole shell. In Windows XP, Vista and 7 (Aero turned off) this Program Manager (Progman) contained a window (SysListView32), that rendered the desktop icons. So if you set that Program Manager as your parent window, you could position yourself right behind those desktop icons. EDIT: Scroll to the comments section below, there is a comment from Kristjan Skutta, that describes how to do it in windows 7 with aero turned on: http://www.codeproject.com/Messages/5164145/Re-Windows.aspx

There is a great article on drawing to the desktop (in front of and behind desktop icons) here on CodeProject:

But for the life of me, I could not find an approach that worked for Windows 8.

The Problem with Windows 8+

Windows 7 and Windows 8 are very similar. To make the Windows XP approach work for Windows 7, you had to turn off aero desktop. With Windows 8, you cannot turn aero off, so there has to be another way.

The following image shows how the window tree is structured in Windows 8.

Spy++ showing the bottom section of the window tree

The SysListView32 is now separated from the Program Manager. This alone is not that big of a problem.
The problem is the desktop background. It is now fused with the Desktop Icons. So we can only draw over everything including the icons, or draw under everything including the background. It is not possible to position a window in the Z-Order so it is between the desktop icons and the desktop wallpaper. I tried every position.

A Way to Find a Solution

What led me to the solution presented in this article was the personalization dialog. When you set a wallpaper manually, you do not see a sudden change, instead the wallpaper is set using a smooth fade animation. In my opinion, such an animation is only possible if the system can somehow draw behind the desktop icons, since setting a wallpaper in rapid succession would be very slow and ugly.

So I set up Spy++, opened the personalization dialog and changed the wallpaper. It turns out, that when you change the desktop wallpaper, a new WorkerW window between the WorkerW instance that holds the Desktop Icons (SysListView32) and the Desktop Manager is created.

Spy++ showing the bottom section of the window tree

I took the handle of that new WorkerW window and put it into my test program and it was finally able to draw behind the desktop icons, directly above the wallpaper!

One problem remained. When I closed the personalization dialog, it took down that new WorkerW window with it.

I had to find a way to trigger the creation of this WorkerW window.

Spy++ reports the window as a sibling and child of the Program Manager, so it looks like the Program Manager creates it. I turned on message monitoring for the Program Manager using Spy++ and found what I was looking for.

Spy++ showing the bottom section of the window tree

Right after the click to change the desktop wallpaper, the Program Manager receives a bunch of messages. The first one being a user defined and undocumented message. This reeks after being "recently" added.

I extended the test program to send exactly this user defined message (0x052C) to the Program Manager. And it caused exactly what I was hoping for. After receiving the message, the Program Manager creates the WorkerW window.

With all ingredients ready, I wrote a small demo application that illustrates how to draw on the desktop (behind the icons) and how to put a window behind the desktop icons.

The Code

Obtain Program Manager Handle

First, we begin by finding the handle of the Progman window. We can use the <a href="http://www.pinvoke.net/default.aspx/user32/FindWindow.html">FindWindow</a> function provided by the Windows API to accomplish this task.

C#
// Fetch the Progman window
IntPtr progman = W32.FindWindow("Progman", null);

Send Message to Program Manager

To trigger the creation of a WorkerW window between the desktop icons and the wallpaper, we have to send the Program Manager a message. That message is an undocumented one, so there is no fancy Windows API name for it, except 0x052C. In order to send the message, we use the Windows API method <a href="http://www.pinvoke.net/default.aspx/user32/SendMessageTimeout.html">SendMessageTimeout</a>.

C#
IntPtr result = IntPtr.Zero;

// Send 0x052C to Progman. This message directs Progman to spawn a 
// WorkerW behind the desktop icons. If it is already there, nothing 
// happens.
W32.SendMessageTimeout(progman, 
                       0x052C, 
                       new IntPtr(0), 
                       IntPtr.Zero, 
                       W32.SendMessageTimeoutFlags.SMTO_NORMAL, 
                       1000, 
                       out result);

Obtain Handle to Newly Created Window

Now, we have to obtain a handle to that newly created WorkerW window. Since there is more than one window with title "" and class "WorkerW", we have to go through the window tree sequentially. This can be done using the <a href="http://www.pinvoke.net/default.aspx/user32/EnumWindows.html">EnumWindows</a> function.

<a href="http://www.pinvoke.net/default.aspx/user32/EnumWindows.html">EnumWindows</a> calls supplied EnumWindowProc for every top level window. From there, we can check if the current window contains a child named "SHELLDLL_DefView", which indicates that the current window represents the desktop icons. We then take the next sibling of that window.

C#
// Spy++ output
// .....
// 0x00010190 "" WorkerW
//   ...
//   0x000100EE "" SHELLDLL_DefView
//     0x000100F0 "FolderView" SysListView32
// 0x00100B8A "" WorkerW       <-- This is the WorkerW instance we are after!
// 0x000100EC "Program Manager" Progman

IntPtr workerw = IntPtr.Zero;

// We enumerate all Windows, until we find one, that has the SHELLDLL_DefView 
// as a child. 
// If we found that window, we take its next sibling and assign it to workerw.
W32.EnumWindows(new W32.EnumWindowsProc((tophandle, topparamhandle) =>
{
    IntPtr p = W32.FindWindowEx(tophandle, 
                                IntPtr.Zero, 
                                "SHELLDLL_DefView", 
                                IntPtr.Zero);

    if (p != IntPtr.Zero)
    {
        // Gets the WorkerW Window after the current one.
        workerw = W32.FindWindowEx(IntPtr.Zero, 
                                   tophandle, 
                                   "WorkerW", 
                                   IntPtr.Zero);
    }

    return true;
}), IntPtr.Zero);

Demo 1: Draw Graphics Between Icons and Wallpaper

With the workerw handle in hand, the fun stuff begins. The first demo is about using the System.Drawing classes to just draw something.

This demo draws a rectangle in the upper left corner of the desktop. If you use multiple monitors, be aware, that the desktop area spans a rectangle across all monitors, so make sure your left monitor is turned on and your monitor placement actually maps the top left corner to a monitor, in case you have four of them, with one atop the other three.

Note: Everything you draw onto this layer will stay there until you paint over it, invalidate it, or reset your wallpaper.

C#
// Get the Device Context of the WorkerW
IntPtr dc = W32.GetDCEx(workerw, IntPtr.Zero, (W32.DeviceContextValues)0x403);
if (dc != IntPtr.Zero)
{
    // Create a Graphics instance from the Device Context
    using (Graphics g = Graphics.FromHdc(dc))
    {

        // Use the Graphics instance to draw a white rectangle in the upper 
        // left corner. In case you have more than one monitor think of the 
        // drawing area as a rectangle that spans across all monitors, and 
        // the 0,0 coordinate being in the upper left corner.
        g.FillRectangle(new SolidBrush(Color.White), 0, 0, 500, 500);

    }
    // make sure to release the device context after use.
    W32.ReleaseDC(workerw, dc);
}

Demo 2: Put a Windows Form behind desktop icons

This demo shows how to put a normal Windows Form behind desktop icons. In essence, this can be done by setting the parent of a Windows Form to our WorkerW window. To set the parent of a form, we can use the <a href="http://www.pinvoke.net/default.aspx/user32/SetParent.html">SetParent</a> Windows API function.

Note: For this function to work, the form has to be already created. The form.Load event seems to be the right place for it.

For the sake of a short example, I created the Form in place, without the "Project->Add Windows Form..." dialog and the designer.

C#
Form form = new Form();
form.Text = "Test Window";

form.Load += new EventHandler((s, e) =>
{
    // Move the form right next to the in demo 1 drawn rectangle
    form.Width = 500;
    form.Height = 500;
    form.Left = 500;
    form.Top = 0;

    // Add a randomly moving button to the form
    Button button = new Button() { Text = "Catch Me" };
    form.Controls.Add(button);
    Random rnd = new Random();
    System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
    timer.Interval = 100;
    timer.Tick += new EventHandler((sender, eventArgs) =>
    {
        button.Left = rnd.Next(0, form.Width - button.Width);
        button.Top = rnd.Next(0, form.Height - button.Height);
    });
    timer.Start();

    // This line makes the form a child of the WorkerW window, 
    // thus putting it behind the desktop icons and out of reach 
    // for any user input. The form will just be rendered, no 
    // keyboard or mouse input will reach it. You would have to use 
    // WH_KEYBOARD_LL and WH_MOUSE_LL hooks to capture mouse and 
    // keyboard input and redirect it to the windows form manually, 
    // but that's another story, to be told at a later time.
    W32.SetParent(form.Handle, workerw);
});

// Start the Application Loop for the Form.
Application.Run(form);

You will probably notice that there is no way to interact with the form, once its parent is set to the WorkerW window. The desktop is not designed to have interactive children, so all events regarding mouse movement, keyboard input, etc. will not reach our Form.

There is a way around that. You can subscribe to the low level WH_KEYBOARD_LL and WH_MOUSE_LL events, also known from key loggers and mouse capture software. Via those events, you can receive mouse movements, clicks and key presses regardless of where they occur. You would have to forward those messages to your form and perform your own hit testing.

Conclusion

One command to rule them all ;)

C#
W32.SendMessageTimeout(W32.FindWindow("Progman", null), 
                       0x052C, 
                       new IntPtr(0), 
                       IntPtr.Zero, 
                       W32.SendMessageTimeoutFlags.SMTO_NORMAL, 
                       1000, 
                       out result);

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) SecureGUARD GmbH
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionWindows 11 23H2 (OS Build 2261.4169) - VB.NET code and handles Pin
Member 137929303-Oct-24 12:31
Member 137929303-Oct-24 12:31 
QuestionHow to prevent a new workerw to spawn when wallper is changed Pin
Member 1327764215-Mar-24 9:17
Member 1327764215-Mar-24 9:17 
Suggestionabout mouse Pin
Rankor 20248-Jan-24 4:06
Rankor 20248-Jan-24 4:06 
QuestionMarshal::GetLastWin32Error have unpredictable result Pin
RoninZhao8-Feb-23 21:18
RoninZhao8-Feb-23 21:18 
SuggestionI converted to VB.NET and it works great Pin
Member 137929308-May-22 19:47
Member 137929308-May-22 19:47 
QuestionFor Windows 7 without Aero Pin
dsg mjf18-Mar-22 20:44
dsg mjf18-Mar-22 20:44 
QuestionHow to move the window consistently if the Primary Monitor is not the first one on the left) Pin
Z3N1G4T418-Feb-22 3:03
Z3N1G4T418-Feb-22 3:03 
GeneralMy vote of 5 Pin
a5nb11-Feb-22 6:39
a5nb11-Feb-22 6:39 
QuestionTransparent Canvas look Pin
Gerry Lindsay21-Apr-21 10:35
Gerry Lindsay21-Apr-21 10:35 
GeneralMy vote of 5 Pin
Armand124-Mar-21 8:14
Armand124-Mar-21 8:14 
QuestionAmazing stuff man Pin
StargazeR Weerakkody10-Jul-20 8:36
StargazeR Weerakkody10-Jul-20 8:36 
QuestionVery Slow Bitmap Drawing Pin
Erik Turner4-Jul-20 5:16
Erik Turner4-Jul-20 5:16 
AnswerRe: Very Slow Bitmap Drawing Pin
Gerald Degeneve15-Apr-21 17:45
professionalGerald Degeneve15-Apr-21 17:45 
SuggestionBetter parameters for SendMessage 0x52C to support high-contrast mode Pin
Kristjan Skutta2-Jul-20 10:30
Kristjan Skutta2-Jul-20 10:30 
GeneralGreat Article and I've created a project to show videos and images on your desktop wallpaper Pin
AlexanderPro27-Aug-19 4:56
AlexanderPro27-Aug-19 4:56 
GeneralThe Graphics stops working after about 20 seconds Pin
datvm10-Aug-19 17:00
datvm10-Aug-19 17:00 
SuggestionHow It Used To Work Pin
Konstantin Pandipulovich24-May-19 9:56
Konstantin Pandipulovich24-May-19 9:56 
QuestionThank you Pin
Sandro Dzaganishvili9-Mar-19 1:10
Sandro Dzaganishvili9-Mar-19 1:10 
QuestionHow to disable it and add other winform apps to behind the desktop icons Pin
dddrrr5-Sep-18 13:04
dddrrr5-Sep-18 13:04 
QuestionClear drawed objects Pin
User 1327360510-Jun-18 0:49
User 1327360510-Jun-18 0:49 
AnswerRe: Clear drawed objects Pin
Gerald Degeneve1-Aug-18 10:30
professionalGerald Degeneve1-Aug-18 10:30 
GeneralRe: Clear drawed objects Pin
Member 144800594-Jun-19 0:25
Member 144800594-Jun-19 0:25 
AnswerRe: Clear drawed objects Pin
Member 1066863228-Apr-20 15:36
Member 1066863228-Apr-20 15:36 
GeneralRe: Clear drawed objects Pin
Armand127-Mar-21 6:39
Armand127-Mar-21 6:39 
QuestionHow to redirect WH_MOUSE_LL to form? Pin
KimNeutral10-Dec-17 20:32
KimNeutral10-Dec-17 20:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.