Подтвердить что ты не робот

Запуск приложения Adobe AIR несколько раз

Время автономной работы Adobe предотвращает одновременное запуск более одного экземпляра воздушного приложения. Безопасно ли обойти это ограничение путем произвольного изменения идентификатора издателя? Кто-нибудь знает, планирует ли Adobe разрешить несколько параллельных экземпляров в Air 2.0?

4b9b3361

Ответ 1

Мы успешно реализовали хак, чтобы обойти это ограничение в чистом режиме AIR, не изменяя идентификатор издателя (для которого, как мне кажется, требуется несколько сертификатов).

Как вы знаете, AIR реализует свой Mutex с помощью уникального идентификатора приложения. Этот идентификатор вычисляется с использованием идентификатора приложения и идентификатора издателя (извлеченного из сертификата, который подписал приложение).

В каталоге установки приложения AIR есть папка META-INF (или в /share/using Linux). Эта папка META-INF содержит папку AIR, содержащую файл "application.xml". Этот файл содержит тег <id />, который определяет идентификатор приложения, который используется при вычислении идентификатора мьютекса. Если ваше приложение может писать в установочной папке, вы можете использовать API File, чтобы редактировать его во время выполнения, произвольно меняя тэг <id />, позволяя одновременно запускать несколько процессов одного и того же приложения.

У этого есть некоторые раздражающие побочные эффекты, такие как создание новой папки в папке File.applicationStorageDirectory каждый раз. Но используя LocalConnection, вы можете свести к минимуму это, повторно используя один и тот же идентификатор несколько раз, регистрируя, какие из них могут быть повторно использованы. Кроме того, SharedObject хранятся в этой папке, поэтому их нельзя использовать (или их нужно копировать каждый раз, когда создается новый экземпляр, и синхронизируются, хотя LocalConnection).

Насколько я знаю, Adobe не планирует удалять это собственное ограничение. Он был реализован для многоплатформенных целей, особенно на MacOS, где док делает это более сложным (не так просто запустить одно и то же приложение дважды с док-станцией).

Официальный способ сделать это - поймать событие InvokeEvent.INVOKE и сделать что-то вроде открытия нового окна. И никаких изменений, запланированных для AIR 2.0 в этом поведении, не было.

Ответ 2

Помогло бы вам инкапсулировать логику вашего приложения как класс, который мог бы запускаться в окне, и позволить пользователю создавать несколько экземпляров этого окна в одном приложении? это поможет?

В чем основная причина, по которой вам потребуется несколько приложений?

Ответ 3

Дупликатор Air Application поможет вам в этой части. Используя это, вы можете запускать несколько экземпляров одного и того же приложения AIR.

https://github.com/chrisdeely/AirAppDuplicator

Простое копирование вашего каталога приложений с новым именем и новым идентификатором приложения.

Ответ 4

Это прервет автоматическое обновление, будет предупреждено.

Ответ 5

Просто сделал быстрый класс для реализации решения Tyn. Просто позвоните. MultipleInstanceAirApp.changeMetaInfId(stage);

Вам не нужна часть сцены, но я использую ее, чтобы изменить положение окна, когда я тестирую. В любом случае, наслаждайтесь!

import flash.display.Stage;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.utils.ByteArray;

/**
 * @author Lachhh
 */
public class MultipleInstanceAirApp {
    static private var loadFile : File;
    static private var thePath:String = "./META-INF/AIR/application.xml";
    static private var myGameId:String = "YOUR_GAME_ID";
    static private var stage:Stage ;
    static private var metaInfString:String ;

    static public var instanceNumber:int = 0;

    static public function changeMetaInfId(pStage:Stage):void {
        stage = pStage;
        var path:String = File.applicationDirectory.resolvePath(thePath).nativePath; 
        loadFile = new File(path);

        loadFile.addEventListener(Event.COMPLETE, onLoadMetaInf);
        loadFile.addEventListener(IOErrorEvent.IO_ERROR, onIoError);
        loadFile.load();
    }

    private static function onLoadMetaInf(event : Event) : void {
        loadFile.removeEventListener(Event.COMPLETE, onLoadMetaInf);
        metaInfString = loadFile.data.toString();
        replaceMetaInfIdIfFound();
        saveStringToMetaInf(metaInfString);
    }

    static public function saveStringToMetaInf(s:String):void {
        var b:ByteArray = new ByteArray();
        b.writeUTFBytes(s);
        saveFile(b);
    }

    static public function saveFile(data:ByteArray):void {
        var thePath:String = File.applicationDirectory.resolvePath(thePath).nativePath;     
        var saveFile:File = new File(thePath);
        var fileStream:FileStream = new FileStream();
        fileStream.openAsync(saveFile, FileMode.WRITE);
        fileStream.writeBytes(data);
        fileStream.addEventListener(Event.CLOSE, onClose);
        fileStream.close();
    }

    static private function replaceMetaInfIdIfFound():void {
        if(checkToReplaceId(1, 2)) return ;
        if(checkToReplaceId(2, 3)) return ;
        if(checkToReplaceId(3, 4)) return ;
        checkToReplaceId(4, 1);

    }

    static private function checkToReplaceId(i:int, newI:int):Boolean {
        var id:String = getGameIdWithBrackets(i);
        var newId:String = getGameIdWithBrackets(newI);
        if(metaInfString.indexOf(id) != -1) {
            metaInfString = myReplace(metaInfString, id, newId);
            instanceNumber = newI;
            return true;
        }
        return false;
    }

    private static function onClose(event : Event) : void {
        trace("all done!");
        placeScreenAccordingToInstanceNumber();
    }

    static private function placeScreenAccordingToInstanceNumber():void {;
        switch(instanceNumber) {
            case 1 : 
                stage.nativeWindow.x = 115;
                stage.nativeWindow.y = 37;
                break;
            case 2 : 
                stage.nativeWindow.x = 115 + 660;
                stage.nativeWindow.y = 37;
                break;
            case 3 : 
                stage.nativeWindow.x = 115;
                stage.nativeWindow.y = 37 + 380;
                break;
            case 4 : 
                stage.nativeWindow.x = 115 + 660;
                stage.nativeWindow.y = 37 + 380;
                break;
        }
    }

    private static function onIoError(event : IOErrorEvent) : void {
        trace("io Error");
    }

    static private function getGameIdOriginalWithBrackets():String {
        return "<id>" + myGameId + "</id>";
    }

    static private function getGameIdWithBrackets(i:int):String {
        if(i == 1) return getGameIdOriginalWithBrackets();
        return "<id>" + myGameId + i + "</id>";
    }

    static public function myReplace(msg:String, toFind:String, toBeReplacedWith:String):String {
        return msg.split(toFind).join(toBeReplacedWith) ;
    }
}

Ответ 6

package hobis.airpc 
{
    import flash.events.Event;
    import flash.filesystem.File;   
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.utils.ByteArray;
    import jhb0b.utils.MArrayUtil;

    public final class MAppXmlUpdateCounter
    {
        private static var _AppXmlFile:File;

        public static function Update():void
        {
            _AppXmlFile = new File(File.applicationDirectory.nativePath);           
            _AppXmlFile = _AppXmlFile.resolvePath('META-INF\\AIR\\application.xml');
            _AppXmlFile.addEventListener(Event.COMPLETE, ppOpened);
            _AppXmlFile.load();         
        }

        private static function ppOpened(evt:Event):void
        {
            const trx1:RegExp = /<id>[\s\S]*?<\/id>/;
            const trx2:RegExp = /<([^>]+)>/g;

            var tXmlStr:String = _AppXmlFile.data.toString();
            var tMatArr:Array = tXmlStr.match(trx1);
            if (!MArrayUtil.is_empty(tMatArr))
            {
                var tIdTagStr:String = tMatArr[0];
                var tIdValStr:String = tIdTagStr.replace(trx2, '');

                var tOriVal:String;
                var tNumVal:uint;
                var tStrArr:Array = tIdValStr.split('-');
                if (tStrArr != null)
                {
                    if (tStrArr.length == 2)
                    {
                        tOriVal = tStrArr[0];
                        tNumVal = int(tStrArr[1]);                              
                    }
                    else
                    if (tStrArr.length == 1)
                    {
                        tOriVal = tStrArr[0];
                        tNumVal = 0;
                    }
                    tNumVal++;

                    var tIdNewStr:String = '<id>' + tOriVal + '-' + tNumVal + '<\/id>';                 
                    var tNewXmlStr:String = tXmlStr.replace(tIdTagStr, tIdNewStr);                  
                    ppSaveFile(tNewXmlStr);
                }
            }
            _AppXmlFile = null;
        }

        private static function ppSaveFile(val:String):void
        {
            var tfs:FileStream;
            try
            {
                tfs = new FileStream();
                tfs.openAsync(_AppXmlFile, FileMode.WRITE);
                var tba:ByteArray = new ByteArray();
                tba.writeUTFBytes(val);         
                tfs.writeBytes(tba);                
                tba.clear();
            }
            catch (e:Error) { }
            try
            {
                tfs.close();
            }
            catch (e:Error) { }
        }
    }   
}