How to Read an SDK
By The Microsoft Scripting Guys
Welcome to Sesame Script, the column for beginning script writers. The goal of this column is to teach the very basics of Windows scripting for system administration automation. We’ll provide you with the information you’ll need to begin reading and understanding scripts and to start modifying those scripts to suit your own needs. If there’s anything in particular about scripting you’re finding confusing, let us know; you’re probably not alone.
Check the Sesame Script Archives to see past articles.
On This Page
What’s in it for Me?
Formatting Issues
To Boldly (or Italicly) Go…
Placeholders
Language Reference: Once More
Advanced: WMI SDK
Wrap-Up
This article will explain to you how to decipher the code you see in Software Development Kits (SDKs) and Language References. For those of you completely new to this, we’ll start by explaining why in the world you would ever want to look at an SDK or a language reference.
What’s in it for Me?
Isn’t that always the question? All right, we’ll cater to your ego and explain. The best way is with an example. Suppose you want to find out information about a particular process. You’ve browsed through the Script Center and found a Hey, Scripting Guy! article on “How Can I Determine the Date and Time a Process Started?” Here’s the script that answers that question:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colProcessList = objWMIService.ExecQuery _
("Select * from Win32_Process Where Name = 'notepad.exe'")
For Each objProcess in colProcessList
Wscript.Echo objProcess.CreationDate
Next
Note: If you read the full Hey, Scripting Guy article (which you should be doing every day, by the way), you’ll see that the date returned isn’t very readable, but that another script is provided that shows how to format the date into something more recognizable as a date. For our purposes, we’re sticking with the simple version. |
So you’re thinking “Hey, that’s great, but I don’t really care about the date and time the process started, I want to know the path to the executable file that started the process.” How would you go about figuring out whether a process could tell you the path to its executable file? As you can see from the Select statement in this code, we found the process by looking at the Win32_Process class. If you go to the WMI SDK and look at the Win32_Process class, you can see that this class has a long list of properties:
Once we show you how to read this SDK, you’ll be able to find the property in this list that will give you the name and path to the executable file that started the process.
All right, we won’t keep you in suspense. The property is ExecutablePath. Replace CreationDate in the preceding script with ExecutablePath, and suddenly you have exactly the script you wanted:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colProcessList = objWMIService.ExecQuery _
("Select * from Win32_Process Where Name = 'notepad.exe'")
For Each objProcess in colProcessList
Wscript.Echo objProcess.ExecutablePath
Next
As you can see, knowing at least a little bit about SDKs can really come in handy. So we’re going to show you that little bit and help get you started.
Formatting Issues
Before we really get into our discussion of SDKs, we need to sidetrack for a moment. We’re going to talk briefly about Web publishing at Microsoft.
As you’ve probably noticed, the Microsoft Web site is pretty big. Actually, it’s very big. It’s so big that it has to be broken down into many many sections, and a lot of people are responsible for those various sections. For example, there’s an MSDN section (made up of a lot of smaller sections), and a TechNet section (also made up of a lot of smaller sections). The thing about these different sections is that they’re managed differently. Different tools and templates are used to create and publish Web content.
You’re probably wondering by now why we’re telling you this. We do have a good reason: we’re making excuses for what you’re about to see. And the Scripting Guys are great at making excuses, we consider it an art form.
When you look at SDKs for Microsoft products, you’re quite often looking on MSDN. Here’s an example of something you might see:
This page shows you the definition of the Terminate method of the Win32_Process class. Notice the boldface and italics in the code section? Well, this is where the excuses start. Because MSDN is targeted towards developers, their Web site is really good at showing code syntax and things like that. TechNet, on the other hand, doesn’t work with developers very often, it’s geared towards IT professionals like yourself. They do a wonderful job of providing information for system administrators and they present that information beautifully. (Yes, we do on occasion have to do a little sucking up to them, since they host the Script Center.) What they don’t do a very good job of just yet is provide templates that display code syntax. What that means is that we can’t reproduce the boldface and italics here that you see on MSDN. So we’ll throw in graphics here and there, and explain what’s going on as best we can.
Note: If you’ve ever wondered about the meaning of life and why dogs roll in stinky things, we can’t help you. But if you ever wondered why there was no boldface or italics in scripts on the Script Center, well, now you know: our templates don’t support that kind of thing. |
Now that we’re off that little sidetrack, let’s get to the real subject of this article: reading reference material.
To Boldly (or Italicly) Go…
There are a few basic rules of syntax that tend to be followed globally.
Boldface elements should be entered exactly as shown in the syntax statement.
Italic elements are either placeholders, which need to be replaced by actual values or variables from your script, or optional elements, depending on the reference.
Elements in square brackets ([]) are optional.
Plain text usually indicates a placeholder.
Is this sounding a little scary? All right, let’s have some fun with it first. Have you ever heard of Mad Libs®? Mad Libs is a game where a story is missing some of its nouns, verbs, adjectives, and so on. You ask someone to supply these words, put them in their places, then read the final story. Let’s give it a try:
Think of a noun.
Think of a verb (past tense).
Think of another noun.
Now put those words into this sentence:
The nounverb overthe noun**.**
Note: See what we mean about the problems with bold and italic? In code we can’t see them at all - in plain text we can see them, but we can’t string them together without losing all our spaces. The sentence should read like this: |
Did your final sentence read “The cow jumped over the moon”? Probably not. But that’s okay, that’s what makes Mad Libs fun.
Putting variables into a VBScript statement might not be quite as much fun, but it works the same way. Let’s look at the syntax for our old friend Wscript.Echo, as it’s shown in the Windows Script Host Reference:
The first part of the statement, object, is not boldface, so you wouldn’t ever type in the word object as-is. You need to replace this statement with an actual object. The next part of the statement is the call to the Echo method. As you can see, Echo is in boldface. Based on rule 1 above, that means you’ll type it in exactly as it’s shown.
Next is [Arg1]. From rule 3, we know that anything in square brackets is optional; you don’t have to put anything after Echo. But if you want to actually echo something, Arg1 is where it would go. Arg1 isn’t boldface, so you know you don’t want to just type Arg1 right into your script - you need to replace Arg1 with an actual argument. Also, the square brackets are only to show that Arg1 is optional, you don’t type the brackets into your script.
Following [Arg1] are [,Arg2], [,Arg3], and an ellipsis (…). The commas are there to show you that if you have a second argument (Arg2), you have to precede it with a comma to separate Arg1 and Arg2. Again, you must replace Arg2 with an actual argument, and you don’t type in the square brackets. The same is true for Arg3. The ellipsis indicates that you can add on as many of these arguments as you want. (If there were no ellipsis you’d be restricted to the number of arguments actually shown, in this case three arguments.)
So in place of
object.Echo [Arg1] [,Arg2] [,Arg3] ...
you’d enter something like this:
Wscript.Echo "test" ,"script"
In this case, Wscript is your object, Echo is typed exactly as shown in the syntax statement, "test" replaces [Arg1], and ,"script" replaces [,Arg2]. Oh, and here’s your output:
Note: Spacing between the arguments and the comma isn’t significant. The following three statements are all identical in the eyes of VBScript:
|
You might have noticed we didn’t have any italics in our syntax statement (which we mentioned in rule 2). That’s only because the Windows Script Host Reference doesn’t use them within its syntax statements. Different reference sets use different standards. To demonstrate this, here’s an example from the ActiveX Data Objects Reference:
Don’t worry too much about the inconsistencies. Once you’ve spent some time with these references it gets easier to understand them, and the slight differences won’t be as confusing as they might seem right now.
Placeholders
You’re probably wondering how in the world we knew that object should be Wscript, and that we could put strings in for Arg1 and Arg2. Simple: the reference told us. Take a look at the next section of the WSH Reference for the Echo method:
We can see that the object is defined as the Wscript object. So we know that object.Echo will always be Wscript.Echo. As for the Args, the reference tells us right there that they’re optional, and that they should contain the strings we want to display.
Language Reference: Once More
We’re going to take a quick look at another fairly simple example. This time we’ll use the DateAdd function, found in the VBScript Language Reference.
This function adds a value to a date and returns a new date. So how do we use this function to do that? Well, we can see the part that’s in boldface, so let’s start with that:
DateAdd( )
Next we see interval. When we look at the description of interval in the Arguments section we find out it’s a string. However, reading further we find out that this string must be one of a specific set of values, found further down in the documentation. We’ll get back to that in a moment.
Now we need a number. From the Arguments section we can see that this is the number we want added to the date. Let’s say we want to add five to our date. (Five what? We’ll get back to that when we get back to our interval setting.) Here’s what we have now:
DateAdd( , 5, )
The last element shown is date. The description says this is a “Variant or literal representing the date to which interval is added.” We saw in our previous article that all VBScript variables are Variants, so that doesn’t tell us much. But, it tells us it’s a date, so we should enter a date:
DateAdd( , 5, "10/10/2006")
Why did we put quotes around the date? Because if we didn’t, VBScript would do its data type guessing game and see that not only does this value have numbers in it, but we’re even performing division on it; therefore it must be a number. It will dutifully divide the numbers 10, 10, and 2006, and your date result will be a little different from what you expected.
Now back to that first argument, interval. If we look a little further down in the documentation, we find a section titled Settings:
This section gives us the string values that we can put into our function. Let’s say we want to add five years to our date, so our interval needs to be in years. According to the Settings, the interval string for year is "yyyy", so our function call now looks like this:
DateAdd("yyyy", 5, "10/10/2006")
We’re done, right? Oh, wait a minute…where’s our new date? For that we have to go back to the top of our documentation and see that, above the syntax statement, it says this function returns a date. That means to get the date we assign the output of this function to a variable:
newDate = DateAdd("yyyy", 5, "10/10/2006")
And there you have it. You just used the VBScript Reference to add a specified interval to a date. Keep in mind that you can use variables for any of these values. Here’s a complete script that does that, and echoes back the new date:
dtOldDate = "10/10/2006"
iTimeToAdd = 5
strInterval = "yyyy"
dtNewDate = DateAdd(strInterval, iTimeToAdd, dtOldDate)
Wscript.Echo dtNewDate
Advanced: WMI SDK
Let’s get a little more complicated and try one from the Windows Management Instrumentation Software Development Kit. For this one we’re going to assume you’ve worked with WMI, and understand what we mean when we say that the following statement binds to the WMI service on the local computer:
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Also, one thing we’re not going to do here is explain how to find the class, method, or property that you need. That’s a big topic, and one that isn’t easily answered. As a matter of fact, it doesn’t even have a really good answer. Instead we’re going to assume you somehow found what you needed, and you want to look at the SDK to figure out how to use it. Now, on with the show…
In our previous examples, we went directly to the methods we wanted to call. In WMI, we’re going to start with the class. The reason for this is that all WMI properties and methods are associated with a class. Once we have the class, we can use the SDK to find out what properties and methods exist on that class and how to use them. (See Class is in Session for a description of classes, properties, and methods and how they relate to one another.)
Win32_Service Class
For this example, we’re going to look at how to retrieve the status of services running on a computer. We do that with a script like this:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colRunningServices = objWMIService.ExecQuery("Select * from Win32_Service")
For Each objService in colRunningServices
Wscript.Echo objService.DisplayName & VbTab & objService.State
Next
Notice that we query for the Win32_Service object. Our query returns a collection of all the Win32_Service objects on the computer, which we loop through and then echo the DisplayName and State properties of those services. (See Thrown for a Loop for more on looping through collections.)
Win32_Service Properties
How did we know what properties we needed? By looking at the WMI SDK of course.
The SDK shows the definition of the Win32_Service class. Let’s look at the first line:
class Win32_Service : Win32_BaseService
The first element in the line, class, tells us that Win32_Service is a class. (Bet you could have figured that one out yourself.) The second element is the name of the class, Win32_Service.
Next is a colon (:) followed by Win32_BaseService. What’s that? First we’ll tell you that you can safely ignore this part of the statement. If you want to skip the rest of this paragraph, feel free. Still here? All right, we’ll let you in on the big secret. This element is the class that Win32_Service is derived from. Hang on, don’t give up yet, it gets better. In programming languages such as C and C++, you can create classes that define properties and methods. But what you can also do is create other classes that are based on (derived from) those classes. The new class will inherit all the properties and methods from the original class, and it can also add properties and methods of its own. It can even create a property or method that will override the same property or method from the parent class. In short, Win32_Service is a copy of Win32_BaseService with a few changes and additions to the properties and methods. So Win32_BaseService is the parent class of Win32_Service, or in correct programming lingo, Win32_Service is derived from Win32_BaseService.
Note: Often in WMI the parent class you’ll see in the SDK can’t really be used to set or retrieve data. The sole purpose of some classes is to act as templates for other classes (to allow other classes to derive from them), such as most of the CIM_ classes. |
For those of you who took a coffee break, the break is over now, come on back.
The next line of the statement is an opening brace ({). You’ll find a closing brace (}) at the end of the statement (followed by a semicolon ;). This is just standard syntax for defining a block of code in languages like C and JScript. These braces tell us that everything in between them relates to the Win32_Service class. A semicolon defines the end of a line. So whereas in VBScript if you want to continue a line you have to put an underscore at the end of each line you want to continue, in JScript a line is assumed to be continued until you put in a semicolon to end it.
Within the braces is a list of all the properties belonging to the Win32_Service class. Each property is listed with its data type first, then the name of the property. Let’s look at the first property:
boolean AcceptPause;
This tells us that the AcceptPause property of the Win32_Service class contains a Boolean value, a value of either True or False. As you already know (or at least you do if you read our previous Sesame Script article), knowing the data type is important to those of us who script in VBScript so that we know what type of data to expect when we read from a property, and what type of data we need if we write to a property. In addition, the data type will tell you whether the property returns a single value or an array. This is very important, because if you retrieve the value of a property and try to echo it you’ll receive an error if that property has returned an array. If the property returns an array, you need to loop through the array and echo each element individually. You can tell a property contains an array because the property name will be followed by square brackets. For example, the InsertionStrings property of the Win32_NTLogEvent class returns an array, and is defined like this:
string InsertionStrings[];
Because none of the property names in the Win32_Service class definition are followed by square brackets, we know that none of them are arrays. We also know that all the Win32_Service class properties are read-only. How do we know that? Again, the SDK told us. There are two ways in which to determine whether a property is read-only or read-write. The first is simply by clicking on the property name in the SDK. That will bring up a small pop-up window describing the property. As you can see, the Access Type for the AcceptPause property is Read-only:
You can also scroll down to see the property descriptions:
Note that the property descriptions are the most useful feature of the SDK. It’s possible to get properties from other sources, such as Scriptomatic or Wbemtest.exe. But the descriptions tell you whether the property is read-write or read-only, and explain what kind of behavior and information you can expect from that property.
Win32_Service Methods
Suppose you want to start a service on a machine. You can use a property to check whether the service has been started (with, what else, the Started property), but you can’t use a property to perform an action, such as start a service. For that you need a method. The Win32_Service class has several methods:
It should be pretty obvious which one we want to use to start a service: StartService. If we click on the StartService method from the previous screen, we get the full definition of this method:
As you can see, the method is defined like this:
uint32 StartService()
The first element in the definition is a data type: uint32. This tells you what type of value is going to be returned from the method call. In other words, when you call StartService to start a service and when the call is complete, you’ll receive a number (a 32-bit integer in this case) in return. You can then look at the Return Values section of the documentation to determine what the values you might receive represent. In this case, if you call StartService and receive a 0 as a return value, you know your call was successful. If you receive a 10, that means “Service Already Running” - in other words, your attempt to start this service failed because it’s already been started.
Looking back at the method definition (or, more accurately, the method declaration), you’ll see that the name of the method is followed by opening and closing parentheses (). This tells you that you don’t need to pass any information to StartService in order for the method to run. That makes sense for this method: you already know the service, because you’re calling the StartService method on the service object you want to start; therefore, no other information is necessary. And just to see how it works, here’s a script that starts the Smart Card service and echoes the return value from the StartService method:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='SCardSvr'")
For Each objService in colServiceList
errReturn = objService.StartService()
Wscript.Echo errReturn
Next
More Win32_Service Methods
When we say “more” we actually mean “one more,” because, well, this column is getting kind of long, and it’s almost lunchtime….
The StartService method we just looked at didn’t have any parameters. As we said, that means StartService didn’t need any additional information to be able to run. That’s not the case with many other methods. For example, let’s look at the ChangeStartMode method of the Win32_Service class.
uint32 ChangeStartMode(
string StartMode
);
Just like with the StartService method, the ChangeStartMode method returns a uint32, a number. And also just like StartService, the SDK provides you with the possible values that will be returned and what those values mean.
The difference between ChangeStartMode and StartService comes when you look inside the parentheses. StartService was followed by empty parentheses, meaning it doesn’t take any parameters. However, ChangeStartMode contains a value within the parentheses:
string StartMode
Does this look a little familiar? It might remind you of the properties we looked at earlier. The declaration is the same: a data type followed by a name. In this case it’s the data type and the name of the parameter you’ll pass to the method.
Let’s step back of a second and talk about why ChangeStartMode has a parameter. As you can see from the Properties page of the Smart Card service, services have a Startup Type:
In the GUI you have the option of selecting Automatic, Manual, or Disabled. When you change the startup type (or start mode) from a script, you need to tell the ChangeStartMode method which mode you’d like to change to.
Which brings us back to the parameter, StartMode. Let’s take a look at the description of the StartMode parameter in the SDK. Here’s the first line of the description:
[in] Start mode of the Windows base service.
The first thing you see is [in]. This tells you that StartMode is an input parameter. (If it were an output parameter, the description would start with [out].) An input parameter is a parameter that you have to give a value to. In our case, we’ll be giving this parameter a string value (which we know from the data type of the parameter we saw earlier) specifying the start mode. In contrast, if this were an output parameter, you’d put an empty variable in the method call, and the method would fill that variable with a value - it would output a value to you.
Next you’ll see a table, sort of like this:
Value |
Meaning |
---|---|
Boot |
Device driver started by the operating system loader. This value is valid only for driver services. |
System |
Device driver started by the operating system initialization process. This value is valid only for driver services. |
Automatic |
Service to be started automatically by the service control manager during system startup. |
Manual |
Service to be started by the service control manager when a process calls the StartService method. |
Disabled |
Service that can no longer be started. |
This table specifies what values you can pass in this parameter, and what each of those values means. For example, if you want to prevent a service from being started, you would pass the ChangeStartMode method the string “Disabled”. Here’s a script that does just that:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colServiceList = objWMIService.ExecQuery _
("Select * from Win32_Service where Name='SCardSvr'")
For Each objService in colServiceList
errReturn = objService.ChangeStartMode("Disabled")
Wscript.Echo errReturn
Next
Now, wasn’t that easy?
Wrap-Up
That’s about it for today. Thank you for joining us in our exploration of Language References and SDKs. Please discard all candy wrappers and cups in the trash cans on your way out. And buckle up and drive safely on your way home.