Home > howto > Desktop Background Slideshow in Windows Home Basic, with Powershell

Desktop Background Slideshow in Windows Home Basic, with Powershell

I’m using Windows 7 Home Basic in my home computer, since this was the version that came bundled with the hardware. For the most part I don’t miss what the higher SKUs have to offer, although every now and then I’ll stumble at something missing and wish it was available. This is the case with the “desktop background slideshow”.

The desktop background slideshow

This is an interesting feature (available in all SKUs *above* Home Basic) which allows you to select a group of photos and have the desktop background change on an interval (say every 15m) to a new image. Quite interesting when you have a folder full of very nice images. But alas, Home Premium only allows you to point at a single image, no slideshow goodness for you.

Desktop background slideshow feature in Microsoft Windows

One thing I don’t like about the implementation of the feature in Windows is that you have to select which images will be part of the whole slideshow, you doesn’t just point to a folder and forget. The consequence of this design decision is that if later you add new images to that folder they won’t immediately become part of the slideshow, you’ll have to come back to this dialog and add them in. Not ideal …

Hacking a slideshow on a budget

Changing the desktop background is not a very difficult proposition. There’s an API call which can do just that: SystemParametersInfo, in user32.dll (using the SPI_SETDESKWALLPAPER action parameter).

In earlier versions of Windows you were limited to BMPs for background, so even if the UI gave you de appearance of accepting any image file, behind the scenes a conversion would take place and what would get stored in the registry was the path to the converted image. In Windows 7 things are easier, you just set the desktop background to any image you’d like the OS takes care of the details.

A note about Windows XP

If you search the interwebs for scripted methods for changing the desktop background, you’ll come upon several references suggesting that you write to the registry key HKEY_CURRENT_USER\Control Panel\Desktop\Wallpaper and then make a call to UpdatePerUserSystemParameters method on user32.dll, in order to let the OS know of the changed setting. Well, this method don’t work reliably on Windows 7 any longer: the registry key still controls the user’s desktop background, but the call to UpdatePerUserSystemParameters no longer reliably updates the active session (once the user logs out and in again, things will be updated though). Bottom line, the correct (supported) method to update the desktop background and many other system parameters is through a call into SystemParametersInfo.

Putting it all together with Powershell

In order to accomplish a slideshow, we need to script the following sequence of opperations:

  1. List the images within a given folder
  2. Select one of the images at random
  3. SystemParametersInfo giving it the full path to the image

Any scripting language which would let you accomplish all of the above would do, but for the sake of this implementation I’ll use Microsoft’s Powershell, which comes pre-installed in Windows 7 (all SKUs, Home Basic included!). Powershell is a very nice scripting language overall, and it makes performing the steps above a real breeze:

Listing images within a given folder

$folder = "c:\Users\Public\Pictures\Wallpaper"
Get-ChildItem $folder

And the results of the above would look something like this:

    Diretório: C:\Users\Public\Pictures\Wallpaper
 
 
Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        10/03/2011     14:45     522419 01178_chicagoatnight_1680x1050.jpg
-a---        10/03/2011     14:51     293592 01301_rubik_1680x1050.jpg
-a---        10/03/2011     14:58     390960 01338_crazysun_1680x1050.jpg
-a---        10/03/2011     14:45     556026 01356_crepuscule_1680x1050.jpg
-a---        10/03/2011     14:36     948187 01542_thebirthofeuropa_1680x1050.jpg
-a---        10/03/2011     14:57     687594 01603_droplets_1680x1050.jpg
-a---        10/03/2011     14:57     609453 01637_rb_1680x1050.jpg
-a---        10/03/2011     14:38     699733 01645_bergs_1680x1050.jpg
-a---        10/03/2011     14:51    1176550 01663_lakelouise_1680x1050.jpg
-a---        10/03/2011     14:59    1201259 01700_fallisintheair_1680x1050.jpg
-a---        10/03/2011     14:45     791767 01705_sunsetinfrontofme_1680x1050.jpg
-a---        10/03/2011     14:48    1124629 01729_monumentalsimplicity_1680x1050.jpg
-a---        10/03/2011     14:51     710181 01737_elemental_1680x1050.jpg

One of the nice things about Powershell, and which sets it apart from most other shell environments, is that although the output above appears as just a bunch of text, it is actually a collection of files, for which the default representation happens to be the listing above (technically, it is a System.IO.DirectoryInfo object, which has information about the directory itself, along with its contents). The significance of this is that instead of having to manipulate text in order to work with this result, I can just pass it along to other methods which work on collections of stuff and not have to worry at all about formatting. For the next step:

Selecting a single image, at random

Easier than taking candy from a kid: there’s a commandlet (a command, in Powershell parlance) which selects a random element from any list (any iterable collection), conveniently named Get-Random. Since the results of our directory listing are iterable, we can just pipe them through Get-Random:

Get-ChildItem $folder | Get-Random

And the result would be something like this:

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        10/03/2011     14:51    1176550 01663_lakelouise_1680x1050.jpg

The resulting text output resembles that of the directory listing, but with only one entry. However, because we’re now referencing a single item in the listing, the object type for the result is System.IO.FileInfo – if the listing included sub-directories and one of them was selected at random, we’d have a result of type System.IO.DirectoryInfo here.

Calling the Win32 API to set the desktop background

Here’s where we hit a bit of a snag: PowerShell, as awesome as it is, don’t have an easy way to call into a native API. To solve this problem we could compile a managed or native executable to serve as a call proxy, and invoke this program from the script; but this would make for a solution which is not self-contained, and I’d rather have everything required in the script file itself, no extra dependencies.

And there is a way: we get some help from the Add-Type cmdlet, which allows you to compile code inline and build new types at runtime. All we need is a suitable piece of C# code for PInvoking the user32 method; with a little help from pinvoke.net, we get the following definition:

[DllImport("user32.dll")]
public static extern uint SystemParametersInfo(
    uint uiAction,
    uint uiParam,
    string pvParam,
    uint fWinIni);

Using Add-Type to compile and then invoke this method is very straight forward:

$type = Add-Type -MemberDefinition $signature `
    -Name Win32Utils -Namespace SystemParametersInfo `
    -PassThru
 
$null = $type::SystemParametersInfo(20, 0, $fileName, 3)

Putting it all together in a single script

To conclude this part of the exercise, lets combine all steps above in a very long and complex script which will select a random image from a given folder and set it as the new desktop background:

$folder = "c:\Users\Public\Pictures\Wallpaper"
 
$file = Get-ChildItem $folder | Get-Random
 
$signature = @'
[DllImport("user32.dll")]
public static extern uint SystemParametersInfo(
    uint uiAction,
    uint uiParam,
    string pvParam,
    uint fWinIni);
'@
 
$type = Add-Type -MemberDefinition $signature `
    -Name Win32Utils -Namespace SystemParametersInfo `
    -PassThru
 
$null = $type::SystemParametersInfo(20, 0, $file.FullName, 3)
 
"Wall paper is now set to $($file.FullName)"

Save this script in a text file with the extension “ps1” and you’re ready to execute it at will. Well, almost, as we’ll see:

Executing the script outside of the Powershell prompt

Once you have the script saved, you can execute it from any prompt as follows:

powershell path-to-your-script\name.ps1

But chances are you’ll get an error like this:

O arquivo C:\Users\Public\Util\Shuffle_desktop_background.ps1 não pode ser carregado. O arquivo C:\Users
\Public\Util\Shuffle_desktop_background.ps1 não está digitalmente assinado. O script não será executado
no sistema. Consulte "get-help about_signing" para obter mais detalhes..
Em linha:1 caractere:31
+ Shuffle_desktop_background.ps1 <<<<
    + CategoryInfo          : NotSpecified: (:) [], PSSecurityException
    + FullyQualifiedErrorId : RuntimeException

What just happened is that Powershell comes out of the box with a policy in place which onl allows for signed scripts to be executed. This is fine for most users, which will not fiddle with creating scripts of their own, but a bit too harsh for most power users. You can check your current execution policy by invoking the cmdlet Get-ExecutionPolicy, which should display AllSigned as its result.

To modify the policy you must open a Powershell prompt as administrator, then type the following command:

Set-ExecutionPolicy RemoteSigned

This call configures the system to require signatures in all non-local scripts, but allow the execution of unsigned local scripts. After this change, try re-running the image script and see what happens it should succeed this time.

Turning this one-time change into a proper slideshow

Task-SchedulerNow that we have the script, all we have to do in order to get a new desktop background every 15min is to remember to open the command line and execute this script once every 15min; pretty easy, uh? Well, if you’re anything like me, you’re lazy and want to assign this task to someone else; that someone else being your computer. Windows has a handy task scheduler built-in which is used for all kinds of pre-programmed tasks. You can create a task and have its action be the script call (powershell path-to-script). For the action trigger, either have it run every few minutes/hours, or at every logon, if you only want to change the background once every time you log onto the machine.

Getting rid of the Powershell Command Window Pop-up

As commenter Vikram pointed out (thanks!), when you invoke the Powershell interpreter to run the script, you get the Powershell command window to briefly show-up on your desktop. If you set an aggressive schedule for your task, like every 5min, this gets really annoying. Although the Powershell executable accepts a “-WindowStyle Hidden” parameter, the window will still flash briefly, before PS decides to hide it. Not good.

The best way that I’ve found to work around this issue is to have an intermediate program act as a Powershell host with no window, and use that to launch the script. This was suggested by Scott Weinstein in his post entitled Scheduling PowerShell tasks without a console window.

To implement his suggestion, in your Task Scheduler action, instead of calling Powershell.exe to run the script, you call the PoshExec.exe host you created following Scott’s instructions. And just in case you have any trouble creating the EXE from the source code, I’m leaving here a compiled version of the World’s Smallest Powershell Host Application that you can download (executable, source-code, MD5 hash).

To summarize, your action in TaskScheduler will look something like this:

  • Action: Start a program
  • Program/Script: c:\Path-to-program\PoshExec.exe
  • Arguments: c:\Path-to-script\YourScript.ps1
Categories: howto Tags: ,
  1. June 22nd, 2011 at 08:54 | #1

    Dear Claudio Andre,

    This is a brilliant way to bypass downloading all those SW to have desktop slideshow on Win 7 Home basic! I have tried this & I must say that I am mighty impressed with this simple solution! Thanks a ton for this great tip!!!
    Just one issue though – every time my wallpaper changes, the taskmgr.exe prompt window also opens up & closes (giving an onlooker a feeling that my system is perhaps infected with some kinda virus) Could you please help me to fix this?

    What I mean is, can I just have my wallpaper change without having to see any extra window open & close everytime the wallpaper changes?

    Best,
    Vikram

  2. June 22nd, 2011 at 10:20 | #2

    Hi,

    Great tip! I have used this on my W7 Home Basic & appreciate the ease of achieving the result!
    But the event scheduler opens & closes an annoying window everytime the wallpaper changes. Can we fix this? (Make the window not come at all?)
    If a screen shot would make better sense of the issue that I am facing, http://www.techsupportforum.com/forums/f217/desktop-wallpaper-slideshow-in-win-7-home-basic-without-3rd-party-sw-issues-582330.html#post3320126
    is the link!

    Once again, thanks a lot for your guidance, in advance!

  3. June 22nd, 2011 at 17:47 | #3

    Vikram, thanks for calling my attention to this annoying side effect … I’ve been meaning to amend the post for a while, but kept postponing it.

    I will update the blog post shortly, but meanwhile, the solution I’ve found to be the most convenient comes from this guy: http://weblogs.asp.net/sweinstein/archive/2008/12/03/scheduling-powershell-tasks-without-a-console-window.aspx

    I intend to add this to the recipe in the post …

  4. June 24th, 2011 at 03:53 | #4

    Thanks a lot for the quick response! I am not very tech saavy…& the asp thingy seems a bit complicated… your detailed instructions will help me more in understanding what to do… So will wait till you update your blog ;-)

    Thank you once again!

  5. June 27th, 2011 at 00:00 | #5

    Hi there Vikram! If you managed to get it working with the (somewhat convoluted) instructions I gave in this post, then you’re “tech-savvy enough” ;-)

    In any case, I did update the post and added a link to download the “micro powershell host” program which you can use to run the script without the command window showing-up. Please let me know if you have any trouble making it work for you.

    Regards,
    Claudio

  6. rakesh
    January 16th, 2013 at 11:40 | #6

    Rakesh, well thanks claudio andre it works well and easiy way ….. Can we change the time slide to 1 minute ?

  7. tamilselvi
    February 21st, 2013 at 11:02 | #7

    i need know how to change my desktop images as slideshow in windows7 homebasic,and i dn’t find personalize option in my control panel

  8. Ashish
    June 1st, 2013 at 07:39 | #8

    Hi Claudio,

    Your solution is working perfectly, however like Vikram i am also facing that annoying popup.
    I tried to create an EXE from the .cs file , however not sure how do i do that.
    I also tried to download the exe file that you gave, it gives me an error
    CLR erro:8004005. The program will now terminate
    Can you help?

    Regards,
    Ashish

  9. July 1st, 2013 at 21:48 | #9

    Hi Ashish, do you have the latest Microsoft .NET Framework installed? You need that to run the EXE provided … If you not sure, you can grab it here: http://www.microsoft.com/net/downloads

    (you can chose the “.NET Framework 4 Client Profile” from the “For the Desktop” column)

  1. No trackbacks yet.