65.9K
CodeProject 正在变化。 阅读更多。
Home

C++ 中的增强透明 Flash 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (15投票s)

2011 年 4 月 8 日

CPOL

4分钟阅读

viewsIcon

74174

downloadIcon

3806

使用 C++ 实现的 OLE 容器,用于托管 Flash Player 控件,支持 C++ 和 Flash ActionScript 之间的调用和回调。

EnhancedFlashControl screen shot

引言

本文基于 Makarov Igor 撰写的优秀文章。它扩展了功能,支持 C++ 和 Flash ActionScript 之间的调用和回调。

背景

由于我正在寻找一些(C++)代码来播放带有 ActionScript 的 Shock Wave Flash (.swf) 文件,所以我找到了上面引用的文章。但是,它不支持调用和回调。关于 Flash ExternalInterface 的文档很多,但我发现很难找到一个可用的示例。所以我添加了它,并认为它对其他人也可能有用。

Using the Code

由于 Flash 在 ExternalInterface 上使用 XML 来序列化参数来回传递,所以我决定使用 TinyXML 库。它包含在示例代码中。

第一部分. Flash ActionScript

让我们从 ActionScript 部分开始。在 "HelloWorld.as" 文件中,有一行告诉 Flash 的 ExternalInterface 注册一个回调

// Register a callback on the Flash External Interface that can
// be called from C++ (or any other container, e.g. javascript)
ExternalInterface.addCallback("setButtonText", onSetButtonText);

上面的代码使用名称 onSetButtonText 注册了 ActionScript 函数,该函数可以从例如 JavaScript(如果容器是 Web 浏览器)或 C++(在本例中)通过名称 setButtonText 调用。

函数 onSetButtonText 仅用于演示目的,它简单地设置按钮的文本

public function onSetButtonText(arg:String):String {
    button.setLabel(arg);
    return "OK";
}

每次用户在 Flash ActionScript 中单击按钮时,都会调用 mouseDownHandler。对于动画,它会更改 xy 坐标,并调用 count 来获取按钮的新文本

private function mouseDownHandler(event:MouseEvent):void {
    button.x += 1;
    button.y += 1;
    button.setLabel(count());
}

以下是如何从 ActionScript 调用 Flash ExternalInterface 的示例。它调用 addNumbers 函数。在接下来的段落中将描述在 C++ 代码中处理此调用的方法。

// Demonstration on how to use the Flash ExternalInterface
// This example calls the function 'addNumbers' in the C++ container app
public function count():String {
    // calls the external function "addNumbers" 
    // (in JavaScript, or container projector)
    // passing two parameters, and assigning that function's result 
    // to the variable "counter"
    ExternalInterface.marshallExceptions = true;
    try {
        counter = ExternalInterface.call("addNumbers", counter, 1);
        return String(counter);
    }
    catch(e:Error) {
        return e.toString();
    }
    return String("Error");
}

第二部分. C++ 代码中的事件处理

如前所述,代码是重用的。函数 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 在 <invoke> XML 元素中传递调用详细信息。它通过 hDoc.FirstChildElement("invoke") 查找。调用函数的名称在 <invoke> 元素的 "name" 属性中指定。它通过调用 QueryStringAttribute 来查找。如果所有信息都正确,整个元素树将被传递给 addNumbers 方法。

// Handle a call from Flash ActionScript (in .swf file)
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);
        // Parse the XML string to into an XML doc
        doc.Parse(c_str);
        delete[] c_str;

        TiXmlHandle hDoc(&doc);
        // Look for the invoke element
        TiXmlElement *pInvokeElement = hDoc.FirstChildElement("invoke").Element();
        if (pInvokeElement != NULL)
        {
            std::string functionName;
            int result = pInvokeElement->QueryStringAttribute("name", &functionName);
            if (result == 0)
            {
                if (functionName == "addNumbers")
                {
                    // Finally, handle the request
                    hr = addNumbers(pInvokeElement);
                }
            }
        }
    }
    return hr;
}

上面的代码展示了一种处理来自 Flash ActionScript 的函数调用请求的非常简单的方法。如果您想处理更多调用,请随时改进。毕竟,这只是演示了机制的基础。

第三部分. 将结果从 C++ 返回到 ActionScript

因此,CFlashWnd::FlashCall 现在能够解码被调用的函数名。现在,让我们看看如何解码函数参数并将函数结果(返回值)作为数字返回给 Flash 对象(ExternalInterface

HRESULT CFlashWnd::addNumbers(TiXmlElement *pInvokeElement)
{
    HRESULT hr = E_INVALIDARG; // Default result if something is wrong

    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"<number>%d</number>", number1 + number2);
                // Set the return value in the Flash object (ExternalInterface)
                hr = m_lpControl->SetReturnValue(result);
            }
        }
    }
    return hr;
}

上面的代码展示了如何通过调用 ActiveX 接口上的 SetReturnValue 将结果返回给 Flash。它还展示了如何使用 Tiny XML 库中的函数来获取所需的参数值。

第四部分. 从 C++ 调用 ActionScript

下面展示了一个从 C++ 调用 ActionScript 的简单示例。请注意,必须在 ActionScript 中调用 ExternalInterface.addCallback,参见第一部分,否则将会失败!

BSTR _result = m_lpControl->CallFunction(L"<invoke name=\"setButtonText\" 
returntype=\"xml\"><arguments><string>Click me!</string></arguments></invoke>");

构建 Flash ActionScript

要将 .as 文件编译为 Flash 播放器可以播放的 .swf 文件,我使用了 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 

CustomBuild.gif

致谢

特别感谢以下作者

没有他们,编写代码将更加困难!

法律事宜

不幸的是,我们生活在一个这样的消息是不可避免的世界……

本文和源代码已提供给您随意使用,不提供任何保证!本文和源代码仅演示基本原理,不应用于可能对人、动物造成伤害或带来任何风险的情况。请自行承担使用风险,我概不负责。我不能承担责任,您已被警告。

很抱歉,我希望这听起来不会太冒犯。因为归根结底,编码应该是有趣的!

历史

  • 2011 年 4 月 8 日:首次发布
© . All rights reserved.