Flash 多次启动AdobeAIR应用程序

Flash 多次启动AdobeAIR应用程序,flash,air,Flash,Air,AdobeAIR运行时防止同时启动多个air应用程序实例。通过任意更改发布者ID来规避此限制安全吗?有人知道Adobe是否计划在Air 2.0中允许多个并发实例吗?如果您将应用程序的逻辑封装为可以在窗口中运行的类,并允许用户在一个应用程序中创建该窗口的多个实例,会有帮助吗?那会有帮助吗 您需要多个应用程序的主要原因是什么?我们成功地实施了一项黑客攻击,以一种纯空中方式绕过了这一限制,而无需更改发布者id(我认为它需要多个证书) 正如您所知,AIR通过使用唯一的应用程序标识符来实现其互斥。使用应用

AdobeAIR运行时防止同时启动多个air应用程序实例。通过任意更改发布者ID来规避此限制安全吗?有人知道Adobe是否计划在Air 2.0中允许多个并发实例吗?

如果您将应用程序的逻辑封装为可以在窗口中运行的类,并允许用户在一个应用程序中创建该窗口的多个实例,会有帮助吗?那会有帮助吗


您需要多个应用程序的主要原因是什么?

我们成功地实施了一项黑客攻击,以一种纯空中方式绕过了这一限制,而无需更改发布者id(我认为它需要多个证书)

正如您所知,AIR通过使用唯一的应用程序标识符来实现其互斥。使用应用程序id和发布者标识符(从签署应用程序的证书中提取)计算此标识符

在AIR应用程序的安装目录中,有一个META-INF文件夹(或在/share/using Linux中)。此META-INF文件夹包含一个AIR文件夹,其中包含一个“application.xml”文件。此文件包含定义应用程序标识符的
标记,用于计算互斥体标识符。如果应用程序可以在安装文件夹中写入,则可以在运行时使用
文件
API对其进行编辑,随机更改
标记,允许同一应用程序的多个进程同时运行

这会产生一些恼人的副作用,比如每次都在
File.applicationStorageDirectory
文件夹中创建一个新文件夹。但是,使用
LocalConnection
,您可以通过记录哪些标识符可以自由重用来多次重用同一标识符,从而最大限度地减少这种情况。此外,
SharedObject
存储在此文件夹中,因此无法使用(或者必须在每次创建新实例时进行复制,并通过
LocalConnection
进行同步)

据我所知,Adobe不打算取消这种本机限制。它是为多平台的目的而实现的,特别是在MacOS上,dock使其变得更加复杂(用dock启动同一个应用程序两次不是很容易)


官方的方法是捕获
InvokeEvent.INVOKE
事件,然后打开一个新窗口。AIR 2.0在这一行为方面没有计划进行任何更改。

这将中断自动更新,请注意。

AIR Application Replicator将在这方面帮助您。使用它,您可以运行同一AIR应用程序的多个实例


它只是简单地用新名称和新应用程序id复制你的应用程序目录。

刚刚创建了一个快速类来实现Tyn的解决方案。只要打个电话<代码>MultipleInstanceAirApp.ChangeMetainId(阶段)

你并不真的需要舞台部分,但我在测试时用它来改变窗口的位置。 无论如何,享受吧

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) ;
    }
}
导入flash.display.Stage;
导入flash.events.Event;
导入flash.events.IOErrorEvent;
导入flash.filesystem.File;
导入flash.filesystem.FileMode;
导入flash.filesystem.FileStream;
导入flash.utils.ByteArray;
/**
*@author lachh
*/
公共类MultipleInstanceAirApp{
静态私有var loadFile:File;
静态私有变量thePath:String=“./META-INF/AIR/application.xml”;
静态私有变量myGameId:String=“您的游戏\u ID”;
静态私有var阶段:阶段;
静态私有变量metaInfString:String;
静态公共变量instanceNumber:int=0;
静态公共函数changeMetainId(pStage:Stage):无效{
阶段=阶段;
变量路径:String=File.applicationDirectory.resolvePath(thePath).nativePath;
loadFile=新文件(路径);
loadFile.addEventListener(Event.COMPLETE,onLoadMetaInf);
loadFile.addEventListener(IOErrorEvent.IO_ERROR,onIoError);
loadFile.load();
}
私有静态函数onLoadMetaInf(事件:event):void{
loadFile.removeEventListener(Event.COMPLETE,onLoadMetaInf);
metaInfString=loadFile.data.toString();
replaceMetainDiffound();
saveStringToMetaInf(元输入字符串);
}
静态公共函数saveStringToMetanf(s:String):void{
变量b:ByteArray=newbytearray();
b、 可写字节;
保存文件(b);
}
静态公共函数saveFile(数据:ByteArray):void{
var thePath:String=File.applicationDirectory.resolvePath(thePath.nativePath);
var saveFile:File=新文件(路径);
var fileStream:fileStream=newfilestream();
openAsync(saveFile,FileMode.WRITE);
fileStream.writeBytes(数据);
addEventListener(Event.CLOSE,onClose);
fileStream.close();
}
静态私有函数replaceMateInfidifound():void{
if(checkToReplaceId(1,2))返回;
if(checkToReplaceId(2,3))返回;
if(checkToReplaceId(3,4))返回;
checkToReplaceId(4,1);
}
静态私有函数checkToReplaceId(i:int,newI:int):布尔值{
变量id:String=getGameID和括号(i);
var newId:String=getgameid和括号(newI);
if(metaInfString.indexOf(id)!=-1){
metaInfString=myReplace(metaInfString,id,newId);
instanceNumber=newI;
返回true;
}
返回false;
}
私有静态函数onClose(事件:event):void{
跟踪(“全部完成!”);
根据标准编号()放置屏幕;
}
静态私有函数placeScreenAccordingOinstanceNumber():void{;
开关(instanceNumber){
案例1:
stage.nativeWindow.x=115;
stage.nativeWindow.y=37;
打破
案例2:
stage.nativeWindow.x=115+660;
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) { }
        }
    }   
}