Write logon scripts that work on all Windows OSs
For years, many organizations have made the Windows 9x family the desktop OS of choice on their networks. However, because of the end of the product family's life cycle and the increased flexibility of Windows XP and Windows 2000, a wave of migrations from Win9x to the Windows NT family has materialized. This wave consists primarily of small organizations that haven't had compelling reasons to make the move before now.
This bulge of migrations will eventually ensure significantly more stable and manageable desktop environments. However, many administrators face migration-associated problems during the transition phase. One problem is that administrators might need to support multiple environments — NT, Windows Me, and Win9x — for a while. And if those administrators run into a migration difficulty, they could easily think it's due to a mistake they've made, when it might actually be due to an undocumented problem.
In my experience, the primary administrative difficulty with supporting transitional environments is that Windows Script Host (WSH)based logon scripts that work perfectly well on some Windows OSs don't work on others. Two problems occur. First, you can't directly launch WSH logon scripts on NT 4.0, Windows Me, or Win9x systems. Second, the network interface on Windows Me and Win9x isn't initialized when a logon script begins execution, so the WshNetwork object (i.e., WScript.Network) returns an empty string. For example, if you use the WshNetwork object's UserDomain property on Windows Me or Win9x systems, it returns an empty string rather than the name of the user's domain. Although both of these probles are poorly documented, they are easy to solve.
To implement the solutions I provide, you need WSH. You can download WSH 5.6 at http://msdn.microsoft.com/downloads/sample.asp?url=/msdn-files/027/001/733/msdncompositedoc.xml. Microsoft constantly reorganizes its Web site, so if that URL doesn't work, try http://msdn.microsoft.com/scripting; this link has consistently had a correct redirection link to the download's current location.
Technically, Win95 doesn't support WSH 5.6. However, this lack of support isn't due to a fundamental incompatibility; Microsoft simply has never officially validated WSH 5.6 against Win95. In practice, WSH 5.6 appears to be completely stable and functional on Win95.
The decision about which version of WSH to install will depend on your situation. As you ponder the possibilities, consider the significant improvements that WSH 5.6 offers (e.g., the Exec method, which allows retrieval of output from console commands). Also consider that Windows .NET Server (Win.NET Server) 2003 and XP come with WSH 5.6 installed and you can't downgrade them to an earlier version of WSH. Should you decide to update all your client systems, be aware that NT 4.0, Windows Me, and Win9x use a different version of WSH than Win2k uses, and the WSH 5.6 setup package won't prevent you from installing the software on an incompatible platform.
Launching a WSH Logon Script
Let's take a look at a sample logon script. Listing 1 shows logon.vbs, a short script that retrieves information about the user, computer, and domain. You'd typically use a script like logon.vbs to map drives and printers; here, I use it just to look at the information the script initially gathers from the WshNetwork object. Creating a WshNetwork object in a script lets you easily map network drives and printers and access information about the user on the network.
If you make logon.vbs the logon script for a test user on your domain, you're likely to discover that the script won't run if the user is trying to log on to an NT 4.0, Windows Me, or Win9x system. None of these versions of Windows can use a WSH script for a logon script. Lmscript.exe, the file these OSs use for logon-script processing, can use only batch files.
The solution to this problem is to stage the script. Set all users to use a batch file, logon.bat, as a logon script. Then, have logon.bat launch logon.vbs. This approach works because all native 32-bit Windows clients recognize a batch file as a logon script.
The batch file needs to contain only one line:
The Start command runs logon.vbs in a new thread and doesn't block completion of the user's logon process. If a Windows Me or Win9x system doesn't run the script in a separate thread, the logon console window might hang or even halt execution of the logon process. When you use the Start command, the script runs outside the batch file, which closes and lets the rest of the background logon process continue.
You might be unfamiliar with the %0 argument. This argument returns the full path to the batch file. For example, if the path to the batch file in the above Start command is Z:\logon.bat, the %0 argument returns Z:\logon.bat, and the entire Start command returns
The \..\ portion of the returned path trims the preceding element (i.e., \logon.bat) from the path, so the above path reduces to Z:\logon.vbs. In other words, the %0 argument ensures that the logon.vbs file that the batch file executes is the file that's in the same folder as the batch file. The %0 argument is useful in batch files, such as logon scripts, that you might run from a network share.
Now that you know how to ensure that legacy clients launch a WSH script, log on to a Windows Me or Win9x system with an account that you've set to run the logon script from a batch file. Often — although not consistently — the code
User = Net.UserName
in logon.vbs will generate an error message indicating that the user hasn't logged on to the network. If you cancel the logon, then try to run a local copy of the same logon script, you still get this error.
What makes this problem particularly difficult to resolve is that some Windows Me and Win9x systems might run the script correctly. However, those systems won't run the script reliably. You can't depend on WshNetwork to work correctly in a Windows Me or Win9x logon unless you take certain precautions.
The cause of this problem is Windows Me's and Win9x's method of running a logon script. The lmscript.exe file in the System directory loads and starts the logon script. When lmscript.exe finishes, it notifies the logon API. Only then does the OS initialize the user's network environment. Consequently, a Windows Me or Win9x batch file logon script runs before the user has actually logged on to the network. The WshNetwork object uses an API call to get information about the username, but the API call will fail if the user hasn't yet established a network identity. When you use the Start command to launch a WSH script from a batch file running within lmscript.exe, lmscript.exe and the WSH process run asynchronously and you can't be certain that lmscript.exe will finish before the WSH script checks the username.
Trying to run the WSH script synchronously will ensure that it fails. If you were to change
you'd force the console-mode WSH host to launch the script and run synchronously with lmscript.exe. Because you'll have no user identity until lmscript.exe is finished and lmscript .exe can't finish until the WSH script is finished, you'll never get a username.
The fix for this initialization problem is straightforward: You simply wait for the user's network environment to initialize. You can instantiate WshNetwork, then continually check its UserName property until that attribute contains a value. If the UserName attribute doesn't contain a value, your script should wait for a while before checking the attribute again to avoid hogging the CPU.
To prevent the script from terminating with an error, you need to disable WSH's error handling before attempting to use WshNetwork. To disable error handling, insert the statement
On Error Resume Next
into logon.vbs. After the script retrieves the UserName attribute, turn back on error handling by using the statement
On Error Goto 0
The following code snippet makes sure the user's network environment is initialized:
Set Net = _
Do While Net. UserName = _
"": WScript.Sleep 50: Loop
On Error Goto 0
Because the objective is to devise a logon script that will run on all versions of Windows, you'll use the same script for NT systems even though NT doesn't need the loop function. The above snippet won't cause a problem on NT systems and will barely slow down the script. NT 4.0 systems won't execute the loop because they immediately initialize the UserName attribute. Listing 2 shows a modified version of logon.vbs that contains the loop code and won't generate an error when it tries to get the username on a Windows Me or Win9x system.
When you use this approach, you must make sure that the WSH script runs asynchronously with lmscript.exe, which you do by using the Start command in the logon batch file. If the console-mode CScript engine were the default WSH host and you were to omit the Start command, the WSH script would loop endlessly, waiting for the user's network environment to be initialized. But, until the WSH script finishes, lmscript.exe can't exit and notify the logon API that it can initialize the network. The Microsoft article "Cannot Retrieve UserName Property in Windows Script Host" (http://support.microsoft.com/?kbid=233976) mentions this problem but doesn't explain the need to run the script asynchronously or how to use the WScript.Sleep method to prevent excessive CPU use.
Setting the Domain
When you run a modified logon script like the one in Listing 2, you'll have one more problem: Windows Me and Win9x clients won't return a domain name. Because the %userdomain% environment variable is undefined on Windows Me and Win9x, the WshNetwork object can't expand the variable to get the UserDomain attribute, and logon.vbs returns an empty string. The Microsoft article "INFO: UserDomain Method Does Not Work Under Win95 with WSH" (http://support.microsoft.com/?kbid=188602) documents this problem. The solution requires that your logon script use different techniques for Windows Me and Win9x than for XP, Win2K, and NT.
In a transitional environment, the simplest solution is to never directly use the UserDomain property. Instead, use a wrapper procedure to pull the domain name correctly from whatever OS is running logon.vbs. "INFO: UserDomain Method Does Not Work Under Win95 with WSH" provides some code that you can use for the wrapper. I've modified that code slightly to make it a simple WSH function that you can easily drop into any script; Listing 3 shows the modified wrapper code. This robust replacement code works even when the Windows Me or Win9x system is a workgroup member. Web Listing 1 (http://www.winscriptingsolutions.com, InstantDoc ID 27328) shows a version of logon.vbs that includes the wrapper code.
For users who are new to WSH logon scripting, I've written a more realistic script that does more than just illustrate how to work around the problems you'll encounter in transitional networks. The version of logon.vbs that Web Listing 2 shows steps through the process of time synchronization and mapping user and shared drives.
Wrapping It Up
Over the next 3 years, I expect corporate Windows networks to become primarily XP- and Win2K-based. But in the near term, administrators still need to support quite a few Windows Me and Win9x platforms. The processes I've outlined can help you work around the most common problems you'll encounter when you write logon scripts for these legacy clients. If you want to use WSH to script more than logons for those clients, don't hesitate — the problems I've dealt with here constitute the extent of the difficulties you'll have with WSH's core functionality on the Windows Me and Win9x OSs.
If you go beyond the WSH core, you might need to install support packages. For example, if you want to make client-side use of Windows Management Instrumentation (WMI) and Active Directory Service Interfaces (ADSI) on legacy clients, you'll need to install the WMI and ADSI extensions. You can download the WMI extension at http://msdn.microsoft.com/downloads/default.asp?url=/code/sample.asp?url=/msdn-files/027/001/576/msdncompositedoc.xml. The Windows Me and Win9x ADSI extension is in the Clients folder on the Win2K Server installation CD-ROM.