Hey, Scripting Guy! Question

Hey, Scripting Guy! I have a folder on a server that contains employee photos, photos that will be used on our organization chart. New photos are taken daily for new staff members, and all the .JPG files are put into this folder. Because the photos are often different sizes, I would like to write a script that checks the folder for photos that are not the specified size, then runs a third-party application to resize the images. How can I search this folder for image files that are not 100 pixels by 121 pixels?
-- BJ

SpacerHey, Scripting Guy! AnswerScript Center

Hey, BJ. You know, this is very pivotal day for the Scripting Guy who writes this column. His car is just about out of gas, which means he has to do one of two things: 1) drive the car until it runs out of gas, and then abandon it in the middle of the road; or, 2) fill it up with gas.

Back in the good old days that wouldn’t have been much of a decision: if his car was out of gas then he’d just buy more gas. In our new and improved modern era, however, this is a much tougher choice; after all, it might very well be cheaper to just buy a new car than to have to fill the old one up with gas.

Admittedly, the price of gas has been a … bit … high for quite some time now. However, in just the past few days that price has crossed a line that never should have been crossed: in the Redmond area it now costs more than $4 to buy a single gallon of premium unleaded. Just a couple weeks ago the Scripting Guy who writes this column said, “There’s no way I’ll ever pay $4 a gallon for gas. No way.” Well, now it’s time for him to put those principles to the test.

Note. Wasn’t it just a year or so ago that the Scripting Guy who writes this column said, “There’s no way I’ll ever pay $2 a gallon for gas. No way.”? Well, to tell you the truth, we don’t remember. Still, that was a rash and foolhardy remark. Which, all things considered, does sound like the sort of thing he’d say, doesn’t it?

Truth be told, the Scripting Guy who writes this column will probably cave in and buy the gas; he really doesn’t have much choice. (If you’ve ever tried taking the bus from his house in Kirkland to Microsoft – a 9-mile trip that by bus takes well over an hour to complete – you’d know why he really doesn’t have much choice.) However, he is toying with the idea of buying the cheaper, medium-grade gas. Granted, that’s not recommended for his car (although it won’t hurt anything), but the “plus” grade does sell for less than $4 a gallon.

Oops. Never mind; turns out that the medium-grade of gas has also skyrocketed past the $4-a-gallon barrier. So much for Plan B.

You know, now that you mention it, the Scripting Guy who writes this column probably should stop buying all those expensive Italian sports cars that require the highest-grade gas. But hey, he does have a reputation to maintain.

Well, OK. He actually does have a reputation, but it isn’t one he’s particularly interested in maintaining.

Note. In case you’re wondering, the Scripting Guy who writes this column doesn’t begrudge the oil companies for continuing to raise the price of gas on an hourly basis, even while they report record profits each and every quarter. After all, the people who run the oil companies probably own a lot of cars; can you imagine how much they must spend on gas each month?

What this all means, of course, is that the Scripting Guy who writes this column can’t afford to get fired right now, which means he should really stop complaining and start working. Although, now that he thinks about, not having to drive in to work every day would save an awful lot of money ….

You know what? While the Scripting Guy who writes this column ponders this dilemma, why don’t the rest of you take a moment to look over the following script, a script that reports back all the image files in a folder that haven’t been sized to 100 pixels wide by 121 pixels high:

Set objShell = CreateObject ("Shell.Application")
Set objFolder = objShell.Namespace ("C:\Images")

For Each strFileName in objFolder.Items
    intHorizontalSize = objFolder.GetDetailsOf(strFileName, 27) 
    intHorizontalSize = Replace(intHorizontalSize, " pixels", "")

    intVerticalSize = objFolder.GetDetailsOf(strFileName, 28) 
    intVerticalSize = Replace(intVerticalSize, " pixels", "")

    If intHorizontalSize <> 100 OR intVerticalSize <> 121 Then
        Wscript.Echo strFileName.Path
    End If
Next

OK, let’s see what we have here. This script kicks off by creating an instance of the Shell.Application object, an object that lets us get at file properties (such as the vertical and horizontal size of an image file) that WMI and the FileSystemObject can’t get to. (The one drawback to the Shell.Application object: it’s designed for use on the local computer only.) After we create an instance of the Shell object we use the oddly-named Namespace method to bind to the folder C:\Images, the folder where we’ve stashed all our employee pictures:

Set objFolder = objShell.Namespace ("C:\Images")

Once we’re connected to the Images folder we can retrieve a collection of all the files in that folder simply by referencing the folder’s Items property. That also means we can use the following line of code to set up a For Each loop that enables us to loop through each and every file in that collection:

For Each strFileName in objFolder.Items

As it turns out, the Shell object (or, more correctly, the Folder object, the object we create when we call the Namespace method) includes a method named GetDetailsOf; as the name implies, this method can be used to retrieve detailed information (i.e., property values) for a given file. To use the GetDetailsOf method you need to pass it a file name and one of the following integer values, depending on the property you’re interested in:

Integer Value

Property

0

Name

1

Size

2

Type

3

Date Modified

4

Date Created

5

Date Accessed

6

Attributes

7

Status

8

Owner

10

Title

11

Subject

12

Category

13

Pages

14

Comment

15

Copyright

16

Artist

17

Album Title

18

Year

19

Track Number

20

Genre

21

Duration

22

Bit Rate

23

Protected

24

Camera Model

25

Date Picture Taken

26

Dimensions

27

Horizontal Image Size

28

Vertical Image Size

29

Episode Name

30

Program Description

32

Audio Sample Size

33

Audio Sample Rate

34

Channels

35

Company

36

Description

37

File Version

38

Product Name

39

Product Version

40

Keywords

If you look closely, you’ll see two properties (and integer values) that are of particular interest to us:

27

Horizontal Image Size

28

Vertical Image Size

Are we going to be able to use these two properties to solve BJ’s problem? We’re about to find out.

Inside the loop, the first thing we do is use this line of code to retrieve the horizontal image size (integer value 27) and store that information in a variable named intHorizontalSize:

intHorizontalSize = objFolder.GetDetailsOf(strFileName, 27)

That’s the good news. The bad news (although the news isn’t all that bad) is that we don’t get back a number; instead, we get back a string value similar to this:

132 pixels

Like we said, that’s bad news, but not that bad; after all, we can extract the image size (i.e., the value 132) by using this line of code:

intHorizontalSize = Replace(intHorizontalSize, " pixels", "")

What are we doing here? To tell you the truth, not all that much: we’re simply using VBScript’s Replace function to remove the string value pixels (including the blank space that comes before the word) from intHorizontalSize. And what will that do for us? Why, that will set the value of intHorizontalSize to this:

132

Which is more like it.

After determining the horizontal size of the image we then use these two lines of code (and a very similar process) to determine the vertical size of the image:

intVerticalSize = objFolder.GetDetailsOf(strFileName, 28) 
intVerticalSize = Replace(intVerticalSize, " pixels", "")

All we have to do now is determine whether the picture has a horizontal size of 100 pixels and a vertical size of 121 pixels. That’s something we can check using this line of code:

If intHorizontalSize <> 100 OR intVerticalSize <> 121 Then

If the horizontal size is not equal to 100 or the vertical size is not equal to 121 we then echo back the file Path (in your case, BJ, you’d probably send this information to your third-party application in order to resize the picture):

Wscript.Echo strFileName.Path

And what if the horizontal size is equal to 100 and the vertical size is equal to 121? That’s great; that means this picture is already correctly sized, and we don’t have to do anything at all. Either way, we then go back to the top of the loop and repeat the process with the next file in the collection.

By the time we’re done we should see an onscreen report listing the image files that are not correctly size:

C:\Images\JonathanHaas.jpg
C:\Images\KenMyer.jpg
C:\Images\PilarAckerman.jpg

Which is pretty much the report we were hoping to get.

At any rate, it’s now time to head off to the gas station. And yes, that means that the Scripting Guy who writes this column has decided to bite the bullet and refill the gas tank. Why? Let’s put it this way: if it was a question of getting to work things might be different. But seeing as how he needs the gas to get away from work, well ….

See you all Monday.

Note. Well, depending on how much the price of gas goes up over the weekend. Maybe he should just have his direct deposit switched from the bank to his local Shell station ….

P.S. Yes, that’s right, we need to add a postscript to this article. Why? Because after we got all done with it, we discovered that this doesn’t work on Windows Vista. Apparently that table we showed you with all the values in it no longer applies. But the good news is that Windows Vista has all new values. Here’s a script, using the new values, that will answer BJ’s question on computers running Windows Vista:

On Error Resume Next

Set objShell = CreateObject ("Shell.Application")
Set objFolder = objShell.Namespace ("C:\Test")

For Each strFileName in objFolder.Items
    intSize = objFolder.GetDetailsOf (strFileName, 31) 
    arrSize = Split(intSize, " x ")

    intLength = Len(arrSize(0))
    intHorizontalSize = Right(arrSize(0), intLength -1)

    intLength = Len(arrSize(1))
    intVerticalSize = Left(arrSize(1), intLength - 1)

    If intHorizontalSize <> 100 OR intVerticalSize <> 121 Then
        Wscript.Echo strFileName.Path
    End If
Next

As it turns out, the GetDetailsOf method works just fine on Windows Vista; in some ways, it actually works better, because a lot more file properties are now exposed to the method. (We’ll post a complete list of these properties in the Script Center next week; stay tuned.) However, many of the property values have been renumbered; on Windows Vista retrieving items 27 and 28 won’t do you much good.

Instead, we ended up retrieving item 31, which gave us the total dimensions of the picture, using an output format similar to this:

?150 x 354?

In case you’re wondering, the question marks aren’t really questions marks; instead, they appear to be some sort on non-printable character. That’s why we ended up going through all sorts of gyrations: splitting the value into an array, then using the Right function to grab all but the first character from the first array item and the Left function to grab everything except the last character in the second array item. It’s a bit crazy but, in the end, we came up with a script that works on Vista.