使用 Xamarin Mac、C# 的跨浏览器、跨平台本机消息传递

Cross-Browser, Cross-Platform native messaging using Xamarin Mac, C#

我尝试重新创建示例以使用 chrome 扩展连接到我的 Xamarin Mac 应用程序,我将用作所有其他浏览器的 backbone,但它没有用出现以下错误:

与本机消息传递主机通信时出错。

文档在纸面上看起来很简单,但在现实生活中并不那么简单。

这是我的阅读和收听功能(那里有很好的例子,https://github.com/anewtonlevey/NativeMessaging):

    public void Listen()
    {

        JObject data;
        while ((data = Read()) != null)
        {
            ProcessReceivedMessage(data);
        }

    }




    private JObject Read()
    {
        try
        {
            Stream stdin = Console.OpenStandardInput();


            byte[] lengthBytes = new byte[4];
            stdin.Read(lengthBytes, 0, 4);


            var buffer = new char[BitConverter.ToInt32(lengthBytes, 0)];

            using (var reader = new StreamReader(stdin)) while (reader.Peek() >= 0) reader.Read(buffer, 0, buffer.Length);
            return JsonConvert.DeserializeObject<JObject>(new string(buffer));


        }
        catch (Exception e)
        {
            Console.WriteLine("Read error: {0}", e);
            return null;
        }
    }

在我的主要功能中,我使用以下方式调用它们:

        host = new ExtendedHost();


        host.Listen();

请注意,我正在为 macOS 应用程序使用来自 Xamarin 表单的本机消息传递,从我的角度来看,这可能是字节顺序,如以下文档所示:

与本地消息传递主机通信时出错:

https://developer.chrome.com/extensions/nativeMessaging#native-messaging-host-protocol

当我从 Web 扩展程序发送消息时,我的另一个 window C# 应用程序似乎想要打开,这意味着错误似乎发生在读取步骤。

非常感谢帮助。

谢谢。

编辑:

我建议采用字节顺序,因为文档表明响应也应该是正确的,否则会导致整个过程损坏,如 本地消息传递协议文档 中所建议的那样,但它是肯定不是问题,因为我仍然可以解析发送的数据:

https://developer.chrome.com/apps/nativeMessaging#native-messaging-host-protocol

我看到响应与它有关,并且用于发送消息的函数(因为有人建议响应错误可能导致整体错误)会导致问题,或者Console.StandardInput() 没有按照建议工作,但我也不这么认为。

我的想法是尝试附加到 运行 应用程序导致了问题,因为我使用 ps aux | grep app_name,并将路径放在主机文件中。

这是协议中提到的:

本地消息传递协议 Chrome 在单独的进程中启动每个本机消息传递主机,并使用标准输入 (stdin) 和标准输出 (stdout) 与其通信。

我还在另一个 post 中看到了来自 Xan 的回答,即本机消息无法附加到 运行 进程并且 Chrome 应该启动该进程:

Talk to MacOSX app from Chrome Extension

不要忘记我在调试模式下使用 Xamarin macOS 的原生消息传递 API,并且我的应用程序是 macOS 应用程序。

欢迎提供有关该主题的更多信息,例如:

当然,如果您认为这是导致问题的原因。

提前致谢!

编辑:

将输出发送到 stderr 时,出现以下错误:

读取错误:System.NotSupportedException:流不支持读取 at System.IO.FileStream.Read (System.Byte[] array, System.Int32 offset, System.Int32 count) [0x0002e] in /Library/Frameworks/Xamarin.Mac.framework/Versions/Current/src/Xamarin.Mac/mcs/class/corlib/System.IO/FileStream.cs:493

难道Console.OpenStandardInput不能跨平台? 如果是这样,那么解析标准输入的另一种方法可能是什么,因为我看到管道可以工作,但我不确定它们是否可以以跨平台的方式部署。

读取时没有任何错误或输出,只有在启动应用程序时出现上述错误。 对我来说,OpenStandardInput 似乎是罪魁祸首。

任何想法表示赞赏!谢谢

基本上,从扩展发送的前 32 位对应于消息长度,按本机顺序。

由于前 32 位是 int 类型,并且我尝试解析 JSON 对象,与本机消息传递主机通信时出现错误 。文档提到问题出在损坏的本机消息传递协议中。

最糟糕的是,我在Python例子上看到了,却以为这是程序员做的。

我在 Stack Overflow 上偶然发现了这个线程,它帮助确认了 (Chrome App fails to communicate with native host on windows)。

要以更详细的方式调试 Chrome,请在 --enable-logging=stderr --v=1 中启动 Chrome命令行,所有的stderr都会在那里输出。

文档中提到了它,但是在这里和那里找到的使用 C# 的示例不包括它们。

如果我有时间,我将通过编辑此答案来确认这是问题所在,并提供代码以使其在 Visual Studio for macOS 上运行。

编辑

几天前我设法让这个工作。我修改了代码,使其在没有 while 循环的情况下工作,因为 StreamReader 读取 stdin 直到结束,因此不请求 while 循环(这导致我以前的代码挂起)。 SendMessage 方法也不同于其他示例中的其他方法。更少的行,并且工作得很好。

我的工作示例:

public void Listen()
{


    JObject data;

    //while ((data = Read()) != null)
    //{
    //    ProcessReceivedMessage(data);
    //}

    if((data = Read()) != null)
    {
        string message = ProcessReceivedMessage(data);
        SendMessage(message);
    }
}


    private JObject Read()
    {

        Stream stdin = Console.OpenStandardInput();

        byte[] lengthBytes = new byte[4];
        stdin.Read(lengthBytes, 0, lengthBytes.Length);

        char[] buffer = new char[BitConverter.ToInt32(lengthBytes, 0)];

        var reader = new StreamReader(stdin);
        //int messageLength =
        reader.Read(buffer, 0, buffer.Length);

        return JsonConvert.DeserializeObject<JObject>(new string(buffer));
    }





    public void SendMessage(JToken data)
    {
        var bytes = Encoding.UTF8.GetBytes(data.ToString(Formatting.None));
        var lengthBytes = BitConverter.GetBytes(bytes.Length);
        var stdout = Console.OpenStandardOutput();

        stdout.Write(lengthBytes, 0, 4);
        stdout.Write(bytes, 0, bytes.Length);
        stdout.Flush();
    }