Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AIR NativeProcess standardInput not connecting to app

Tags:

c#

air

I'm writing an AIR app that launches a C# console application and they need to communicate. I'd like to use standard input/standard output for this, however I can't seem to get it to work.

When the C# app gets input from standard input it's supposed to send it back via standard output, and if it receives "exit" then it quits. I can test this from the command line and it works correctly. When I send a string from AIR, I get no response from the C# app.

I'm sending an argument when I launch the C# app, and I do get a response from that, so my AIR app is at least able to receive messages from standard output, it's just standard input that is not working. When I send a message from AIR via standardInput I get a progress event with bytesLoaded = 3 when I send the keycode, and bytesLoaded = 5 when I send the "exit" command.

Here is the C# code:

static void Main(string[] args)
{
    if (args.Length > 0)
    {
        Console.WriteLine(args[0]);
    }
    while (true)
    {
        string incoming = Console.ReadLine();
        string outgoing = "received: " + incoming;
        Console.WriteLine(outgoing);
        if (incoming == "exit")
            return;
    }
}

And here is the AS3 code:

private function init(e:Event=null):void {
    this.removeEventListener(Event.ADDED_TO_STAGE, init);
    NativeApplication.nativeApplication.addEventListener(Event.EXITING, onAppClose);

    var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    var file:File = File.applicationDirectory.resolvePath("test.exe");
    info.executable = file;

    process = new NativeProcess();
    info.arguments.push("native process started");
    process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
    process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
    process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, onInputProgress);
    process.addEventListener(Event.STANDARD_OUTPUT_CLOSE, onOutputClose);
    process.addEventListener(Event.STANDARD_ERROR_CLOSE, onErrorClose);
    process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
    process.addEventListener(IOErrorEvent.STANDARD_INPUT_IO_ERROR, onIOError);
    process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);

    process.start(info);
}

private function onKeyUp(e:KeyboardEvent):void {
    if (e.keyCode == Keyboard.ESCAPE) 
        process.standardInput.writeUTFBytes("exit\n");
    else {
        var msg:String = e.keyCode + "\n";
        process.standardInput.writeUTFBytes(msg);
    }
}

private function onOutputData(e:ProgressEvent):void {
    var data:String = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable); 
    trace("Got: ", data); 
}
like image 209
Dave Wolfe Avatar asked Jan 26 '13 00:01

Dave Wolfe


2 Answers

I encountered this issue a few months back but I never resolved it as I just used command line args instead. I have just returned to it though as I am keen to find out know what's going on.

I have now found that targeting .NET 3.5 or earlier makes it work as expected for me. Switch back to to v4.0 and I get nothing on stdin. I'm not exactly sure where the issue lies, but if you can get by with v3.5 then it might be a solution for you.

like image 86
Tom Makin Avatar answered Nov 19 '22 05:11

Tom Makin


In case anyone else besides me still use Adobe AIR with C# console apps, here's a solution to the problem:

Just like @tom-makin points out in the linked answer, you need to create another console app that runs on .NET 3.5, which then opens your newer .NET console app and passes input to it. Here's my take on such an app:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace StdInPiper
{
    class Program
    {
        static void Main(string[] args)
        {
            // Remove the first arg from args, containing the newer .NET exePath.
            string exePath = args[0];
            var tempArgs = new List<string>(args);
            tempArgs.RemoveAt(0);
            string argsLine = "";
            foreach (string arg in tempArgs)
            {
                argsLine = argsLine + " " + arg;
            }
            argsLine = argsLine.Trim();

            var process = new Process();            
            process.EnableRaisingEvents = true;

            process.StartInfo.UseShellExecute = false;            
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.Arguments = argsLine;
            process.StartInfo.FileName = exePath;

            process.OutputDataReceived += (sender, eventArgs) =>
            {
                Console.Write(eventArgs.Data);
            };

            process.ErrorDataReceived += (sender, eventArgs) =>
            {
                Console.Error.Write(eventArgs.Data);
            };

            process.Exited += (sender, eventArgs) =>
            {
                process.CancelOutputRead();
                process.CancelErrorRead();
                Environment.Exit(Environment.ExitCode);
            };

            process.Start();            
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();                        

            while (true)
            {
                Thread.Sleep(20);
                string line = Console.ReadLine();
                if (line != null)
                {
                    process.StandardInput.WriteLine(line);
                }
            }            
        }
    }
}
like image 31
Lerdal Avatar answered Nov 19 '22 06:11

Lerdal