Flash 와 App간 통신 방법 3가지를 소개했었는데, AS3로 넘어오면서 watch명령이 없어져서 기존에 사용하던 fscommand/setVariable 방법을 사용할수 없어졌습니다. watch 명령이 아주 비효율적이라 아예 없앴다고 합니다. LocalConnection 방법은 편법성으로 사용자들이 알아낸 방법이므로, ExternalInterface로 Flash와 APP간 통신을 구현했습니다.


1. Application에서 Flash 함수 호출

Actionscript3 소스:
ExternalInterface.addCallback("callFromApp", function(a, b) { return "ok:"+a+","+b; });


C++ 소스:
string invXml = ExternalInterface::MakeInvokeXml("callFromApp", arg1, arg2);
string ret = ExternalInterface::ParseResultXml(m_cFlash.CallFunction(invXml.c_str()).GetBuffer(0));


2. Flash에서 Application 호출

Actionscript3 소스:
ExternalInterface.call("testfunc", "hello", 1, true, 1==2, 1.1);

C++ 소스:
void CFlashTestApp::OnFlashCallShockwaveflash1(LPCTSTR request)
{
   string cmd;
   vector<string> args;

   if (ExternalInterface::ParseInvokeXml(request, cmd, args)) {
      // 함수콜 처리하기~
   }
}


--
Flash와 APP간 통신은 모두 XML을 통해서 이루어집니다.
1. 어플에서 XML을 만들고 파싱해야합니다.
2. 외부 XML 파서를 쓸수 있지만, 사용하는 XML이 복잡하지 않아서 자체 XML 파서를 만들었습니다.
3. Flash에서는 type이 있지만, C++에서는 모두 string으로 받도록 했습니다. C++에서 보낼때도 모두String인자로 보냅니다. 어차피 XML이 문자열로 되어 있으므로, string으로 처리해도 퍼포먼스에 크게 영향이 없을것으로생각되고, Variant를 C++에서 구현하는것은 복잡하고 짜증나는 작업이죠.
4. ActionScript에서 전달한 인자 중 복잡한 자료구조는 파싱하지 못하고,   String, Number,Boolean등과 1차 Array만 파싱하도록 했습니다. C++에서는 인자 받은걸 순서대로 vector에 넣어서 처리를단순화했습니다. Array안에 Array등을 처리하려면 C++에서도 복잡한 자료구조가 필요하고, 그러면 함수처리하는 부분도복잡해질듯해서 단순화했습니다.
5. STL string을 이용해서 파싱했습니다.


XML 파싱 함수들 입니다.

string peek_element(const string &xml);
xml에 포함된 첫 element를 가져옵니다. peek = 엿보기?

bool parse_element(const string &name, string &xml, string &attrs, string &inner);

xml에 포함된 첫 element를 파싱해서 attribute부분과 innerXml부분으로 나누어줍니다. xml에서는 파싱한 부분을 제거합니다.
   name = a
   xml = <a attr="haha">aab</a><b>zzz</b>
이런식으로 호출되면...
   attrs = attr="haha"
   inner = aab
   xml = <b>zzz</b>
이런식으로 값이 바뀝니다.

bool parse_attributes(const string &attrs, StringMap &attrMap);
<element attr1="val1" attr2="val2">
위에 같은 xml attributes을 파싱해서 attrMap에 insert합니다. "attr1"→"val1", "attr1"→"val2"

string xml_encode(const string &x); // <, &, > → &lt;, &amp;, &gt;

string xml_decode(const string &x);
// &lt;, &amp;, &gt; → <, &, >

Flash -> Application XML 예제
ExternalInterface.call("testfunc", "hello", 1, true, 1==2, 1.1);

<invoke name="testfunc" returntype="xml">
   <arguments>
       <string>hello</string>
       <number>1</number>
       <true/>
       <false/>
       <number>1.1</number>
   </arguments>
</invoke>


Flash -> Application XML 파싱 소스:
// external interface 파싱
bool parse_arg(string &xml, string &val)
{
   string attrs, i;
   if (parse_element("string", xml, attrs, i)) {
       val = i;
   } else if (parse_element("number", xml, attrs, i)) {
       val = i;
   } else if (parse_element("true", xml, attrs, i)) {
       val = "true";
   } else if (parse_element("false", xml, attrs, i)) {
       val = "false";
   } else if (parse_element("boolean", xml, attrs, i)) {
       val = i;
   } else {
       return false;
   }
   return true;
}

void parse_args(string &xml, vector<string> &args)
{
   string val;
   while (1) {
       if (parse_arg(xml, val)) {
           args.push_back(val);
       } else {
           break;
       }
   }
}

bool ParseInvokeXml(const string &_xml, string &cmd, vector<string> &args)
{
   string xml=_xml;
   string attrs;
   string innerXml;

   parse_element("invoke", xml, attrs, innerXml);

   StringMap attrMap;
   parse_attributes(attrs, attrMap);

   StringMap::const_iterator f = attrMap.find("name");

   if (f != attrMap.end()) {
       cmd = f->second;
       string elm = peek_element(innerXml);
       string p, i;
       if (parse_element("arguments", innerXml, attrs, i)) {
           innerXml = i;
           if (parse_element("array", innerXml, attrs, i)) {
               innerXml = i;
               while (parse_element("property", innerXml, attrs, i)) {
                   parse_args(i, args);
               }
           } else {
               parse_args(innerXml, args);
           }
       }
   }
   return true;
}

+ Recent posts