
Solution to Event 5 in the 2008 Winter Scripting Games Sudden Death Challenge.
Like we said in the instructions, Event 5 was a little tougher than the other events in the Sudden Death Challenge. (But like we also said, it wouldn’t be much of a challenge if everyone got a perfect score, would it?) But while this event might have been difficult it wasn’t impossible.
No, really, it wasn’t.
That’s especially true if you took our hint and followed the link to the WMI section of the Script Repository. If you did, you should have stumbled upon a script titled List All the Properties and Methods of the Win32 Classes. That script provides the basic framework for crafting an answer to Event 5.
But instead of talking about this why don’t we just show you some code? Here’s a VBScript solution to Event 5; needless to say, we could also have solved this problem using Perl or Windows PowerShell:
strComputer = "."
Set objWMIService=GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
For i = 65 to 89
strCharacter = Chr(i)
x = 0
For Each objClass in objWMIService.SubclassesOf()
If Left(objClass.Path_.Class,5) = "Win32" Then
Set strClass = objWMIService.Get(objClass.Path_.Class)
For Each strItem in strClass.Properties_
If Left(strItem.Name, 1) = strCharacter Then
Wscript.Echo strItem.Name & " " & objClass.Path_.Class
x = 1
Exit For
End If
Next
If x = 1 Then
Exit For
End If
End If
Next
Next
So how does this script work? Well, it starts off by binding to the WMI service on the local computer; that part seems straightforward enough. After that, however, the script veers off into the unexpected:
For i = 65 to 89
Why a loop that runs from 65 to 89, and why here? Well, as those of you who tried Event 4 know, the uppercase letters have ASCII values that run from 65 (A) to 89 (Y). VBScript won’t let us set up a loop that runs from A to Y. Therefore, we’re doing the next best thing: we’re setting up a loop that runs from the ASCII value of A to the ASCII value of Y.
Note. Why don’t we loop from A to Z instead of from A to Y? That’s easy: on our copy of Windows (Windows XP) there aren’t any property names in the root\cimv2 namespace that begin with the letter Z, at least not in the Win32_ classes. |
The first thing we do inside this loop is use the Chr function to convert the ASCII value of the loop variable i to an actual letter:
strCharacter = Chr(i)
Because i is equal to 65 (the first time through the loop anyway), that’s going to make strCharacter equal to A.
After setting the value of a counter variable named x to 0 we then use this line of code to loop through a collection of all the classes found in the root\cimv2 namespace:
For Each objClass in objWMIService.SubclassesOf()
As you can see, there’s nothing particularly hard about that; we simply reference the SubclassesOf() property, a property that contains information about each class in a given namespace. Inside this loop the first thing we do is check to see if we’re dealing with a Win32 class; that’s something we do by using the Left function to determine whether or not the first five characters in the class named are Win32:
If Left(objClass.Path_.Class,5) = "Win32" Then
Suppose the first five characters aren’t Win32? In that case, we’re not dealing with a Win32 class; instead we’re dealing with a class like, say, CIM_DataFile. Therefore, we go back to the beginning of our For Each loop and try again with the next class in the collection.
If it turns out that we are dealing with a Win32 class we then use this line of code to create an object reference to the class itself:
Set strClass = objWMIService.Get(objClass.Path_.Class)
Once we’ve done that we set up another For Each loop, this one designed to walk us through all the properties of the class:
For Each strItem in strClass.Properties_
So what do we do inside this loop? Well, now that we have the properties for a given class we want to see if any of these property names start with the letter A. We can do that by checking to see if the first character in the property Name equals the value of the variable strCharacter:
If Left(strItem.Name, 1) = strCharacter Then
If the two values don’t match then we simply try again with the next property in the class. But what do we do if the values do match?
In that case, the first thing we do is echo the name of the property, a few blank spaces, and the name of the class:
Wscript.Echo strItem.Name & " " & objClass.Path_.Class
We set the value of the counter variable x to 1, then use the Exit For statement to exit our innermost For Each loop.
Why did we set the value of x to 1? Well, when we exit our inner For Each loop we need to know why we exited the loop. That is, did we exit the loop because we found a property name starting with the letter A, or did we exit the loop because we’ve looked through all the property names for this class and couldn’t find one that started with the letter A? If we set the value of x to 1 then the script knows that we exited the loop because we found the property we were looking for.
In fact, that’s the first thing we do when we exit the loop; we check to see if x = 1:
If x = 1 Then
If x is equal to 1 that means that we found a property name beginning with A. Therefore, we use the Exit For statement to exit the For Each loop that loops us through all the classes in the root\cimv2 namespace. In turn, that takes us back to our original For Next loop where we then repeat the entire process, this time with i equal to 66 (and strCharacter equal to B). If x is not equal to 1 then we simply grab the next class in the collection and check to see if it has any property names that begin with A. Etc. etc.
When all is said and done you should get back data similar to (but not necessarily exactly like) this:
AdditionalDescription Win32_JobObjectStatus BankLabel Win32_PhysicalMemory Caption Win32_OnBoardDevice DriveName Win32_VolumeChangeEvent EventType Win32_PowerManagementEvent FileName Win32_ModuleLoadTrace GuaranteesDelivery Win32_NetworkProtocol HotSwappable Win32_OnBoardDevice ImageBase Win32_ModuleLoadTrace JobCountSinceLastReset Win32_Printer KernelModeTime Win32_Process LengthAllowed Win32_SystemSlot MachineName Win32_ComputerSystemEvent Name Win32_OnBoardDevice OEMEventCode Win32_PowerManagementEvent ProcessID Win32_ModuleLoadTrace QuotaNonPagedPoolUsage Win32_Process Removable Win32_OnBoardDevice SECURITY_DESCRIPTOR Win32_ComputerSystemEvent TIME_CREATED Win32_ComputerSystemEvent UserStackBase Win32_ThreadStartTrace Version Win32_OnBoardDevice WaitMode Win32_ThreadStartTrace XOffCharacter Win32_SerialPortConfiguration Year Win32_CurrentTime
Like we said, this one was hard. But definitely not impossible.