July 12, 1999
Four Steps to Scripting Nirvana
What About Security?
Is There Anything Better than Scripting Nirvana?
I get asked all the time about how scripting has become so successful, why it gets used in so many applications, and would I share the secret of our success. This last is a somewhat perplexing question to answer, because people tend to think that we have hidden away in Building 41 at the Microsoft Campus some amazing marketing machine that is covertly marketing script to every developer there ever was. (We do have a great marketing team in Building 41, and they do provide some very nice T-shirts every so often, for which I'm very grateful.) Rather than answering this question for the rest of my life, I thought I'd share some of the reasons why I think script is successful and how those might apply to any developer out there. If nothing else, it will be somewhere I can point the inquisitive minds rather than having to answer the question.
The single, most impressive—even cool—feature of script is the ability to have a language embedded into your application. This means, with a little forethought, you can make your application extremely flexible and even meet the requirements that your users told you about the day after you shipped the product. If you design your application with an object model that covers most of the functionality, your application can be scripted to change behavior to meet what the user really wanted it to do—and even to add completely new features. This concept is not new, but the ability to integrate scripting into your application quickly and easily is.
The ease of integration is a key asset to scripting. You're developing your application, and you're bang on schedule when something somewhere causes the ship date to slip. This leaves you in a quandary: Do you just sit tight on your existing feature set, or do you investigate adding a small work item that would help your customers out and keep you busy over the next month? Your boss tells you that the competitor is just about to ship with a great product that has everything you have and for only $99; that often solves the quandary for you. You're commanded to look at what they've done and come up with something fabulous that costs nothing, can be implemented in two days, and will knock the competition for six. Script in this case is your friend, since you can add scripting to your application very easily, pretty cheaply, and in such a cool way that your competitors will be begging for mercy! Okay, so I exaggerate a little—but you get the picture.
Four Steps to Scripting Nirvana
To illustrate, I thought I'd include a small example application and add script to it, so you could at least have some code to go away with. My example app is really simple; it's just a glorified notepad. I've written it in Visual Basic®, but it could be written in any language (Visual C++®, Visual J++®, Delphi, and so on).
- Create the object model
In order to be scriptable, your application needs an object model. If you're a Visual Basic developer, this is a piece of cake since your application has an object model by default. If you're developing using a different language, then as long as your application supports IDispatch as well as your own interface you should set.
For my example, the object model is pretty simple. The only objects provided for customization are the textbox used for editing and the menu bar. The application has a number of other objects, but I've chosen to make only these two available for ease of description.
- Tell the script engine about your object model
Once you have defined your object model, you have to add this information to the script engine, so that script that the engine runs will "know" about the objects. In this example, I'm going to use the Microsoft Script Control that provides a Visual Basic-friendly mechanism for calling the Windows Script Interfaces. If you want to know how to use the Windows Script Interfaces directly then check out http://msdn.microsoft.com/scripting/
To add the object into the script engine using the script control, you first need to add the script control into your project via the components dialog box. Once you have it in your project, add an instance on to your form. Once you have this instance you just need to call the addobject method on the script control. The addobject method takes three arguments: Name, Object, and AddMembers. In this example, I'm going to add the text box that is the editor in the notepad application. To do this, I have one line of code in my form load:
ScriptControl1.AddObject "Editor", txtEditor
This adds the txtEditor object model into the script engine and gives it a name: Editor. So in any script that my application runs will be able to access the object model of the text box via the editor object. So Editor.text = "Hello World" will set the text to be Hello World.
- Add the script code into the script engine
Once the object model has been added to the script engine, we need to be able to add some script to run against the object model. To do this, there is an addcode method on the script control that just takes a string. The string contains the script that you wish to run. Since it's a string, it's entirely up to you where you store the script. You could store it in a database, text file, or even OLE-structured storage.
Figure 1. The Script Code Editor
In the example with this article I don't actually save the script out to a file, but you could easily add code to the save event handler that would save the script code to your chosen store before adding it to the engine. The code is added to the script engine via the addcode method:
This code takes the contents of the txtCodeEditor text box and puts the script into the script engine ready to be run.
- Run the script code
Once the script and the objects are inserted into the script engine all that is left to do is run the code. Without calling the run method on the script control the script will be compiled in the engine but will never run. Running the code is very simple: You just call the run method with the name of the procedure that you want to run. Typically, your users will want some form of user interface to pick the scripts they want to run. The script control doesn't provide a dialog box to do, this but building one in Visual Basic is remarkably easy.
Figure 2. The Simple Scripts dialog box
The Script Control has a Procedures collection that returns all the procedures that are in the script engine. This makes getting the names of all the procedures very simple: Iterate through the collection, extract the name of each procedure, and call the additem method on a Visual Basic list box.
Dim objProcedure As Procedure For Each objProcedure In frmNotepad.ScriptControl1.Procedures lstScripts.AddItem objProcedure.Name Next
To run the selected procedure, the run command button calls the run method with the text of the list box:
Once you've added these four steps to your application, you have a fully scriptable application, and users can now add their own scripts to customize your application to exactly how they want it. Not bad in seven lines of code!
So far, everything has been quite simple—but the application allows you to run only procedures from the scripts dialog. It would be great if you could get the script to run on events fired by your object model. Luckily, the script control allows you to write script that will automatically fire when an event on an object occurs. First, you have to tell the script control to change its state to connected, so that it automatically connects script to events. To do this, you use a state property that can be set to either connected or initialized. By default, it will be set to initialized, so to turn on event handling you need to set it to connected:
Scriptcontrol1.State = Connected
Once the engine is in a connected state, any script procedure that conforms to the event handler the naming convention of the language will be connected to the object it specifies. For example, the Editor object has a lostfocus, event so sub Editor_lostfocus would be called whenever that event fires. In Visual Basic Scripting Edition (VBScript), the naming convention is object_eventname; in JScript®, it's object::eventname.
What About Security?
There's a lot of press coverage at the moment about the viruses being spread in applications through e-mail. Since VBScript and JScript were originally developed for use in Internet Explorer, where safety and security are major issues, there is a built-in mechanism for putting the script engines into safe mode. When the script engine is put into safe mode, the script will be able to create only objects that have been marked as safe for scripting. This means that a CreateObject on the MAPI control will fail, since it's not marked safe for scripting—yet a call to the Scripting.Dictionary object will work, because it's safe for scripting. Tthe script engines are set into safe mode by setting the usesafesubset property; when set to true, only objects marked safe for scripting will be created.
Is There Anything Better than Scripting Nirvana?
Integrating script is a great addition to any application, but there are a few things that it doesn't provide for you, such as a decent development environment, compiled code, maybe a forms package. If these are important to your application, then you're in luck, because you can license Visual Basic for Applications from Microsoft. Visual Basic for Applications provides an integrated development environment that is very similar to the "real" Visual Basic, so you get all the cool intellisense, color-coding features in the script editor. This alone might be worth using Visual Basic for Applications, since the text box approach I use in my application really doesn't provide any help for the coder. Visual Basic for Applications also allows you to compile your Visual Basic code, giving you considerable speed benefits over script. Finally, it adds a forms package, allowing users frustrated with msgbox and inputbox limitations to create rich forms hosting ActiveX® controls and so forth. You can download a trial version of the SDK directly from http://msdn.microsoft.com/vba/
Another approach you might want to take with your application is allowing external scripts to use your application. Windows 98 and Windows 2000 ship with Windows Script Host (WSH) that allows scripts to be run from the command line or by double-clicking on them. If you expose a public object model, WSH scripts will be able to call methods, set properties, and respond to events from your application. This mechanism might make sense for your application (e.g., if you were writing a set of administration objects), but it can be slower than hosting the engine directly, because the script is running outside of your application process. The object model you expose will also be available to any language that understands COM, so you can expose your application to C++, Java, and Visual Basic developers as well as to scripters.
Script provides a simple, lightweight run time that you can build into your applications and really add considerable value for a relatively small investment in time. The script control makes hosting a script engine even easier for Visual Basic developers, so you can make your application scriptable in seven lines of Visual Basic. The example I have used in this article uses VBScript, but the language could just as easily be JScript, PerlScript, Python, and so forth. The key is that you choose which language best suits your users' needs. Typically, we find that VBScript is the language of choice, but it's important that you get the choice. You can get the script control free of charge from http://msdn.microsoft.com/scripting/scriptcontrol/. I look forward to hearing how you get on with adding scripting to your applications, so please use our newsgroups or send mail to firstname.lastname@example.org.
Andrew Clinick is a senior program manager in the Microsoft Programmability group, so chances are, if there's script involved, he's probably had something to do with it.