Hey, Scripting Guy! Here’s what I would like to do. I would like to put a script in the SendTo folder that would enable a user to easily change the file extension for a file. (Typically our users hide the file extensions, which makes it difficult for them to change those extensions when they need to.) However, I haven’t been able to figure out how to do this. Can you help?
Hey, JvdB. We hope everyone is enjoying – and participating in – the 2008 Winter Scripting Games. Remember, you still have plenty of time to enter: while the deadline for Events 1 and 2 is tomorrow (February 20, 2008, which is still ample time) the deadline for Events 9 and 10 isn’t until March 3, 2008. In other words, there’s plenty of time to join in on all the fun, and plenty of time to qualify to win one of the great prizes we’re giving away as part of the 2008 Games. Like they said, a good time will be had by all.
So are the Scripting Guys having a good time? Well, we are now, but we weren’t back in the wee hours of the morning on opening day, February 15th. In order to be a little more fair to people who don’t live in the Pacific Standard Time time zone, we decided to publish all the event instructions and related Web pages at midnight, Redmond time. (Originally we had planned to publish everything at 8:00 AM Redmond time.) With that in mind, at the stroke of midnight on the 15th the Scripting Guy who writes this column began publishing pages. That was about 200 pages when you count all the localized event instructions.
As it turned out, all 200 of those pages published flawlessly, except for 2: the home pages for the Beginners Division and the Advanced Division, the two pages we really needed. What followed from there was 2½ hours of sheer frustration (and desperation), as the Scripting Guy who writes this column tried – repeatedly – to get those pages to show up on the Web. Failing that, he then began looking for ways to work around the problem, a task that was exacerbated by the fact that the pages were taking around 15-to-20 minutes to appear live (as compared to the more usual 1-to-2 minutes). He eventually went to bed around 2:30 AM, which gave him just enough time to fall asleep before the alarm went off at 6:15.
See? A good time will be had by all.
Incidentally, this continued the Scripting Guys’ near-perfect record of having an out-and-out disaster on day 1 of a major event:
For the opening day of Scripting Week 1, well over 2,000 people signed up for the webcast. For some reason, however, the webcast people assumed that less than 500 would actually show up. As more and more people tried to log on, the servers began to bend under the load, and we ended up crashing all of Microsoft.com.
Which, in many ways, is still the Scripting Guys proudest achievement.
For the opening day of Windows PowerShell Week, some 1,200 people tried to log on to the webcast. Due to technical glitches, however, only about 300 people managed to get in, and only about half of those were actually able to hear the webcast. (Although considering the fact that Scripting Guy Jean Ross was doing the presenting, you could make a strong case that the people who couldn’t hear the webcast were actually the lucky ones.)
In order to make the opening day of TechEd 2007, Scripting Guy Greg Stemp planned to take a red-eye flight from Seattle to Orlando. Just as he reached the ticket counter to check in, his flight was cancelled. He ended up missing all of the first day, not showing up until the evening of day 1, right about the time everything closed. (Editor’s Note: Although considering the fact that Scripting Guy Jean Ross did manage to get to Orlando and had everything well under control, you could make a strong case that everyone actually lucked out that day.)
In other words, a disaster on day 1 is pretty much par for the course, at least for the Scripting Guys.
After that somewhat less-than-auspicious start, however, things began to go much better. (Which is good; we can’t even imagine what it would have been like had things begun to go worse.) As it is, we’re pretty happy with the way the Games are going; for example, as of Sunday night, we already had about half as many entries as we did during the entire 2007 Scripting Games.
But yes, the Scripting Guys are greedy, and we’d like to entice even more of you to play along. We’ve offered you fun and education, but you didn’t bite. We offered you prizes, but you wouldn’t budge. Well, now it’s time to sweeten the pot a little: what if we offered you a script that, if stored in the SendTo folder, would enable a user to quickly and easily change the file extension for a file?
That’s what we thought you’d say:
As you can see, the script kicks off by using this line of code to retrieve the first command-line argument used when the script was started:
strFilePath = Wscript.Arguments(0) strFilePath = Replace(strFilePath, "\", "\\") strNewExtension = InputBox("Please enter the new file extension (without the period):") strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colFiles = objWMIService.ExecQuery _ ("Select * from CIM_DataFile where Name = '" & strFilePath & "'") For Each objFile In colFiles strNewName = objFile.Drive & objFile.Path & objFile.FileName & "." & strNewExtension errResult = objFile.Rename(strNewName) Next
As many of you know, if you drag a file or folder onto a .VBS file the complete path to that file or folder is automatically supplied as a command-line argument to the script. (That is, to the .VBS file.) As it turns out, the same thing is true of the SendTo command. If you use the SendTo command to send a file or folder to a script, the complete path to that file or folder is automatically supplied as a command-line argument. In other words, suppose you right-click a file named C:\Scripts\Test.txt and then choose SendTo. If you “send” that file to a script in the SendTo folder, what do you suppose is going to be the first item in that script’s Arguments collection? You got it: C:\Scripts\Test.txt.
That actually makes sense. The second line of code might not:strFilePath = Replace(strFilePath, "\", "\\")
What’s going on here? Well, we’re going to use this file path in a WMI query. As you know, our file path is going to look something like this:C:\Scripts\Test.txt
So is there something wrong with that path? As a matter of fact, there is: it contains two instances of the \ character. Why is that a problem? Well, the \ happens to be a reserved character in WMI; if you try to use a reserved character in a WMI query your script is going to fail. This line of code might look harmless enough, but it’s not:Set colFiles = objWMIService.ExecQuery _ ("Select * from CIM_DataFile where Name = 'C:\Scripts\Test.txt'"
So does that mean that you can’t use file paths in a WMI query? No; instead, it just means that any time you use a reserved character in a WMI query you need to “escape” that character. Because the escape character just happens to be a \, that means doubling up each and every \ in the path:C:\\Scripts\\Test.txt
That, needless to say, is what line 2 does: it uses the Replace function to replace each instance of the \ character with this: \\. That’s going to result in a path that we can use in a WMI query.
Next we prompt the user to enter the new file extension (minus the period), storing that value in a variable named strNewExtension:strNewExtension = InputBox("Please enter the new file extension (without the period):")
After connecting to the WMI service on the local computer, we then – at long last – issue our WMI query:Set colFiles = objWMIService.ExecQuery _ ("Select * from CIM_DataFile where Name = '" & strFilePath & "'")
What we’re doing here is binding directly to the file that the user clicked on. To do that we select all instances of the CIM_Datafile class that have a Name (path) equal to the value of the variable strFilePath; that’s the variable containing our modified path (C:\\Scripts\\Test.txt). Because file paths must be unique on a computer, that’s going to bind us to the file the user clicked on.
Note. This is probably a good time to mention that this script can work on only the local computer. Without getting into too much detail, if you try clicking on a UNC path WMI will assume that the file is found on the local machine. In theory, you could get this to work against files stored on a remote computer, but you’d have to have some way to tell the script the name of that remote machine. Alternatively, you could map a drive to the folder on the remote computer. That should work fine, because, in that case, the file path will include a drive letter rather than a UNC path.
Even though there can be only one item in the collection returned by our query, a collection is still a collection; that means we need to set up a For Each loop to loop through all the items in that collection. Within that loop the first thing we do is this:
strNewName = objFile.Drive & objFile.Path & objFile.FileName & "." & strNewExtension
What we’re doing here is using selected properties of the original file as well as the new file extension to construct the new file path. (For all intents and purposes, you rename a file by giving it a new file path.) As you probably guessed, the Drive property returns the drive where the file is stored. In other words:
The Path property is a kind of oddball little property that returns folder information minus the drive letter and the file name. (Beat us; that’s just the way it works.) In our case, the complete file path is C:\Scripts\Test.txt; that means that the Path property is equal to \Scripts\. Combined with the Drive property, that gives us a path that, so far anyway, is equal to this:
Next up is the FileName property, which is the name of the file minus the file extension (and, of course, minus the dot between the name and the extension). Thus:
And then last but surely not least, we tack on the dot and the file extension (stored in the variable strNewExtension). Let’s assume that the new extension is log; that’s going to give us a brand-new file path that looks like this:
And no, in this case we don’t have to escape all the \ characters in the path. We need to do that only when we use that path in a query.
All that’s left now is to call the Rename method and rename the file:
errResult = objFile.Rename(strNewName)
That’s all we have to do. Best of all, this works just fine even if the user has chosen to hide file extensions in Windows Explorer. That’s because WMI doesn’t have to “see” the file extension; it knows what the file extension is.
WMI knows everything.
Two things to keep in mind here. First, this script must be stored in the SendTo folder, and for obvious reasons: if it’s not, then you can’t access it using the SendTo command. Second, this script works only on files. You can try sending a folder to the script, but nothing will happen. That’s because folders aren’t members of the CIM_DataFile class; instead, they’re members of the Win32_Directory class.
That should do it, JvdB. If you haven’t already done so, we’d recommend that you check out the 2008 Winter Scripting Games. It’s safe to say that most of competitors are having a good time so far, and enjoying the challenge. And remember, we have both a Beginners Division and an Advanced Division, and you can compete in any (or all) of three scripting languages: VBScript; Windows PowerShell; and Perl. In other words, a little something for everyone.
Oh, and we have one other recommendation for you: if you ever stage a major event of some kind, well, whatever else you do don’t invite the Scripting Guys to participate on Day 1. Just trust us on that one.