Click here to Skip to main content
13,148,214 members (60,159 online)
Click here to Skip to main content
Add your own
alternative version

Stats

50.7K views
1.5K downloads
47 bookmarked
Posted 22 Dec 2014

Draw Behind Desktop Icons in Windows 8+

, 22 Dec 2014
Rate this:
Please Sign up or sign in to vote.
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.

// 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>.

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.

// 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.

// 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.

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 ;)

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)

Share

About the Author

Gerald Degeneve
Software Developer (Senior) SecureGUARD GmbH
Austria Austria
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionHow to make this span the whole screen? Pin
Member 1341454217-Sep-17 17:23
memberMember 1341454217-Sep-17 17:23 
QuestionUse Transparency Key in Form Pin
Member 1229476412-Aug-17 22:19
memberMember 1229476412-Aug-17 22:19 
QuestionDoesn't Work on Windows 10 Pin
Member 1117041224-Apr-17 5:11
memberMember 1117041224-Apr-17 5:11 
AnswerRe: Doesn't Work on Windows 10 Pin
Gerald Degeneve29-May-17 8:14
professionalGerald Degeneve29-May-17 8:14 
GeneralRe: Doesn't Work on Windows 10 Pin
Member 1341454217-Sep-17 16:21
memberMember 1341454217-Sep-17 16:21 
QuestionWhy debugging is normal, the EXE form is not running directly under the desktop icon Pin
Member 129368086-Jan-17 2:01
memberMember 129368086-Jan-17 2:01 
QuestionHow to do not show the form after closing Pin
Member 129368085-Jan-17 18:14
memberMember 129368085-Jan-17 18:14 
QuestionDual monitors Pin
Member 1180984627-Nov-16 22:48
memberMember 1180984627-Nov-16 22:48 
AnswerRe: Dual monitors Pin
Member 1180984624-Jan-17 23:38
memberMember 1180984624-Jan-17 23:38 
GeneralRe: Dual monitors Pin
Member 1341454217-Sep-17 16:43
memberMember 1341454217-Sep-17 16:43 
QuestionMessage Closed Pin
21-Oct-16 5:31
memberMember 1149574921-Oct-16 5:31 
QuestionDead link for project Pin
Member 1273256811-Sep-16 5:09
memberMember 1273256811-Sep-16 5:09 
AnswerRe: Dead link for project Pin
OriginalGriff11-Sep-16 5:11
protectorOriginalGriff11-Sep-16 5:11 
GeneralRe: Dead link for project Pin
Kwok Chi Leong27-Nov-16 4:05
professionalKwok Chi Leong27-Nov-16 4:05 
GeneralRe: Dead link for project Pin
OriginalGriff27-Nov-16 4:30
protectorOriginalGriff27-Nov-16 4:30 
QuestionIs there any conflict with the personalization dialog? Pin
Tomaž Štih26-Apr-16 22:19
memberTomaž Štih26-Apr-16 22:19 
QuestionClear drawn graphics Pin
Philip Lindner10-Apr-16 9:33
memberPhilip Lindner10-Apr-16 9:33 
QuestionDoesn't work with win8.1 and wpf window Pin
stepe25-Sep-15 3:52
memberstepe25-Sep-15 3:52 
GeneralThanks! Pin
stepe22-Jul-15 4:42
memberstepe22-Jul-15 4:42 
QuestionNot Working On My Machine? Pin
Member 1177853919-Jun-15 9:03
memberMember 1177853919-Jun-15 9:03 
AnswerRe: Not Working On My Machine? Pin
Gerald Degeneve6-Jul-15 11:42
professionalGerald Degeneve6-Jul-15 11:42 
GeneralRe: Not Working On My Machine? Pin
Sean Loper17-Mar-16 23:33
memberSean Loper17-Mar-16 23:33 
QuestionA Bit of Help please Pin
Member 104219847-Jun-15 8:09
memberMember 104219847-Jun-15 8:09 
AnswerRe: A Bit of Help please Pin
Gerald Degeneve8-Jun-15 8:07
professionalGerald Degeneve8-Jun-15 8:07 
GeneralWorks with Windows 10 Insider Preview Pin
Gerald Degeneve5-Jun-15 6:21
professionalGerald Degeneve5-Jun-15 6:21 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.170924.2 | Last Updated 23 Dec 2014
Article Copyright 2014 by Gerald Degeneve
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid