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

1

7

I tried to recreate the example to connect to my Xamarin Mac application using a chrome extension I will use as backbone for all other browsers, but it didn't work with the following error:

Error when communicating with the native messaging host.

The documentation seems straightforward on paper, but isn't that simple in the real life.

Here are my Read and listen functions (great examples there,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;
        }
    }

From my main function , I call them using the following:

        host = new ExtendedHost();


        host.Listen();

To note that I am using native messaging from Xamarin forms for a macOS application, as from my point of view, it might be the endianness, as the following documentation suggests:

Error when communicating with the native messaging host:

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

Another window of my C# app seems to want to open when I am sending the message from the web extension, meaning that the error seems to occur at the reading step.

Help much appreciated.

Thanks.

Edit:

I suggested endianness as the documentation suggest that the response should be correct as well or would render the whole process corrupted, as suggested in the native messaging protocol doc, but it is not the issue for sure as I should still be able to parse the data sent:

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

I saw that the response has something to do with it, and that the function used to send message(as it was suggested that error in response could cause an overall error) would cause the issue, or that Console.StandardInput() doesn't work as suggested, but I don't think so as well.

My thought is that trying to attach to the running app caused the issue as I used ps aux | grep app_name, and put the path within the host file.

Here is the mention in the protocol:

Native messaging protocol Chrome starts each native messaging host in a separate process and communicates with it using standard input (stdin) and standard output (stdout).

I also saw this answer from Xan in another post that Native messaging can't attach to running processes and Chrome should launch the process:

Talk to MacOSX app from Chrome Extension

Not to forget that I am using the native messaging API with Xamarin macOS in Debug mode, and that my app is a macOS app.

More informations are welcome on that subject, such as:

  • a way to get a path to the app which Chrome can use with its native messaging API
  • a solution to get this path programmatically, which would work on all platforms if possible as Console.WriteLine(Path.Combine(Assembly.GetExecutingAssembly().Location)); didn't do the trick.

Of course, if this is what you think is causing the issue.

Thanks in advance!

EDIT:

When sending output to stderr, I got the following error:

Read error: System.NotSupportedException: Stream does not support reading 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

Could it be that Console.OpenStandardInput doesn't work cross platform? And if so, what could be another method to parse the standard input, as I saw that pipes could work, but I am not sure if they can deployed in a cross-platform fashion.

I didn't got any error or output when reading, only the above error at starting of the app. For me, OpenStandardInput seems to be the culprit.

Any thoughts appreciated! Thanks

Petua answered 10/2, 2020 at 21:1 Comment(3)
I have not tried this in .NET but I have done this using C++, the application had to be a console application on Windows for stdin to work. I tried to do so using a GUI (hidden) app but stdin was not working from there. Maybe it will work after making your app a console application?Athiste
Hi. Thanks for having taken the time. I have pointed to the executable, which runs from the command as well in .NET. I am using Xamarin macOS, and made an app for macOS. Problem being that I can run my app executing a bash script, but when I try to run it, it doesn't work. This is highly annoying. Yesterday it worked, but I got some error regarding the Messaging. My bash script definitively works, and launch an app from the command line. Mozilla doesn't seem to be able to access it through the bash script, and throw "Attempt to postMessage on disconnected port "Petua
And when I input the path to the executable directly, which goes like /Application/Appname.app/Contents/MacOS/Appname, I got the following error. Native application tried to send a message of 543451477 bytes, which exceeds the limit of 1048576 bytes. To note that I am seeing NOTHING coming on stin and stdout.Petua
P
0

Basically, the first 32 bits sent from the extension corresponds to the message length, in native order.

Since the first 32 bits are of type int, and I tried to parse JSON objects, Error when communicating with the native messaging host is raised. The documentation mentions that the issue resides within a broken Native Messaging protocol.

The worst part is that I saw it on the Python example, but thought that this was implemented by the programmer who did it.

I stumbled upon this thread on Stack Overflow which helped confirm, (Chrome App fails to communicate with native host on windows).

To debug Chrome in a much more verbose fashion, launch Chrome with --enable-logging=stderr --v=1 in the command line, and all stderr will be outputted there.

The documentation is mentioning it, but the examples found here and there using C# don't include them.

If I have time, I will confirm that this was the issue by editing this answer, and provide code for getting this to work on Visual Studio for macOS.

Edit

I managed to have this to work a few days ago. I modified the code so that it works without the while loop, as the StreamReader reads stdin until the end, thus not requesting a while loop (this caused my previous code to hang). The SendMessage method also differs from other seen in other examples. Fewer lines, and works just fine.

My working example:

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();
    }
Petua answered 15/2, 2020 at 1:12 Comment(3)
Hi @Petua I am also implementing a Chrome extension to talk with a xamarin app. Did you put this listener in app.cs or another file? Think you can make a Gist? Thx!Divertimento
Hello @ArcadeRenegade. Sorry for not replying earlier as I haven't seen your message.I haven't worked on this in a while but can tell you that I used the MVC approach, so this file is not be in the app.cs.Petua
It's ok I ended up going with native macos and window apps anyways thx @PetuaDivertimento

© 2022 - 2024 — McMap. All rights reserved.