在开发过程中,经常需要在C++和Flash ActionScript之间进行数据交换和函数调用。本文将介绍如何实现这一功能,包括ActionScript部分的编写、C++代码的事件处理、结果返回以及如何从C++调用ActionScript。
在寻找能够播放Shock Wave Flash (.swf) 文件并使用ActionScript的C++代码时,发现了Igor Akarov的文章,但该文章并未提供调用和回调的支持。虽然Flash的ExternalInterface有大量文档,但很难找到一个实际工作的示例。因此,对其进行了扩展,并认为这可能对其他人也有用。
由于Flash使用XML在ExternalInterface之间传递参数,决定使用TinyXML库。该库包含在示例代码中。
让从ActionScript部分开始。在文件"HelloWorld.as"中,有一行代码告诉Flash的ExternalInterface注册一个回调:
public function onSetButtonText(arg:String):String {
button.setLabel(arg);
return "OK";
}
上述代码注册了一个名为onSetButtonText的ActionScript函数,可以通过名称setButtonText从例如JavaScript(如果容器是Web浏览器)或C++(在这个例子中)调用。
每次用户在Flash ActionScript中点击按钮时,都会调用mouseDownHandler。为了动画效果,它改变了x和y坐标,并调用count()获取按钮的新文本:
private function mouseDownHandler(event:MouseEvent):void {
button.x += 1;
button.y += 1;
button.setLabel(count());
}
下面是一个如何从ActionScript调用Flash ExternalInterface的示例。它调用了addNumbers函数。在接下来的段落中描述了如何在C++代码中处理这个调用:
public function count():String {
ExternalInterface.marshallExceptions = true;
try {
counter = ExternalInterface.call("addNumbers", counter, 1);
return String(counter);
} catch (e:Error) {
return e.toString();
}
return String("Error");
}
如前所述,代码被重用。函数CFlashWnd::Invoke已经增强,以便理解从Flash发出的事件。如果所有参数都正确,它将调用FlashCall方法。
HRESULT STDMETHODCALLTYPE CFlashWnd::Invoke(...) {
if (wFlags == DISPATCH_METHOD) {
switch (dispIdMember) {
case 0xc5:
// FlashCall (from ActionScript)
if (pDispParams->cArgs != 1 || pDispParams->rgvarg[0].vt != VT_BSTR)
return E_INVALIDARG;
return this->FlashCall(pDispParams->rgvarg[0].bstrVal);
}
}
}
CFlashWnd::FlashCall然后进行繁重的工作,即解包XML。通过调用Tiny XML库函数doc.Parse解析请求。Flash通过
HRESULT STDMETHODCALLTYPE CFlashWnd::FlashCall(_bstr_t request) {
HRESULT hr = S_FALSE;
if (m_lpControl != NULL) {
TiXmlDocument doc;
const char *c_str = _com_util::ConvertBSTRToString(request);
doc.Parse(c_str);
delete[] c_str;
TiXmlHandle hDoc(&doc);
TiXmlElement *pInvokeElement = hDoc.FirstChildElement("invoke").Element();
if (pInvokeElement != NULL) {
std::string functionName;
int result = pInvokeElement->QueryStringAttribute("name", &functionName);
if (result == 0) {
if (functionName == "addNumbers") {
hr = addNumbers(pInvokeElement);
}
}
}
}
return hr;
}
现在CFlashWnd::FlashCall能够解码被调用的函数名称。接下来,让看看如何解码函数参数并将函数结果(返回值)作为数字传递回Flash对象(ExternalInterface):
HRESULT CFlashWnd::addNumbers(TiXmlElement *pInvokeElement) {
HRESULT hr = E_INVALIDARG;
TiXmlElement *pArgumentElement = pInvokeElement->FirstChildElement("arguments");
if (pArgumentElement != NULL) {
TiXmlElement *pArgumentNumber = pArgumentElement->FirstChildElement("number");
if (pArgumentNumber != NULL) {
int number1 = atoi(pArgumentNumber->GetText());
pArgumentNumber = pArgumentNumber->NextSiblingElement("number");
if (pArgumentNumber != NULL) {
int number2 = atoi(pArgumentNumber->GetText());
WCHAR result[80];
_snwprintf_s(result, 80, 80, L"%d ", number1 + number2);
hr = m_lpControl->SetReturnValue(result);
}
}
}
return hr;
}
下面是一个如何从C++调用ActionScript的简单示例。注意,必须在ActionScript中调用了ExternalInterface.addCallback,否则会失败!
BSTR _result = m_lpControl->CallFunction(L"Click me! ");
要将.as文件编译成.swf文件,以便Flash播放器播放,使用了Adobe的免费mxmlc编译器。可以从他们的网站下载。
要将其作为Visual Studio项目的一部分构建,请添加自定义构建规则:
"F:\Program Files\Adobe\Flex_SDK_4.0\bin\mxmlc.exe" --show-actionscript-warnings=true --strict=true -static-link-runtime-shared-libraries -output $(OutDir)\$(InputName).swf $(InputName).as