2015-03-30 11 views
7

Ich arbeite an einem C# -Programm, das derzeit Node über Process.Start() ausführt. Ich fange das stdout und stderr von diesem Kindprozess ein und leite es aus meinen eigenen Gründen um. Ich bin dabei, den Aufruf von Node.exe durch einen Aufruf von Edge.js zu ersetzen. Um dies tun zu können, muss ich in der Lage sein, stdout und stderr zuverlässig aus dem in Edge ausgeführten Javascript zu erfassen und die Nachrichten in meine C# -Anwendung zurück zu bekommen.Wenn Sie Edge.js von C# aufrufen, wie hookst du stdout und stderr?

Ansatz 1

ich diesen Ansatz für Vollständigkeit, falls jemand beschreiben werde empfiehlt es :)

Wenn der Edge-Prozess beendet wird, ist es ziemlich einfach ist, sich damit zu befassen, indem einfach ein msgs Array deklarieren und Überschreiben process.stdout.write und process.stderr.write mit neuen Funktionen, die Nachrichten auf diesem Array ansammeln, dann am Ende, einfach die msgs Array zurückgeben. Beispiel:

var msgs = []; 
process.stdout.write = function (string) { 
    msgs.push({ stream: 'o', message : string }); 
}; 
process.stderr.write = function (string) { 
    msgs.push({ stream: 'e', message: string }); 
}; 

// Return to caller. 
var result = { messages: msgs; ...other stuff... }; 
callback(null, result); 

Offensichtlich funktioniert das nur, wenn der Edge-Code beendet wird, und Nachrichten können im ungünstigsten Fall groß werden. Es ist jedoch wahrscheinlich eine gute Leistung, da nur ein Marshalling-Aufruf erforderlich ist, um alle Nachrichten zurück zu erhalten.

Ansatz 2

Dies ist ein wenig schwieriger zu erklären. Statt Nachrichten zu sammeln, "haken" wir stdout und stderr mit einem Delegaten, den wir von C# senden. In der C# erstellen wir ein Objekt, das wir in Rand passieren, und das Objekt eine Eigenschaft hat stdoutHook genannt:

dynamic payload = new ExpandoObject(); 
payload.stdoutHook = GetStdoutHook(); 

public Func<object, Task<object>> GetStdoutHook() 
{ 
    Func<object, Task<object>> hook = (message) => 
    { 
     TheLogger.LogMessage((message as string).Trim()); 
     return Task.FromResult<object>(null); 
    }; 

    return hook; 
} 

Ich konnte wirklich mit einer Aktion weg, aber Rand erscheint die Func<object, Task<object>> zu verlangen, es gewann Die Funktion sonst nicht übernehmen. Dann wird in der Javascript, können wir diese Funktion und verwenden Sie es wie folgt ermitteln:

var func = Edge.Func(@" 
    return function(payload, callback) { 
     if (typeof (payload.stdoutHook) === 'function') { 
      process.stdout.write = payload.stdoutHook; 
     } 

     // do lots of stuff while stdout and stderr are hooked... 
     var what = require('whatever'); 
     what.futz(); 

     // terminate. 
     callback(null, result); 
}"); 

dynamic result = func(payload).Result; 

Fragen

Q1. Beide Techniken scheinen zu funktionieren, aber gibt es einen besseren Weg dies zu tun, etwas, das Edge vielleicht eingebaut hat, das ich verpasst habe? Beide Lösungen sind invasiv - sie benötigen etwas Shim-Code, um die eigentliche Arbeit, die in Edge erledigt werden soll, einzubinden. Dies ist nicht das Ende der Welt, aber es wäre besser, wenn es eine nicht-invasive Methode gäbe.

Q2. In Ansatz 2, wo ich hier eine Aufgabe zurückkehren

return Task.FromResult<object>(null); 

es fühlt sich falsch an eine bereits abgeschlossen „null Aufgabe“ zu sein, zurück. Aber gibt es eine andere Möglichkeit, dies zu schreiben?

Q3. Muss ich im Javascript-Code rigoroser sein, wenn ich stdout und stderr einhänge? Ich stelle fest, in zwei edge.js gibt dieser Code ist, ehrlich gesagt ich bin nicht sicher, was hier passiert, aber es ist ein bisschen komplexer als meine rohe Überschreibung von process.stdout.write :-)

// Fix #176 for GUI applications on Windows 
try { 
    var stdout = process.stdout; 
} 
catch (e) { 
    // This is a Windows GUI application without stdout and stderr defined. 
    // Define process.stdout and process.stderr so that all output is discarded. 
    (function() { 
     var stream = require('stream'); 
     var NullStream = function (o) { 
      stream.Writable.call(this); 
      this._write = function (c, e, cb) { cb && cb(); }; 
     } 
     require('util').inherits(NullStream, stream.Writable); 
     var nullStream = new NullStream(); 
     process.__defineGetter__('stdout', function() { return nullStream; }); 
     process.__defineGetter__('stderr', function() { return nullStream; }); 
    })(); 
} 

Antwort

2

Q1: Es ist nichts in Edge integriert, das das Aufzeichnen von stdout oder stderr von Node.js-Code beim Aufrufen von Node aus CLR automatisch ermöglicht. Irgendwann dachte ich daran, eine Erweiterung von Edge zu schreiben, die das Marshalling von Streams über CLR/V8-Grenzen hinweg einfach macht. Unter der Haube wäre es Ihrem Ansatz 2 sehr ähnlich.Es könnte als eigenständiges Modul über Edge erfolgen.

Q2: Zurückgeben einer abgeschlossenen Aufgabe ist in diesem Fall sehr geeignet. Ihre Funktion hat die Node.js-Ausgabe erfasst, verarbeitet und in diesem Sinne "abgeschlossen". Die Rückkehr zu einer Aufgabe, die mit Null abgeschlossen wurde, ist wirklich ein moralisches Äquivalent zur Rückkehr von einer Aktion.

Q3: Der Code, auf den Sie verweisen, ist nur in Windows-GUI-Anwendungen und nicht in Konsolenanwendungen relevant. Wenn Sie eine Konsolenanwendung schreiben, sollte das Überschreiben von write auf der Ebene des Node.js-Codes genügen, den Sie an Edge.js übergeben. Beachten Sie, dass die Signatur write im Knoten eine optionale encoding parameter to be passed in zulässt. Sie scheinen es sowohl in Approach 1 als auch 2 zu ignorieren. Insbesondere in Approach 2 würde ich vorschlagen, den JavaScript-Proxy in eine JavaScript-Funktion, die die Parameter vor der Zuweisung zu process.stdout.write normalisiert, in C# -Rückruf einzubinden. Andernfalls kann der Edge.js-Code annehmen, dass der an einen write-Aufruf übergebene Parameter encoding eine Rückruffunktion ist, die der Aufrufkonvention Edge.js folgen würde.

+0

Danke Tomasz, also im Grunde Approach 2 mit Augenmerk auf Codierung ist meine beste Wette für jetzt. Es wäre großartig, wenn es einen einfacheren Weg dazu gäbe, vielleicht eine Überladung der Edge.Func(), die ein Paar von optionalen Streams oder Delegaten nehmen kann. –