SAMPLE: MFCAxs.exe Implements an Active Script Host Using MFC

This article was previously published under Q168214
This article has been archived. It is offered "as is" and will no longer be updated.
MFCAxs.exe is a sample that contains MfcAxscrVb. MfcAxscrVb is aninstructional sample Active Scripting host written using MFC. It hosts theVBScript DLL; however, the Active Scripting mechanisms are generic to anyActive Scripting engine. MfcAxscrVb shows just one possible way to providedispatch objects from the host, to implement the host's Active Scriptinginterfaces, and to connect the messages of windows in the host with eventsfired to the scripting engine.

MfcAxscrVb includes a Visual C++ 5.0 workspace and project file. Althoughno VC++ 4.2 compatible makefile or mdpfile is provided, the source code forMfcAxscrVb is compatible with MFC 4.2b.

Prerequisites: COM, Automation
More information
The following file is available for download from the Microsoft Download Center:
For additional information about how to download Microsoft Support files, click the following article number to view the article in the Microsoft Knowledge Base:
119591 How to Obtain Microsoft Support Files from Online Services
Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help to prevent any unauthorized changes to the file.

Running the Sample

When you run the MfcAxscrVb sample you will notice a large edit control inthe upper left-hand corner of the application. You can paste or type scriptinto this window and select the "Run Script" button to invoke the script.Several test scripts are provided for you in the Script.txt andTestevents.txt files.

Exposing the Host: Dispatch Objects

The main focus for this sample is the implementation of dispatch objectsusing MFC. The reason for this is simple. A scripting language isn't muchuse if there isn't anything to script.

The core technology upon which all Active Scripting depends is mostcertainly that of Automation. A solid working knowledge of Automation isabsolutely necessary before attempting to code any Active Scripting host.There are several good references for Automation: chapters 14 and 15 ofBrockshmidt's "Inside OLE," chapter 11 of Dale Rogerson's "Inside COM," andthe "OLE Automation Programmer's Reference."


What you need are some objects that allow for scriptable properties andmethods through IDispatch but also generate scriptable events. MFCdefinitely does most of the grunt work for you when implementing dispatchinterfaces for a COM object. However, the only MFC class that supports bothincoming dispatch interfaces and outgoing dispatch interfaces, or eventsinks, is COleControl. COleControl is much too bulky for what you want. Youjust want events, a dispatch interface, and type information. VBScriptneeds to read type information for dispatch objects that support events.You could either create this on the fly, or create a static type libraryfor your host's dispatch objects and obtain the ITypeInfo for eachindividual object.

While probably not necessary, MfcAxscrVb has a base class calledCCmdTargetPlus that supports these three pieces: dispatch interface,connection points with a control-style event map, and easily accessibletype information through IProvideClassInfo. Most of the code inCmdTargetPlus was added to allow for event maps, which makes adding eventsto derived classes much easier.

Dispatch Objects:

All host-provided dispatch objects are implemented using CCmdTargetPlus.There are a few things they need to do to work well. Refer to any of thedispatch objects (CAButtonDispatch, CBButtonDispatch, and so forth) for aboilerplate on what is necessary. A brief rundown on MfcAxscrvb's objectmodel is listed at the end of this document.

If you add your own objects, be sure to double-check for three unique GUIDsfor the primary dispatch and primary events interfaces, and for the clsidfor the object in sum. This needs to be done in the implementation file andin the .odl file. (Cut and Paste helps here.) Cut and paste all of theclass wizard macros, and then change them to the new class name. ClassWizard is very particular, but if you follow everything just right in the.odl, .h, and .cpp files, you can use it to add new events, properties, andmethods to your dispatch object as if it were an MFC OLE Control. This issort of handy.

MfcAxscrVb keeps the object implementing the dispatch mechanisms separatefrom the MFC object, which it is actually referring to. For example, thedialog box has a button called the "AButton," which is scriptable. The MFCbutton object is CButton. The CCmdTargetPlus-derived class,CAButtonDispatch, is separate and distinct. This is a key point. Only whatthe host programmer explicitly chooses to expose for basic MFC objects andWindows controls will be scriptable.

CEventsButton, CEventsEdit, CeventsListBox:

For events, there needs to be some code that responds to a Windows event--say, a button click--and generates an event for the script engine.(Remember an event is a dispatch Invoke on an interface handled byconnection points in the host object.) This sample accomplishes this byadding standard MFC message handlers into objects derived from the variousclasses. Each of these handlers merely has to call FireEvent (courtesy ofthe code added to CCmdTargetPlus) on the dispatch object. Everything elsehappens automatically.

Because the dispatch object and the actual MFC window object are separatein this sample, the dialog parent needs to explicitly hook the twotogether. This is done in the Mfcaxscrvbdlg constructor.

Named Items

Once you have dispatch objects, you have to set them up so that the scriptengine knows about them. The collection of all the named dispatch objectsthat the script engine knows about is called the "Script Namespace." Itemsare added to the script namespace through the IActiveScript::AddNamedItemmethod. As I mentioned previously, it is the host's duty to implement anIDispatch for the object and support type-information through ITypeInfo forthe object. When the script engine needs to resolve a reference to a nameditem, it uses the IActiveScriptSite::GetItemInfo method to request anIUnknown pointer (which it queries mainly for IDispatch) and an ITypeInfopointer.

Not everything that a host wants to be scriptable needs to be a named item.Usually the host will have a hierarchy of objects, where sub-objects areaccessible through a higher-level object. A common metaphor is theApplication->Document->Item hierarchy, where the Document object isaccessible from the top-level Application object as an IDispatch propertyof the Application object itself. In the same manner, the Document objectitself exposes several sub-objects and each is an Item that you can obtainthrough an Item array or other contrivance.

Using this scheme, the scripting engine is smart enough to navigate to asub-item when the script code says the following without needing Documentor Item to be added to the script namespace:
Set Obj = Application.Document.Item(1)				
As long as the sub-objects are exposed as get-properties of their parentobject, the script engine finds them successfully. Interestingly enough,the script engine is able to "assume" the top-level item in the hierarchy.What this means is that if Application is declared as the top-level item,then it is sufficient to say the following for the prior script code:
Set Obj = Document.Item(1)				
This is exactly how Internet Explorer 3.0 allows you to script code in thecontext of the Window object without always prefixing all references toobjects with "Window." A script host needs to identify the top-level scriptitem in the call to AddNamedItems by using the SCRIPTITEM_GLOBALMEMBERSflag. What usually isn't made clear by Active Scripting documentation,however, is that only named items can support events. Using the aboveexample, there is no syntax to say the following to handle an event in aparticular item:
   Sub Application.Document.Item(1)_OnEvent ' this doesn't work!				
So, for MfcAxscrvbdlg, because we want to be able to handle the events ofevery object in the hierarchy, every object is added as a Named Item. Eachobject is also accessible through the top-level Scripter object (the dialogitself), which does not support events currently.

WebBrowser Control:

In this sample, the script host exposes only one external object to thescript namespace. This is the WebBrowser control hosted on the dialog byMFC's default control containment support. All you need is an AddRef'ddispatch pointer to the control, and you're set. For events, the externalobject needs to expose an ITypeInfo pointer somehow. MfcAxscrVb gets thisthrough IProvideClassInfo::GetClassInfo. As a last resort, a host couldread the object's type-information itself to expose an ITypeInfo.Fortunately, the WebBrowser control supports GetClassInfo, so MfcAxscrVbdoesn't do this.

If you wanted to add other external automation objects to the namespace,all that usually is necessary is to obtain an IUnknown interface usingCoCreateInstance. Alternatively, the host can support a generic means forobtaining external objects, albeit ones that don't need to have eventshandled. In Visual Basic, the function to do this is called CreateObject.

Getting to Other Apps: HostCreateObject

Other than the set of properties used to expose all of the child objects,the main Scripter object (the dialog class itself) exposes only one specialmethod. This method demonstrates a simple means for simulating VB'sCreateObject function. Note that the method is named HostCreateObject toemphasize that this method is not provided for free by the script engine,but the host must implement it itself. Some hosts clearly would not want toprovide this ability for security reasons, IE3 is a prime example. Theprimary goal of HostCreateObject is to return the dispatch pointer of therequested object. Once the scripting engine has the dispatch pointer, itknows what to do from there.

NOTE: As of version 2.0, the VBScript and JScript engines now supportCreateObject as a built-in function. The host does not need to implementthis function. This function will, however, test an object to make sure itis safe for scripting before allowing the object to be used. Objects deemthemselves safe for scripting by either supporting the IObjectSafetyinterface or marking the appropriate Component Category registry entriesfor Safe-For-Scripting. Refer to the Microsoft Knowledge Base for moreinformation.

Class Wizard Support

The last little bit of trickery in MfcAxscrVb is the manipulations made tosupport ClassWizard. Class Wizard, technically isn't "dumb." It is justmentally challenged. It is pretty convenient for what it does, but rememberthat it just blindly searches files looking for those special ClassWizardcomments. If you follow the format for COleControl, you can just about getaway with using ClassWizard's Automation and Events tabs.

One complication is that MfcAxscrVb uses derivation in some of its objecthierarchy. All of the button objects are derived from a commonCButtonDispatch object that exposes properties and methods, all of which itwould individually support. However, when it comes time to fill out the.odl file, each individual object needs to contain all of the propertiesand methods of the parent class. Needless to say, this is going to confuseClass Wizard when it comes time to number the DISPIDs. If you use ClassWizard, make sure you double-check the generated DISPIDs in two places: the.odl file for the automation class and the enumeration inside the classdefinition. When working with the derived button classes, it is easy to seethe same DISPID assigned to multiple properties and methods. It is mucheasier to correct the DISPIDs, though, than to add everything by scratch,so the functionality has been left in MfcAxscrVb.

With Visual C++ 5.0's improved support of IDL methods and propertiesthrough ClassView, it's a toss up whether ClassWizard is needed or not. ButMfcAxscrVb supports it for now. Enjoy.

Appendix A: Object Model


This is the master object from which all other objects are derived. It is"IMfcaxscrvbDlg" in the .odl file but is added as the Named Item"Scripter." It has one method, HostCreateObject, which is described above.

Button Objects:

AButton BButton, CancelButton, OKButton, RunScript

  • Caption: Text of button face.
MethodsPress: Acts as if the button were pushed.
  • Press: Acts as if the button were pushed.
  • OnClick: Fired when button is pushed.
  • OnMouseOver: Fired when mouse rolls over button.
  • OnFocus(bSet): Fired when focus is given or taken from button.

BroCon is the name of the WebBrowser control on the dialog box. It supportsall properties and methods as documented in the Internet Client SDK/ActiveXSDK for the WebBrowser control.


  • AppendLine(strToAdd): Adds strToAdd to end of edit text window.
  • InsertLine(strToAdd, nWhere): Adds strToAdd at line #nWhere.
  • RemoveLine(nWhere): Removes line # nWhere from edit window.
  • OnMouseOver: Fired when mouse rolls over window.
  • OnChar(strChar): Fired when character strChar is entered into window.
  • OnFocus(bSet): Fired when focus is given or taken from button.

  • AddString(strIn): Adds StrIn into list box.
  • ClearList: Clears all strings from list box.
  • RemoveString(strRemove): Removes first instance of StrRemove and removes it from list box.
  • SelectString(strSelect): Selects string specified by strSelect.
  • OnMouseOver: Fired when mouse rolls over window.
  • OnFocus(bSet): Fired when focus is given or taken from button.
  • OnSelCancel(strCancelled): When selection is cancelled in a list boxelement strCancelled.
  • OnSelChange(strChange): When a list box element strChange is selected by the user.

Article ID: 168214 - Last Review: 10/26/2013 01:13:00 - Revision: 3.0

  • Microsoft Foundation Class Library 4.2
  • kbnosurvey kbarchive kbdownload kbfile kbinfo kbsample KB168214