【问题标题】:When calling Edge.js from C#, how do you hook stdout and stderr?从 C# 调用 Edge.js 时,如何挂钩 stdout 和 stderr?
【发布时间】:2015-03-30 21:02:30
【问题描述】:

背景

我正在开发一个当前通过 Process.Start() 运行 Node 的 C# 程序。我正在从这个子进程中捕获标准输出和标准错误,并出于我自己的原因重定向它。我正在考虑用对 Edge.js 的调用来替换 Node.exe 的调用。为了能够做到这一点,我必须能够从 Edge 中运行的 Javascript 可靠地捕获 stdout 和 stderr,并将消息返回到我的 C# 应用程序中。

方法 1

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

如果 Edge 进程终止,只需声明一个 msgs 数组并使用在该数组上累积消息的新函数覆盖 process.stdout.writeprocess.stderr.write 就很容易处理,然后在最后,只需返回msgs 数组。示例:

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 代码终止时才有效,并且在最坏的情况下 msgs 可能会变大。但是,它可能表现良好,因为只需要一次编组调用即可获取所有消息。

方法2

这有点难以解释。我们不是累积消息,而是使用从 C# 发送的委托来“挂钩”stdout 和 stderr。在 C# 中,我们创建一个将传递给 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;
}

我真的可以通过一个动作侥幸逃脱,但 Edge 似乎需要 Func&lt;object, Task&lt;object&gt;&gt;,否则它不会代理该功能。然后,在 Javascript 中,我们可以检测到该函数并像这样使用它:

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;

问题

第一季度。这两种技术似乎都有效,但是有没有更好的方法来做到这一点,Edge 内置的东西可能我错过了?这两种解决方案都是侵入性的——它们需要一些 shim 代码来包装要在 Edge 中完成的实际工作。这不是世界末日,但如果有一种非侵入性的方法会更好。

第二季度。在方法2中,我必须在这里返回一个任务

return Task.FromResult<object>(null);

返回一个已经完成的“空任务”感觉不对。但是还有其他写法吗?

第三季度。在挂钩 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; });
    })();
}

【问题讨论】:

    标签: node.js async-await task-parallel-library stdout edge.js


    【解决方案1】:

    Q1:当从 CLR 调用 Node 时,Edge 中没有内置任何东西可以自动捕获 Node.js 代码的 stdout 或 stderr。在某个时候,我想到了编写 Edge 的扩展,这将使跨 CLR/V8 边界的编组流变得容易。在引擎盖下,它与您的方法 2 非常相似。它可以作为 Edge 之上的独立模块完成。

    Q2:在这种情况下,返回已完成的任务非常合适。您的函数已捕获 Node.js 输出并对其进行处理,并且实际上在这个意义上已经“完成”了。返回一个用 Null 完成的任务实际上相当于从一个 Action 返回。

    Q3:您指向的代码仅与 Windows GUI 应用程序相关,与控制台应用程序无关。如果您正在编写控制台应用程序,只需覆盖 write 在传递给 Edge.js 的 Node.js 代码级别就足够了。请注意,Node 中write 的签名允许可选的encoding parameter to be passed in。您似乎在方法 1 和 2 中都忽略了它。特别是在方法 2 中,我建议将 JavaScript 代理包装到 C# 回调到一个 JavaScript 函数中,该函数在将参数分配给 process.stdout.write 之前对其进行规范化。否则,Edge.js 代码可能会假定传递给 write 调用的 encoding 参数是遵循 Edge.js 调用约定的回调函数。

    【讨论】:

    • 感谢 Tomasz,所以基本上关注编码的方法 2 是我目前最好的选择。如果有更简单的方法来做到这一点,那就太好了,也许是 Edge.Func() 的重载,它可以采用一对可选的流或委托。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    相关资源
    最近更新 更多