SAMPLE: MFCAxs.exe Implements an Active Script Host Using MFC
ID: Q168214
|
The information in this article applies to:
-
Microsoft Internet Explorer (Programming) versions 3.01, 3.02, 4.0, 4.01
-
Microsoft Visual Basic, Scripting Edition, versions 1.1, 2.0
-
The Microsoft Foundation Classes (MFC), used with:
-
Microsoft Visual C++, 32-bit Editions, version 5.0
SUMMARY
MFCAxs.exe is a sample that contains MfcAxscrVb. MfcAxscrVb is an
instructional sample Active Scripting host written using MFC. It hosts the
VBScript DLL; however, the Active Scripting mechanisms are generic to any
Active Scripting engine. MfcAxscrVb shows just one possible way to provide
dispatch objects from the host, to implement the host's Active Scripting
interfaces, and to connect the messages of windows in the host with events
fired to the scripting engine.
MfcAxscrVb includes a Visual C++ 5.0 workspace and project file. Although
no VC++ 4.2 compatible makefile or mdpfile is provided, the source code for
MfcAxscrVb is compatible with MFC 4.2b.
Prerequisites: COM, Automation
MORE INFORMATION
The following file is available for download from the Microsoft
Software Library:
MFCAxs.exe
Release Date:
For more information about downloading files from the Microsoft Software
Library, please see the following article in the Microsoft Knowledge Base:
Q119591 How to Obtain Microsoft Support Files from Online Services
Running the Sample
When you run the MfcAxscrVb sample you will notice a large edit control in
the upper left-hand corner of the application. You can paste or type script
into this window and select the "Run Script" button to invoke the script.
Several test scripts are provided for you in the Script.txt and
Testevents.txt files.
Exposing the Host: Dispatch Objects
The main focus for this sample is the implementation of dispatch objects
using MFC. The reason for this is simple. A scripting language isn't much
use if there isn't anything to script.
The core technology upon which all Active Scripting depends is most
certainly that of Automation. A solid working knowledge of Automation is
absolutely necessary before attempting to code any Active Scripting host.
There are several good references for Automation: chapters 14 and 15 of
Brockshmidt's "Inside OLE," chapter 11 of Dale Rogerson's "Inside COM," and
the "OLE Automation Programmer's Reference."
CCmdTargetPlus:
What you need are some objects that allow for scriptable properties and
methods through IDispatch but also generate scriptable events. MFC
definitely does most of the grunt work for you when implementing dispatch
interfaces for a COM object. However, the only MFC class that supports both
incoming dispatch interfaces and outgoing dispatch interfaces, or event
sinks, is COleControl. COleControl is much too bulky for what you want. You
just want events, a dispatch interface, and type information. VBScript
needs to read type information for dispatch objects that support events.
You could either create this on the fly, or create a static type library
for your host's dispatch objects and obtain the ITypeInfo for each
individual object.
While probably not necessary, MfcAxscrVb has a base class called
CCmdTargetPlus that supports these three pieces: dispatch interface,
connection points with a control-style event map, and easily accessible
type information through IProvideClassInfo. Most of the code in
CmdTargetPlus was added to allow for event maps, which makes adding events
to 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 the
dispatch objects (CAButtonDispatch, CBButtonDispatch, and so forth) for a
boilerplate on what is necessary. A brief rundown on MfcAxscrvb's object
model is listed at the end of this document.
If you add your own objects, be sure to double-check for three unique GUIDs
for the primary dispatch and primary events interfaces, and for the clsid
for the object in sum. This needs to be done in the implementation file and
in the .odl file. (Cut and Paste helps here.) Cut and paste all of the
class wizard macros, and then change them to the new class name. Class
Wizard 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, and
methods to your dispatch object as if it were an MFC OLE Control. This is
sort of handy.
MfcAxscrVb keeps the object implementing the dispatch mechanisms separate
from the MFC object, which it is actually referring to. For example, the
dialog box has a button called the "AButton," which is scriptable. The MFC
button object is CButton. The CCmdTargetPlus-derived class,
CAButtonDispatch, is separate and distinct. This is a key point. Only what
the host programmer explicitly chooses to expose for basic MFC objects and
Windows 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 by
connection points in the host object.) This sample accomplishes this by
adding standard MFC message handlers into objects derived from the various
classes. Each of these handlers merely has to call FireEvent (courtesy of
the code added to CCmdTargetPlus) on the dispatch object. Everything else
happens automatically.
Because the dispatch object and the actual MFC window object are separate
in this sample, the dialog parent needs to explicitly hook the two
together. This is done in the Mfcaxscrvbdlg constructor.
Named Items
Once you have dispatch objects, you have to set them up so that the script
engine knows about them. The collection of all the named dispatch objects
that the script engine knows about is called the "Script Namespace." Items
are added to the script namespace through the IActiveScript::AddNamedItem
method. As I mentioned previously, it is the host's duty to implement an
IDispatch for the object and support type-information through ITypeInfo for
the object. When the script engine needs to resolve a reference to a named
item, it uses the IActiveScriptSite::GetItemInfo method to request an
IUnknown pointer (which it queries mainly for IDispatch) and an ITypeInfo
pointer.
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 are
accessible through a higher-level object. A common metaphor is the
Application->Document->Item hierarchy, where the Document object is
accessible from the top-level Application object as an IDispatch property
of the Application object itself. In the same manner, the Document object
itself exposes several sub-objects and each is an Item that you can obtain
through an Item array or other contrivance.
Using this scheme, the scripting engine is smart enough to navigate to a
sub-item when the script code says the following without needing Document
or 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 parent
object, 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 the
context of the Window object without always prefixing all references to
objects with "Window." A script host needs to identify the top-level script
item in the call to AddNamedItems by using the SCRIPTITEM_GLOBALMEMBERS
flag. What usually isn't made clear by Active Scripting documentation,
however, is that only named items can support events. Using the above
example, there is no syntax to say the following to handle an event in a
particular item:
Sub Application.Document.Item(1)_OnEvent ' this doesn't work!
So, for MfcAxscrvbdlg, because we want to be able to handle the events of
every object in the hierarchy, every object is added as a Named Item. Each
object is also accessible through the top-level Scripter object (the dialog
itself), which does not support events currently.
WebBrowser Control:
In this sample, the script host exposes only one external object to the
script namespace. This is the WebBrowser control hosted on the dialog by
MFC's default control containment support. All you need is an AddRef'd
dispatch pointer to the control, and you're set. For events, the external
object needs to expose an ITypeInfo pointer somehow. MfcAxscrVb gets this
through IProvideClassInfo::GetClassInfo. As a last resort, a host could
read the object's type-information itself to expose an ITypeInfo.
Fortunately, the WebBrowser control supports GetClassInfo, so MfcAxscrVb
doesn'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 using
CoCreateInstance. Alternatively, the host can support a generic means for
obtaining external objects, albeit ones that don't need to have events
handled. 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 special
method. This method demonstrates a simple means for simulating VB's
CreateObject function. Note that the method is named HostCreateObject to
emphasize 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 to
provide this ability for security reasons, IE3 is a prime example. The
primary goal of HostCreateObject is to return the dispatch pointer of the
requested object. Once the scripting engine has the dispatch pointer, it
knows what to do from there.
NOTE: As of version 2.0, the VBScript and JScript engines now support
CreateObject as a built-in function. The host does not need to implement
this function. This function will, however, test an object to make sure it
is safe for scripting before allowing the object to be used. Objects deem
themselves safe for scripting by either supporting the IObjectSafety
interface or marking the appropriate Component Category registry entries
for Safe-For-Scripting. Refer to the Microsoft Knowledge Base for more
information.
Class Wizard Support
The last little bit of trickery in MfcAxscrVb is the manipulations made to
support ClassWizard. Class Wizard, technically isn't "dumb." It is just
mentally challenged. It is pretty convenient for what it does, but remember
that it just blindly searches files looking for those special ClassWizard
comments. If you follow the format for COleControl, you can just about get
away with using ClassWizard's Automation and Events tabs.
One complication is that MfcAxscrVb uses derivation in some of its object
hierarchy. All of the button objects are derived from a common
CButtonDispatch object that exposes properties and methods, all of which it
would individually support. However, when it comes time to fill out the
.odl file, each individual object needs to contain all of the properties
and methods of the parent class. Needless to say, this is going to confuse
Class Wizard when it comes time to number the DISPIDs. If you use Class
Wizard, make sure you double-check the generated DISPIDs in two places: the
.odl file for the automation class and the enumeration inside the class
definition. When working with the derived button classes, it is easy to see
the same DISPID assigned to multiple properties and methods. It is much
easier 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 properties
through ClassView, it's a toss up whether ClassWizard is needed or not. But
MfcAxscrVb supports it for now. Enjoy.
Appendix A: Object Model
Scripter:
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
Properties- Caption: Text of button face.
Methods
Press: Acts as if the button were pushed.
- Press: Acts as if the button were pushed.
Events- 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:
BroCon is the name of the WebBrowser control on the dialog box. It supports
all properties and methods as documented in the Internet Client SDK/ActiveX
SDK for the WebBrowser control.
EditCon:
Methods- 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.
Events- 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.
Lbox:
Methods- 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.
Events- 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 box
element strCancelled.
- OnSelChange(strChange): When a list box element strChange is selected by the user.
Additional query words:
Keywords : kbfile kbole kbsample kbIE301 kbIE400 kbIE401 kbMFC kbVBScript100 kbVBScript200 kbVC500 kbIE302
Version : WINDOWS:1.1,2.0,3.01,3.02,4.0,4.01; winnt:5.0
Platform : WINDOWS winnt
Issue type :
Last Reviewed: April 6, 1999