Controlling the Order that Services start

The other day I had a problem where a new service was starting, but it seemed to delay the start of another service which I needed to be available early in the boot (Say at the GINA).

A quick google found that Services can be a part of Service Groups. The Service Groups can be ordered by using the registry editor. The Order is listed at

HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrdering

The list is in the form of a multi-string value. Services are started from top to bottom. To delay a service you can just move the Service Group it belongs to down. A little thought is required since you are moving the order that services are started. You don't want to start a service before its dependent services have all started.

To find out which group a service belongs to you can go to

HKLM\SYSTEM\CurrentControlSet\Services

Find your service and look for the Group value name. It looks like it is possible for a service to not have a group.

Using DPInst

Someday you may need to install a driver on a windows system. If you are in an enterprise environment, you may have a few requirements. First, you don't want the end-user prompted to install drivers. They may actually do it and end up installing something you don't want them to install. That brings us to a second need, a silent install. We don't want the user to be interupted. Lastly, it is important to have some flexibility on what were installing. For instance, maybe we only want to install the driver if the device is installed. OR maybe we want to pre-install a driver for later use. Whatever our case may be, DPInst is a good solution in most cases.

First things first, you can download DPInst as part of the Driver Installation Framework from Microsoft's WHDC site. You may instead want to install the full Windows Driver Kit (WDK) which will include future updates to DPInst.

You can refer to the command-line switches for more info, but here are some of the more useful switches.

/path

This switch changes the DPInst working directory. Normally DPInst will only search the directory it is ran from.

/s

Switches to silent mode. This will quietly install drivers.

/LM

Uses Legacy mode to install drivers. That allows you to install drivers that aren't signed.

/sh

Scans your hardware and installs only if the device is present and the new driver is better than what is already installed.

/se /sa /sw

These are for suppressing EULA, Add/Remove Programs Entry, and the Wizard in that order. The silent switch should take care of the first and last switch. The Add/Remove Programs Entry you may want to remove.

My five VBScript 'best practices'

I've been programming in VBScript for a while now. Over that time I've seen some things that for better or worse made me take note. I thought that I would share those, in an attempt to at worst open a dialogue on some level and at best convince some VBScripters to slightly modify their actions.


1. Misuse of On Error Resume Next


The On Error Resume Next statement can be quite useful, but all too often it finds its way to the top of a VBScript. Doing this will basically render the entire script free of errors. The statement does have its uses though. Lets say that I want to open a file for writing. Lets consider the things that could go wrong

  • No Permission to write there
  • File exists already
  • File is locked
  • Disk is full
  • User doesn't have enough disk quota space
  • Saving to Network Drive that is offline
We could spend half a day and code around each one of these items. Instead we could do the following:

On Error Resume Next

If Err Then
HandleError
Err.Clear
End If
On Error Goto 0

What happens here is that everything between the On Error Resume Next and the On Error Goto 0 statement don't raise errors. Everything before and after will still cause issues. We can test for errors by checking the Err variable. It returns true for non-zero numbers. So if we have an error code we enter the condition and handle the error. Once done handling our error we call Err.Clear. This clears out the error code so when we test it later in the code we don't get this error. The last statement simply turns error suppression back off.

Also, if you call On Error Resume Next in a function or subroutine then it will only stay in effect for that function and all calls made by that function.

I prefer the second option, just because I like knowing where error suppression starts and stops. With the second option, you can turn on error suppression in one function, call a second function and still have error suppression turned on.

My main point in all of this, is that you have to know where your script is failing. Covering up the problems will curse you sooner or later. Its ok to use error suppression, but so long as you are handling the problems when they arise.

2. Not using Option Explicit

I'd almost like to say as a general rule, you should take both this and the previous rule together and for every script that has On Error Resume Next as the first line of code, should be replaced with Option Explicit. In fact most every script should have Option Explicit as the first line.

What Option Explicit will do for you, is make sure your variables are declared. It does this, by not allowing undeclared variables in your code. So if I said

Option Explicit

i = 0

I'd get an error of "Variable is undefined 'i'"

I'd have to add

Option Explicit

dim i
i = 0

You can add multiple variables by separating them by commas

dim LastName, FirstName, BirthDate

All this will save you the hassle of debugging your code only to find out that you mistyped your variable name.

This may seem like a lot of work to do. Ultimately though, it is only really 2 lines of code. Once to turn this option on and a second to declare your variables. What you gain is completely eliminating one of the more common reasons why scripts fail.

3. Don't repeat yourself

I've seen a lot of scripts that are three or even four times longer than they need to be. This happens because they are simply copying and pasting their code and only modifying one or two parts. Whenever you find yourself in this situation, you should immediately start thinking of sub routines. Sometimes you may not notice until after you've written your script. In those cases, if you ever think that you'll need to modify this script you should go back and refactor it correctly.

Here is an example of what we can refactor.

ItemToWrite = Items(ItemOne)
WshShell.RegWrite "HKLM\Software\MyProduct\ItemOne", ItemToWrite

ItemToWrite = Items(ItemTwo)
WshShell.RegWrite "HKLM\Software\MyProduct\ItemTwo", ItemToWrite

ItemToWrite = Items(ItemThree)
WshShell.RegWrite "HKLM\Software\MyProduct\ItemThree", ItemToWrite

This gets refactored into

WriteKey "HKLM\Software\MyProduct\ItemOne",Items(ItemOne)
WriteKey "HKLM\Software\MyProduct\ItemTwo",Items(ItemTwo)
WriteKey "HKLM\Software\MyProduct\ItemThree",Items(ItemThree)

Sub WriteKey(KeyPath,ItemToWrite)
WshShell.Regwrite KeyPath,ItemToWrite
End Sub

Now we have one line per item instead of two and a single point where we are writing to the registry.

But, lets go a bit further. In this code we repeat "HKLM\Software\MyProduct\" three times. What happens if we misspelled one of those lines. Worse is if we need to change it. We now have to change three lines. Lets change this to

Const MYKEY = "HKLM\Software\MyProduct\"
WriteKey MYKEY & "ItemOne",Items(ItemOne)
WriteKey MYKEY & "ItemTwo",Items(ItemTwo)
WriteKey MYKEY & "ItemThree",Items(ItemThree)

Sub WriteKey(KeyPath,ItemToWrite)
WshShell.Regwrite KeyPath,ItemToWrite
End Sub

We did add a line, but in turn we saved our self the trouble of changing three lines whenever we change that key path.

Whenever you intend to use a string or a number several times, it is best to simply put it in a constant and simply reference it. If you need to modify the string once and then use it multiple times, I'd recommend just using a variable instead of a constant. Either way it will help you when modifying your code.

4. Not using descriptive variable names

In some of my examples, you may see me using a single letter for a variable (Such as i). In practice though this is only good in a few specific examples. Many scripts that are giving examples of some technique will often use a non-descriptive variable(s). I think this leads to individuals using non-descriptive variables in their actual code. Your script will determine your level of descriptiveness, but sometimes just saying 'Name' can be good. Othertimes you'll need "LastName'. Either is better than 'n'.

I deviate from this in one distinct way. When using a loop that has a variable to be used as the index of an array I'll likely use just 'i'. This is fairly common. Sometimes nested for loops for will i and then j. At this point you may be better off using 'Row' and 'Col'. Again, that all depends on your situation. In those situations though, you are using a variable in a single location and not interspersed throughout your code.

5. Not using Comments

I look at programming/scripting languages as a compromise in communicating your actions with the computer and with other users. Comments allow us to extend our communication with the user. By user, I don't just mean you. I also mean the next person who gets the 'joy' of reading your code.

A lot of people do believe in writing comments, but still don't get it right. A comment should be used to say why you did something, not what you did. Most mediocre scripters will be able to take your script and decipher what you did. What they won't be able to do, is figure out why you wrote the code you did.

Taking this approach can sometimes mean writing less comments than you normally would. Jeff Atwood expanded on the idea of commenting on why you did something and had some excellent suggestions on what you can do to be more descriptive with your intent.

Using Devcon

A while back I wrote about using SetupAPI functions in VB.NET. The goal was to be able to work with devices and ultimately enable and disable a device.

The code was what I would eventually use, but initially I used Devcon. Devcon is essentially the Device Manager over a command line. It is freely available. You can even get the source code from the Windows Server 2003 Driver Development Kit (DDK). It may be available in the newer Windows Driver Kit (WDK) as well.

To demonstrate the usefulness of devcon, we can look at some examples from my blog and see how they coorelate to devcon commands

Retrieving Device Setup Class Names and Getting Friendly Class Names:
We can use the following command

devcon classes

This would give us output similar to:

Listing 53 setup class(es).
WCEUSBS : Windows CE USB Devices
USB : Universal Serial Bus controllers
PnpPrinters : IEEE 1394 and SCSI printers
Dot4 : IEEE 1284.4 devices
Dot4Print : IEEE 1284.4 compatible printers
CDROM : DVD/CD-ROM drives
Computer : Computer
DiskDrive : Disk drives
Display : Display adapters
fdc : Floppy disk controllers
hdc : IDE ATA/ATAPI controllers
Keyboard : Keyboards
MEDIA : Sound, video and game controllers
Modem : Modems
Monitor : Monitors
Mouse : Mice and other pointing devices
MTD : PCMCIA and Flash memory devices
MultiFunction : Multifunction adapters
Net : Network adapters
NetClient : Network Client
NetService : Network Service
NetTrans : Network Protocol
PCMCIA : PCMCIA adapters
Ports : Ports (COM & LPT)
Printer : Printers
SCSIAdapter : SCSI and RAID controllers
System : System devices
Unknown : Other devices
FloppyDisk : Floppy disk drives
Processor : Processors
MultiPortSerial : Multi-port serial adapters
SmartCardReader : Smart card readers
VolumeSnapshot : Storage volume shadow copies
1394Debug : 1394 Debugger Device
1394 : IEEE 1394 Bus host controllers
Infrared : Infrared devices
Image : Imaging devices
TapeDrive : Tape drives
Volume : Storage volumes
Battery : Batteries
HIDClass : Human Interface Devices
61883 : 61883 Device Class
LegacyDriver : Non-Plug and Play Drivers
SDHost : Secure Digital host controllers
UsbPcCardReader : USB PC-Card Readers
Avc : AVC Device Class
Enum1394 : IEEE 1394 IP Network Enumerator
MediumChanger : Medium Changers
NtApm : NT Apm/Legacy Support
SBP2 : SBP2 IEEE 1394 Devices
Bluetooth : Bluetooth Radios
WPD : Windows Portable Devices
USB : Motorola USB Device

You'll note that we aren't receiving the Class Guid. I'm not sure if this is that important anyway.

Actually Getting to the Devices and Getting Device Names:

There are a couple of ways we can do this. The first option is to simply specify a class. That happens with the = argument

devcon find =net

PCI\VEN_14E4&DEV_169D&SUBSYS_105E147B&REV_11\4&1B41B794&0&00E0: Broadcom NetLink (TM) Gigabit Ethernet
ROOT\CNTX_VPCNETS2_MP\0000 : Linksys Wireless-G USB Network Adapter #9 - Virtual Machine Network Services Driver
ROOT\CNTX_VPCNETS2_MP\0009 : Broadcom NetLink (TM) Gigabit Ethernet - Virtual Machine Network Services Driver
ROOT\CNTX_VPCNETS2_MP\0010 : Linksys Wireless-G USB Network Adapter #10 - Virtual Machine Network Services Driver
ROOT\MS_L2TPMINIPORT\0000 : WAN Miniport (L2TP)
ROOT\MS_NDISWANBH\0000 : WAN Miniport (Network Monitor)
ROOT\MS_PSCHEDMP\0001 : Broadcom NetLink (TM) Gigabit Ethernet - Packet Scheduler Miniport
ROOT\MS_PSCHEDMP\0002 : Linksys Wireless-G USB Network Adapter - Packet Scheduler Miniport
ROOT\MS_PTIMINIPORT\0000 : Direct Parallel
...
31 matching device(s) found.

We can also use

devcon listclass Net

To find all devices, even those that aren't present, we type

devcon findall =Net

We can also filter based on how the device is connected. This command will show just PCI devices in the net class

devcon find =Net PCI\*

You can do this without even specifying the class as well.

devcon find PCI\*

Getting Status on a Device:

Similar to the last set of commands we can use

devcon status =Net

PCI\VEN_14E4&DEV_169D&SUBSYS_105E147B&REV_11\4&1B41B794&0&00E0
Name: Broadcom NetLink (TM) Gigabit Ethernet
Driver is running.
ROOT\CNTX_VPCNETS2_MP\0000
Name: Linksys Wireless-G USB Network Adapter #9 - Virtual Machine Network Se
rvices Driver
Driver is running.
ROOT\CNTX_VPCNETS2_MP\0009
Name: Broadcom NetLink (TM) Gigabit Ethernet - Virtual Machine Network Servi
ces Driver
Driver is running.
ROOT\MS_L2TPMINIPORT\0000
Name: WAN Miniport (L2TP)
Driver is running.
ROOT\MS_NDISWANBH\0000
Name: WAN Miniport (Network Monitor)
Driver is running.
ROOT\MS_PSCHEDMP\0001
Name: Broadcom NetLink (TM) Gigabit Ethernet - Packet Scheduler Miniport
Driver is running.
ROOT\MS_PSCHEDMP\0002
Name: Linksys Wireless-G USB Network Adapter - Packet Scheduler Miniport
Driver is running.
ROOT\MS_PSCHEDMP\0010
Name: Linksys Wireless-G USB Network Adapter #7 - Packet Scheduler Miniport
Driver is running.
ROOT\MS_PTIMINIPORT\0000
Name: Direct Parallel
Driver is running.
...
31 matching device(s) found.

The same arguments as last time can be used.

Enabling/Disabling a device:

You can use wildcards again, although I usually am very explicit with what I am enabling or disabling.


devcon disable @"HDAUDIO\FUNC_01&VEN_10EC&DEV_0880&SUBSYS_147B9B01&REV_1008\4&EEB356C&0&0001"
HDAUDIO\FUNC_01&VEN_10EC&DEV_0880&SUBSYS_147B9B01&REV_1008\4&EEB356C&0&0001: Disabled
1 device(s) disabled.

devcon enable @"HDAUDIO\FUNC_01&VEN_10EC&DEV_0880&SUBSYS_147B9B01&REV_1008\4&EEB356C&0&0001"
HDAUDIO\FUNC_01&VEN_10EC&DEV_0880&SUBSYS_147B9B01&REV_1008\4&EEB356C&0&0001: Enabled
1 device(s) enabled.



All this works quite well in most situations. There were a couple reasons why I eventually opted with the VB.NET way of doing things. First, efficiency. There is a lot of overhead to enabling and disabling a device with Devcon. This can slow down a program, especially when enabling and disabling a device can be quite time consuming in a computers way of imagining time. 500 milliseconds can be an eternity. 2-3 seconds can be a long time for a user to look at no progress.

The Second reason is just the nature of devcon. Since it is meant to be similar to the Device Manager itself, it acts like the Device Manager. This means devices are disabled on a per-profile basis. So, if you diable a device in one hardware profile and then boot in a different profile, it won't continue to be disabled. In my particular project, this wasn't ideal.

If these reasons aren't compelling enough for you to code this in .NET or C++, then you should look at using Devcon to accomplish your task.

Using RunOnceEx

Last week we figured out how to run something once per user. This week, we will look at running something once on a computer. I'm sure that there are fifty different ways to do this. Personally I like this method, because of three features:

  1. Easily able to run multiple items
  2. Easily able to sequence items
  3. Shows progress with a GUI
RunOnceEx provides us with all of that. We'll start with the registry key.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx

Inside of that is a value name you will create called TITLE (REG_SZ). This will be the title displayed on the window showing the progress of our installation.

We then create keys under that. For convenience sake most people number them. This also will help to sequence installs. Inside each key, the default value will be the string that will be displayed when installing items in the key. You create values (REG_SZ), each will be ran in alphabetical/numeric order. The data can be pretty much anything. MSI, EXE, BAT, VBS. I'm not sure what else you'd need, but I think its covered.

Once you've populated this registry structure you can reboot/logoff. The next login that has rights to modify the RunOnceEx registry key will execute the keys in order. Alternatively you can execute the RunOnceEx Process anytime with the following command:

rundll32.exe iernonce.dll,RunOnceExProcess

Please note that the DLL used here is from Internet Explorer. At one point this command did not work when running IE7. I believe this issue may have been addressed. If not, the common workaround I've seen, is the use of the original IE6 dll and simply explicitly calling its path. Regardless, simply logging in will kick off the process.

Here is a sample of the registry for RunOnceEx. Simply copy this into a file and save with a .REG extension.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx]
"TITLE"="Installing Stuff"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\1]
@="Notepad"
"1"="C:\\WINDOWS\\notepad.exe"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\2]
@="Calculator x 2"
"1"="C:\\WINDOWS\\System32\\calc.exe"
"2"="C:\\WINDOWS\\System32\\calc.exe"



Here is what the above item will look like











What will happen with the above example is Notepad will run, followed by two calculator processes. Each process must end, before the next process begins.

References:
MSFN

Using Active Setup

On more than one occasion I've had the need to change a user setting. These settings may be replacing a file or a registry key.

Possibly the first idea in solving this problem is to simply go through each user folder in Documents and Settings and work with each folder in sequence. By doing this, you'll likely have some permission problems and also, if working with the registry, have the overhead of loading each registry hive for each user. I've also found that depending on your environment you may be modifying profiles that are stale or completely obsolete.

What we can use instead, is Active Setup. In a nutshell, Active Setup runs when a user logs into the workstation and compares their registry with the local machine. For each entry found missing with the user's registry, it is executed. Normally an entry will be executed once per user and at the time the user logs in. You'll know it is running based on a small window in the top left corner popping up when the user logs in.

To get started You should add the following


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Active Setup\Installed Components\UniqueID]
"Version"=""
"Stubpath"=""
@=""

The UniqueID is just that. If you look you'll see this is mostly Guids, but it doesn't have to be. Of course you'll want this to be unique.

Version is the version of what you'll be running. Typically you won't have to change this. Where it comes in handy is if you want to update this script and run it again under the same UniqueID. If you update the version then the entry will be ran again.

The Stubpath (Command) is the command that will be executed. I have seen MSIs, EXEs, and VBScripts all run without issue. I've also seen RunDll calls as well.

The default valuename (@) is what will be displayed when the command is being executed.


References:
Active Setup - WikiDesktop
etlengineering.com

10 Tools every Desktop Engineer should have.

While working on various projects and troubleshooting more than a few problems, I have found a few tools that are an absolute necessity to perform my typical duties. All but one of these tools are free. I'd like to present them here in no particular order.

1. Process Monitor
Some people may have used FileMon or RegMon. This tool consolidates both tools into one. For those who haven't used either, Process Monitor adds itself as a low level device and allows you to monitor all activity on your disk and registry. I do mean all activity. Running this for a matter of 5 seconds can generate over fifty thousand entries easily. Luckily for us, there are excellent filters that allow us to comb through all this data. Also, Mark Russinovich has several recorded sessions showing how to use Process Monitor.

2. Process Explorer
This is a task manager replacement with a ton of extra features. You can simply check an option to actually replace the task manager. I should point out that this tool and the above tool were created by SysInternals and were eventually acquired by Microsoft. At the SysInternals site you can find more tools that are just as useful as these two tools.

3. Wireshark
Just as Process Monitor showed you events on your system. Wireshark shows you events going across your network connection. This application was previously called Ethereal. You can break down every packet coming from your system and see exactly what is being sent across the wire.

4. Beyond Compare
This is the only application that isn't free. You may be able to find other applications to do this work, but this application is great. Beyond Compare allows you to compare files and folders. You can look line-by-line and see what is different in an individual file, or look at a folder structure looking for files that are different.

5. Irfanview
If your work is anything like mine, you likely need to write a decent amount of documenation. A lot of my documentation contains screenshots. This application will help by taking a jpg screen capture anytime you hit a hotkey you define (Ctrl-F11 by default). it saves the file in a directory. You can save the whole screen or just the foreground window.

6. Notepad++
I used notepad a lot. Until I came across this application. It has a tabbed interface, allowing for multiple documents. It will detect when a file is updated and prompt you to reload. It also includes syntax highlighting for a ton of languages. It provides Regular Expression support, but probably not as much as you may find in vim. Nevertheless, it is a good replacement for Notepad.

7. VMWare Player
Testing and retesting, is a daily routine for me. Because of that, it is important to have something to easily test with. VMWare solves that problem. You may say that this only plays existing images. That problem is easily solved with EasyVMX, which allows you to create VMWare disks for use in the VMWare player. If this is more work, then you can buy the actual VMWare Workstation, but for me this works well.

8. Orca
This is the free tool provided by Microsoft to view MSI files. It is quick and dirty way to see MSIs and their tables. The biggest drawback is that you have to get it off the Windows SDK in order to get the MSI to install.

9. WScript Shell Documentation
I make it a point with scripting and writing code in general to not memorize code. I do this for a few reasons. First, I have enough junk to remember that I shouldn't bother stuff that is easily found in a book or a help file. Secondly, I should always be aware that my code can be better. If I simply remember a way to do task X, then I'll never bother to improve that task. For those reasons, I always have the documentation for VBScript readily available.

10. TortoiseSVN
Anyone who scripts should have some way at looking at where they came from with any particular script. Too many times I've seen a script in a directory that has about 10 different types of backups for that one script. You'll see script.vbs. Then script2.vbs, script.old, script.bak, or scriptREALLYOLD.vbs. Why bother when you can use free source control. Sure you have to learn how to use Source control, but shouldn't you do that anyway? Besides, learning the basics, can be done off of YouTube if you wanted. Personally I like Jeff Atwood's blog entry on setting up source control.

So, that does it. This is not an exhaustive list and perhaps one day I'll compose an addendum to it. But, I really think that each of these tools are essential to a normal Desktop Engineer.