This article will focus on Visual Studio for Applications, or VSA, and explain how it can be integrated with .NET programs. It will also explain a few key areas you must understand in order to successfully integrate VSA into your programs.

VSA provides an excellent means of providing extensibility to your program by allowing script code to modify your program long after the main object model has been designed and compiled. Allowing your programs to host and run script files is an excellent way to allow others to extend your program in ways you may not have foreseen at its induction. Many times, added functionality is required by end users or high demand clients that require new changes to be recompiled into the old system by its creators. This takes time and money, and is often only necessary because modifications require the use of professional build tools, something many users aren’t skilled at using. However, with a solid object model exposed to a scripting interface, anyone capable of writing macros using a text editor and following an SDK can tweak much of the functionality of your program without the major hassles involved with professional build tools.

VSA replaces the older technology Visual Basic for Applications, or VBA. VSA allows .NET programs to host and compile scripting languages such as VBScript and Jscript. Using VSA, you can create a scripting engine directly inside your .NET programs, and pass your own object instances and assembly references to the engine and script. Using your own custom object model, you can allow the scripts to modify your program after it has been compiled. Anyone with a text editor can follow up and add custom scripts or change existing ones, without having to bother you to make these changes.

There is supposed to be a VSA SDK available for download from the Microsoft Script Center, but I cannot find a link to it. Feel free to look for yourself by starting here at the Microsoft Script Center. The MSDN Library contains some help on the topic but it is limited. As usual, my most valued resource ended up being my old faithful friend Google. Just go out and lookup the various interfaces that I will discuss and you will find plenty of helpful articles.

The following assemblies are distributed with the .NET Framework and should be located in the GAC, or global assembly cache, on your system. Microsoft.Vsa.dll, Microsoft.Jscript.dll, and Microsoft.VisualBasic.Vsa.dll are included with the .NET Framework 1.1, and are readily accessible. Microsoft.Vsa.dll contains the core interfaces and objects needed to implement your own scripting engine and scripting host.

You will need to decide which scripting language your program will support. Microsoft has supplied two engines, one for JScript and the other for VBScript. You may create your own, but that is beyond the scope of this document. The two classes that you may choose from are as follows. Using these classes, you will be allowed to run JScript or VBScript inside your programs. This is the language that you will use to script your program.

Once you have located and added references to the assemblies mentioned above, you need to implement the IVsaSite interface to allow your program to communicate with the script engine. Implementing this interface allows the engine to notify you of compilation errors, as well as request information such as object instances from your program as they are needed. This is an added bonus as you can defer the creation of objects until they are needed by the engine.

A scripting engine is a class that implements the IVsaEngine, as defined in the Microsoft.Vsa namespace. A script engine is capable of loading, compiling, and running script code. A scripting engine must be initialized before it can be used. Once initialized, code items, global items such as object instances, or references can be added to the engine. With my experiences, the order of initialization must occur in the proper sequence in order to avoid errors.

Creation and initialization of a scripting engine is quite simple. The following steps are a guideline from my experiences that determine the order of operations for initializing a new scripting engine. The order is not concrete, but I have experienced errors when these steps are executed out of the order specified.

Once you are ready, and have determined which type of scripting engine you will use, simply create an instance of the desired class. This new instance will be used as your scripting engine. I typically use a variable of type IvsaEngine, but you may use the actual type of engine in your code.

// use the vbscript engine provided in the assembly Microsoft.VisualBasic.Vsa.dll
_engine = new Microsoft.VisualBasic.Vsa.VsaEngine();
// or// use the jscript engine provided in the assembly Microsoft.JScript.dll
_engine = new Microsoft.JScript.Vsa.VsaEngine();

A unique name called a root moniker is used to identify each scripting engine. The root moniker is in the format “protocol://path”and should be unique to the host. You can use whatever you want as the root moniker, but try naming it something relative to your program. For example, try “yourCompany://yourProgram/language/engineCount”. This will allow easier identification of the engines during debugging and inspection.

// set the root moniker used by the engine// (ex: "MyApp://MyVsaEngine/Instance#X")
_engine.RootMoniker = rootMoniker;

Each scripting engine will need to communicate with an IVsaSite instance. The scripting engine interface contains a property called “Site” that needs to be assigned your implementation of the IVsaSite interface.

// set the site used by the engine to ourself// as we are the host and will need to communicate with the engine
_engine.Site = this;

After the root moniker and site have been set, you must call the InitNew method on the engine. This prepares the engine to have new items added to it. Items such as code items, reference items, or global items. It must also occur before you set the root namespace of the engine.

In addition to the root moniker, you will need to specify a root namespace. All types created by the engine will be nested into this root namespace. When the engine runs a script, it will compile the script into an in-memory assembly. You will have access to all of the types created by the script, and the types loaded by the engine. You will need to use the root namespace you specified when the engine was created to gain access to the types you are looking for.

// set the root namespace used by the engine
_engine.RootNamespace = rootNamespace;

The first things that need to be addressed after the engine has been created and initialized are any references that may be needed by your scripts. References can be added to the engine by adding items of type VsaItemType.Reference. Any type that will be used in the script must have a reference supplied to the engine, otherwise your scripts will break. Just like in your program, you cannot use a type if you do not have a reference to its assembly.

You may create as many or as few assembly references as you want, however, without creating references to the Types in use, you will run into errors when the script executes. To create an assembly reference, add an item of type VsaItemTypes.Reference to the engine with its “AssemblyName” set to the name of the assembly. For example, to add an assembly reference to the System.dll assembly, the following code would be necessary:

Be mindful to add all of the references that your scripts might need. References such as System.dll or Mscorlib.dll are critical to the successful execution of your scripts. No references are added by default for you, you must take it upon yourself to add the needed references. Your scripts will be limited by the references it has access to.

After you have created all of the assembly references, you now need to add the script to be run by the engine. This can be done by adding an item of type VsaItemTypes.Code and setting its “SourceText” property to the contents of the script. This should be done before any global items are added to the script. Loading the script from a file is the most extensible means in my opinion, however you are not limited to this means. You could supply the script body from any source available, such as a resource file, or even in your own source code. If you choose to load the script from a file, simply use a StreamReader or another .NET framework class to read the contents of the file into a StringBuilder or String variable. After the script has been loaded from the file, simply set the “SourceText” property using the loaded script.

This is perhaps the most interesting part of hosting your own scripting engine. Many large scale applications allow scripts to modify the objects that are running the program. Using VSA, you can publish your own object instances from your program to be used by the scripts. How cool is that?!

To publish your own object instances and make them available to the script, you will add items of type VsaItemType.AppGlobal to the engine. As you add these global items to the engine, the name that you supply the item will be the name of the variable, or object instance, in the script. So take care when naming these items as it will affect their usage in the script. Take for example, you want to publish a form instance to a script. If you name the global item “mainForm”, then it will be accessible in the script as a global variable named “mainForm”. You may then call methods upon the variable just like you would in your program. To close the form, you might use mainForm.Close(); if you were using JScript.

After you add the code item, it is necessary to set the item’s “TypeString” property to the fully qualified type name of the object you are publishing. So again, if you were publishing a Form object, you might use the following code to set the item’s TypeString.

Here's some sample code that demonstrates how you might publish one of your object instances to be available to script:

Now, you might be wondering how the script is going to gain access to the actual instance you want to use? No where did you specify any variable instance yet to be used for this “mainForm” variable. The key is in the fact that the scripting engine communicates back with your program using the IVsaSite interface. Whenever the engine encounters an item of type VsaItemType.AppGlobal, it will call back on the IVsaSite interface to retrieve the object instance for the item.

At first, this seems a bit odd, but it does have its advantages. Because you are not required to pass the object instance to the engine when the global item is created, you can defer the object creation until the engine calls for it. This may not be useful, but under the right conditions, it could prove extremely useful to keep a small memory footprint and reduce resource consumption. This does present a problem for the implementer of the IVsaSite interface. How can you return the instance of the object when then engine calls for it, since it will ask for it using the name you used when you added the item?

This can be a simple case of using a Hashtable to store the object instances by their item name when they are added to the engine as a global item. You could create some other lookup or creation mechanism, but for the purposes of simplicity, we will assume that you will store the object instance in a Hashtable when you add a global item to the engine. Something like this…

Now that you have a way to lookup the instance by its item name, all you have to do is wait for the engine to call you back and return the object when it asks for it. Your specific implementation of the IVsaSite interface will dictate exactly how this lookup process will work. Only trial and error combined with careful design will lead you to the most useful implementation for your needs. Using a lookup table is pretty basic and not all that complex, so it’s easy to setup and debug. Here’s how this lookup appears in my example project. This code can be found in my VsaScriptingHost class.

///<summary>/// When the engine calls back the IVsaSite to ask for a global item,
/// return the instance if we've cached it previously
///</summary>///<paramname="name">The name of the global item
/// to which an object instance is requested</param>///<returns></returns>publicobject GetGlobalInstance(string name)
{
// enumerate the items in our global item lookup tableforeach(DictionaryEntry entry in _globalItemLookupTable)
// carefully comparing the item names// to the name of the item requested by the engineif (string.Compare(entry.Key.ToString(), name, true) == 0)
// and if we find it, return the object instance to the enginereturn entry.Value;
// and finally if we couldn't find any instance// for that name, just don't worry about itreturnnull;
}

Just because you can publish objects for the scripts to program against, doesn’t mean that you should. The more objects that you expose, the more resources you will use, and the slower things will get. Just keep that in mind as a rule of thumb. Try and keep this in mind as you publish objects for use in the scripts. Try to only publish objects that you know will be useful in the scripts. Only you can determine what is or is not useful for your scripts.

Publishing your own object model in this fashion allows the scripts to easily modify your existing program. Things like changing menu items or responding to program events, suddenly become trivial script code. Publishing a well thought out object model for the scripts to program against, effectively supplies the hooks needed to extend your program in the future.

There isn’t much left at this point. You’ve added your assembly references, you’ve loaded the script and added the code item, and finally, you’ve added your own objects so that the script can program against them. All you need to do at this point is compile the script and run it! You can accomplish this easily by calling the following methods upon the scripting engine:

// compile the script
_engine.Compile();
// run the script
_engine.Run();

The scripting engine will call you back using the IVsaSite interface through the OnCompilerError() method. Any errors encountered during compilation, or during script execution, will be captured and directed to this method. Depending upon the error, you will need to take the appropriate actions to handle the error. It’s been my experience that once you get an error, there’s not much you can do but correct the script and try again. Make sure you use decent amounts of structured exception handling in your script files to prevent errors from reaching this method.

So in the interest of wrapping things up, and getting you on to running your own scripts using VSA, let’s review some of the major points we’ve covered. Visual Studio for Applications, or VSA, replaces the older technology known previously as VBA, or Visual Basic for Applications. VSA is not limited to only VBScript, any .NET scripting language can be used. You may have to create a custom engine for scripting languages if you are trying to use some language other than VBScript or Jscript.

Microsoft has supplied us with two pre-built engines that can be used just for these languages. You will need to install the .NET Framework, and add references to the Microsoft.Vsa.dll and the assembly containing the engine of your choice. The aforementioned engines can be found in these assemblies. The VBScript engine is located in the Microsoft.VisualBasic.Vsa.dll, and the JScript engine is located in the Microsoft.Jscript.dll.

You must first create and then initialize your scripting engine before you can add items to it. Items define assembly references, the script to be executed, and any objects that you want to publish to the script. You will need to implement the IVsaSite interface to enable communication between the script engine and your program. Without this interface, you will not be notified of errors in the scripts, nor can the engine request instances for the published objects when it requires them.

The scripting engines are capable of compiling scripts, and then storing that compiled IL for later use. This can improve performance, but it’s not as extensible as compiling the scripts when your program runs. I would recommend just loading the scripts from a file at runtime. This allows anyone to modify the scripts and make changes to them using a simple text editor. Much easier to modify text than precompiled Intermediate Language.

I hope this article has helped you to understand the benefits of VSA, and helped you to understand how you might implement it in your own programs. I have written a small example and assembly that you can download and run/debug at your leisure. It features a scripting host that can run either VBScript or JScript code files. I tended to dislike VBScript so I focused on JScript, and so you will find more JScript examples than VBScript examples. The examples show in a very simple application how to publish objects and modify the program using the scripts. The scripts also show how to respond to events inside the program, directly in the script files!

The demo application simply creates a simple form object and publishes it to the scripts. The scripts modify the form by adding some menu items, and wiring up to some of the form's events.

I've created some wrapper classes to help make creating a scripting host and running script files trivial. There are two main classes with which you will work with:

VsaScriptingHost

VsaScriptingHostFactory

The VsaScriptingHost class exposes methods and events to help you load your scripts. Add objects and assembly references to the scripting host. This class also helps you compile safely and catch exceptions by means of an event. The events you'll need to work with are as follows:

In addition to the VsaScriptingHost class, I also created the VsaScriptingHostFactory which helps to create instances of the VsaScriptingHost class. The factory class is capable of determining the type of script engine to use based upon a script file. In other words, JScript (*.js) script files would result in a scripting host instance that is capable of running JScript. This is all quite painless with the factory hiding most of the grunt work and details from you. From the sample code, this is how you might combine these classes to work for you:

// create a new set of hosts
VsaScriptingHost[] hosts = _factory.Create(@"MyScriptingHost",
@"Scripting", true, Application.StartupPath);
// wire up to the events of each hostforeach(VsaScriptingHost host in hosts)
{
host.AssemblyReferencesNeeded +=
new ScriptingHostEventHandler(OnAssemblyReferencesNeeded);
host.CompilerException += new
ScriptingHostCompilerExceptionEventHandler(OnCompilerException);
host.GlobalItemsNeeded += new ScriptingHostEventHandler(OnGlobalItemsNeeded);
}
// execute the hostsforeach(VsaScriptingHost host in hosts)
host.Execute();

Using those events as triggers to setup your scripts and apps to be extended by the scripts is also quite trivial. From the sample, here's how I exposed the form instance to the scripts. Also note that I added the starting assembly (*.exe) as a reference to the host, because the form class is found in that assembly. Do not forget to add your assembly references!!!

Well, that's about it. Take a look at the source to the host and the hosting factory if you are wondering about the gritty details. I hope this helps you understand how you can add scripting capabilities to your .NET applications.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Hi,It is very nice solution.I have tried this solution in an application.It is working pretty good.
But i have experienced a problem when iam passing path of script to the application with the following code.
Iam using openfileDialog control to select the script file.and then trying to run the code given in the forum.It's not working.Insted of using openfiledialog if i give path directly it is working.

I found your article quite useful and by following this I have created my own scripting engine which also reads scripts from file and creates GlobalObjects to expose my Object MOdel to script. Whenever the engine calls back the IVsaSite to ask for a global item, it returns instance if it exists in _globalItemLookupTable.

Now if i want to create more than one instances of my Object Model inside script, I cannot do this because "GetGlobalInstance" method only returns instance and not the type. Is there any way to get type instead of instance so that I can create instance inside my script using new keyword.

I had considered adding VSA support to Razor, but it's really not a core concept for what Razor was built for. It would be trivial to write a SnapIn for Razor that exposed the hosting engine and SnapIns to scripts using a VSA host.

I won't be tackling that project anytime soon, or ever. As far as I know VSA is dead, and I've already bored myself of tinkering with it, lol, but it would be trivial to implement. Good luck should you choose to do so, come hit the [C]ode[R]eflection forums at http://mrbelles.brinkster.net/forums if you want help writing SnapIns or other areas of the framework.

A while back I was interested in accomplishing everything that is described here and learned the following when investigating VSA:

From Summit Software support on April 4, 2004:

In July 2003, Microsoft decided to withdraw Visual Studio from Applications (VSA) from its web site and from the Visual Studio Integration Program (see www.vsipdev.com for more information about VSIP). They also decided to discourage companies from beginning new evaluations of the VSA SDK. At the same time, Summit Software stopped acting as Microsoft's sales and support agent for VSA.<br />
<br />
Microsoft has not yet announced its strategy for offering any alternatives to VSA for companies seeking Macro editing and debugging features based on the technology found in Visual Studio .NET and the .NET Framework. Although I do not expect Microsoft to make any announcements in the near future, I know that Microsoft is very interested in learning about the needs of companies like yours who are building .NET applications that require Macro editing and debugging capabilities based on .NET.<br />
<br />
Microsoft continues to offer Visual Basic for Applications (VBA) for licensing to companies who want to utilize the same Macro editing and debugging facilities found in Microsoft Office. In fact, Microsoft will soon release the VBA SDK version 6.4, which includes the same version of VBA that Microsoft shipped as part of Microsoft Office 2003.<br />
<br />
Summit Software continues to represent Microsoft as its exclusive licensing agent for VBA. We have worked with several customers who are evaluating the possibility of integrating VBA into .NET applications. If you would like to find out more about this option, please let me know.

And, finally, from Microsoft themselves on April 18, 2004:

My apologies that information has been difficult to find; I will pass the VSIP page suggestion along to the right folks.<br />
<br />
We are working quite hard on the next application programmability platform, however, VBA is the current solution. VBA continues to ship in the latest versions of Office and will be actively supported for many years to come. Given your stated timeframe, VBA may be the best solution to get your project into production. While VBA is not .NET compatible, the object model work required to deliver a solid VBA-enabled application will be valid in the coming .NET-based applications programmability world.<br />
<br />
Regards,<br />
Lee Coward<br />
VBA Program Manager<br />

I've stopped trying to make VSA work since then. But, your posting caught my eye nonetheless. VSA was the right direction for VBA to go in, but... They're not there yet.

Thanks for the info, I had no idea that VSA had depreciated in worth, when I originally wrote the article. I didn't research it too thoroughly as you can tell, or hopefully I would have discovered the same information, but this definitely explains why I couldn't find any of the darn SDK's for VSA.

I really always liked the idea of scripting capabilities (i program, of course i am going to like code based solutions lol), this was just one of those fun projects to kill time. I'm really glad I posted, because everyone has been really helpfull to provide me with the info regarding the death of VSA. Had I not posted, never would have known.

Have you tried compiling and running VB.NET scripts repeatedly? The engine leaks resources every time you create one. In my application, I had to repeatedly run scripts 1000's of times, I eventually ran out of virtual memory. What I had to do was create a pool of engines and reuse them as much as possible. And it was frustrating because there was no documentation and MS says VSA is going to be discontinued.

Nice article though. I wish I had this when I needed it a few months back.

I've not really tested many vb scripts. I just don't like VB. :P I much prefer curly brackets and semi colons, just because of my C background.

I've only ran JScript through mine, other than the small VB script in the sample. Knowing that VSA is going to be discontinued, could explain why I can't find the sdk anymore. Do you have a link that talks about it being discontinued? Or what will replace it?

It sounds like I may need to learn a different technology here and re-post!

Well, the article did say that VSA compiles the script into a .NET assembly. That's probably your "leak", since .NET doesn't allow you to unload assemblies. If this matters to you, you'd have to host the VSA objects in a different AppDomain, and unload the AppDomain when you're done with that script and ready to compile a new one.

To my understanding VSA supports scripting languages. The only scripting languages that I've read it currently supports are VBScript.NET and JScript.NET. I'm sure someone may have created other engines, or will in the future to support other scripting languages.

Any .NET language that supports the CLR, could host a VSA site and compile and run scripts. I do not believe VSA to be the proper technology for running C# code, or VB code. That is what the compiler services and code dom's are for. At least according to my readings and the examples I've seen.

Perhaps I am not understanding you question correctly? And it's completely possible that I could be completely wrong too. That happens quite a bit! :P

What are your thoughts in more detail? Can VSA be used to run more .NET languages? I may have misinterpreted my readings in that it was soley for scripting technologies.

Excluding .NET there are two Microsoft integration methods for "attaching" some form of interpreted language into/onto an Application.

The (expensive) one is VBA, where a whole C++ SDK is needed but you get for free a standard editor/debugger/compiler - in fact, you also get both pure COM late binding and a sort of half-way house where the DISPID/NAMES are cached even though the IDispatch call is used.

The language here is closer to VB than to VBScript, hence the term VBA.

The other way is the Microsoft Script Host Control with associated VBSCript and JScript implementations. Here you can host the control yourself, expose objects, run methods, etc but, while there are debugger interfaces available, out-of-the-box nothing else is provided. Incidently, this is pure IDispatch without any caching of DISPID/NAMES. That is, these are true Scripting Languages, whereas everything else mentioned here (e.g. VB.NET) are not.

Btw, this is how .vb and .js files are executed. The program cscript.exe or wscript.exe hosts the Control mentioned and does the work for you. It also handles a new XML job format.

The evolution of VBA is VSA which is simply an SDK from the same Microsoft partner company that does VBA with all the advantages of .NET - that is, in theory, any .NET available language could be used instead of the old VBA, although the VSA SDK would limit which ones and how. The language would be VisualBasic.NET or C#.NET or anything with CodeDom etc support.

Unfortunately, I've not seen any Microsoft clear way forward on the Script Host Control and the cscript.exe/wscript.exe/XML job methods.

There are people on CodeProject who've submitted .NET equivalents of the XML job methods with a custom exe to interpret them.

There's also a mysterious reference on the VSA website regarding a free Microsoft Script IDE for .NET things.

Any information you've found on this IDE would be greatly appreciated.