Chapter 22

VBScript as a Component in Other Applications

by Bill Schonger and Paul Lagasse


CONTENTS

Browsers are great, but what if you want to take VBScript along with you to another application to add some scripting support? No problem, if you're a programmer at heart. ActiveX Scripting, which VBScript is a part of, is designed to allow a lot of mobility in the use of the code. Whether it's upgrading the code to Visual Basic for Applications or Visual Basic itself, or building your own program which interprets VBScript, you have a lot of options. In this chapter, we'll cover a variety of those options and how you can use them to your benefit, including some further details on ActiveX Scripting methods themselves. Topics covered include

Understanding what VBScript really is behind the scenes isn't tough, but going out and creating your own container to integrate VBScript is another matter entirely. It requires a pretty good familiarity with COM and OLE programming, which in and of themselves imply a lengthy history of Windows programming expertise. Rather than assuming you have all that background, we'll look at the general concepts behind ActiveX scripting engines and hosts, but we'll also be sure to provide enough specifics and references, in case you're on the advanced track and need the implementation specifics.

Porting VBScript to VBA and Visual Basic

Application development in VBScript can be a two-way street. Conversion utilities exist to take the extended functionality of VBA and Visual Basic applications and chop them down into ActiveX/VBScript hybrids, but it's just as easy to port your application up the evolutionary ladder. This isn't to say that you'll want to prototype your applications in VBScript and then move them up, but it makes a very attractive upgrade path available for developers or people who are introduced to Visual Basic by way of VBScript. After all, isn't attracting developers to Visual Basic syntax a big reason why Microsoft will put a lot of effort into making VBScript a standard? It just means that there will be more developers who won't want to move out of the syntax they learned and are comfortable with. Talk about an easy sell...

There are significant differences between VBScript, its older sibling VBA, and the parent Visual Basic language set. The most obvious is the increase in available features. For each step you move up the VB evolutionary chain, you gain a number of functions that were previously too insecure for use in one or the other of the language subsets. File Input and Output jumps out immediately as a powerful addition, but inter-process communications and general systems functions are also important.

A consideration to keep in mind when "moving up" your code is that you have more flexibility in performance, and a lot more to worry about with error checking. Variants are the primary data type in VBScript, but once you get into the bigger siblings, you have to worry about matching up data types and converting back and forth, to make sure you don't get any problems.

All About ActiveX Scripting

To give people the most flexibility in being able to run different scripts in different programs on different operating systems, Microsoft developed the ActiveX Scripting specification, of which VBScript is just one implementation of the idea. Consisting of two components, the scripting engine and the scripting host, ActiveX Scripting allows a lot of interchangability, both in what kind of script or programming language can be used and what applications can use it.

Script Engines

The component that actually interprets the scripting language is the script engine. Whether the language in question is VBScript, Perl, TCL/TK, or Joe Bob's Discount Script, an engine is specifically geared toward that one language and carries out whatever interpretive tasks are required in order for the script to be understood. In addition, it also has to be friendly enough to get along with applications that are instructing it to run.

Assuming for the moment that you're not too worried about languages other than VBScript, let's take a moment to look at just how the whole Scripting Engine thing works. At its root, a scripting engine is an OLE (object linking and embedding) control that supports the right combination of entry points, or "hooks," for other properly OLE-enabled ActiveX containers to talk to it and trade information about what's going on in the script. If either one is missing specific hooks, or doesn't adhere closely to the necessary standards, things are going to get messy real quick.

When you embed a script into an HTML page with the <SCRIPT> tag, what you're really doing is setting a flag for the browser to say "Oh geez, what do I do with this?". When the browser reaches that tag, it takes a quick look at the LANGUAGE parameter inside the <SCRIPT> tag, and determines whether it knows how to handle that particular language, either based on an OBJECT tag pointing the browser to a new interpreter, or if the browser's built-in table of languages says "Hey, we know that language, we can handle it." In the case of Internet Explorer 3.0, the languages that the browser can handle without questions are JavaScript (or JScript, as Microsoft likes to call it) and, of course, VBScript.

NOTE
If you were to build an engine for your own special scripting language, you could use an <OBJECT> tag to make sure that your special scripting engine was downloaded onto the user's system if it wasn't already there. This would require giving it a unique class ID (CLSID), just like any other ActiveX object, and hoping that the user trusts you enough to download the component just to run your script. You did say your scripting language was secure, didn't you?

Just like the plug-and-play architecture of Windows 95 and Macintosh systems, ActiveX scripting engines provide plug-and-play support for any language you'd care to dream up an interpreter for.

Scripting Engine OLE Interfaces

The innards of the scripting engines don't need to be too terribly complex-just enough to safely handle the language that will be interpreted, and to provide the basic hooks that are expected by the browser. If you've written an OLE COM object before, you're more than halfway there! To make sure you have the information you need to finish the other half, we'll take a look at what hooks, in this case OLE interfaces, are needed in the scripting engine itself. If you haven't written an OLE COM object before, or don't intend to soon, there's a significant amount of distance you'll need to go before tackling your first scripting engine, and you might want to skip over the next few paragraphs and jump right to Scripting Hosts.

The primary interface for a scripting engine is IActiveScript. This in turn provides the subsequent hooks for all information to be passed between a host and the internals of the engine in an orderly fashion. The header information for IActiveScript would be defined as shown in Listing 22.1, with each component serving a particular need.


Listing 22.1. Header information for IActiveScript.

 1: Interface IActiveScript : IUnknown

 2:   {

 3:   HRESULT SetScriptSite([in] IActiveScriptSite *pScriptSite);

 4:   HRESULT GetScriptSite([in] REFIID iid, [out] void **ppvScriptSite);

 5:   HRESULT SetScriptState([in] SCRIPTSTATE   ss);

 6:   HRESULT GetScriptState([out] SCRIPTSTATE *pss);

 7:   HRESULT Close();

 8:   HRESULT AddNamedItem([in] LPCOLESTR pstrName, [in] DWORD dwFlags);

 9:   HRESULT AddTypeLib([in] REFGUID guidTypeLib, [in] WORD wMaj, [in] WORD wMin, 

      [in] DWORD dwFlags);

10:   HRESULT GetScriptDispatch([in] LPCOLESTR pstrItemName, [out] IDispatch 

      **ppdisp);

11:   HRESULT GetCurrentScriptThreadID([out] SCRIPTTHREADID *pstid);

12:   HRESULT GetScriptThreadID([in] DWORD dwWin32ThreadID, [out] SCRIPTTHREADID 

      *pstid);

13:   HRESULT GetScriptThreadState([in] SCRIPTTHREADID stid, [out] 

      SCRIPTTHREADSTATE *psts);

14:   HRESULT InterruptScriptThread([in] SCRIPTTHREADID stid, [in] EXCEPINFO *pei, 

      [in] DWORD dFlags);

15:   HRESULT Clone([out] IActiveScript **ppscript);

16:   };


NOTE
If you're familiar with header information and declarations, you'll notice that the interface that all functions are bound to, for both scripting engines and later on in scripting hosts, is the Iunknown interface. This is the heart of what defines an ActiveX control-the capability to accept and support the Iunknown interface so that future controls and functions have a generic entry point to assess information through.

Threading and Scripting Engines
ActiveX Scripting engines are normally designed to be free-threading OLE COM objects, where methods contained in IActiveScript and all underlying interfaces can be called without marshalling. What in the world is marshalling? It's a special process for packaging and unpackaging parameters to allow a remote procedure call to happen. In essence, you have to manage synchronization and other processes by yourself, because you want the Scripting Engine to be as flexible as possible, without forcing more work on the Scripting Host. To do this with a little less pain and agony, two primary restrictions are placed on IActiveScriptSite's use within the engine: 1) The script site will never call itself through a thread it created, and 2) The script site will never be called from within a simple state control method for threads (like Clone()). What does all this buy you? A little more safety in dealing with threads, and more flexibility in the use of Scripting hosts.

The next big interface is IActiveScriptParse. It's not normally required, but if the scripting language is going to allow expression text to be evaluated, or dynamic pieces of scripting code to be added during runtime, it becomes mandatory. Listing 22.2 shows how the interface would be defined in your code.


Listing 22.2. Header information for IActiveScriptParse.

 1: Interface IActiveScriptParse : IUnknown

 2:   {

 3:   HRESULT InitNew();

 4:   HRESULT AddScriptlet(

 5:       LPCOLESTR  pstrDefaultName,

 6:       LPCOLESTR  pstrCode,

 7:       LPCOLESTR  pstrItemName,

 8:       LPCOLESTR  pstrSubItemName,

 9:       LPCOLESTR  pstrEventName,

10:       LPCOLESTR  pstrEndDelimiter,

11:       DWORD      dwFlags,

12:       BSTR      *pbstrName,

13:       EXCEPINFO *pexcepinfo);

14:   HRESULT ParseScriptText(

15:       LPCOLESTR  pstrCode,

16:       LPCOLESTR  pstrItemName,

17:       IUnknown  *punkContext,

18:       LPCOLESTR  pstrEndDelimiter,

19:       DWORD      dwFlags,

20:       VARIANT   *pvarResult,

21:       EXCEPINFO *pexcepinfo);

22:   };


NOTE
With the exception of BSTR, EXCEPINFO, and VARIANT, all parameters for IActiveScriptParse are passed into the function.

Last, but not least, is the scripting engine's persistence information, IPersist. This retains parameters, object ClassID data, and other incoming data so that it can be held and used by the engine and the script for whatever purpose that information was intended for. IPersist can actually come in three flavors: IPersistStorage, IPersistStream, and IPersistFile. The use of one type of IPersist interface over the other varies by situation. For example, if you were using a custom scripting engine, you could include a data= parameter to open a connection and bind the data to storage, making use of the IPersistStorage interface. It's all a matter of use, so you should be prepared for any of them.

Scripting Engine Programming Considerations

In "Communications Between Scripting Engines and Hosts," later in this chapter, we'll look at just how communications flow between scripting hosts and scripting engines. For the moment, though, there are some other important considerations to keep in mind if you're planning on taking the big leap and programming your own scripting engine.

First and foremost, think about threading. Your scripting engine will have a lot of work to do, and you can't consider it secure and robust if your execution threads are tripping over one another. That's terribly inconvenient. Since each scripting engine is a free-threaded OLE COM object, and the host doesn't help untangle the threads, you'll have to be careful to perform synchronization methods on your own, and properly.

Next, consider the language you're thinking of writing the engine for. If it's a company project and you have to build a proprietary language interpreter for your intranet, that's one situation. If, on the other hand, you really like some language and wish you could use it in your Web pages, that's another thing entirely. Remember, you're going to have to take into account all the possible security holes, what functions just can't be done (not that there will be too many of them, if you don't care about security), and who your target audience is. No one's really going to want to download a 1MB scripting engine just to look at your page and the whiz-bang things you can do with your own scripting language. Sure, it'd make a cool project for a computer science major or someone with a real desire to experiment, but it's not horrendously practical.

Languages that are in popular use but don't have scripting engines available yet are good targets for people trying to gain widespread financial or ego-boosting return on their investment of time. Perl and TCL/TK, at the time of writing, were the two most likely candidates for engines that would be widely used. If you haven't seen any engines out there for them by the time you read this, maybe you can give Larry Wall or Sun Microsystems a call and have a long talk with them about lending a hand.

Last, but not least, is security. It's been mentioned before, and it will keep being mentioned. People want to know that what they're using won't compromise their system's integrity. They also want the utility or language they're using to do everything except make toast, so there's a little conflict there. You have to logically balance security issues with functionality, and then test the heck out of the engine to avoid glaring security holes. It'd be pretty embarrassing to get your system trashed by someone else using a security hole in your scripting engine.

Script Hosts

If script engines are what provide the locomotive power, script hosts are the chassis, the wheels, the steering, and the brakes of the whole traveling entourage. They provide the space for engines to come alive, and provide open links so that data can pass back and forth between the engine and the host itself. Even Microsoft's Internet Explorer is nothing more than a glorified scripting host-it has the links in and out, and it takes advantage of them just like any other application can if it wants to. You just have to know what's involved.

Scripting Host OLE Interfaces

In OLE terms, a scripting host is a container that is aware of specific OLE interfaces-IActiveScriptSite and IActiveScriptSiteWindow, to be specific. IActiveScriptSite is the big kahuna of hooks, since it creates a place for the scripting engine to run in the server's namespace, store any extra data, and open the channel for events to be processed inside the script engine itself, not just the host interface. IActiveScriptSiteWindow is actually optional, and is only used by those scripting hosts that want to support User_Interface (UI) interactions on the same object as IActiveScriptSite.

Since IActiveScriptSite is the primary interface and mandatory for the whole thing to function, we'll look at it in more detail. This interface has to make a number of different functions available for passing control over the script back and forth, while being open enough to allow synchronization between the scripting engine and the host. The operations available in IActiveScriptSite can be defined in a header, as shown in Listing 22.3.


Listing 22.3. Header information for IActiveScriptSite.

 1: interface IActiveScriptSite : IUnknown

 2:   {

 3:   HRESULT GetLCID([out] LCID *plcid);

 4:   HRESULT GetItemInfo(

 5:       [in]  LPCOLESTR   pstrName,

 6:       [in]  DWORD       dwReturnMask,

 7:       [out] IUnknown  **ppunkItem,

 8:       [out] ITypeInfo **ppTypeInfo);

 9:   HRESULT GetDocVersionString([out] BSTR *pstrVersionString);

10:   HRESULT OnScriptTerminate([in] VARIANT *pvarResult, [in] EXCEPINFO 

      *pexcepinfo);

11:   HRESULT OnStateChange([in] SCRIPTSTATE ssScriptState);

12:   HRESULT OnScriptError([in] IActiveScriptError *pase);

13:   HRESUT    OnEnterScript();

14:   HRESULT   OnLeaveScript();

15:   };


Knowing what each of those components is there for will make understanding the host creation process a lot easier, so let's take a moment to get introduced to each one. GetLCID is pretty straightforward, as it returns the localization string associated with the scripting host's user interface, so that any error messages or other data that get sent back appear in the correct language on the end user's machine.

GetItemInfo allows the script to go outside its boundaries and collect additional information about an object and related interface information, which it then stores in its three return parameters. GetDocVersionString is basic version checking, which returns a unique identifier (from the script host's point of view) to identify the version of the currently running script. The purpose of OnScriptTerminate is pretty self-explanatory; it informs the host when the script has finished executing. OnStateChange is used to monitor the current state of the script, whether it's running, waiting, stopped, or done. OnScriptError notifies the host that something bad has happened, and passes it an object reference from which the host can extract further information, as well as instructions to either continue, terminate, or re-initialize the script itself. OnEnterScript is called when the script is first executed and must be called every time event handling passes to an object. Just as OnEnterScript must be called every time an entry or re-entry occurs into the script, OnLeaveScript must be called to prepare the script for such an occasion. Think of them as opening and closing brackets- you have to make sure you have matching sets, or you'll get nasty errors.

IActiveScriptSiteWindow is much less involved than IActiveScriptSite, as can be witnessed by looking at Listing 22.4, showing the header information used by the interface. All it does is perform two actions: obtain the window that can function as the owner for all pop-ups (performed by GetWindow), and enable or disable the modality of the main window and any dialog boxes (through EnableModeless).


Listing 22.4. Header information for IActiveScriptSiteWindow.

1: interface IActiveScriptSiteWindow : IUnknown

2:   {

3:   HRESULT GetWindow(HWND *phwnd);

4:   HRESULT EnableModeless(BOOL fEnable);

5:   };


Communications Between Scripting Engines and Hosts

With all the interfaces in place, communications can take place between the scripting engine and the scripting host. There are a number of behind-the-scenes steps involved, so let's look at the order in which they take place.

First, an instance of the scripting engine needs to be created in memory. To do this, the scripting host calls the CoCreateInstance() function. This function uses the class ID (CLSID) of the scripting language to identify the engine to be used. If one can't be found, the host will need to create a URL moniker (file address) for the engine, then download and bind it with the BindToObject() function.

Next, the script is loaded in preparation for interpretation. This is where the persistence of the scripting engine and host come into play. If the script has previously been lodged in memory, an IPersist method is called to bring the script directly into the scripting host. Otherwise, a new instance of the script itself is created, either through an IPersist method or by using IActiveScriptParse if information is being dynamically appended.

Once the script is locked and loaded, any pages, forms, or other entities that are top-level named entities are then directly imported into the script engine's namespace through the AddNamedItem() function of IActiveScript. This means that while forms and OLE objects from an HTML page would be loaded, HTML controls and other inherent elements wouldn't be. They would be referenced individually later, through the ITypeInfo and Idispatch methods.

Now it's time to initialize the script and let it loose. By calling SetScriptState through IActiveScript, the scripting host essentially executes the Main() equivalent in the scripting engine. This would include any prerequisite event polling, data binding, and other such supporting function work. This dirty work initiated by the SetScriptState call, which has to occur before the actual script begins its execution, involves using a function such as GetItemInfo from IActiveScriptSite, and methods such as IConnectionPoint and Idispatch's Invoke() function, to make sure that data, function entry points, and events are all properly handled to avoid exception errors.

And now the script is running! While it seems like a lot of work, once the functions are added into the code, everything is reasonably automatic, barring some catastrophe of programming typos.

Licensing

There are three basic ways to implement VBScript support in your application, depending on your level of coding expertise. The simplest is to wait for someone else to develop a function that takes care of it for you. Far from being a facetious statement, this is the absolute truth: If you can license or out and out purchase a well-built method of supporting VBScript in the manner you need, you're far better off going this route. An excellent example would be NCompass Labs' creation of ScriptActive, a Netscape Navigator browser plug-in that enables VBScript support where none previously existed. To you, as a developer, this provides seamless support-you write your code, and it functions as you expect. It's all just plug and play, like an additional ActiveX component.

In fact, this may be the route that a number of companies follow-waiting for the creation of a generic ActiveX control that serves as just a runtime VBScript interpreter and provides simple but effective hooks into it for non-programmers. After all, not everyone will want to go out and code in C just to get the functionality of VBScript into something that doesn't support it. But for those of you who do want to do some intricate coding, there are the other two options to consider.

The Binary Route

One step down the ease-of-use ladder from the "wait for someone else" approach is the use of the precompiled VBScript binary. In the form of VBScript.DLL, Microsoft provides you with the pieces you need to pass data back and forth while interpreting VBScript code. The advantage to this route is pretty clear-you're being provided with a be-all and end-all interpreter for the VBScript functions you want to use, and all you have to do is tie it in through function calls. By providing you with the DLL and some header files (and eventually, maybe some documentation), Microsoft expects that you'll be able to do the tough stuff without too much pain and agony. This is both the recommended and the most logical method for building VBScript into any application, be it a word processor, database application, or something of your own devising.

A benefit of using the VBScript binary is that users can download the latest copy of the binary itself from Microsoft, instantly adding features and fixing bugs whenever an update is available. By sharing a common DLL between potential scripting hosts, this also leads to shared use of that DLL resource. So when Internet Explorer and your newly created VBScript-enabled application reach for that same space of memory, everyone can get along peacefully. You also get the benefit of building your application to the general ActiveX scripting host specifications, which means you could plug in any other ActiveX scripting engine that was developed later on. Instant upgradability, no hassles.

By filling out the VBScript binary licensing agreement, you essentially agree to distribute VBScript binaries only in their original form, that you won't pick them apart to see how they work, and that you can send the distributable files out royalty-free. Pretty nice arrangement, huh? Don't take our word for it, though, make sure you check out the legal agreement word for word at http://www.microsoft.com/VBScript/us/vbsdown/vbseula.htm before you decide to do something with it. Don't expect to find much hidden in the agreement, though. Microsoft's statement on the whole thing is summed up in two sentences: "Licensing the VBScript binary for use in your application is very easy-simply acknowledge Microsoft in the "About Box" of your application. There is no charge for this license."

An important consideration is that getting hold of the VBScript binary is really just obtaining a pre-packaged item. Microsoft can sell the same package to countless developers, without incurring really significant costs or efforts to track down what's done with it, or answer too many questions. If you're looking for the cheap way out, this is definitely it. As we mentioned, barring any redistribution which isn't explicitly mentioned in the VBScript binary licensing agreement, it's free! Otherwise, you take the next step, the one that requires the involvement of the Microsoft legal department.

Source Code

Porting VBScript to another platform, or doing other things that require you to get at the very innards of VBScript itself, will require the VBScript runtime source code, which can be modified, tweaked, or mucked about with however you see fit. As you could probably guess, there are a large number of restrictions that get placed on you when you decide to go this route. If you develop a new technology, get a patent, or benefit in any number of ways from your hard work, Microsoft does too. In fact, if you look at the licensing, they become your partner. Not a "let's split it 50-50" partner, but rather a partner with as much ownership as you over what you've created and the rights to redistribute, sell, relicense, or otherwise make a tidy sum off your work. And did we mention that any profit they make is solely theirs?

Ownership considerations aside, VBScript Source Code licensing is not a common option. It's much more likely that your needs can be served by either porting your own scripting language or by using the VBScript binary licensing arrangement to take advantage of the binary convenience and price difference.

Putting It All Together-Microsoft's "Spruuids" Example

While different reasons exist for putting together a scripting host, the folks at Microsoft thought a game would be a neat way to demonstrate it all. "Spruuids" is one of the examples included in the ActiveX Developer's Kit, but it's in kit form, and some assembly is required to make it work. If you have the ActiveX SDK, or if you obtain it from Microsoft, you'll have all the files and sample code you need to get a jump start on creating your own host. Although it's not possible to cover everything that's done in the Spruuids sample, we'll look at a few pieces of it so that you can get an idea of what the creators of this whole hosting thing had in mind.

First, depending on the version of your ActiveX Developer's Kit, you may or may not be easily able to find the sample. In earlier editions, references were included in the online Infoviewer text, but later editions hid the reference a little deeper. If you have sample code installed, you'll find everything you really need in the Samples/AXScript/Spruuids/Src directory. If you don't have the sample code, try getting hold of the latest ActiveX Developer's Kit from http://www.microsoft.com/activex, or through a subscription to the Microsoft Developer's Network (any level).

The first stop on the tour is the README file found in the Src directory. This provides the outline of what performs what task so that you can skip over the pieces that you don't want to know about yet and jump to the heart of the matter. The three files to pay close attention to for their relation to ActiveX script hosting are:

NOTE
If you did check the Readme.txt file, and then looked at that list, you might be a bit confused. According to the Readme file, there's supposed to be an ActivScp.h file that serves as the ActiveX scripting header file. That may have been true in a previous release, but in recent releases, Game.h has taken its place and contains the header information for IActiveScriptSite. In the rush to get the ActiveX Developer's Kit out the door in answer to the demands for it, that's a small change that was overlooked and might be corrected by the time you get your development kit.

Inside Game.h, it's pretty easy to spot the section that deals with ActiveX scripting support- Microsoft puts a big comment line before and after the section, as shown in Listing 22.5.


Listing 22.5. Contents of Game.h header file.

 1: // ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####

 2:   // *** IActiveScriptSite methods ***

 3:   STDMETHOD(GetLCID)(LCID *plcid);

 4:   STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown 

      **ppiunkItem, ITypeInfo **ppti);

 5:   STDMETHOD(GetDocVersionString)(BSTR *pszVersion);

 6:   STDMETHOD(RequestItems)(void);

 7:   STDMETHOD(RequestTypeLibs)(void);

 8:   STDMETHOD(OnScriptTerminate)(const VARIANT *pvarResult, const EXCEPINFO 

      *pexcepinfo);

 9:   STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState);

10:   STDMETHOD(OnScriptError)(IActiveScriptError *pscripterror);

11:   STDMETHOD(OnEnterScript)(void);

12:   STDMETHOD(OnLeaveScript)(void);

13:   // *** IActiveScriptSiteWindow methods ***

14:   STDMETHOD(GetWindow)(HWND *phwnd);

15:   STDMETHOD(EnableModeless)(BOOL fEnable);

16: // #####  END  ACTIVEX SCRIPTING SUPPORT #####


This provides the hooks that Game.cpp is going to need, and that we've explored before. Since the idea of the program is to have a user interface (it is a game, after all), IActiveScriptingWindow is included, to allow interaction with the UI windows.

The contents of VBSGuids.h is really one item-the Globally Unique Identifier (GUID or Class ID) of the VBScript engine itself-{B54f3741-5B07-11cf-A4B0-00AA004A55E8}. Try saying that two times fast.

Inside Game.cpp is the heart and soul of the interactions, but if you tear it apart you'll see it's not really that mysterious. Neat, yes. Fun, yes. But not overwhelming. Again, it's going to require a real knowledge of C++ programming to get an idea of what's going on, but picking out the pieces that enable the ActiveX scripting support is about as easy as it gets-they're delineated, as shown in Listing 22.6.


Listing 22.6. What ActiveX scripting sections of Game.cpp look like.

 1: // ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####

 2: // Add the Game as a Named Item & load its code

 3:   g_clineOffset = 0;

 4:   hr = pgame->ParseFile(g_pszCodeFile, L"Game");

 5:   if (hr)

 6:     {

 7:     MessageBox(g_papp->m_hwndDlg, s_pszError ? s_pszError : "Unspecified 

        Error", "Spruuids", MB_OK | MB_ICONEXCLAMATION);

 8:     return hr;

 9:     }

10: // #####  END  ACTIVEX SCRIPTING SUPPORT #####


Future Hosts

Developers are already starting to build ActiveX scripting hosts for a variety of purposes. When Netscape didn't jump at providing ActiveX scripting support, NCompass Labs created the ScriptActive plug-in, which serves as a scripting host that builds seamless VBScript support right into Netscape Navigator. It's a perfect example of how extensibility on all fronts can provide quite an opportunity for developers. What will the next scripting hosts be? Anything from ActiveX plug-n-play components all the way to complete tools built around ActiveX scripting as their internal scripting language for access to functions. The possibilities are endless.

The big question is how many other languages will follow suit, and whether the extensibility of ActiveX scripting hosts will overpower or just complement existing JavaScript development. All that remains to be seen is who builds what people want, and how soon they do it.