当从C#调用Edge.js时,如何钩住stdout和stderr?

背景

我正在通过Process.Start()运行Node的C#程序。 我从这个subprocess捕获stdout和stderr,并根据我自己的原因redirect它。 我正在考虑用一个调用Edge.js代替Node.exe的调用。 为了能够做到这一点,我必须能够可靠地捕获在Edge中运行的Javascript的标准输出和标准错误,并将消息传回到我的C#应用​​程序中。

方法1

我会描述这种方法的完整性,以防有人推荐它:)

如果Edge进程终止,则只需简单地声明一个msgs数组,并用新的函数覆盖process.stdout.writeprocess.stderr.write来处理这个问题是相当容易的,这些函数会在该数组上累积消息,最后只需返回消息数组。 例:

 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); 

显然这只有在Edge代码终止的情况下才有效,在最坏的情况下,消息可能会变大。 但是,它很可能会performance良好,因为只有一个编组调用才能获取所有消息。

方法2

这有点难以解释。 我们不用累积消息,而是使用从C#发送的委托来“挂接”stdout和stderr。 在C#中,我们创build一个对象,我们将传入Edge,并且该对象具有一个名为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; } 

我真的可以逃脱一个行动,但边缘似乎需要Func<object, Task<object>> ,否则将不会代理该function。 然后,在Javascript中,我们可以检测到这个function,并像这样使用它:

 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; 

问题

Q1。 这两种技术似乎都有效,但是有没有更好的方法来实现这一点,Edge的内置或许是我错过了? 这两种解决scheme都是侵入式的 – 它们需要一些填充代码来包装要在Edge中完成的实际工作。 这不是世界末日,但是如果有一种非侵入性的方法会更好。

Q2。 在方法2中,我必须在这里返回一个任务

 return Task.FromResult<object>(null); 

返回已经完成的“空任务”感觉不对。 但是还有另外一种写作方式吗?

Q3。 在连接stdout和stderr时,我需要在Javascript代码中更严格吗? 我注意到在double-edge.js中有这样的代码,坦率地说,我不确定这里发生了什么,但是比我粗略的覆盖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; }); })(); } 

Q1:Edge中没有任何东西可以在从CLR调用Node时自动捕获Node.js代码的stdout或stderr。 在某个时候,我想写一个Edge的扩展,使CLR / V8边界上的stream处理变得容易。 在引擎盖下面,它和你的方法2非常相似。它可以在Edge的顶部作为一个独立的模块来完成。

Q2:在这种情况下,返回完成的任务是非常合适的。 你的函数已经捕获了Node.js的输出,处理了它,实际上已经完成了。 使用Null完成的任务返回实际上是从Action返回的道德等价物。

问题3:您指向的代码仅在Windows GUI应用程序中相关,而不是在控制台应用程序中。 如果您正在编写控制台应用程序,那么只需在您传递给Edge.js的Node.js代码级别覆盖write即可。 请注意,Node中的write签名允许传入可选的编码参数 。 你似乎在方法1和方法2中都忽略了它。特别是在方法2中,我build议将JavaScript代理包装到C#callback函数中,然后将其分配给process.stdout.write之前对参数进行process.stdout.write 。 否则,Edge.js代码可能会假设传递给write调用的encoding参数是一个遵循Edge.js调用约定的callback函数。