3

I'm developing an AIR application in Flash Builder (Flex) and I needed the option to communicate with a serial port on the computer. So I'm using Serproxy to help me with that.

I want to be able to launch serproxy.exe when my application runs. I've tried two methods, and neither of them are working for me.

I have set supportedProfiles with extendedDesktop.


First method:

var file:File = File.applicationDirectory.resolvePath("assets/serproxy.exe");
file.openWithDefaultApplication();

This proceeds to open the program, but then immediately closes it. No errors are thrown.


Second method:

var file:File = File.applicationDirectory.resolvePath("assets/serproxy.exe");

var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
nativeProcessStartupInfo.executable = file;
var process:NativeProcess = new NativeProcess();

process.start(nativeProcessStartupInfo);

Although through research this method has been found to work, it simply does nothing for me. No errors are thrown, and no program is launched.


If anyone has any ideas please let me know! Thanks

Chunky Chunk
  • 16,553
  • 15
  • 84
  • 162
Ben
  • 5,117
  • 2
  • 27
  • 26

3 Answers3

4

I've solved it. My issue was that since Serproxy opens with cmd.exe, there were some issues keeping the file open, as Windows likes to automatically close it. I got around this by creating a shortcut to the file, and pre-pending its target with

C:\Windows\System32\cmd.exe /K "C:\...assets\serproxy.exe"

Then I could run the shortcut with

var file:File = File.applicationDirectory.resolvePath("assets/serproxy.lnk");
file.openWithDefaultApplication();

And the window stayed open!

Ben
  • 5,117
  • 2
  • 27
  • 26
  • I was struggling with this for ages! A problem with this method though is if you package the app, and install it to a different directory the shortcut will break. Do you have any ideas how I could overcome this? – davivid Jul 14 '11 at 07:38
  • @davivid Well I never actually got that far, as I ended up switching from Serproxy to Python+PySerial - it's reliable, runs in the background and is cross-OS. You could generate the .lnk file using the File class in AIR2. If the .lnk file doesn't exist, get the absolute path of serproxy.exe using resolvePath and generate the .lnk file - whenever you boot up the app. Just view the source of a .lnk file in a Hex editor to see how to write the bytes. – Ben Jul 15 '11 at 02:44
1

I wrote an application last year called Disco Desktop which uses TinkerProxy to allow AIR to communicated with Arduino over USB serial port. My TinkerProxy and TinkerProxyEvent classes is posted below.

The application bundles serproxy and uses Native Process API to call it so there are 2 different installers - Mac OS X and Windows. TinkerProxy.as extends Socket, writes the serproxy configuration file at runtime according to the users' input and launches serproxy based on this configuration as a background process. The terminal/cmd window is never visible.

Note: call open() instead of connect(). Included in the installers are a simple device schematic and sketch for Arduino.

TinkerProxy:

package com.mattie.net
{
//Imports
import com.mattie.events.TinkerProxyEvent;
import flash.desktop.NativeApplication;
import flash.desktop.NativeProcessStartupInfo;
import flash.desktop.NativeProcess;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.filesystem.File;
import flash.net.Socket;
import flash.system.Capabilities;
import flash.utils.Timer;
import flash.utils.Endian;

//Class
public class TinkerProxy extends Socket
    {
    //Properties
    private var systemIsWindowsProperty:Boolean;
    private var openingProperty:Boolean;
    private var connectedProperty:Boolean;

    //Variables
    private var windowsProxyFile:String;
    private var macProxyFile:String;
    private var tinkerProxyApplication:File;
    private var tinkerProxyConfigurationFile:File;
    private var serialPort:String;
    private var baudRate:uint;
    private var networkAddress:String;
    private var networkPort:uint;
    private var loadDelay:uint;
    private var loadDelayTimer:Timer;
    private var initializeDelay:uint;
    private var initializeDelayTimer:Timer;
    private var comDatabits:uint;
    private var comStopbits:uint;
    private var proxyTimeout:uint;
    private var writeConfigStream:FileStream;
    private var tinkerProxyProcess:NativeProcess;

    //Constructor
    public function TinkerProxy(windowsProxyFile:String = "serproxy.exe", macProxyFile:String = "serproxy.osx", endian:String = Endian.LITTLE_ENDIAN)
        {
        //Set Included File Proxy Names
        this.windowsProxyFile = windowsProxyFile;
        this.macProxyFile = macProxyFile;

        super();
        super.endian = endian;

        init();
        }

    //Resolve The Operating System
    private function init():void
        {
        //Check If Source Tinker Proxy Files Are Included In Application Directory
        if  (!File.applicationDirectory.resolvePath(windowsProxyFile).exists && !File.applicationDirectory.resolvePath(macProxyFile).exists)
            throw new Error("Tinker Proxy source files \"" + windowsProxyFile + "\" (Windows) and/or \"" + macProxyFile + "\" (Mac) cannot be found in application directory (Included Files)");

        //Resoslve Operating System
        if  (Capabilities.os.toLowerCase().indexOf("windows") > -1)
            {
            systemIsWindowsProperty = true;
            tinkerProxyApplication = File.applicationDirectory.resolvePath(windowsProxyFile);
            tinkerProxyConfigurationFile = File.applicationStorageDirectory.resolvePath(windowsProxyFile.substring(0, windowsProxyFile.lastIndexOf(".exe")) + ".cfg");  
            }
            else if (Capabilities.os.toLowerCase().indexOf("mac") > -1)
            {
            systemIsWindowsProperty = false;
            tinkerProxyApplication = File.applicationDirectory.resolvePath(macProxyFile);
            tinkerProxyConfigurationFile = File.applicationStorageDirectory.resolvePath(macProxyFile + ".cfg");
            }
            else
            {
            throw new Error("TinkerProxy Error:  Operating System Is Not Supported");
            }
        }

    //Open Tinker Proxy Socket Connection
    public function open(
                        serialPort:String,
                        baudRate:uint,
                        networkAddress:String = "127.0.0.1",
                        networkPort:uint = 5331,
                        loadDelay:uint = 1000,
                        initializeDelay:uint = 2000,
                        comDatabits:uint = 8,
                        comStopbits:uint = 1,
                        proxyTimeout:uint = 63115200
                        )
        {
        //Disable Opening Socket If Currently Opening
        if  (!openingProperty)
            {
            //Set Accessor
            openingProperty = true;

            //Dispatch Event
            dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.LOADING));

            //Check If Connection Parameters For Configuration File Have Changed
            if  (
                this.serialPort == serialPort   &&
                this.baudRate == baudRate                           &&
                this.networkAddress == networkAddress               &&
                this.networkPort == networkPort                     &&
                this.comDatabits == comDatabits                     &&
                this.comStopbits == comStopbits                     &&
                this.proxyTimeout == proxyTimeout
                )
                    {
                    //Assign Timer Variables
                    this.loadDelay = loadDelay;
                    this.initializeDelay = initializeDelay;

                    //Launch Tinker Proxy Application If Connection Parameters Have Not Changed
                    launchTinkerProxyApplication(null);
                    return;
                    }

            //Assign Variables
            this.serialPort = serialPort;
            this.baudRate = baudRate;
            this.networkAddress = networkAddress;
            this.networkPort = networkPort;
            this.loadDelay = loadDelay;
            this.initializeDelay = initializeDelay;
            this.comDatabits = comDatabits;
            this.comStopbits = comStopbits;
            this.proxyTimeout = proxyTimeout;

            //Add Event Listeners To New File Stream
            writeConfigStream = new FileStream();
            writeConfigStream.addEventListener(Event.CLOSE, launchTinkerProxyApplication);
            writeConfigStream.addEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);

            //Write Tinker Proxy Configuration File
            writeConfigStream.openAsync(tinkerProxyConfigurationFile, FileMode.WRITE);

            writeConfigStream.writeUTFBytes("serial_device1=" + serialPort + File.lineEnding);
            writeConfigStream.writeUTFBytes("comm_ports=1" + File.lineEnding);
            writeConfigStream.writeUTFBytes("net_port1=" + networkPort + File.lineEnding);              
            writeConfigStream.writeUTFBytes("newlines_to_nils=false" + File.lineEnding);
            writeConfigStream.writeUTFBytes("comm_baud=" + baudRate + File.lineEnding);
            writeConfigStream.writeUTFBytes("comm_databits=" + comDatabits + File.lineEnding);
            writeConfigStream.writeUTFBytes("comm_stopbits=" + comStopbits+ File.lineEnding);
            writeConfigStream.writeUTFBytes("comm_parity=none" + File.lineEnding);
            writeConfigStream.writeUTFBytes("timeout=" + proxyTimeout + File.lineEnding);

            writeConfigStream.close();
            }
        }

    //Launch Tinker Proxy Application
    private function launchTinkerProxyApplication(evt:Event):void
        {
        if  (evt)
            {
            //Remove File Stream Event Listeners
            writeConfigStream.removeEventListener(Event.CLOSE, launchTinkerProxyApplication);
            writeConfigStream.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);
            }

        //Start Tinker Proxy Application As Native Process
        var tinkerProxyProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
        tinkerProxyProcessStartupInfo.executable = tinkerProxyApplication;

        var processArguments:Vector.<String> = new Vector.<String>();
        processArguments[0] = tinkerProxyConfigurationFile.nativePath;
        tinkerProxyProcessStartupInfo.arguments = processArguments;

        tinkerProxyProcess = new NativeProcess();
        tinkerProxyProcess.start(tinkerProxyProcessStartupInfo);

        //Delay Process To Allow Tinker Proxy Application To Initialize
        loadDelayTimer = new Timer(loadDelay, 1);
        loadDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
        loadDelayTimer.start();
        }

    //Initialize Tinker Proxy Socket Connection
    private function connectTinkerProxy(evt:TimerEvent):void
        {
        //Dispatch Event
        dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.INITIALIZING));

        //Remove Tinker Proxy Application Initilization Timer
        loadDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
        loadDelayTimer = null;

        //Add Connection Error Event Listeners
        addEventListener(Event.CONNECT, initializeDelayTimerHandler);
        addEventListener(Event.CLOSE, connectionErrorEventHandler);
        addEventListener(IOErrorEvent.IO_ERROR, connectionErrorEventHandler);
        addEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionErrorEventHandler);

        //Connect Socket (Super)
        try {
            super.connect(networkAddress, networkPort);
            }
            catch(error:IOError)        {connectionErrorEventHandler(null);}
            catch(error:SecurityError)  {connectionErrorEventHandler(null);}
        }

    //Delay Process To Allow Device To Initialize
    private function initializeDelayTimerHandler(evt:Event):void
        {
        removeEventListener(Event.CONNECT, initializeDelayTimerHandler);

        initializeDelayTimer = new Timer(initializeDelay, 1);
        initializeDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, tinkerProxyConnectionComplete);
        initializeDelayTimer.start();
        }

    //Tinker Proxy Socket Has Been Successfully Connected
    private function tinkerProxyConnectionComplete(evt:TimerEvent):void
        {
        //Set Accessors
        openingProperty = false;
        connectedProperty = true;

        //Dispatch Event
        dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.CONNECT));

        //Remove Device Initilization Timer
        initializeDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, tinkerProxyConnectionComplete);
        initializeDelayTimer = null;
        }

    //Throw Error If Stock Connect Method Is Explicitly Called
    override public function connect(host:String, port:int):void
        {
        throw new Error("Cannot call connect() method on TinkerProxy instance.  Call open() method instead."); 
        }

    //Close Tinker Proxy Application
    override public function close():void
        {
        //Stop Configuration File And Timers If Socket Is Currently Opening
        if  (openingProperty)
            {
            //Set Accessor
            openingProperty = false;

            //Stop File Stream
            if  (writeConfigStream.hasEventListener(Event.CLOSE))
                {
                writeConfigStream.close();
                writeConfigStream.removeEventListener(Event.CLOSE, launchTinkerProxyApplication);
                writeConfigStream.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);
                }

            //Stop Process Initialization Timer
            if  (loadDelayTimer.running)
                {
                loadDelayTimer.stop();
                loadDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
                loadDelayTimer = null;
                }

            //Stop Device Initialization Timer
            if  (initializeDelayTimer.running)
                {
                initializeDelayTimer.stop();
                initializeDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
                initializeDelayTimer = null;
                }
            }

        //Close Socket (Super)
        super.close();

        //Close Tinker Proxy Application
        tinkerProxyProcess.exit(true);
        tinkerProxyProcess = null;

        //Dispatch Event
        dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.DISCONNECT));

        //Set Accessor
        connectedProperty = false;

        //Remove Connection Error Event Listeners 
        removeEventListener(Event.CLOSE, connectionErrorEventHandler);
        removeEventListener(IOErrorEvent.IO_ERROR, connectionErrorEventHandler);
        removeEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionErrorEventHandler);
        }

    //Server Automatically Closed The Socket Due To A Connection Error
    private function connectionErrorEventHandler(evt:*):void
        {
        //Set Accessors
        openingProperty = false;
        connectedProperty = false;

        //Dispatch Event
        dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.ERROR));

        //Remove Device Initilization Timer
        if  (initializeDelayTimer != null)
            {
            if  (initializeDelayTimer.running)
                initializeDelayTimer.stop();

            initializeDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, tinkerProxyConnectionComplete);
            initializeDelayTimer = null;
            }

        //Remove Connection Error Event Listeners 
        removeEventListener(Event.CLOSE, connectionErrorEventHandler);
        removeEventListener(IOErrorEvent.IO_ERROR, connectionErrorEventHandler);
        removeEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionErrorEventHandler);

        //Close Tinker Proxy Application
        tinkerProxyProcess.exit(true);
        tinkerProxyProcess = null;
        }

    //IO Error Event Handler 
    private function IOErrorEventHandler(evt:IOErrorEvent):void
        {
        throw new Error("TinkerProxy IOError: " + evt);
        }

    //Accessors
    public function get systemIsWindows():Boolean
        {
        return systemIsWindowsProperty;
        }

    public function get opening():Boolean
        {
        return openingProperty;
        }

    override public function get connected():Boolean
        {
        return connectedProperty;
        }
    }
}

TinkerProxyEvent:

package com.mattie.events
{
//Imports
import flash.events.Event;

//Class
public class TinkerProxyEvent extends Event
    {
    //Constants
    public static const LOADING:String = "Loading";
    public static const INITIALIZING:String = "Initializing";
    public static const CONNECT:String = "Connect";
    public static const DISCONNECT:String = "Disconnect";
    public static const ERROR:String = "Error";

    //Constructor
    public function TinkerProxyEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
        {
        super(type, bubbles, cancelable);
        }
    }
}
double-beep
  • 5,031
  • 17
  • 33
  • 41
Chunky Chunk
  • 16,553
  • 15
  • 84
  • 162
-1

Use URLRequest to launch apps using AIR

public function clickButton():void{
var request : URLRequest = new URLRequest('C:\\path to serproxy\serproxy.exe');
navigateToURL(request)

also here are the paths to default folders
var appDir:File    = File.applicationDirectory;
var appStoreDir:File= File.applicationStorageDirectory;
var desktopDir:File = File.desktopDirectory;
var docDir:File    = File.documentsDirectory;
var userDir:File    = File.userDirectory;
var rootDirArr:Array = File.getRootDirectories();  
Satish
  • 6,457
  • 8
  • 43
  • 63
  • 1
    Didn't work, just "downloaded" the file through my browser. Thanks for your help anyway. – Ben Jun 09 '11 at 00:25