Microsoft

Creating logon scripts with KiXtart, part 2: Basic elements

In this second installment of our series of Daily Drill Downs on KiXtart, Richard Charrington examines loops, subroutine calls, and the GoTo statement.


In part 1 of this series of Daily Drill Downs, I introduced KiXtart and examined a few constructs and commands available in the language. In part 2, I’ll discuss some more of the basic elements.

Loops
First, I’ll take a look at Do, While, GoSub, and GoTo. I’ll provide examples as I go along.

Like the if...else...endif construct, do...until works exactly as you’d expect. The syntax is:
Do

 code
until "expression"
So the code, which can be one or more lines, is executed until the result of expression is true. The code will always execute at least once. As with if..., expression may be a test or a command. Of course, if expression never equates to true, the code will keep executing forever.
If your program gets stuck in a loop, you may be able to use [Ctrl][Break] to stop it. Most times, you’ll need to use [Ctrl][Alt][Del] and click the End Task button.
The while...loop construct is identical to do....until, except that the test is carried out first. So, where the code between do and until will always be executed at least once, the code between while and loop will only be executed if and for as long as the expression equates to true.

The syntax for while…loop is:
while "expression"
 code
loop
If expression is initially false, the code will never execute.

While not exactly a loop construct, GoSub allows you to run a section of code as many times as you need. It also allows you to write more structured code and to reduce the duplication of code segments (reusable code). The syntax is:
gosub expression
where expression must resolve to a label name. For example:
$number = 1
gosub "label" + $number
exit

:label1
 subroutine code
return
The above code also shows that when "adding" a string and a number, the number is converted to a string and the "addition" becomes concatenation. The Return command returns control to the line following the GoSub command.

Although GoTo should be avoided where possible (if you’re writing structured code), there are times when it can be useful or maybe even unavoidable. So, for completeness I am including it here. The syntax is
goto "expression"
As with the While construct, expression must resolve to a label name.

Shelling out
There may be points in your script where you want to use a DOS command, run a batch file, execute an application, or run another KiXtart script. The following commands allow you to do all of these things.

The Shell command is used to load and run a program. Its syntax is:
shell "command"
The command parameter can be any 16-bit or 32-bit application.

If you’re going to run command-interpreter commands, specify the correct command interpreter as part of the command (see my examples below). With Shell, the KiXtart script execution is stopped until the external program exits.

If the program you want to run needs to set environment variables, you may need to specify additional environment space by using the /E parameter.

The Shell command sets the value of @ERROR to the exit code of the program that’s run. Here are some examples:
SHELL @LDRIVE + "\UPDATE.EXE"
SHELL "%COMSPEC% /e:1024 /c DIR C:"
SHELL "SETW USERNAME=@USERID"
SHELL "CMD.EXE /C COPY " + @LDRIVE + "\FILE.TXT C:\"
SHELL "%COMSPEC% /C COPY Z:\FILE.TXT C:\"
SHELL "C:\WINNT\SYSTEM32\CMD /E:1024 /C " + @LDRIVE + "\SMSLS.BAT"
The Run command is exactly the same as Shell except that KiXtart script execution will continue while the external program is running. The syntax for Run is the same as for Shell.

The Call command is used to call another KiXtart script. When the called script exits, or a return is encountered in the called script, execution continues in the original script following the line containing the Call command. The syntax is:
call "expression"
Theoretically, there’s no limit to the number of scripts that can be nested. Obviously, the practical limit on the number of scripts you can call is determined by such factors as:
  • The amount of available memory at the time KiXtart runs.
  • The size of the scripts.
  • The number of variables defined.

Don't call us; we'll call you
Seeing the Shell and Run commands above, you may be tempted to use DOS commands wherever you feel you need them. Before you do, have a look at the following commands that are available from inside KiXtart:

Cls Clear screen
Copy Copy files
Dir List directory
Md Make a directory
Rd Remove a directory
Del Delete file(s)
Cd Change directory
Set Set environment variable (see documentation for "setl" and "setm")
Sleep Stop processing for given number of seconds
(Net) Use Map a network drive

As I mentioned, these commands are all available from within KiXtart and are the exact equivalent of their namesakes in DOS (except Use, which is identical to Net Use). There are some slight differences in the syntax between the KiXtart commands and the DOS equivalents, so check the documentation before you use them.

Also, if you need to change the current drive in DOS, you’d enter the drive letter; in KiXtart, you can do exactly the same, but to make it tidier, you can, optionally, precede the drive letter with Go (for example, go a:).

More than one result
There are times when you may need to use a sequence of If statements to take different actions, depending on the result of a particular expression. In some languages, that’s exactly what you have to do. So you’d write something like this:
if $x=1
 ? "One"
endif
if $x=2
 ? "Two"
endif
if $x=3
 ? "Three"
endif
if $x < 1 AND $x > 3
 ? "Out of range"
endif
Fortunately, KiXtart has a neater way of doing this. The Select construct is available in many advanced programming languages. It’s very similar to the list of If statements above, except that you do away with all the Endif statements and you have a simpler "catchall" than the last If in the example above.

Using Select, you can write the above example as:
SELECT
CASE $x=1
 ? "One"
CASE $x=
 ? "Two"
CASE $x=3
 ? "Three"
CASE 1
 ? "Out of range"
ENDSELECT
I think you’ll agree that this is much neater and easier to read. Notice that you don’t need to specify the out-of-range expression. The reason for this is that only one of the Case statements will be executed, so if $x doesn’t equate to 1, 2, or 3, the final Case statement will be executed. Only one Case statement is executed, so in the following example, the code following
CASE $x>0 AND $x<2
will never be executed. Why?
SELECT
CASE $x=1
 ? "One"
CASE $x=2
 ? "Two"
CASE $x=3
 ? "Three"
CASE $x>0 AND $x<2
 ? "This line is never reached"
CASE 1
 ? "Out of range"
ENDSELECT
The expression following Case doesn’t have to be the same on each line. The first Case could be $x=1; the next could be InGroup("Administrators")—although it’s more likely that the expressions will be of a similar type.

Registry
One of the most powerful features available with KiXtart is the ability to work with the registry. You can read, change, delete, and add keys and values. This allows KiXtart to be used to enforce certain user-interface configurations so that, even if users know what they need to change to get around your standards, they’ll have to make those changes after each logon. This isn’t difficult for a technically competent user, but it is annoying and, therefore, a reasonable deterrent nonetheless.

Read
If you want to check the value of a registry key, there are two functions you need. First, you may simply need to know if the key actually exists. For this you use ExistKey. The syntax is:
existkey ("subkey")
It is important to note that if the subkey exists, the function returns 0 or false. This is because for any other result, the function returns an error code indicating what went wrong. For example:
$Result = ExistKey("HKEY_CURRENT_USER\Console\Configuration")
If $Result = 0
 ? "Key exists...."
Endif
Once you know that the subkey exists, or if you’re happy to skip that check, you can find the value contained in an entry under the subkey. For this, you use the function ReadValue, whose syntax is:
readvalue ("subkey", "entry")
where subkey identifies the subkey containing the entry, and entry identifies the entry whose value you want to discover. To read the default entry of a key, specify an empty string as the entry name (“”).

If @ERROR is set to 0, then the result of the function is the value of the entry; otherwise, it is the error code representing what went wrong.

REG_MULTI_SZ (multistring) variables are returned with the pipe symbol (|) used as the separator between strings. If a string contains a pipe symbol character, it is represented by two pipe symbol characters (||).

REG_DWORD variables are returned in decimal format. Here’s an example:
$Rows = ReadValue("HKEY_CURRENT_USER\Console\Configuration", "WindowRows")
If @ERROR = 0
 ? "Number of window-rows: $Rows"
Endif
Change
Once you know an entry exists and you’ve checked its value, you may want to change it. To do so, you use WriteValue, whose syntax is
writevalue ("subkey", "entry", "expression", "data type")
where subkey identifies the subkey where you want to write a value entry. (This subkey MUST exist.) entry represents the name of the entry. (To write to the default entry of a key, specify an empty string as the entry name; if the entry does not exist, it will be created.) expression is the data to store as the value of the entry, and data type identifies the data type of the entry.

The following data types are supported:
  • REG_NONE
  • REG_SZ
  • REG_EXPAND_SZ
  • REG_BINARY
  • REG_DWORD
  • REG_DWORD_LITTLE_ENDIAN
  • REG_DWORD_BIG_ENDIAN
  • REG_LINK
  • REG_MULTI_SZ
  • REG_RESOURCE_LIST
  • REG_FULL_RESOURCE_DESCRIPTOR

@ERROR is set to 0 if the value is written to the registry successfully. Here’s an example:
WriteValue(("HKEY_CURRENT_USER\Console\Configuration", "WindowRows", 24 "REG_DWORD")
If @ERROR = 0
 ? "Value written to the registry"
Endif
Delete
If you want to delete a registry key, use DelKey, or to delete an entry from a key, use DelValue.
KiXtart does not ask for confirmation when registry values are overwritten or when subkeys are deleted. Always be very careful when changing the registry, and preferably back up your system before changing registry values. You can back up parts of the registry in KiXtart using SaveHive (see the KiXtart documentation).
The syntax of DelKey is:
delkey ("subkey")
Here’s an example:
$SUCCESS=0
If DelKey("HKEY_CURRENT_USER\MyRegKey") = $SUCCESS
 ? "Key deleted...."
Endif
Here I set a variable called $SUCCESS and test the result of the function rather than using a separate test of @ERROR. This makes the code more readable and the reason for the test more obvious.

The syntax of DelValue is:
delvalue ("subkey", "entry")
and here’s an example:
$SUCCESS=0
If DelValue("HKEY_CURRENT_USER\MyRegKey", "TestValue") = $SUCCESS
 ? "Value deleted...."
Endif
Add
KiXtart includes functions to add keys and values to the registry. I’ve already covered the method for adding a value (WriteValue). To add a key, you use AddKey. The syntax is:
addkey("subkey")
Here’s an example:
$SUCCESS = 0
If AddKey("HKEY_CURRENT_USER\MyRegKey") = $SUCCESS
 ? "Key added...."
Endif
It is only necessary for the hive part of the subkey to be present—the subkey tree will be created from scratch if need be. For example, the code
AddKey("HKEY_CURRENT_USER\MyRegKey\MySubkey\ThirdLevel")
will work just fine, even if MyRegKey and MySubkey don’t exist.

Registry examples
I’ve shown you a few short examples to demonstrate the use of each registry function. Below I have a slightly longer example to show how registry functions would be used in practice:
1. $SUCCESS = 0
2. If ExistKey("HKEY_CURRENT_USER\MyRegKey\MySubkey") <> $SUCCESS
3. If AddKey("HKEY_CURRENT_USER\MyRegKey\MySubkey") <> $SUCCESS
4. ? "AddKey failed with error code (" = @ERROR + ")"
5. Exit 100000+@ERROR
6. Endif
7. Endif
8. If WriteValue("HKEY_CURRENT_USER\MyRegKey\MySubkey","TestValue","Test","REG_SZ") = $SUCCESS
9. ? "TestValue written to registry successfully"
10. Else
11. ? "WriteValue failed with error code (" + @ERROR + ")"
12. Exit 200000+@ERROR
13. Endif
14. ; rest of code
In line 2 I check to see whether the key I want to add a value to exists. If not, in line 3 I attempt to add it. If that fails, in line 4 I display an error message and exit. Note that I add 100000 to the error code so that in a calling program, I can tell where the failure occurred and what the error code was.

If the key already existed, or has been successfully added, I end up at line 8. Here we attempt to write a value with the name TestValue. If that fails, I display an error message and exit. This time I add 200000 to the error code. I reach line 14 only if everything went well.

This is how I’d handle the failure exits:
CALL "AddMyValue"
SELECT
CASE @ERROR < 200000
 ? "AddKey failed – "
CASE @ERROR < 300000
 ? "WriteValue failed – "
ENDSELECT
SELECT
CASE @ERROR = 200002
 "not found" ?
CASE @ERROR = 200005
 "access denied" ?
CASE @ERROR = 200012
 "invalid access" ?
CASE 1
 @ERROR ?
ENDSELECT
The first Select construct displays where the failure occurred; the second tags on the reason for the failure. The second Select list may well be longer if you want to take into account all possible failure codes for registry editing. Remember that the question mark (?) equates to printing a new line.

Conclusion
In this installment of my series of Daily Drill Downs, I’ve covered loops, subroutine calls, and the ubiquitous GoTo. I’ve also shown how KiXtart can execute external commands and scripts, and I’ve described the Select construct. A major part of this Daily Drill Down has concentrated on registry manipulation, because that’s one of the most powerful uses of a logon script.

In my next installment, I’ll show you how to manipulate the DOS window that KiXtart will be running in, how to communicate with a user, and how to read and write files.

Richard Charrington’s computer career began when he started working with PCs—back when they were known as microcomputers. Starting as a programmer, he worked his way up to the lofty heights of a Windows NT systems administrator, and he has done just about everything in between. Richard has been working with Windows since before it had a proper GUI and with Windows NT since it was LANManager. Now a contractor, he has slipped into script writing for Windows NT and has built some very useful auto-admin utilities.

The authors and editors have taken care in preparation of the content contained herein, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.

Editor's Picks

Free Newsletters, In your Inbox