Update 2015-07-03:

So I got two comments from Paul Wiegmans. These were mainly that the functions delivered different hostname vs netbios version. And that I had missed a good function. And I’m so used to that since windows limits the Netbios computername to 16 characters, where the last one is a reserved character so I forgot to test for longer versions.

Updated blogpost with all the glory:

Last weekend my company and a couple of customers had an event in the Swedish village of Åre. To cut to the chase we had both speakers from Knowledge factory, TrueSec and Microsoft at the event. And during Bruce Payette’s presentation I noticed that he used hostname instead of $env:computername as I and other use. So I talked a little with him about it, and decided to write a blog entry about it. So we discussed a couple of options mostly using $env, the .NET method and hostname.exe. I also decided to test the speed of a couple of ways. Lets start with the speeds and go from there.

What are the main players in this drama then?:

  • The environment variable
  • hostname process way
  • WMI/CIM way
  • .NET MachineName
  • Kernel32 function call
  • .NET GetHostName function

The environment variable

The first and most simple solution for most coders are to use the $env:computername. This is a fast and efficient way of doing it. But it has its drawbacks. Every part of $env can be written over by a command in your script. But it is fast end efficient.

$env:computername

hostname process way

Windows ships with the same binary that linux systems has, just write hostname and the system will return the name of the computer. This requires the creating of a process and not just a read of information. And as you will see gives a great penalty.

hostname.exe

The way of WMI

As most things there is a way of doing it using WMI. WMI has been preinstalled since Windows 2000 and as a download prior to that. On my system there is 1107 classes in the default namespace of root\cimv2. WMI is also available when running WinPE by using the commandline tool wimc. WMI uses CIM to access the data, but since WMI the more famous.

Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty name

Way of CIM

You could also use Get-CIMInstance  to access Common Information Model, which the WMI way also does. So this is just cutting out the middleman.

(Get-CIMInstance CIM_ComputerSystem).Name

.NET MachineName

You could use .NET which is easily accessed through powershell. Infact all you need it to enclose the class within [brackets] and start calling stuff. This is the fast and good solution.

[system.environment]::MachineName

Kernel32.dll request

I wrote another post about this because it got a bit bigger than this small area. But the short story, could make the call kernel32.dll and ask for the computername. Its more complicated then needed and doesn’t improve the speed very much or at all. So I would say don’t use it.

.NET GetHostName function

This one was suggested by Paul. It uses the system.net.dns class and its gethostname function.

[System.Net.Dns]::GetHostName()

Different results

As Paul also specified, I didn’t really care enough about computernames longer than 15 characters, since the different approaches gives different results. So this is a table of what the different

Different results for different methods
Method Result
The environment variable NetBIOS computername
hostname process way Full computername
WMI/CIM way NetBIOS computername
.NET MachineName NetBIOS computername
Kernel32 function call NetBIOS computername
.NET GetHostName function Full computername

Speedtests

So I ran through each method 10000 times and wrote down the average time it took. Some ways (kernel32) is really biased against as it doesn’t remember the system.runtime between tries.

Different methods and used ticks
Method Avg Ticks
.NET MachineName 148
$Env 402
.Net GetHostName 538
Kernel32 3306
WMI 91361
hostname 93548
CIM 99398

I was planing a diagram here, but since I only had small or large values it looked bad. So use your imagination.

How to test for your self.

1..10000 | % {measure-command {$env:computername}}|Measure-Object -Average Ticks
1..10000 | % {measure-command {hostname}}|Measure-Object -Average Ticks
1..10000 | % {measure-command {(Get-WMIObject Win32_ComputerSystem).Name}}|Measure-Object -Average Ticks
1..10000 | % {measure-command {(Get-CIMInstance CIM_ComputerSystem).Name}}|Measure-Object -Average Ticks
1..10000 | % {Measure-Command {[system.environment]::MachineName}}|Measure-Object -Average Ticks
1..10000 | % {Measure-Command {Get-Kernel32ComputerName}}|Measure-Object -Average Ticks
1..10000 | % {Measure-Command {[System.Net.Dns]::GetHostName()}}|Measure-Object -Average Ticks

The conclusion

So in all future scripts I know what I want, I want to use .NET. It is simple fast and secure. That the solution is mostly built-in is a good part of my reasoning to. I would like to have a Get-ComputerName but I can simplify that part myself, atleast until we get a cmdlet from Microsoft.

Simplifying it:

Just wrapping [system.environment]::MachineName to something more elegant and easy. Function Get-VComputerName {[system.environment]::MachineName}