Introduction
This is an inventory application that scans computers in your network. You can list computers of particular types (i.e., SQL servers), and you can extract computer information from two different sources: the registry or Windows Management Instrumentation (WMI).
Run netinventory.exe -? to see how it is used. Here is an example:
The command above will produce a file, NetInventory.xml - 3.3 KB, containing the operating system name and the service pack for all SQL servers in the network.
Background
I built the tool to solve recurring problems such as:
- List SQL servers in the network (you can use Server Management Objects (SMO), but that is painfully slow)
- List primary domain controllers
- List operating systems for computers in the network
- List computer types and serial number
- List installed software for computers in the network
In order to get this kind of inventory information, I needed MS System Center Configuration Manager, or some other inventory system or asset management system. This tool requires no installation, and does the job (almost) as well.
Using the code
There are five basic problems to solve in this application:
- List computers in the network
- Control the timeout of the network connection to a computer that doesn't respond
- Get registry information from a remote computer
- Get WMI information from a remote computer
- Manage command line switches
Listing computers in the network is described in another article. The class NetWorkBrowser
, written by Sacha barber, encapsulates the use of the Win32 API function Netapi32.dll: NetServerEnum
. I used Sacha's class as is, except for adding the enumeration ServerType
. To list SQL servers in your network, simply call the function getNetworkComputers()
and specify the server type:
NetworkBrowser nb = new NetworkBrowser();
ArrayList arr = nb.getNetworkComputers(ServerType.SV_TYPE_SQLSERVER);
Console.WriteLine("{0} : {1}", serverType.ToString(), arr.Count);
foreach (string name in arr)
{
Console.WriteLine(name);
}
Console.WriteLine();
One of the issues that need to be addressed when connecting to computers in your network is dealing with computers that don't respond. When connecting to the registry or WMI on a remote computer, you don't have control over the timeout. If a number of computers don't respond and you have to wait, say 30 seconds for each connection timeout, scanning the network will take forever. The class TimeoutOperation
is designed to run operations impatiently. To use the class, inherit it and override DoWork()
with the operation logic. On timeout, isCancelled
will be set to true
and the main thread will resume execution:
class WaitAMinute : TimeoutOperation
{
public int counter = 0;
public WaitAMinute(int timeoutMiliseconds) : base(timeoutMiliseconds)
{
}
protected override void DoWork()
{
for (counter = 0; counter < 60; counter++)
{
if (isCancelled)
break;
Thread.Sleep(1000);
}
}
}
WaitAMinute w = new WaitAMinute(3000);
w.Start();
Console.WriteLine("{0}", w.counter);
Getting registry information from a remote computer is encapsulated in the class RegistryReader
. To use the class, call the static method GetRegistryKey()
. In the example below, the method is used to get all the values from the registry key HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion on the machine with the hostname MYPC. The timeout is set to 500 milliseconds. If the machine does not respond within the timeout period, an ApplicationException
will be thrown.
ArrayList arr = new ArrayList();
try
{
RegistryReader.GetRegistryKey("MYPC",
@"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion",
null, 500, ref arr);
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
foreach (IDictionary dict in arr)
{
foreach (DictionaryEntry de in dict)
{
Console.WriteLine("{0} = {1}", de.Key, de.Value);
}
}
Getting WMI information from a remote computer is encapsulated in the class WMIQuery
. To use the class, call the static method Execute()
. You must specify a valid query using the WQL syntax, a valid WMI namespace, hostname of the remote computer, and a connection timeout. The timeout works the same way as in the class RegistryReader
as described above. This example will get the BIOS name and the serial number from the machine with the hostname MYPC:
ArrayList arr = null;
try
{
arr = WMIQuery.Execute("select Caption, SerialNumber from Win32_BIOS",
@"root\CIMV2", "MYPC", 500);
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
foreach (IDictionary dict in arr)
{
foreach (DictionaryEntry de in dict)
{
Console.WriteLine("{0} = {1}", de.Key, de.Value);
}
}
To manage command line switches, I used the classes Parser
and CommandLineSwitchAttribute
written by Ray Hayes. These are described in another article. I made one modification to the class Parser
(actually to SwitchRecord.BuildPattern()
, to be precise) that allowed enumerated values as integers, because I wanted to be able to combine ServerType
values.
Points of interest
TimeoutOperation
may not scale well up to thousands of computers since the number of running threads waiting to connect to remote computers could be growing depending on your network configuration and the specified timeout value. I recommend setting the timeout to 0 when scanning large networks, letting the program run overnight, setting the timeout to the default value of 500 milliseconds when scanning a limited number of workstations, and getting the result set immediately.
History
- Version 1.0.0.0 - November 2007.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.