Обработка VerifyError: ошибка № 1014 при загрузке SWF-файлов с использованием AS3

Мы создаем систему с основным SWF-файлом для приложения и загружаем отдельные инструменты из отдельных SWF-файлов — в будущем будут возникать проблемы с версиями, поскольку отдельные SWF-файлы поступают из CMS (особенно сейчас поскольку мы все еще разрабатываем, но и в будущем, когда другие разработчики могут создать несовместимый инструмент). Я изо всех сил стараюсь предотвратить их, насколько это возможно, но мне бы очень хотелось иметь возможность отображать сообщение пользователю системы при загрузке несовместимого swf.

Это будет означать, что нам нужно поймать этот VerifyError или, по крайней мере, определить, что загрузка по какой-то причине не удалась - я понятия не имею, как с этим справиться на данный момент. Я подозреваю, что это возможно с использованием 10.1 и системы uncaughtError, но в настоящее время мы ориентируемся на flash player 10. У кого-нибудь есть хорошая идея? (мы уже обрабатываем IOErrorEvent.IO_ERROR)

ОБНОВЛЕНИЕ: я создал решение, которое сканирует байт-код перед импортом, похоже, это сработает. Я выложу решение позже.


person Simon Groenewolt    schedule 16.07.2010    source источник
comment
Вы когда-нибудь находили/разрабатывали решение этой проблемы? И вы где-то разместили это, как вы упомянули? Это было бы очень полезно для меня. Ваше здоровье!   -  person nexus    schedule 07.01.2012
comment
Я нашел полу работающее решение, но в конце концов мне пришлось прибегнуть к большому количеству проверок всех источников и очень жесткому пути обновления, который в основном не позволяет изменять API. Моя проблема заключалась в том, что я не мог понять все особенности ABC, которые я анализировал, поэтому я мог просмотреть самые тривиальные варианты использования некоторых классов, но обнаружил, что невозможно быть уверенным, что я действительно проверил все классы, которые были в использовании. Я посмотрю и посмотрю, смогу ли я опубликовать что-то, что покажет (часть) эту попытку решить эту проблему.   -  person Simon Groenewolt    schedule 08.01.2012
comment
Ну, если вы когда-нибудь пост, что я очень заинтересован!   -  person nexus    schedule 26.01.2012
comment
@nexus Наконец-то я опубликовал свой собственный класс, который использовал в качестве ответа на свой вопрос.   -  person Simon Groenewolt    schedule 11.06.2012


Ответы (5)


Лучший способ сделать это — использовать одну из предложенных библиотек bhups. Я использовал senocular. для следующего примера. Кроме того, поскольку библиотека senocular предоставляет только базовые операции для проанализированного SWF, вам может понадобиться спецификация формата SWF (adobe.com/devnet/swf/pdf/swf_file_format_spec_v10.pdf), чтобы получить необходимую информацию из загруженного SWF.

В следующем примере перечислены все имена классов из загруженного SWF:

package swf
{
 import flash.events.Event;
 import flash.net.URLRequest;
 import flash.net.URLStream;
 import flash.utils.ByteArray;
 import flash.utils.Endian;

 import swf.SWFReader;

 public class GetSWFInfo
 {

  private var swfInfo:SWFReader;

  public function GetSWFInfo()
  {
   var urlRequest:URLRequest = new URLRequest("theswf.swf");
   var loader:URLStream = new URLStream();   
   loader.load(urlRequest);
   loader.addEventListener(Event.COMPLETE, onComplete);
  }


  public function onComplete(e:Event):void {
   var recivedByteArray :ByteArray = new ByteArray();
   URLStream(e.currentTarget).readBytes(recivedByteArray);


   //create a new instance of SWFReader
   swfInfo = new SWFReader();
   //readTag it's a callback function that will be called when a tag is read during the SWF parse process.
   //read more on tags in the SWF specification document
   swfInfo.tagCallback =  readTag;
   //start parsing
   swfInfo.parse(recivedByteArray); 
  }



  public function readTag(tag:uint, bytes:ByteArray):void {


   //76 it's the tag type for SymbolClass tag
   //read more in the SWF specification document
   if (76 == tag) {


    var classesArray:Array = new Array();
    var symbolsNumber:uint = 0;
    var currentId:uint = 0;

    bytes.endian = Endian.LITTLE_ENDIAN;

    //read the symbols Number
    //again read more in the SWF specification document
    symbolsNumber = bytes.readShort();

    bytes.position = 4;

    while (true) {

     var i:uint = bytes.position;

     //every string name ends with a null byte
     //again read more in the SWF specification document
     while(bytes[i] != 0) i++;

     var readAmount:uint = i - bytes.position;

     classesArray.push(bytes.readUTFBytes(readAmount));

     //the last ID is always the base class Id, and it's 0
     currentId=bytes.readUnsignedShort();

     bytes.position++;     

     if (currentId==0) {
      break;
     }
    }

    //this two should be equal
    trace(classesArray.length + 1);//the number of elements in the classesArray
    trace(symbolsNumber);//the number of classes retrived from the SWF

    //list the names
    var name:String;
    for each (name in classesArray) {
     trace(name);
    }

    //now you have an array with all the class names that you can use to compare

   }
  }
 }

}

person Valentin Radu    schedule 23.07.2010
comment
Спасибо за ваше предложение, но как это поможет мне предотвратить VerifyError? Я не вижу способа найти классы, от которых зависит swf - просто возможность перечислить имена классов в swf мне не помогает, или я что-то упустил? - person Simon Groenewolt; 23.07.2010
comment
Что ж, если вы загрузите swf с помощью URLStream, вы точно не получите ошибки. Затем вы можете проверить содержимое, проверить его правильность, ApplicationDomain.hasDefinition и ApplicationDomain. Здесь может пригодиться getDefinition (вы можете использовать эти методы и без синтаксического анализа swf, но синтаксический анализ swf дает вам возможность считывать из него любую информацию, которая вам нужна). Вот как я бы это сделал. Я имею в виду, может быть, я не понимаю вопроса, но если вы хотите убедиться, что сторонние SWF-файлы имеют классы, которые вам нужно использовать в них, я думаю, что это путь. - person Valentin Radu; 23.07.2010
comment
Мне нужно наоборот - сторонний swf зависит от класса, который должен быть в моем swf - если этот класс отсутствует, я получаю VerifyError. Вот почему мне нужно знать классы, в которых он нуждается, но не имеет самого себя, я посмотрю, что я могу найти с помощью swfreader, но я подозреваю, что мне нужно прочитать ABC, чтобы узнать нужную мне информацию. - person Simon Groenewolt; 24.07.2010

Я неправильно понял, что вы пытаетесь сделать.

Ну, на самом деле, думаю, обработчика ошибки верификации нет, и чтобы ее обнаружить, приходится бороться с байт-кодами.

Кстати, у меня есть идея, которая не является ответом на ваш вопрос, но может вам помочь.

сторонний swf зависит от класса, который должен быть в моем swf — если этот класс отсутствует, я получаю VerifyError.

С этого момента я могу посоветовать, что если вы свяжете «отсутствующий класс» со своим swf и загрузите сторонний swf в ApplicationDomain.currentDomain или новый ApplicationDomain (ApplicationDomain.currentDomain), вы сможете избежать «ошибки проверки». (Это связано с тем, что флеш-проигрыватель найдет различие отсутствующего класса в родительском swf-файле.)

Вот мой пример кода, который загружает SWF с ошибкой проверки (http://teionclub.com/test/xml/main.swf).

Avoiding VerifyError - wonderfl build flash online
person 9re    schedule 26.07.2010
comment
Спасибо за ваше предложение, ваше предложение работает, если известен сторонний swf, и мне нужно исправить только пару из них. К сожалению, это не тот случай. В эти выходные я создал решение для своей проблемы, которое сканирует байт-код avm2 и проверяет все классы, используемые в иерархии классов. Я опубликую свое решение позже. - person Simon Groenewolt; 26.07.2010

Я думаю, что есть способ обойти эту проблему.

  1. загрузите SWF с помощью URLLoader или URLStream в ByteArray.
  2. Используйте любую библиотеку с открытым исходным кодом для анализа двоичного файла SWF, например это или это.
  3. проверьте, подтверждает ли он, что весь массив байтов представляет действительный файл SWF.
  4. Если вышеприведенный тест прошел успешно, загрузите этот ByteArray в загрузчик, используя метод loadBytes.
  5. В противном случае покажите пользователю, что это не работает.

Отказ от ответственности. Двоичный файл может быть действительным SWF, но при этом может не поддерживать визуализацию, но при этом вы можете отбросить все недопустимые SWF или любые другие форматы, расширение которых изменено на swf.

person bhups    schedule 21.07.2010
comment
Я не беспокоюсь о том, что это не swf-файл, но как мне обнаружить, что swf имеет зависимости от классов, которые недоступны в загружаемом swf? Ошибка проверки обычно появляется из-за отсутствия зависимостей в моем случае. - person Simon Groenewolt; 21.07.2010

Я работал с таким приложением в прошлом, но я думаю, что было бы лучше исправить загруженный SWF, а не обрабатывать VerifyError. VeriyError указывает на то, что загруженный SWF-файл поврежден или имеет неправильный формат.

И естественно, что сам SWF искажен, а не сам SWF во время передачи. Я предполагаю, что вы пытаетесь загрузить png или другой формат с именем «.swf», или SWF создается каким-либо программным обеспечением, отличным от компилятора Flex или Flash, например swfmill (в последнем случае в этом программном обеспечении будет ошибка).

person 9re    schedule 18.07.2010
comment
спасибо за ваш ответ, однако «исправить» swf не вариант, так как я не буду тем, кто его делает - я согласен с тем, что он не работает, я просто хочу разумно обработать ошибку. (Если я могу обработать ошибку проверки, я могу отобразить сообщение об ошибке «эта штука больше не работает») - person Simon Groenewolt; 18.07.2010

Чтобы, наконец, ответить на мой собственный вопрос, это служебный класс, который я использовал для обнаружения возможных ошибок. Я загружаю SWF в виде массива байтов и сканирую содержимое перед загрузкой в ​​виде фактического мувиклипа.

Как видите, мой код сильно зависит от пакета com.segfaultlabs.swfutils.

Важно: я перестал использовать этот метод предотвращения ошибок, выбрав более ручной подход к проверке файлов, фактически пытаясь загрузить их и посмотреть, работают ли они. Это связано с тем, что утилита не завершена, а мои текущие знания формата ABC недостаточны, чтобы быть уверенным, что я могу разработать проверку, которая всегда будет правильной.

Размещение моего кода здесь в качестве отправной точки для других, которые хотят попробовать его :-)

package nl.ijsfontein.utils
{
    import com.segfaultlabs.swfutils.ABC.ABCCPool;
    import com.segfaultlabs.swfutils.ABC.ABCClass;
    import com.segfaultlabs.swfutils.ABC.ABCInstance;
    import com.segfaultlabs.swfutils.ABC.ABCMethodInfo;
    import com.segfaultlabs.swfutils.ABC.ABCMultiname;
    import com.segfaultlabs.swfutils.ABC.ABCParser;
    import com.segfaultlabs.swfutils.ABC.ABCTraitConstSlot;
    import com.segfaultlabs.swfutils.ABC.ABCTraitsInfo;
    import com.segfaultlabs.swfutils.ABC.ABCinfo;
    import com.segfaultlabs.swfutils.SWFDataInput;
    import com.segfaultlabs.swfutils.SWFFile;

    import flash.system.ApplicationDomain;
    import flash.utils.ByteArray;

    /**
     * utility to see which classes a swf uses, but doesn't contain itself 
     * - this can be used to detect possible VerifyErrors before they happen.
     */
    public class SwfDependencyUtil
    {
        public function SwfDependencyUtil()
        {
        }

        // return null if ok, or name of needed class if external depencendy
        private static function resolveSuper(abc:ABCinfo, superClass:String):String
        {
            //if (superClass.indexOf("flash.") == 0 || superClass.indexOf("*") == 0 || superClass.indexOf("Object") == 0)
            if (superClass.indexOf("*") == 0)
            {
                trace('  super: ' + superClass + " (ignore)");

            }
            else
            {
                var superClassClass:ABCClass = null;
                for each ( var c:ABCClass in abc.classes )
                {
                    if (c.name == superClass)
                    {
                        superClassClass = c;
                    }
                }
                if (superClassClass)
                {
                    trace('  super: ' + superClass + " (resolved internally)");
                    return resolveSuper(abc, superClassClass.iref.base);

                }
                else
                {
                    trace('  super: ' + superClass + " (NOTFOUND)");
                    return superClass;
                }
            }

            return null;
        }

        /*
         * checks: classes, superclasses, static variables, member variables
         * TODO: function arguments
         * won't check: method bodies
         *
         * TODO: refactor to multiple methods
         */
        public static function getDependencies(swfBytes:ByteArray):Array /* of String */
        {
            var result:Array = [];
                swfBytes.position = 0;
                var swfr:SWFFile = new SWFFile(swfBytes);
                var arr:Array;
                if ( swfr.compressed )
                {
                    swfr.dataInput = swfr.uncompress();
                    swfr.readHeader();                  
                };
                arr = swfr.parseTags();
                if ( arr[82] != null )
                {
                    var abc:ABCinfo = new ABCinfo();
                    var cpool:ABCCPool = new ABCCPool();
                    var abcparse:ABCParser = new ABCParser();

                    abcparse.readMethodBytes = true;
                    abcparse.readExceptions = false;
                    for ( var j:int = 0; j < arr[82].length; j += 1 )
                    {
                        swfr.dataInstance.position = arr[82][j].position;
                        try
                        {
                            abcparse.parse( swfr.dataInput as SWFDataInput, abc, cpool, new FakeLogger() );
                            for each ( var c:ABCClass in abc.classes )
                            {
                                trace('class:', c.name);
                                var superClass:String = c.iref.base;
                                var dependency:String = resolveSuper(abc, superClass);
                                if (dependency)
                                {
                                    result.push(dependency);
                                }
                                for each (var mn:ABCMultiname in c.iref.interfaces)
                                {
                                    var interfaceName:String = mn.nsset[0] != "" ? mn.nsset[0] + "::" + mn.name : mn.name;
                                    var interfaceDependency:String = resolveSuper(abc, interfaceName);
                                    if (interfaceDependency)
                                    {
                                        result.push(interfaceDependency);
                                    }
                                }
                                for each (var ti:ABCTraitsInfo in c.traits)
                                {
                                    if (ti is ABCTraitConstSlot)
                                    {
                                        var constName:String
                                        if (QName(ABCTraitConstSlot(ti).type).uri)
                                        {
                                            constName = QName(ABCTraitConstSlot(ti).type).uri + "::" + QName(ABCTraitConstSlot(ti).type).localName
                                        }
                                        else
                                        {
                                            constName = QName(ABCTraitConstSlot(ti).type).localName
                                        }
                                        var constDependency:String = resolveSuper(abc, constName);
                                        if (constDependency)
                                        {
                                            result.push(constDependency);
                                        }
                                    }
                                    else if (ti is ABCMethodInfo)
                                    {
                                        trace('method', ABCMethodInfo(ti).name);
                                    } else
                                    {
                                        trace(ti);
                                    }
                                    // trace(ti.type.localName);
                                }

                                // const (static?) members: c.traits
                            }

                            for each ( var i:ABCInstance in abc.instances )
                            {
//                              trace(i);
                                for each (var instanceTi:ABCTraitsInfo in i.traits)
                                {
                                    if (instanceTi is ABCTraitConstSlot)
                                    {
                                        trace('instance:', createClassNameFromQname(ABCTraitConstSlot(instanceTi).type));
                                        var csdep:String = resolveSuper(abc, createClassNameFromQname(ABCTraitConstSlot(instanceTi).type));
                                        if (csdep)
                                        {
                                            result.push(csdep);
                                        }
                                    }
                                    else if (instanceTi is ABCMethodInfo)
                                    {

                                    }
                                    else
                                    {
                                        trace('unexpected trait type');
                                    }
                                }
                            }

                            abc.dispose();
                        } 
                        catch ( e:Error ) 
                        { 
                            trace( "  Error  ",e.getStackTrace() );
                        };                          
                    };
                    cpool.dispose();
                }
                else
                {
                    trace("No DoABC block... ;(");
                }
            return result;
        }

        private static function createClassNameFromQname(qn:QName):String
        {
            var result:String
            if (qn.uri)
            {
                result = qn.uri + "::" + qn.localName
            }
            else
            {
                result = qn.localName
            }
            return result;
        }

        public static function getUnsatisfiedDependencies(swfBytes:ByteArray):Array /* of String */
        {
            var result:Array = [];
            var dependencies:Array = SwfDependencyUtil.getDependencies(swfBytes)
            for each (var dependency:String in dependencies)
            {
                if (ApplicationDomain.currentDomain.hasDefinition(dependency))
                {
                    trace('ok: ', dependency);
                }
                else
                {
                    trace('ERROR: unsatisfied dependency: ', dependency);
                    result.push(dependency);
                }
            }
            return result;
        }
    }
}
person Simon Groenewolt    schedule 10.06.2012