Very similarily to determining the current user’s username, it can be very useful to be able to determine the current PC’s name. This could be to:
- include with your error loging
- use to lock down your application to running on only certain PCs
- use as a configuration parameter (if PCA then use the folder, if PCB use that folder, ….)
- etc.
Today, I thought I’d quickly cover a couple way this can be accomplished:
Environ
One of the easiest way to determine the computer name is to simply check the computer’s computername environment (in DOS you would do ‘echo %computername%’) variable by using the Environ function.
Environ("computername")
The problem with using Environ() is that it is easy to spoof the environment variable thus bypassing any checks you might put in place. So I don’t recommend this approach.
WScript
The second approach is to use WScript. Now, with Wscript there are a couple approaches that can be used.
WScript Shell Object
The first, just like the Environ function approach, reads the environment variable by utilizing the Wscript.Shell ExpandEnvironmentString Method and thus is also not a reliable solution.
CreateObject("WScript.Shell").ExpandEnvironmentStrings("%COMPUTERNAME%")
WScript Network Object
The better approach is to use the WScript.Network object. Unlike environment variables, this can not easily be spoofed. The code to do so would look like:
CreateObject("WScript.Network").ComputerName
WMI
Another approach would be to use WMI query any number of Win32 classes that have the PC name, I have choosen to use the Win32_ComputerSystem class in my example below. Thus, it can be done by doing something along the lines of:
'---------------------------------------------------------------------------------------
' Procedure : WMI_GetComputerName
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Return the Computer Name
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sHost : host computer to query, omit for the local PC
'
' Usage:
' ~~~~~~
' ? WMI_GetComputerName
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2018-04-26 Initial Release (part of WMI_GetUsernames creation)
' 2 2020-03-18 Restricted WMI Query to only return Name column
' instead of wildcard (*)
'---------------------------------------------------------------------------------------
Public Function WMI_GetComputerName(Optional sHost As String = ".") As String
'Ref: https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem
On Error GoTo Error_Handler
Dim oWMI As Object 'WMI object to query about the PC's OS
Dim sWMIQuery As String 'WMI Query
Dim oComputerSystems As Object
Dim oComputerSystem As Object
Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & sHost & "\root\cimv2")
sWMIQuery = "SELECT Name " & _
"FROM Win32_ComputerSystem"
Set oComputerSystems = oWMI.ExecQuery(sWMIQuery)
For Each oComputerSystem In oComputerSystems
WMI_GetComputerName = oComputerSystem.Name
If WMI_GetComputerName <> "" Then Exit For
Next oComputerSystem
Error_Handler_Exit:
On Error Resume Next
Set oComputerSystem = Nothing
Set oComputerSystems = Nothing
Set oWMI = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: WMI_GetComputerName" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
API
The last possible solution would be to use a Windows API such as:
#If VBA7 And Win64 Then
'x64 Declarations
Declare PtrSafe Function GetComputerName Lib "kernel32" Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#Else
'x32 Declaration
Declare Function GetComputerName Lib "kernel32" Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#End If
Function ComputerName() As String
Dim sBuff As String * 255
Dim lBuffLen As Long
Dim lResult As Long
lBuffLen = 255
lResult = GetComputerName(sBuff, lBuffLen)
If lBuffLen > 0 Then
ComputerName = Left(sBuff, lBuffLen)
End If
End Function
Just like for VBA – Recognize User, Get Username, there are a number of ways to get the information you seek, but not all are reliable/secure, so choose carefully depending on your need!
Also, some will state that APIs are the efficient way to do things. I say Bah Humbug to them. When I tested, the difference was measured in microseconds, so negligible and when I look at the simplicity of the CreateObject(“WScript.Network”), 1 line, no API declarations to worry about or adapt for bitness… and you don’t call this type of thing over and over. You call it once at the startup, set a global variable or TempVars and use it afterwards. So all that to say that such statements are merely fearmongering.
If i want to take the Computer name behind remote connection instead of Terminal Server Name how can i do that?
Private Declare Sub CopyMemory Lib “kernel32” Alias “RtlMoveMemory” (Destination As Any, ByVal Source As Long, ByVal Length As Long)
Private Declare Sub WTSFreeMemory Lib “wtsapi32.dll” (ByVal pMemory As Long)
Private Declare Function WTSQuerySessionInformation Lib “wtsapi32.dll” Alias “WTSQuerySessionInformationW” (ByVal hServer As Long, ByVal sessionId As Long, ByVal wtsInfoClass As Long, ByRef pBuffer As Long, ByRef dwSize As Long) As Long
Public Function GetClientName() As String
Dim pBuffer As Long
Dim dwSize As Long
If WTSQuerySessionInformation(0, -1, 10, pBuffer, dwSize) Then
Dim clientName As String
clientName = String(dwSize, 0)
CopyMemory ByVal StrPtr(clientName), ByVal pBuffer, dwSize
WTSFreeMemory pBuffer
GetClientName = clientName
End If
End Function
This one Returns the Client Name behind the Remote Connection but it’s not returns the Computer Name if the Application is open in Local Pc.
Is there a way to figure if the App is open in Remote Connection Or Local so i can choose between this Function “WMI_GetComputerName” or this Function “GetClientName”??
Thanks in advance for your help.