Set your window to be a child window of the desktop (the "Program Manager" or "progman" process). I've succeeded with this method in Windows XP (x86) and Windows Vista (x64).
I stumbled on this method while searching for a way to make a screensaver display as if it were wallpaper. It turns out, this is sort of built in to the system's .scr handler. You use screensaver.scr /p PID
, where PID is the process id of another program to attach to. So write a program to find progman's handle, then invoke the .scr with that as the /p argument, and you have screensaver wallpaper!
The project I'm playing with now is desktop status display (shows the time, some tasks, mounted disks, etc), and it's built on Strawberry Perl and plain Win32 APIS (mainly the Win32::GUI and Win32::API modules), so the code is easy to port to or understand any dynamic language with similar Win32 API bindings or access to Windows' Scripting Host (eg, ActivePerl, Python, JScript, VBScript). Here's a relevant portion of the class that produces the window:
do { Win32::API->Import(@$_) or die "Win32::API can't import @$_ ($^E)" } for
[user32 => 'HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)'],
[user32 => 'HWND SetParent(HWND hWndChild, HWND hWndNewParent)'],
sub __screen_x {
Win32::GUI::GetSystemMetrics(SM_CXSCREEN)
}
sub __screen_y {
Win32::GUI::GetSystemMetrics(SM_CYSCREEN)
}
sub _create_window { # create window that covers desktop
my $self = shift;
my $wnd = $$self{_wnd} = Win32::GUI::Window->new(
-width => __screen_x(), -left => 0,
-height => __screen_y(), -top => 0,
) or die "can't create window ($^E)";
$wnd->SetWindowLong(GWL_STYLE,
WS_VISIBLE
| WS_POPUP # popup: no caption or border
);
$wnd->SetWindowLong(GWL_EXSTYLE,
WS_EX_NOACTIVATE # noactivate: doesn't activate when clicked
| WS_EX_NOPARENTNOTIFY # noparentnotify: doesn't notify parent window when created or destroyed
| WS_EX_TOOLWINDOW # toolwindow: hide from taskbar
);
SetParent($$wnd{-handle}, # pin window to desktop (bottommost)
(FindWindow('Progman', 'Program Manager') or die "can't find desktop window ($^E)")
) or die "can't pin to desktop ($^E)";
Win32::GUI::DoEvents; # allow sizing and styling to take effect (otherwise DC bitmaps are the wrong size)
}
This program buffers output to prevent flickering, which you'll probably want to do as well. I create a DC (device context) and PaintDesktop to it (you could use any bitmap with only a couple more lines -- CreateCompatibleBitmap, read in a file, and select the bitmap's handle as a brush), then create a holding buffer to keep a clean copy of that background and a working buffer to assemble the pieces -- on each loop, copy in background, then draw lines and brush bitmaps and use TextOut -- which is then copied to the original DC, at which time it appears on screen.