Sfondo
Sto lavorando a un programma C# che esegue attualmente il nodo tramite Process.Start(). Sto catturando lo stdout e lo stderr da questo processo figlio e reindirizzandolo per le mie ragioni. Sto cercando di sostituire l'invocazione di Node.exe con una chiamata a Edge.js invece. Per essere in grado di farlo, devo essere in grado di catturare in modo affidabile stdout e stderr dal Javascript in esecuzione in Edge e recuperare i messaggi nella mia applicazione C#.Quando si chiama Edge.js da C#, come si agganciano lo stdout e lo stderr?
Approccio 1
io descrivere questo approccio per completezza in caso qualcuno lo consiglia :)
Se il processo termina bordo, è abbastanza facile da affrontare questo semplicemente dichiarando un array di msgs
e sovrascrivendo process.stdout.write
e process.stderr.write
con nuove funzioni che accumulano messaggi su tale array, quindi alla fine, è sufficiente restituire l'array msgs
. Esempio:
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);
Ovviamente questo funziona solo se il codice bordo termina, e msg possa crescere di grandi dimensioni nel peggiore dei casi. Tuttavia, è probabile che funzioni bene perché è necessaria una sola chiamata di marshalling per recuperare tutti i messaggi.
Approccio 2
Questo è un po 'più difficile da spiegare. Invece di accumulare messaggi, "agganciamo" lo stdout e lo stderr usando un delegato che inviamo da C#. In C#, creiamo un oggetto che passeremo in bordo, e che oggetto ha una proprietà chiamata stdoutHook
:
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;
}
ho potuto realmente ottenere via con un'azione, ma Bordo sembra richiedere l'Func<object, Task<object>>
, ha vinto 's proxy la funzione in caso contrario. Poi, nel Javascript, siamo in grado di rilevare che la funzione e usarlo in questo modo:
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;
Domande
Q1. Entrambe queste tecniche sembrano funzionare, ma c'è un modo migliore di farlo, qualcosa di integrato a Edge forse che mi è sfuggito? Entrambe le soluzioni sono invasive: richiedono un certo codice di shim per avvolgere il lavoro effettivo che deve essere svolto in Edge. Questa non è la fine del mondo, ma sarebbe meglio se esistesse un metodo non invasivo.
Q2. Nel metodo 2, dove devo restituire un compito qui
return Task.FromResult<object>(null);
si sente sbagliato essere la restituzione di un già completato "compito nullo". Ma c'è un altro modo di scrivere questo?
Q3. Devo essere più rigoroso nel codice Javascript durante l'aggancio di stdout e stderr? Prendo atto in doppio-edge.js c'è questo codice, francamente non sono sicuro di quello che sta succedendo qui, ma è un po 'più complessa di quanto la mia sovrascrittura cruda :-) 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; });
})();
}
Grazie Tomasz, quindi fondamentalmente l'approccio 2 con attenzione alla codifica è la mia migliore scommessa per ora. Sarebbe bello se ci fosse un modo più semplice per farlo, forse un sovraccarico di Edge.Func() che può richiedere un paio di stream o delegati opzionali. –