Bakkenalia

Extracts from Torgeir Bakken's posts

Torgeir does not like the idea of keeping up a web page.  (I wouldn't  either, he does too much).

I keep collecting his code from posts in various Microsoft forums since he manages to wrap up some useful problem solutions, and I've finally decided to post the ones I use frequently here so I don't need to do a complete scan of my script collection or heavy Google browsing when I need to use one.


Automated Update Installations

The free Microsoft Software Services (SUS), Win2k and WinXP only:
http://www.microsoft.com/windows2000/windowsupdate/sus/default.asp


With SUS, there are a couple of downsides though...

1)
If the users local account on the client computer is a non-admin (a.k.a. not
members of the Administrators group), you cannot prevent SUS rebooting the
client PC when the patches have been installed. After  a reboot notice and a 5
minutes pause, the computer just reboots. As a non-admin, there is no way to
supress it.

2)
The client can only check for updates once a day, if the client misses the
update check "time window" (a.k.a. it was offline/powered off), it will
"reschedule" the check to next day.

3)
The SUS service needs to run on a Windows 2000 Server (non DC) with IIS
installed.


There is a separate newsgroup for SUS as well:
microsoft.public.softwareupdatesvcs
---------------------------------------------------------------
Set oShell = CreateObject("WScript.Shell")

sCmd = "C:\q323759.exe /q:a /r:n"
oShell.Run sCmd, 1, True


Note that the user running the logon script needs to be a local administrator.


If you want to do a remote installation that is unattended (no user input) and
single file install file based, you can use PsExec.exe in the free PsTools suite
from Sysinternals for this (will work on Win NT 4.0, Win2k and WinXP).

http://www.sysinternals.com/ntw2k/freeware/pstools.shtml

To install e.g. q323759.exe remotely, do like this:

psexec.exe \\some_computer  -c C:\q323759.exe /q:a /r:n

The file "behind" -c must exist on the computer running PsExec.
PsExec will copy the specified program to the remote system for execution and
run it with the additional switches. After the install, the file q323759.exe
will be deleted from the remote computer.

If not domain, username and password can be supplied:
psexec.exe \\some_computer -u user -p pwd -c C:\q323759.exe /q:a /r:n


See here for a complete VBScript example that installs SW on a list of computers

in a text file using PsExec:

From: Torgeir Bakken (Torgeir.Bakken-spam@hydro.com)
Subject: Re: Auto-Installing WMI
Newsgroups: microsoft.public.win32.programmer.wmi
Date: 2002-08-12 20:04:56 PST
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=3D58758B.3139B90C%40hydro.com

[Local copy of this script - AKA]


Lock Workstation Icon In QuickLaunch Tray

' Script that adds a padlock icon in Quick Launch Tray
' that locks the workstation (Win2k/WinXP)

Set oShell = CreateObject("WScript.Shell")

sCurrUsrPath = oShell.ExpandEnvironmentStrings("%UserProfile%")
Set oShortCut = oShell.CreateShortcut(sCurrUsrPath _
  & "\Application Data\Microsoft\Internet Explorer\" _
  & "Quick Launch\Lock Workstation2.lnk")

oShortCut.TargetPath = "rundll32.exe"
oShortCut.Arguments = "user32.dll,LockWorkStation"
oShortCut.IconLocation = "shell32.dll,47"
oShortCut.Save

WMI Uptime Script

Set oOS = GetObject("winmgmts:").InstancesOf("Win32_OperatingSystem")
For Each obj in oOS
  ' if WinXP, use SWbemDateTime for date/time formatting!
  sLastBoot = ConvWbemTime(obj.LastBootUpTime)
  sNow = ConvWbemTime(obj.LocalDateTime)
'	uptime = DateDiff("s",CDate(sLastBoot),CDate(sNow))
 uptime = DateDiff("s",obj.LastBootUpTime,obj.LocalDateTime)
Next

Function ConvWbemTime(IntervalFormat)
  Dim sYear, sMonth, sDay, sHour, sMinutes, sSeconds
  sYear = mid(IntervalFormat, 1, 4)
  sMonth = mid(IntervalFormat, 5, 2)
  sDay = mid(IntervalFormat, 7, 2)
  sHour = mid(IntervalFormat, 9, 2)
  sMinutes = mid(IntervalFormat, 11, 2)
  sSeconds = mid(IntervalFormat, 13, 2)

  ' Returning format yyyy-mm-dd hh:mm:ss
  ConvWbemTime = sYear & "-" & sMonth & "-" & sDay & " " _
               & sHour & ":" & sMinutes & ":" & sSeconds
End Function

WMI CPU ID

wscript.echo CpuID

Function CpuID()
  ' Obtaining CPU identification using WMI
  ' Script author: Torgeir Bakken

  ' Function returns a number of type integer:
  ' 0  ==>  Unknown CPU
  ' 1  ==>  Pentium or Pentium MMX
  ' 2  ==>  Pentium Pro/II or Celeron
  ' 3  ==>  Pentium III
  ' 4  ==>  Pentium 4

  ' based on this table:
  ' Family   Model               Type
  ' 5        <  4                Pentium
  ' 5        >= 4                Pentium MMX
  ' 6        <  3                Pentium Pro
  ' 6        >= 3 < 5            Pentium II
  ' 6        == 5                Pentium II or Celeron
  ' 6        == 6                Celeron
  ' 6        >= 7                Pentium III
  '15        >= 0                Pentium 4

  ' Family 5 and 6 identification based on information from here:
  ' ftp://download.intel.com/support/processors/procid/24161812.pdf

  Dim oWMI, oCpu, sCpuDescr, aCpuDescr, i, iFamily, iModel, iVersion

  Set oWMI = GetObject("winmgmts:")
  For Each oCpu in oWMI.InstancesOf("Win32_Processor")
    sCpuDescr = oCpu.Description
  Next

  aCpuDescr = Split(sCpuDescr)
  For i = 0 to Ubound(aCpuDescr)
    If LCase(aCpuDescr(i)) = "family" Then
      iFamily = CInt(aCpuDescr(i+1))
    End If
    If LCase(aCpuDescr(i)) = "model" Then
      iModel = CInt(aCpuDescr(i+1))
    End If
  Next

  iVersion = (iFamily * 100) + iModel

  Select Case True
    Case iFamily = 5
      ' Pentium or Pentium MMX
      CpuID = 1

    Case iVersion < 607
      ' Pentium Pro/II or Celeron
      CpuID = 2

    Case iFamily = 6
      ' Pentium III
      CpuID = 3

    Case iFamily = 15
      ' Pentium 4
      CpuID = 4

    Case Else
      ' Unknown CPU
      CpuID = 0
  End Select
End Function

Installed Applications Via Registry Enumeration


WScript.Echo InstalledApplications(".")

Function InstalledApplications(node)
 Const HKLM = &H80000002 'HKEY_LOCAL_MACHINE
 Set oRegistry = GetObject("winmgmts://" _
  & node & "/root/default:StdRegProv")
 sBaseKey = _
  "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
 iRC = oRegistry.EnumKey(HKLM, sBaseKey, arSubKeys)

 For Each sKey In arSubKeys

  iRC = oRegistry.GetStringValue( _
   HKLM, sBaseKey & sKey, "DisplayName", sValue)
  If iRC <> 0 Then
   oRegistry.GetStringValue _
    HKLM, sBaseKey & sKey, "QuietDisplayName", sValue
  End If
  If sValue <> "" Then
   InstalledApplications = _
    InstalledApplications & sValue & vbCrLf
  End If
 Next
End Function

Show Network Adapter States

Set oNicStatus = _
GetObject("winmgmts:root\wmi").InstancesOf("MSNdis_MediaConnectStatus")

For Each oNic In oNicStatus

  If oNic.NdisMediaConnectStatus = 0 Then
    sNicStatus = "connected"
  Elseif oNic.NdisMediaConnectStatus = 1 Then
    sNicStatus = "disconnected"
  Else
    sNicStatus = "unknown"
  End If

  WScript.Echo "Status of " & oNic.InstanceName & ": " & sNicStatus

Next

Set An Environment Variable Via WMI

From a Google post.

Note: this works great for modifying a variable for all Terminal Server sessions as well.  The example below creates a variable named %RouterStatus% with the value of UP in the SYSTEM  environment on a server named Mort.  - AKA

SetWmiEnvVar "MORT", "<SYSTEM>", "RouterStatus", "UP"

Sub SetWmiEnvVar( Host, sContext, sVarName, sValue)
 ' Sets an environment variable on an arbitrary host via WMI
 ' NOTE: This will of course not affect currently running
 ' processes.
 ' For a systemwide value, use <SYSTEM> as context
 ' for default user, use <DEFAULT>
 ' otherwis,e use the specific user's name
 ' Get the class object itself
 Dim EnvClass, EnvVarInst
 Set EnvClass = GetObject("WinMgmts://" & Host _
  & "/root/cimv2:Win32_Environment")
 ' Make a new instance of that class
 Set EnvVarInst = EnvClass.SpawnInstance_
 ' File in the key props and props of interest on that instance
 EnvVarInst.UserName = sContext
 EnvVarInst.Name = sVarName
 EnvVarInst.VariableValue = sValue
 ' Write the new instance in to WMI
 EnvVarInst.Put_
End Sub

Code Reuse Techniques

You can call another VBScript using the Run method. The downside is that they
cannot share code and variables (variables can of course be "transferred" using
command line parameters or saving then in registry/in a file).

Here is an example on how to run another script and pass it a parameter on the
command line:

Set WSHShell = CreateObject("WScript.Shell")
WSHShell.Run "wscript c:\Test.vbs param1", , True

In the Test.vbs file, you access the command line parameters with the
WScript.Arguments object:

Set oArgs = WScript.Arguments
For i = 0 to oArgs.Count - 1
   WScript.Echo oArgs(i)
Next



If you want to share code and/or variables, there are several other ways of
doing this. The different methods all have their pros and cons. Here is a quick
overview:


1)
Read the second script into a textstream and run ExecuteGlobal or Execute on it.

Pros:

Can share all variables, functions and subs.

Easy to implement.

Cons:
If you have an error in the included script, you will not get the line number
where the error arose.
Potential for namespace collisions (variable/procedure names) since all script
elements share the same namespace.


2)
Use a Windows Script Host File (.WSF) file and include scripts with script tag.

Pros:
Can share all variables, functions and subs.
Easy to run both VBScript and JavaScript code together.
If you have an error in a included script, you will get the line number where
the error arose.
Easy to implement.

Cons:
Potential for namespace collisions (variable/procedure names) since all script
elements share the same namespace.


3)
Use a Windows Script Component (.WSC, a COM component written in script)

Pros
You will get a COM interface to your script library (that also can be used by
other programs)
Method/property drop down list and syntax help in editors that supports typelibs

No namespace collisions (variable/procedure names) since the script elements
does not share the same namespace.

Ideal for development environment with more than one developer

Cons
The most "complicated" solution

***********************************************************


Here are some more details for each method:

___________________________________________________________

1)
Read the second script into a textstream and run ExecuteGlobal or Execute on it.

Load the 2. script into the 1. script at runtime (on the fly) and execute it
with ExecuteGlobal. They can then also share variables, functions etc. The sub
below does this loading:

Sub Include (sInstFile)
    Dim oFSO, f, s
    Set oFSO = CreateObject("Scripting.FileSystemObject")
    Set f = oFSO.OpenTextFile (sInstFile)
    s = f.ReadAll
    f.Close
    ExecuteGlobal s
End Sub
___________________________________________________________


2)
Use a Windows Script Host File (.WSF) file and include scripts with script tag.

Using Windows Script Files (.wsf)
http://msdn.microsoft.com/library/en-us/script56/html/wsAdvantagesOfWs.asp

An example:
<?xml version="1.0" ?>
<package>
<job>
' this will run "file1.vbs"
<script language="VBScript" src="file1.vbs" />
' this will run "file2.vbs"
<script language="VBScript" src="file2.vbs" />
<script language="VBScript">
'
' Some script code here if you want ...
'
' You can use variables, subs and functions
' from the included files.
'
</script>
</job>
</package>
___________________________________________________________


3)
Use a Windows Script Component (.WSC).

Use a Windows Script Component (.WSC) to get a COM interface for your VBScript
"library".

You can e.g. use Windows Script Component Wizard to create one. Take a look here
for more info:
http://msdn.microsoft.com/downloads/sample.asp?url=/MSDN-FILES/027/001/788/msdncompositedoc.xml

Look up the Windows Script Components chapter in the docs.:

WSH 5.6 documentation download:
http://msdn.microsoft.com/downloads/sample.asp?url=/MSDN-FILES/027/001/728/msdncompositedoc.xml



A simple WSC file example:

From: Michael Harris \(MVP\) (mikhar-spam@mvps.org)
Subject: Re: WSC and ProgID Versions
Newsgroups: microsoft.public.scripting.scriptlets
Date: 2001-10-01 07:04:22 PST
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=u%24yWwGoSBHA.2076%40tkmsftngp03



If you want to create a WSC with Self-Registering Typelib, take a look at this
article:

From: Torgeir Bakken (Torgeir.Bakken-spam@hydro.com)
Subject: Re: Creating a Self-Registering Typelib for a WSC
Newsgroups: microsoft.public.scripting.wsh
Date: 2002-06-09 09:40:06 PST
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=3D03842F.5F6BEC16%40hydro.com





More about WSF/WSC in the thread below:

Subject: Implimenting a common.wsf file
http://groups.google.com/groups?th=cb544426cdee2bdc


Also, if you go the WSC route, be sure not to have this
line in the WSC file if you create a self-registering
typelib:

implements id="Behavior" type="Behavior"

If you have it, the Write method for Scriptlet.TypeLib
will fail.


--
torgeir
Microsoft MVP Scripting and WMI, Porsgrunn Norway
Administration scripting examples and a ONLINE version of the 1328 page
Scripting Guide: http://www.microsoft.com/technet/scriptcenter