Introduction
Flash is nice, but it is mainly used in the Web. How about embedding flash into your own applications AND transfer data from/to them?
I've found out that many have discussed this subject, but sometimes their method will mysteriously fail for a reason. Here is a fully functional and very simple project.
Background
This article assumes you are familiar with C++ and COM. Make sure you review these topics and also review my Win32 ActiveX article. You can also use MFC or ATL to insert the ActiveX control without messing with the truly messy COM implementations. This article also assumes that you know the mechanisms of hosting an ActiveX control and connecting to it for notifications, and although my AX.CPP code includes all functions required, these functions are not discussed in this article in detail.
Also, ActionScript 3.0 knowledge is required.
Creating the ActiveX Control
You may have already been implementing your own methods to insert an ActiveX control in your application, and if so, you do not need to use my AX.CPP to get an IShockwaveFlash
pointer. To insert the "Shockwave.Flash
" ActiveX control, I use the Win32 ActiveX article discussion and AX.CPP, which merely implements an IOleClientSite and allows me to use the class ID in the resource file:
CONTROL "{D27CDB6E-AE6D-11CF-96B8-444553540000}", 888,
"AX", WS_CHILD | WS_VISIBLE, 10, 9, 399, 206
in order to create an "AX
" control (which accepts as the Window title the class ID of the requested ActiveX control). Once we have a HWND
, I can send it an AX_QUERYINTERFACE
to get a pointer to an IShockwaveFlash
:
CLSID iidx = __uuidof(IShockwaveFlash);
IShockwaveFlash* p = 0;
HRESULT hr = (HRESULT)SendMessage(hX,AX_QUERYINTERFACE,(WPARAM)&iidx,(LPARAM)&p);
Of course, your method might have been ATL or similar. The last thing to do is to activate the control in place:
SendMessage(hX,AX_INPLACE,1,0);
Loading the SWF
I simply extract "1.swf" from the resource to a temporary file and load it (along with a white bg color):
_bstr_t x(df);
p->put_BackgroundColor(RGB(255,255,255));
p->put_Movie(x);
Sending Data to the SWF
Registering the ActionScript Function
Many methods are suggested, and I will use the simplest and the most efficient: The way to call an ActionScript 3.0 function from your C++ code. First, let's define (in frame 1) an ActionScript
function in the .FLA file:
import flash.external.ExternalInterface;
function TestFunction1(he:String)
{
this.gotoAndPlay(2);
}
flash.external.ExternalInterface.addCallback("foo1",TestFunction1);
What do we have here? Simply a function called "TestFunction1
" which accepts a String
parameter (This function merely goes to frame 2, leaving the passed string
unused, but it is passed so you understand how you would pass arguments to the function).
After that, we must call flash.external.ExternalInterface.addCallback
and register the "TestFunction1
" as a function that may be called by our container. This function will be visible with the "foo1
" name. You can think of a similar thing in DEF file "EXPORTS
" section.
Calling the Function
IShockwaveFlash*
has a CallFunction
member which we can call. It's simple: Flash expects the entity of the call to be passed as a XML Element with the "name
" "foo1
" and the arguments in XML format. You can learn more on the XML format that Flash expects in this Adobe Help link.
The WM_COMMAND
handler in my code demonstrates all this, and the call is shown below:
_bstr_t fu(L"<invoke name=\"foo1\" returntype=\"xml\">
<arguments><string>Hello</string></arguments></invoke>");
_bstr_t rs = p->CallFunction(fu);
Getting Notified of Events
OK, we have called an AS 3.0 function, but how could an AS 3.0 function call us? Simply - yes you have guessed it already - via IDispatch.
First, we use standard ActiveX advise mechanism to register for a notification - see AX.CPP and the call to AXConnectObject
(you might use your own MFC/ATL methods to connect as well). We pass to it our own IDispatch
implementation which, apart from the usual IUnknown
and IDispatch
methods, implements IDispatch::Invoke
as follows:
HRESULT _stdcall Invoke(DISPID d,REFIID ,LCID ,WORD ,DISPPARAMS* p,
VARIANT FAR*,EXCEPINFO FAR* ,unsigned int FAR*)
{
if (d != 0xC5) return S_OK;
if (!p)
return E_POINTER;
if (p->cArgs != 1)
return E_INVALIDARG;
if (p->rgvarg[0].vt != VT_BSTR)
return E_INVALIDARG;
wstring w = p->rgvarg[0].bstrVal;
MessageBox(0,w.c_str(),L"Message",MB_ICONINFORMATION);
return S_OK;
}
Let's explain this. We check the dispid for 0xC5, the "FlashCall
" function. This function is called from ActionScript
through our IDispatch
, passing a BSTR
with the calling parameters. This as you might have already guessed has the same XML format we used earlier and in my code. I simply display the result in a MessageBox
- of course, you want to use my XML library to parse it and handle the function call.
And here is how we call our function from ActionScript 3.0:
function Act2(et:Event):void
{
flash.external.ExternalInterface.call("ext2",2);
}
This is the listener for the second button we have created. In this case, the "ext2
" will be passed as the "name
" variable in the <invoke>
element, and the number '2
' will be passed as a <number>
element in <arguments>
.
x64 Compatibility
If you have Flash x64 ActiveX installed (for Internet Explorer), this will also work. Flash support in x64 is really new and for complex AS functions you may run into problems, but eventually everything will work.
The Code
Somewhat dirty (as in all my CodeProject articles ;) but it works. The zip contains:
- Project files SLN and VCPROJ for Visual Studio 2008 or later
- 1.FLA and 1.SWF flash source and movie
- AX.H / AX.CPP ActiveX Win32 interfaces
- MAIN.CPP which demonstrates all we have said so far.
- Other solution files like MAIN.RC, X86.MANIFEST and FUNC.H.
- The executable, in case you get bored to open Visual Studio to compile the sample.
GOOD LUCK.
History
- 21-10-2011: Typos, some additions
- 17-10-2011: First release