WMI Associators produce CORRUPT data on Vista?

(13th Feb 2008 - Appears to have been fixed in Vista SP1 RTM)

Overview

If you work on big servers with multiple SCSI devices on multiple controllers, you may want a quick way to manage and monitor them on a per-controller basis. Microsoft's WMI provides a solution, but initial tests indicate enumeration problems on Vista RTM. (It used to work on Windows 2000).

Enumerating SCSI devices, Win32_DiskDrive, Win32_PnPEntity, Win32_PnPDevice

If you want to get a list of devices attached to each SCSI controller, you enumerate instances of  Win32_SCSIController, you then use the Win32_SCSIControllerDevice association class to obtain a collection of Win32_PnPEntity classes. Each Win32_PnPEntity can then be associated with a device object (such as Win32_DiskDrive) through another association class called Win32_PnPDevice. This is where the problem occurs. Although it returns instances of classes such as Win32_DiskDrive, the data is CORRUPT. e.g. the first disk drive shows some parameters from the second disk drive, then the second disk drive shows 'null' parameters and so on.

Associators are bi-directional, raw mapping data

You can use Associators in two directions; e.g. get disk drives associated with a controller, or get controllers associated with a disk drive. You use the same association class, regardless of the direction of the association. Testing in each direction, I find that both sets of results are WRONG, but in different ways. However, if I get all instances of the association class Win32_PnPDevice, this will produce a 1:1 mapping of all associations (as strings) representing the related object paths, and Interestingly this mapping is CORRECT. If the mapping is correct, it's very odd that the data produced from the Associators_() method is wrong, since it's using the same class! In summary

InstancesOf(Win32_PnPDevice)		// WORKS
Win32_PnPEntity -> Win32_DiskDrive	// FAILS
Win32_DiskDrive -> Win32_PnPEntity	// FAILS

Disk layout of the test system (correct)

SCSI ID : DeviceID : Model

6 : \\.\PHYSICALDRIVE1 : HITACHI HUS151436VL3600 SCSI Disk Device
4 : \\.\PHYSICALDRIVE0 : SEAGATE ST373454LW SCSI Disk Device
8 : \\.\PHYSICALDRIVE2 : SEAGATE ST373454LW SCSI Disk Device

Note the disks are not in ascending order, this is normal, but could be related to why the WMI provider is getting confused?

Win32_PnPDevice 1:1 mapping (Correct)

\\WS1\root\CIMV2:Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE1"
\\WS1\root\cimv2:Win32_PnPEntity.DeviceID="SCSI\\DISK&VEN_HITACHI&PROD_HUS151436VL3600\\6&323A7644&0&000600"

\\WS1\root\CIMV2:Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE0"
\\WS1\root\cimv2:Win32_PnPEntity.DeviceID="SCSI\\DISK&VEN_SEAGATE&PROD_ST373454LW\\6&323A7644&0&000400"

\\WS1\root\CIMV2:Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE2"
\\WS1\root\cimv2:Win32_PnPEntity.DeviceID="SCSI\\DISK&VEN_SEAGATE&PROD_ST373454LW\\6&323A7644&0&000800"

Note the association class perfectly matches the true disk layout

Disk layout according to WMI Associators (Wrong)

The list below shows two-way associator mapping. All entries are WRONG. In each case, the indented line should match the one above - it's the same Disk Drive!

Win32_PnPEntity : SEAGATE ST373454LW SCSI Disk Device
 Win32_DiskDrive : \\.\PHYSICALDRIVE0 : HITACHI HUS151436VL3600 SCSI Disk Device
Win32_PnPEntity : HITACHI HUS151436VL3600 SCSI Disk Device
 Win32_DiskDrive : \\.\PHYSICALDRIVE1 : null
Win32_PnPEntity : SEAGATE ST373454LW SCSI Disk Device
 Win32_DiskDrive : \\.\PHYSICALDRIVE2 : null

Win32_DiskDrive : \\.\PHYSICALDRIVE1
 <No Associator>
Win32_DiskDrive : \\.\PHYSICALDRIVE0
 Win32_PnPEntity : HITACHI HUS151436VL3600 SCSI Disk Device : undefined
Win32_DiskDrive : \\.\PHYSICALDRIVE2
 <No Associator>

The first two lines show the model string has been mis-identified, then subsequent drives have no model string at all. In the second set of data, notice where it says <No Associator>; this is plain wrong; they should all have at least one Associator.

Script used to produce the results above

You must use CSCRIPT to run this script. Probably won't make sense unless you have a computer with a SCSI controller and at least three disk drives.

<job>
<!--
Vista - BUG in Win32_PnPDevice Association class when used with Disk Drives

Class Win32_PnPDevice {
	CIM_LogicalDevice ref SameElement; // subclass such as Win32_DiskDrive
	Win32_PnPEntity ref SystemElement;
};

InstancesOf(Win32_PnPDevice) 		// WORKS
Win32_PnPEntity -> Win32_DiskDrive	// FAILS
Win32_DiskDrive -> Win32_PnPEntity	// FAILS

The next line shows how to test in WBemTest, plug in 0,1,2 and note results
ASSOCIATORS OF {Win32_DiskDrive.DeviceID='\\.\PHYSICALDRIVE0'}

-->
<!-- script language="JScript" src="utils.js"></script -->

<script language="JScript">

var strCmp = ".";
var oSvc = GetObject("winmgmts://" + strCmp);

/*
// List all Win32_PnPDevice 1:1 relationships
var cDevices = oSvc.InstancesOf("Win32_PnPDevice");
enumDevices(cDevices);
WScript.Quit();
*/

// Get all PnPEntities who's DeviceClass is DiskDrive
var strWQL = "SELECT * FROM Win32_PnPEntity WHERE ClassGUID = '{4d36e967-e325-11ce-bfc1-08002be10318}'";
var cEntities = oSvc.ExecQuery(strWQL);
enumCollection(cEntities);
_trace("");

// Get all Disk Drives
var cDiskDrives = oSvc.InstancesOf("Win32_DiskDrive");	// can use CIM_MediaAccessDevice
enumCollection(cDiskDrives);
WScript.Quit();

function enumCollection(cCollection) {
	// Enum Collection

	//_trace("Collection contains " + cCollection.Count + " Items");
	var e = new Enumerator(cCollection);
	for (;!e.atEnd();e.moveNext()) {
		var oObject = e.item();	// may not be SWbemObject
		_trace(oObject.Path_.Class + " : " + oObject.Name);
		//getProps(oObject);
		var cAssoc = oObject.Associators_("Win32_PnPDevice");
		enumAssoc(cAssoc);
	}
}

function enumAssoc(cCollection) {
	// Enum Associators

	var nCount = cCollection.Count;
	if (nCount == 0) {
		_trace("\t<No Associator>");
		return;
	}

	//_trace("Collection contains " + cCollection.Count + " Items");
	var e = new Enumerator(cCollection);
	for (;!e.atEnd();e.moveNext()) {
		var oObject = e.item();	// may not be SWbemObject
		_trace("\t" + oObject.Path_.Class + " : " + oObject.Name + " : " + oObject.Model);
		//getProps(oObject);
	}
}

function enumDevices(cDevices) {
	// Enum Devices
	var re = /Win32_DiskDrive/;
	var e = new Enumerator(cDevices);
	for (;!e.atEnd();e.moveNext()) {
		var oObject = e.item();
		var sSameElement = oObject.SameElement;
		if (re.test(sSameElement)) {
			_trace(oObject.SameElement);
			_trace(oObject.SystemElement);
			_trace("");
		}
	}
}

function getProps(oSWbemObject) {
	// Enum all properties of the object
	// Accepts an SWbemObject
	var strClass = oSWbemObject.Path_.Class
	_trace("Properties of the " + strClass + " class:");
	var p = new Enumerator(oSWbemObject.Properties_);
	for (;!p.atEnd();p.moveNext()) {
		var oThisProp = p.item();
		var sName = oThisProp.Name;
		var oValue = oThisProp.Value;
		var sType = typeof oValue;
		_trace("\t" + sName + "\t" + sType + "\t" + oValue);
	}
}

function _trace(strText) {
	// Print a message
	WScript.Echo(strText);
}


</script>
</job>