Let’s pretend that you aren’t feeling well and so you go to the doctor. The doctor conducts an examination and then says, “OK, I know exactly what the problem is. You just need to take two Cillosybarexiums each morning and you’ll be fine.”
“Wow, that’s great,” you say. “Where do I get Cillosybarexium?”
“Well, you can’t,“ says the doctor. “Cillosybarexium hasn’t been invented yet.”
Gee. Thanks.
Now, would Microsoft ever do anything like that to you? Of course not. Well, not that bad anyway. Depending on how you look at it, however, we did do something roughly analogous when we added the Disk Defragmenter to Windows 2000. When Windows 2000 was released we encouraged customers to make use of the Disk Defragmenter, especially when it came to managing servers. And for good reason: you can often-times dramatically increase the performance and reliability of a computer just by ensuring that the hard disks are defragged.
Needless to say, people were thrilled to find a disk defragmenter built right into the operating system. “Wow, that’s great,” you said. “Of course, I don’t actually keep my servers in my office; how do I programmatically check the defragmentation status of a remote computer?”
“Well you can’t,” we said. “That technology hasn’t been invented yet.”
You know, you don’t look so good. Maybe you should take two Cillosybarexiums and finish reading this article in the morning.
| Help Has Arrived | |
| Running a Defragmentation Analysis Report | |
| Defragging a Computer Using the Win32_Volume Class | |
| Better Late Than Never, Right? |
Listen, don’t despair: we have some good news for you. No, it has nothing to do with Cillosybarexium; this news is much better. In Windows Server 2003, you can use WMI to check the defragmentation status on remote computers; in fact, you can even use WMI to defragment those remote machines. Who needs Cillosybarexium when you have WMI?
Note. We should point out that Cillosybarexium is something we just made up; it doesn’t really exist. Furthermore, the Scripting Guys do not advocate the use of drugs unless prescribed by a licensed physician. And, yes, that includes Peter: believe it or not, he was not on drugs at any time when he sang during his webcasts. Good point: maybe we should put him on some kind of drug…. |
So what is it in Windows Server 2003 that makes it so special, at least when it comes to disk defragmenting? Well, in Windows Server 2003 the Win32_Volume class gained two new methods—DefragAnalysis and Defrag—that replicate the functionality of the Disk Defragmenter utility. These were two very nice features to add to the operating system, but keep in mind that these methods can be used only to run a report on or to defragment a Windows Server 2003 computer. You can sit at your Windows XP workstation, connect to a Windows 2003 machine and then defragment that Windows 2003 computer. However, you can’t do the opposite; you can’t sit at the Windows 2003 machine and defragment a Windows XP or Windows 2000 computer. WMI just doesn’t work that way.
But as long as you have Windows Server 2003 computers you can run defragmentation reports and defrag hard disks remotely and programmatically. Let’s take a closer look at what’s involved in carrying out each of these tasks.
As you probably guessed, the DefragAnalysis method is used to run a defragmentation analysis report. This is actually an interesting little method, or at least as interesting as any WMI method can be. DefragAnalysis returns two “out” parameters: a Boolean value (True or False) indicating whether a volume should be defragged, and an embedded instance of the Win32_DefragAnalysis class. This embedded class contains the same properties (and values) that you find when you run a defrag analysis report in the Disk Defragmenter utility. For example, this graphic maps a few of the Win32_DefragAnalysis properties to some of the same properties and values found in the Disk Defragmenter Analysis Report:

For a full-size version of this graphic, click here.
As a matter of fact, the Win32_DefragAnalysis class includes all the properties found in the following table, which happen to correspond very nicely to all the properties found in the Disk Defragmenter Analysis Report:
Property | Description |
AverageFileSize | Average size of the files on a volume, in bytes. |
AverageFragmentsPerFile | Average number of fragments per file on a volume. |
ClusterSize | Size of the file system allocation unit, in bytes. |
ExcessFolderFragments | Number of excess folder fragments on a volume. |
FilePercentFragmentation | Percentage of files on a volume that are fragmented. |
FragmentedFolders | Number of fragmented folders (sub-directories) on a volume. |
FreeSpace | Number of bytes currently available for use on a volume. |
FreeSpacePercent | Percent of volume that is free space. |
FreeSpacePercentFragmentation | Percentage of free space on a volume that is fragmented. |
MFTPercentInUse | Percentage of the Master File Table that is in use. |
MFTRecordCount | Number of records in the Master File Table on a volume. |
PageFileSize | Size of a page file on a volume, in bytes. If there is no page file on a volume, this property is NULL. |
TotalExcessFragments | Number of excess file fragments on a volume. |
TotalFiles | Number of files on a volume. |
TotalFolders | Number of folders (sub-directories) on a volume. |
TotalFragmentedFiles | Number of fragmented files on a volume. |
TotalMFTFragments | Number of Master File Table fragments on a volume. |
TotalMFTSize | Size of the Master File Table on a volume, in bytes. |
TotalPageFileFragments | Number of fragments for a page file. If there is no page file on a volume, this property is NULL. |
TotalPercentFragmentation | Percent of volume that is fragmented. |
UsedSpace | Number of bytes currently used on a volume. |
VolumeName | Name of the volume for which this report is generated. This property can be the drive letter, mount point or the volume GUID name. |
VolumeSize | Total size of a volume, in bytes. |
As we noted, Win32_DefragAnalysis is a somewhat unusual class: for one thing, you can’t directly query the class. This code will not return any data:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_DefragAnalysis")
Instead of working directly with Win32_DefragAnalysis you use ExecQuery to retrieve a collection from the Win32_Volume class. For each item (volume) in that collection you then call the DefragAnalysis method, which returns an instance of the Win32_DefragAnalysis class. In other words, you use code similar to this:
errResult = objVolume.DefragAnalysis(blnRecommended, objReport)
DefragAnalysis will return three things to you. First, you’ll get back the standard return code; in this script we stash the return code in the variable errResult. This tells us whether or not the method succeeded. If errResult is 0, DefragAnalysis succeeded; if errResult is anything but 0 then the method failed and a report could not be generated.
In addition to the standard return code, DefragAnalysis also returns two out parameters. (In case you’re wondering, an out parameter is any parameter a method brings back to you. By contrast, an “in” parameter is any parameter you supply to the method; for example, a file name passed to a Copy method or a Delete method is an example of an in parameter.) The first out parameter is a Boolean value that returns True if the volume needs to be defragged and False if the volume does not need to be defragged; in our script we store the value of this out parameter in the variable blnRecommended. (When dealing with out parameters you supply the variable names, and you can use any valid variable name you want. We could just as easily have named this variable x, y, or z.)
In addition, an instance of the Win32_DefragAnalysis class is returned as our second out parameter; in our script we use an object reference named objReport to refer to this instance. This is important to know because, when reporting back property values, we need to use this object reference; that’s because the defrag analysis properties—such as FilePercentFragmentation—belong to the Win32_DefragAnalysis class rather than Win32_Volume. Because of that we’ll have lines of code similar to this:
Wscript.Echo "File fragmentation: " & objReport.FilePercentFragmentation
You know, you’re right: this might be a little clearer if we show you the script. Here’s a sample script that returns a defrag analysis report for all the volumes on a computer. In this script we connect to the Win32_Volume class and then call the DefragAnalysis method for each volume found in the collection. We then check the return value; if it’s 0 that means no error occurred and we then go through additional code to list the information found in the analysis report. If the return code is something other than 0 we simply note that an error occurred.
Assuming that DefragAnalysis completed successfully we check the value of blnRecommended and indicate whether or not the volume needs to be defragged. We then report back all the individual property values for the Win32_DefragAnalysis class. And then we loop around and do the same thing for any other volumes in the collection.
Here’s what the script looks like:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colVolumes = objWMIService.ExecQuery("Select * from Win32_Volume")
For Each objVolume in colVolumes
errResult = objVolume.DefragAnalysis(blnRecommended, objReport)
If errResult = 0 Then
Wscript.Echo "Volume name: " & objVolume.Name
If blnRecommended Then
Wscript.Echo "You should defragment this volume."
Else
Wscript.Echo "You do not need to defragment this volume."
End If
Wscript.Echo
Wscript.Echo "Volume size: " & objReport.VolumeSize
Wscript.Echo "Cluster size: " & objReport.ClusterSize
Wscript.Echo "Used space: " & objReport.UsedSpace
Wscript.Echo "Free space: " & objReport.FreeSpace
Wscript.Echo "Percent free space: " & objReport.FreeSpacePercent
Wscript.Echo
Wscript.Echo "Total fragmentation: " & _
objReport.TotalPercentFragmentation
Wscript.Echo "File fragmentation: " & _
objReport.FilePercentFragmentation
Wscript.Echo "Free space fragmentation: " & _
objReport.FreeSpacePercentFragmentation
Wscript.Echo
Wscript.Echo "Total files: " & objReport.TotalFiles
Wscript.Echo "Average file size: " & objReport.AverageFileSize
Wscript.Echo "Total fragmented files: " & _
objReport.TotalFragmentedFiles
Wscript.Echo "Total excess fragments: " & _
objReport.TotalExcessFragments
Wscript.Echo "Average fragments per file: " & _
objReport.AverageFragmentsPerFile
Wscript.Echo
Wscript.Echo "Page file size: " & objReport.PageFileSize
Wscript.Echo "Total fragments: " & _
objReport.TotalPageFileFragments
Wscript.Echo
Wscript.Echo "Total folders: " & objReport.TotalFolders
Wscript.Echo "Fragmented folders: " & objReport.FragmentedFolders
Wscript.Echo "Excess folder fragments: " & _
objReport.ExcessFolderFragments
Wscript.Echo
Wscript.Echo "Total MFT size: " & objReport.TotalMFTSize
Wscript.Echo "MFT record count: " & objReport.MFTRecordCount
Wscript.Echo "Percent MFT in use: " & objReport.MFTPercentInUse
Wscript.Echo "Total MFT fragments: " & objReport.TotalMFTFragments
Wscript.Echo
Else
Wscript.Echo objVolume.Name & " could not be analyzed."
Wscript.Echo "Error number " & errResult & " occurred."
Wscript.Echo
End If
Next
See? It’s a bit unusual; after all, very few WMI scripts return another WMI class as an out parameter. If you take a closer look however, you’ll see that the code is really no more complicated than the code used in any other WMI script.
Now that’s pretty cool; after all, it’s extremely useful to be able to programmatically generate a defrag analysis. But what happens if the analysis says we need to defrag a volume? How do we actually defrag a drive using a script? Can we defrag a drive using a script?
We thought you’d never ask.
It’s actually pretty easy to defrag a volume using WMI: all you have to do is connect to the volume in question and then call the Defrag method. For example, here’s a script that defrags volume C: on a computer and then reports back the success (or failure) of the operation:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colVolumes = objWMIService.ExecQuery _
("Select * from Win32_Volume Where Name = 'C:\\'")
For Each objVolume in colVolumes
Wscript.Echo "Please wait while drive " & objVolume.Name & " is defragged."
Wscript.Echo
errResult = objVolume.Defrag()
If errResult = 0 Then
Wscript.Echo "Drive " & objVolume.Name & " successfully defragged."
Else
Wscript.Echo "Drive " & objVolume.Name & " could not be defragged."
Wscript.Echo "Error number " & errResult & " occurred."
Err.Clear
End If
Next
This is actually a very simple little script; in fact, if we took out the error handling it would be only a few lines long. We begin by connecting to the WMI service on the local computer (though we could just as easily connect to—and thus defrag—a remote computer). After the connection is made we bind to drive C on the computer using this line of code:
Set colVolumes = objWMIService.ExecQuery _
("Select * from Win32_Volume Where Name = 'C:\\'")
And no, that’s not a typo: the value for the Name property really must be passed as C:\\. That’s because the \ is a reserved character in WMI; that means we have to “escape” it (by using a second \) any time that character appears in a Where clause.
Note: What if we wanted to defrag all the volumes on the computer? No problem; all we have to do is remove the Where clause that limits the returned data to drive C:
Set colVolumes = objWMIService.ExecQuery("Select * from Win32_Volume")
|
As usual, the ExecQuery method returns a collection of drives meeting our query criteria. In this sample script, that means all the volumes with the Name C:\\. We then loop through the collection of volumes and do the following:
| • | Echo a message stating that the drive is being defragged…and asking the user to be patient. Depending on the size of the drive and the amount of defragmentation required, it can easily take an hour or more to defrag a disk drive. However, defragging a drive using a script takes no longer than defragging a drive using the Disk Defragmenter utility. In fact, WMI actually uses the same defragmenting engine used by the Disk Defragmenter utility. |
| • | Call the Defrag method to kick off the defragmenting process on the volume. |
| • | When defragmenting is complete check the return value (errResult) to determine whether or not the disk was successfully defragged. If errResult equals 0 that means no error occurred and we can assume the disk was successfully defragged. If errResult is anything but 0, something went wrong; thus we echo both a message to that effect as well as the return value itself. |
You’re right: it’s useful to know that the defragmenting process failed, but what good does it do us to have a number like 4 echoed back to the screen? Actually it does us quite a bit of good, as long as you know what these return values mean:
Return Code | Description |
0 | Success |
1 | Access denied |
2 | Not supported |
3 | Volume dirty bit is set |
4 | Not enough free space |
5 | Corrupt Master File Table detected |
6 | Call cancelled |
7 | Call cancellation request too late |
8 | Defrag engine is already running |
9 | Unable to connect to defrag engine |
10 | Defrag engine error |
11 | Unknown error |
As you can see, if our script fails and we get back a return value of 4 that means there is not enough free disk space to defrag the volume.
Important note. Take a look at return value 8: as that implies, this script will fail if the Defrag engine is already running (either because it was started from another script, from the command line, or from the GUI). If you want to get fancy you could easily add a script that checks for the presence of the Dfrgntfs.exe process:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colProcesses = objWMIService.ExecQuery _
("Select * from Win32_Process Where Name = ' Dfrgntfs.exe'")
If colProcesses.Count = 0 Then
Wscript.Echo " Dfrgntfs.exe is not running."
Else
Wscript.Echo " Dfrgntfs.exe is running."
End If
We’ll admit it: maybe we took a little longer to add automated disk defragmenting to Windows than we should have. (Incidentally, there’s also a command line tool—Defrag.exe—that can be used to defragment volumes in Windows Server 2003. However, this tool works only on the local computer.) But now that this capability has been added to Windows there’s no reason not to use it. And now that you can defragment disks remotely and programmatically, well, there’s no reason to use Cillosybarexium either. Throw that stuff away!
Or send it to Peter right before his next webcast.