Launch EXE (Serproxy) with Adobe AIR
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开发者_如何学Go = 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
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!
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);
}
}
}
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();
精彩评论