Hey, Scripting Guy! Question

Hey, Scripting Guy! I saw your column on randomly assigning a picture to use as the wallpaper on a computer. I’d like to do the same thing, except that, instead of picking from a predefined list of pictures, I’d like to randomly assign a picture from the user’s My Pictures folder. How do I do that?

-- JJ

SpacerHey, Scripting Guy! AnswerScript Center

Hey, JJ. You know, this has been a crazy month for the Scripting Guys. When the month began, we were absolutely swamped trying to get everything in place for our trip to Barcelona and TechEd IT Forum. (Not being the brightest of Microsoft employees, the Scripting Guys actually did more work during their “vacation” in Spain than they do in a normal week.) After being gone for 9 days we returned home just in time for Thanksgiving; in fact, the Scripting Editor didn’t even bother to come in for those three days before Thanksgiving.

Note. You know, you’d be amazed at how many people have said that very same thing. And you’re right: things did seem to go incredibly well during her absence, didn’t they?

Now, however, the party’s over (well, at least until Christmas, which is rapidly approaching). Among other things, that means it’s time to get back to work, and time to start tying up a few loose ends. For example, we’ve hardly had the chance to mention the IT Forum Challenge currently going on in the Script Center. Not only is this a fun little contest, but it’s also your absolute last chance to win a Dr. Scripto bobblehead doll: we are down to our last precious few bobbleheads. (No, that doesn’t include the Scripting Editor. We said our precious bobbleheads.)

Note. Yes, it is fun when the Scripting Editor doesn’t come in to work, isn’t it? Let’s just hope she forgets to look at this column when she does come back. What do you say we keep this one as our little secret?

Anyway, we encourage everyone to take a crack at the Challenge; like they say, someone has to win. (Who says that? Our legal department, which won’t let us hold a contest unless we actually give away the prizes. Which explains why we’ve never bothered to give away a new Porsche or BMW; after all, we’d have to actually give the thing away.) And for those of you who’ve been asking, yes, we’ll post a list of the leading scores sometime this week. We had planned to do that while we were in Barcelona but the network connectivity between our hotel and Microsoft was a bit slow, to say the least. (You’d think by now they could breed carrier pigeons that could fly a little faster, but apparently they can’t.)

So what other loose ends do we have to tie up? Well, here’s one: someone out there needs a script that can randomly grab a picture from a user’s My Pictures folder and then assign that picture as the user’s wallpaper. Fortunately for us, we just happened to have a script like that lying around:

Const MY_PICTURES = &H27&
Const HKEY_CURRENT_USER = &H80000001

Set objDictionary = CreateObject("Scripting.Dictionary")

Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace(MY_PICTURES) 
Set objFolderItem = objFolder.Self   
   
strPath = objFolderItem.Path

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(strPath)
Set colFiles = objFolder.Files

i = 0

For Each objFile in colFiles
    i = i + 1
    objDictionary.Add i, objFile.Path
Next

intHighNumber = objDictionary.Count
intLowNumber = 1

Randomize
intNumber = Int((intHighNumber - intLowNumber + 1) * Rnd + intLowNumber)

strPicture = objDictionary.Item(intNumber)   

strKeyPath = "Control Panel\Desktop"
ValueName = "Wallpaper"

strComputer = "."

Set objReg = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")

objReg.SetStringValue HKEY_USERS, strKeyPath, ValueName, strPicture

Got all that? Good, because now for the fun part: figuring out what this hunk of code actually does, and how it does it. As you can see, we start out by defining a pair of constants, MY_PICTURES (which tells the script which special folder we want to work with), and HKEY_CURRENT_USER (which tells the script which registry hive we want to work with). After we define the two constants, we then use this line of code to create an instance of the Scripting.Dictionary object:

Set objDictionary = CreateObject("Scripting.Dictionary")

Why did we create an instance of the Dictionary object? Good question and, to be honest, we didn’t have to go this route; there are other ways that we could have tackled this problem. In the end, however, we decided that this was a reasonable approach: we’ll grab the paths for all the files in the My Pictures folder and add them to the Dictionary. After we’ve done that we’ll generate a random number between 1 and the number of items in the Dictionary (that is, the number of files in the folder); for the moment, we’ll assume we generated the random number 13. We’ll then grab item 13 out of the Dictionary. Voila: we’ve randomly selected a file from the My Pictures folder, and gotten hold of the file path to boot.

Make sense? It does?

Wow. After four years something we said actually made sense. Apparently all that practice is beginning to pay off.

Our next chore is to determine the location of the user’s My Picture folder. That’s what this block of code does:

Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace(MY_PICTURES) 
Set objFolderItem = objFolder.Self   
   
strPath = objFolderItem.Path

Granted, this is a little goofy-looking. However, all we’re doing here is creating an instance of the Shell.Application object and then using the Namespace method to bind to the My Pictures folder (notice that we pass this method the constant MY_PICTURES). Of course, in the mysterious world of the Shell object binding to a folder using the Namespace method is only half the battle; to actually be able to do retrieve information from that folder you need to then reference the Self property, which returns a FolderItem pointing to that folder. (OK, admittedly, that doesn’t make a lot of sense. But that’s not our fault, that’s how the Shell object works.) This line of code returns a FolderItem object for the My Pictures folder:

Set objFolderItem = objFolder.Self

At least line 4 makes sense; in that line we simply grab the value of the folder’s Path property and store it in a variable named strPath:

strPath = objFolderItem.Path

As soon as we know the path to the My Pictures folder we can go ahead and retrieve a collection of all the files in that folder. That’s what this block of code is for:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(strPath)
Set colFiles = objFolder.Files

These lines of code should be a little easier to follow. In lines 1 and 2, we create an instance of the Scripting.FileSystemObject, then use the GetFolder method to bind to the My Pictures folder. (How do we know that we’re binding to the My Pictures folder? Because we pass GetFolder the variable strPath, which contains the path to that folder.) We then use this line of code to return a collection of all the files found in that folder:

Set colFiles = objFolder.Files

Admittedly, the whole thing is a tad-bit cumbersome. But it works: we now have a collection of all the files in My Pictures.

And just exactly what are we going to do with that collection? Well, first of all, we’re going to assign the value 0 to a counter variable named

i = 0

And then we’re going to use this block of code to loop through the collection of files:

For Each objFile in colFiles
    i = i + 1
    objDictionary.Add i, objFile.Path
Next

As you can see, inside that loop the first thing we do is increment the value of i by 1. We then call the Add method to add the file to the Dictionary, using the counter variable i as the Dictionary key and the value of the file’s Path property as the Dictionary item:

objDictionary.Add i, objFile.Path

Note. Dictionary key? Dictionary item? You say you have no idea what those terms even mean? In that case, you might want to take a peek at this edition of Sesame Script, everyone’s favorite scripting column.

Well, you’re right: everyone’s second-favorite scripting column. We were just being modest.

When we’re all done, our Dictionary will contain information similar to this:

Key

Item

1

C:\Documents and Settings\kenmyer\My Documents\My Pictures\Picture1.jpg

2

C:\Documents and Settings\kenmyer\My Documents\My Pictures\Picture2.jpg

3

C:\Documents and Settings\kenmyer\My Documents\My Pictures\Picture3.jpg

We should note that we’re assuming that the only files stored in My Pictures are picture files. If that’s not true you’ll need to include code that makes sure you’re dealing with a picture before you add that file to the Dictionary. How could you do that? Well, without going into any detail, here’s a modified For Each loop that uses the GetExtensionName method to retrieve the file extension for each file, then adds that file to the Dictionary only if the file extension is JPG, BMP, or GIF:

For Each objFile in colFiles
    strExtension = objFSO.GetExtensionName(objFile.Path)
    If strExtension = "jpg" or strExtension = "bmp" or strextension = "gif" Then
        Wscript.Echo strExtension
        i = i + 1
        objDictionary.Add i, objFile.Path
    End If
Next

Now we’re ready to have some fun. To begin with, we use this line of code to assign the total number of files in the Dictionary (the Count property) to a variable named intHighNumber:

intHighNumber = objDictionary.Count

After that, we assign the value 1 to a variable named intLowNumber; from there we can use these two lines of code to generate a random number between 1 (intLowNumber) and the total number of files in the Dictionary (intHighNumber):

Randomize
intNumber = Int((intHighNumber - intLowNumber + 1) * Rnd + intLowNumber)

As you can see, the random number we generate gets stored in a variable named intNumber. Again, let’s say that intNumber is 13. That means we want to use picture number 13 as our wallpaper. How do we retrieve the path for picture number 13? That’s easy: we just return the Item value for the Dictionary element that has a key equal to 13:

strPicture = objDictionary.Item(intNumber)

All we do is pass the Dictionary object the appropriate key (intNumber) and the value of that particular item (which happens to be a file path) is assigned to a variable named strPicture.

From there we simply use a little WMI magic (and the StdRegProv class) to write the new wallpaper path to the registry. We won’t discuss that process in any detail today; for more information, see our previous column on randomly assigning wallpaper to a computer.

Two things to keep in mind here. First, this script is designed to run only on the local computer. Could it be modified to work against a remote computer? Well, probably, although ferreting out the location of the My Pictures folder would be a bit of a chore. For now, assume this is local computer only. But that’s OK, because this is intended to be used as a logoff script anyway.

That, by the way, is point number 2. Could you use this as a logon script? Sure, except that the wallpaper change won’t take effect until the user logs off and then logs back on: changing the wallpaper value in the registry does not cause the desktop to refresh and display that new wallpaper. There are various hacks and workarounds that sometimes seem to work in this regard, but we’re not really in a position to recommend any of them. You can search the Internet and try one of these approaches (at your own risk, mind you). Or, just use this as a logoff script instead.

We hope that answers your question, JJ. In the meantime, the Scripting Guy who writes this column has a ton of other loose ends to tie up, all of them of the highest priority and all of them which must be dealt with immediately

And all of which he will do his darnedest to get to. Right after lunch.