MediaForge/XML Automation and Universal Scripting
by Robert Adamson

Introduction:

Version 4.3.76 of MediaForge/XML developed by XMLAuthor Inc. introduces full automation and universal scripting to the MediaForge authoring system. Even the earliest versions of MediaForge from Instant Replay Corp. include MediaBasic, a very powerful and standard scripting language. The combination of a powerful multithreaded and objected oriented multimedia front end with MediaBasic scripting resulted in early adoption of the MediaForge technology with high marks and reviews including the Best of Comdex award. But that was then and although MediaBasic is a very powerful and robust language compared to proprietary scripting languages such as Lingo from Macromedia, the language has limitations compared to powerful stand alone languages such as Visual C++ or Visual Basic from Microsoft. MediaForge Automation and Universal Scripting does not eliminate MediaBasic from the MediaForge authoring system. It provides a very powerful and extendable method of working with MediaForge projects that can be used either in place of or in conjunction with MediaBasic.

MediaForge Automation and Universal Scripting now makes it possible to script MediaForge projects in any language that is COM enabled, such as C++, VB, Java and JavaScript. Automation and Universal scripting is intended for multimedia developers building commercial applications and for developers already proficient in standard programming languages who prefer using them even in an authoring environment.

Overview:

A good place to start is with an overview of the main ingredients in the MediaForge automation system.

Automation Ingredients

(1) Mirage – An ActiveX control for running MediaForge Projects inside of other programs or web browsers or even inside of MediaForge itself. The Mirage control provides access to the MediaForge Automation Object for complete scripting of MediaForge projects using JavaScript or VBScript.

(2) Mirage Event Firing – These are actions available for any MediaForge event and are used to fire notification events into the Mirage ActiveX control. This makes it possible for Java Script, VBScript or any application for that matter to receive events from MediaForge projects running inside via Mirage.

(3 )MediaForge Automation ObjectThis is the stage door into the MediaForge Authoring system. The MediaForge object exposes hundreds of MediaForge methods, properties and other objects. This object is available to MediaBasic, Mirage (JavaScript, VBScript), and Universal Scripting Objects written as DLLs or ActiveX controls.

(4) Event Handlers Actions available to any MediaForge event for calling Methods inside of Dlls or ActiveX controls. Any Dll or ActiveX controls can now be used to handle MediaForge events and universally script MediaForge. The automation event handlers are CallMethod and CallMethodAsync.

(5) MediaBasic Extensions – The mfoGetObject extension in MediaBasic can obtain the dispatch pointer for any ActiveX control used in MediaForge or the MediaForge Object itself. Once the MediaForge Object is obtained using MediaBasic extensions is optional because the MediaForge object exposes the same extensions through COM.

(6) Exported Function – The exported function GetMediaForge returns a dispatch pointer to the MediaForge object. This makes if possible for any Dll or ActiveX control used by MediaForge to obtain access on its own to the main MediaForge object for scripting purposes.

(7) DLL and ActiveX Scripting Objects – To write scripting objects all that is need is to create either a DLL or and ActiveX control and insert it into a project using the generic MediaForge DLL or ActiveX objects.

A summary diagram of the automation elements is shown below in (figure 1).

figure 1

Implementation:

· Mirage Implementation

For detailed information on using Mirage in a Web Browser including JavaScript and VBScript samples see "Inside Mirage." A few things need to be pointed out here though regarding MediaForge Automation. Two new methods have been added to the Mirage ActiveX control for much more robust usage: "MediaForge" and "ParentMediaForge."

The Mirage "MediaForge" method returns a dispatch pointer to the actual MediaForge automation object for controlling the currently running project. So for example in VBScript within an HTML document you could issue mirage.mediaforge.play "My Song", where mirage is the name of the Mirage ActiveX control object defined in HTML using the <OBJECT tag. In other words:

<OBJECT WIDTH="640" HEIGHT="480" classid="CLSID:21F16767-8DA7-4113-BEB0-F161B313407F"

id="mirage"

CODEBASE="http://mediaforge.com/downloads/xmirage.exe#Version=4,3,76,0">

</OBJECT>

.

.

mirage.mediaforge.play "My Song"

.

So here you are using the MediaForge object in Mirage in order to control the MediaForge project embedded into the HTML page. That alone is a very easy to use and powerful feature providing HTML scripting languages with access to the entire MediaForge project. But what if you have inserted and HTML page into a MediaForge project, basically the opposite scenerio. Using the Web Browser controls in MediaForge is easy, just use the generic ActiveX Object. In this case it would be nice if the JavaScript or VBScript inside of the HTML file used in the MediaForge project could script the project in which the HTML page is embedded. This may seem complicated at first glance, but in the real world this is a great feature. This inverted scripting is the purpose of the "ParentMediaForge" method. This returns a dispatch pointer to the MediaForge Object of the parent project hosting the web control. For example: mirage.parentmediaforge.hide "My Web Site" could be used to hide the web browser control. If this was the same control that holds the VBScript then it would be hiding itself.

Another feature in the Mirage control is Event Firing. Events from Mirage require event handlers with the following syntax:

(Note – All Syntax in this document is described in C++)

void FireMediaForgeEvent(LPCTSTR ObjectName, LPCTSTR Request, long Parm1, long Parm2, long Parm3, long Parm4)

Mirage does not just fire events all by itself, these events are fired back to the hosting client application upon request from the MediaForge project (See #4 above). Events are fired directly from actions on the events tab of any object or they can be fired from MediaBasic or a Scripting Dll or ActiveX control using the MediaForge object’s "FireMirageEvent" method.

As you can see, there is a great deal of connectivity between MediaForge projects and applications that host them using Mirage as well as Web Browser controls hosted by MediaForge thanks to the MediaForge Automation environment.

Example: www.mediaforge.com/samples/automation/channel.html

In the following JavaScript code taken from the sample above, JavaScript is used to script the MediaForge Channel project using the MediaForge automation object obtained from the Mirage MediaForge method. Notice how JavaScript dot notation is used to traverse directly to the MediaForge object.

Important - it is necessary to specify <scripting> on the options parameter of the LaunchMFG method in order to use the MediaForge object from JavaScript or VBScript. The <scripting> request actually causes the MediaForge object to be created within Mirage.

First the project is launched using the Mirage LaunchMFG requesting scripting, then an alert me ssage is displayed. Finally, scripting is performed on the project resulting in a watch and its alpha channel to merge and move around. Almost unlimited scripting of MediaForge projects is possible from either JavaScript or VBScript. In the original version of this sample, MediaBasic was used to merge the clock using the exact same remove and play requests on the objects, but here we have replicated the same requests in JavaScript.

var Project=location.href.replace("html","xmfg");

result =XMirage.LaunchMFG(true,Project", "", "", "<noprogress><sizeable><scripting>");

alert("Press Enter and JavaScript will Merge the watch with its mask")

XMirage.MediaForge.Remove("textImage");

XMirage.MediaForge.Remove("textMask");

XMirage.MediaForge.Remove("mergeButton");

XMirage.MediaForge.Play("Mask");

XMirage.MediaForge.Play("Image");

XMirage.MediaForge.Remove("Mask");

XMirage.MediaForge.Remove("Image");

· Mirage Event Firing Implementation

Within HTML, JavaScript and VBScript are able to handle events fired from any ActiveX control. The Mirage control has one event "MediaForgeEvent" that fires upon request from a MediaForge project. The following piece of HTML is used to handle the MediaForgeEvent and is taken from the sample project www.mediaforge.com/samples/automation/channel.html

<SCRIPT FOR="XMirage" EVENT="MediaForgeEvent(objectName,xmlString, parm1, parm2, parm3, parm4 )" LANGUAGE="JavaScript">

alert(xmlString)

</SCRIPT>

The first parameter is the name of the MediaForge object firing the event and the second parameter is an XML String. The other parameters are integer values that are only used when an event is fired from a project using the MediaForge Object.

There are two ways to fire Mirage events. The first is to use the "Fire Mirage Event" or "Fire Mirage Event Async" found on the actions list in the Events Tab on any object (figure 2). You can also use the Script Flow object to fire the event using the same Actions.

figure 2

The second way is to use the MediaForge Object from within MediaBasic or a scripting DLL or ActiveX Control. For Example:

dim MediaForge as Object

Set MediaForge = mfoGetObject("MediaForge")

result = MediaForge.FireMirageEvent("Result", "<string>Watch is Finished Moving</string> ", 1, 2, 3, 4)

· MediaForge Automation Object Implementation

Code from the SampAutomation project located at www.mediaforge.com/codesamples/automation/sampautomation.zip is used in the following discussions.

Once MediaBasic scripts or DLL and ActiveX scripting objects have access to the MediaForge Automation Object they have the stage door into the entire MediaForge authoring system. The MediaForge object is just a com dispatch pointer. Methods and properties in the MediaForge object can be used with either early or late binding. The trick is to get access to the MediaForge Automation Object. To get the MediaForge Automation Object using MediaBasic see MediaBasic Extension Implementation below. For DLLs see Exported Function Implementation below. For ActiveX controls, a dispatch pointer to the MediaForge Automation Object is provided automatically. All you need to do is include a SetMediaForge method in your ActiveX control and MediaForge will call it with a dispatch pointer to the MediaForge Automation Object. For example in C++:

 

Header File

LPDISPATCH m_pMediaForge; // Dispatch pointer to the MediaForge com object

IMediaForge m_MediaForge; // COM Interface class created with stg.tlb (type library)

.

.

.

//{{AFX_DISPATCH(CSampAutomationCtrl)

afx_msg BOOL EventHandler1(LPCTSTR xmlString);

afx_msg BOOL SetMediaForge(LPDISPATCH pMediaForge);

//}}AFX_DISPATCH

CPP File

BOOL CSampAutomationCtrl::SetMediaForge(LPDISPATCH pMediaForge)

{

m_pMediaForge = pMediaForge;

m_MediaForge.AttachDispatch(m_pMediaForge);

return TRUE;

}

Now the ActiveX Control can script MediaForge using early binding with the m_pMediaForge dispatch pointer or using late binding with the m_MediaForge class. Below is an example of how to use late binding to Play an object. In this sample code below an event handler request method EventHandler1 (see Event Handlers Implementation) is used to Play a MediaForge object called "ExtensionTest." The Play method requires only one parameter, the name of the target object, in this case "ExtensionTest".

Notice how the GetDispID method uses the m_pMediaForge value set in the SetMediaForge method to find the dispatch identifier of the Play method in the MediaForge Automation Object. You can use this late binding approach to call any method inside of the MediaForge Automation Object essentially providing complete authoring control of any MediaForge project.

 

/////////////////////////////////////////////////////////////////////////////

// CSampAutomationCtrl message handlers

BOOL CSampAutomationCtrl::EventHandler1(LPCTSTR xmlString)

{

::MessageBox(NULL, "Ok, it", "worked", MB_OK);

if (m_pMediaForge)

{

DISPID dispid;

BOOL bRetValue = FALSE;

CString strObjectToPlay("ExtensionTest");

bRetValue = GetDispID("Play", dispid);

if (bRetValue)

{

DISPPARAMS dispparms;

memset(&dispparms, 0, sizeof(DISPPARAMS));

dispparms.cArgs = 1;

// allocate mem for VARIANT parm

VARIANTARG* pArg = new VARIANTARG[dispparms.cArgs];

dispparms.rgvarg = pArg;

memset(pArg, 0, sizeof(VARIANT) * dispparms.cArgs);

dispparms.rgvarg[0].vt = VT_BSTR;

dispparms.rgvarg[0].bstrVal = strObjectToPlay.AllocSysString();

// Now make call to method

HRESULT hr = m_pMediaForge->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparms, NULL, NULL, NULL);

if (FAILED(hr))

{

bRetValue = FALSE;

}

}

}

return TRUE;

}

BOOL CSampAutomationCtrl::GetDispID(CString szName, DISPID& dispid)

{

BOOL bRetVal = FALSE;

HRESULT hr;

if (m_pMediaForge)

{

USES_CONVERSION;

LPOLESTR lpOleStr = T2OLE(szName);

hr = m_pMediaForge->GetIDsOfNames(IID_NULL, &lpOleStr, 1, LOCALE_SYSTEM_DEFAULT, &dispid);

if (!FAILED(hr))

{

bRetVal = TRUE;

}

}

return bRetVal;

}

For early binding, a stage door type library STG.TLB is available. You can create a class using the Visual Studio Class Wizard and the stg.tlb file. In the sample program stg.h and stg.cpp was generated. Notice how the SetMediaForge method initializes both the dispatch pointer and the MediaForge Interface class. If you look at stg.h generated by the Class Wizzard you will find the class ImediaForge derived from ColeDispatchDriver. This class makes it very easy to use Methods and Properties in MediaForge. Here is method EventHandler2 from the sample program doing the same thing as EventHandler1 but in this case it is using the IMediaForge class for early binding and ease of use. Early binding is faster and easier but there are situations where you my need to use late binding and that is why both examples are provided.

// Same result as EventHandler1 but with only 1 instruction

BOOL CSampAutomationCtrl::EventHandler2(LPCTSTR xmlString)

{

return m_MediaForge.Play("ExtensionTest"); // Call using early binding

}

· Event Handlers Implementation

If you intend to use Universal Scripting in MediaForge then besides writing a DLL or ActiveX control to script your project as described above you will also need to handle events fired from objects in your project anywhere from Button and Image objects to Audio objects. In the project sample there are two buttons placed on the stage. One button needs to call EventHandler1 when it is pressed and the other needs to call EventHandler2. Below (Figure 3) shows the event tab for the button that calls EventHandler1. When the left mouse button released event occurs on this button an Action "Call Method In Extension Object" Scriptor is requested and the method it is calling is EventHandler1. Methods such as EventHandler1 are passed 2 parameters, the object name and In this case the object name is "Scriptor." This is the name of the MediaForge ActiveX object that uses our SampAutomation OCX. All of the code above was extracted from the SampAutomationCtl.h and SampAutomationCtl.cpp files .

In addition to specifying the scripting object name and the method to call, you can see it is also possible to pass and XML string. Event handlers are passed one parameter which is an option XML String. In this case the developer of the sample decided to pass the name of the button object that caused the event handler to be called.

Passing Parameters from ActiveX control objects to a Scriptor method:

There is an exception to the one parameter XML string event handling. This occurs when the object in the project calling the event handler is itself an ActiveX control. With ActiveX controls there is an option to pass the parameters along to event handlers. If this option is checked, then parameters from any ActiveX control event is forwarded to the handler. This parameter forwarding is by necessity done differently for DLLs and ActiveX control Scriptors. When the Scriptor is an ActiveX control the parmaters are passed directly as if the event parameters came from the control itself. For example, in the sample project there is a method KeyPressed. This is an event handler used to accept KeyPressed events from the Microsoft Edit Mask Control when used in a MediaForge project. As you can see in this case parameters are completely variable.

// This event handler takes a key pressed from the Microsoft Edit Mask Control

void CSampAutomationCtrl::KeyPressed(short FAR* Key)

{

short x = *Key;

}

On the other hand, when ActiveX events with parameters are passed to a DLL scriptor, then the event handler has a fixed handler that must be written. In this case the first parameter is the number of parameters being passed by the control and the second is a pointer to an array or VARIANTS. Here is the typdef for used in DLL Scriptors.

typedef BOOL (__stdcall *MFEVENTHANDLERWITHPARMSTYPE)(unsigned int numParms, VARIANT* pParmsArray);

This might suggest that if your MediaForge project contains ActiveX controls it might be easier to write your Scriptors as ActiveX controls themselves.

figure 3

 

· MediaBasic Extension Implementation

To obtain the MediaForge Automation Object for use in MediaBasic is straight forward:

dim MediaForge as Object

Set MediaForge = mfoGetObject("MediaForge")

.

.

result = MediaForge.Play("My Song")

In MediaBasic scripting can be performed using the MediaForge Automation Object or using MediaBasic Extension. Using extensions, playing the "My Song" object would be accomplished like so:

mfoPlay "MySong"

 

· Exported Function Implementation

When using a DLL as a Scriptor, you need to get hold of the MediaForge Automation Object before you can script the project. Unlike ActiveX Scriptors, the dispatch pointer is not sent automatically to a DLL Scriptor. However getting the MediaForge dispatch pointer from a DLL is pretty easy. The following code gets the module handle for the process and then uses the MediaForge exported function "GetMediaForge" to obtain the dispatch pointer.

// Get the MediaForge Object (dispatch pointer to its interface);

typedef LPDISPATCH (WINAPI* PGETMEDIAFORGE)();

LPDISPATCH MediaForge()

{

LPDISPATCH lpDispatch = NULL;

HINSTANCE hMediaForge = GetModuleHandle(NULL);

if (hMediaForge)

{

PGETMEDIAFORGE pGetMediaForge = (PGETMEDIAFORGE)GetProcAddress(hMediaForge, _T("GetMediaForge"));

if (pGetMediaForge)

{

lpDispatch = pGetMediaForge();

}

}

return lpDispatch;

}

Conclusion:

As you can see, MediaForge Automation is a very powerful feature. We have shown how you can place MediaForge projects in Web Pages and completely script projects and receive events using either JavaScript or VBScript. We have also shown that when web pages are placed inside of a MediaForge project that JavaScript or VBScript in the embedded HTML can also script and handle events for the project.

Next we demonstrated how you can write your own DLL or ActiveX Scriptors as an alternative or a complement to the default MediaForge scripting language MediaBasic. We demonstrated how Scriptors can receive events and how they use the powerful MediaForge Automation Object to get access to the entire MediaForge system. Furthermore we demonstrated how methods and properties in the MediaForge object can be called using either early or late binding. When using late binding, you can use the stage door STG.TLB type library to generate the MediaForge interface class for use in your Scriptor. To get the very latest on the MediaForge Automation Object you can refer to the STG.ODL file. This file was used to generate the type library.