Native Messaging and C# stdin

55 views
Skip to first unread message

John Cardinal

unread,
Jun 13, 2024, 9:31:09 AMJun 13
to chromium-...@chromium.org

I am having an issue with Native Messaging where the host is written in C# (NET 8.0) and running under MS Windows. For certain messages sent from my extension to the host, the number of bytes read from stdin is less than the expected number which is written as a prefix to the message.

 

For example, a message will begin with 0x95ff2400, which is 2,424,725 (0x24ff95 from 0x95ff2400). However, the C# StreamReader.Read() method will only read 2,422,968 bytes, i.e., 1,757 less than expected.

 

It is not the case that a subsequent call to StreamReader.Read()will yield the "missing" bytes. In fact, all the available bytes has been read.

 

The issue seems to be character encoding.

 

  1. On the browser side, the length of the message data agrees with the prefix that Chrome writes as part of the message protocol. My code reads the prefix and decodes it correctly. As described above, however, there are less bytes in the stream. If I deserialize the JSON from the "partial" message, it is complete and matches the JSON sent by the extension.

 

  1. If I replace all characters with codes above 127 using the JSON/JavaScript \uXXXX notation, that avoids the message length mismatch issue.

 

At present, I open stdin with "Console.OpenStandardInput()". I do not explicitly set the encoding. I do not recall why I did not consider possible encoding issues when I wrote the code, but I was probably following some example.

 

Given the Native Messaging protocol specifies a prefix that is in BYTES, I presume I should configure the Console class in C# to read raw bytes and not decode them. I believe the Console class requires stdin and stdout to use the same encoding. So, I'll have to convert from bytes -> characters on stdin and convert from characters -> bytes on stdout.

 

Does my analysis of the issue and plan to fix it make sense?

 

Are there any example C# Native Messaging host programs that demonstrate the correct way to read/write the Native Messaging protocol?

JohnnyCee

unread,
Jun 13, 2024, 10:07:17 AMJun 13
to Chromium Extensions, John Cardinal
OK, I think I see the error of my ways. I can't use StreamReader in C#; that's a subclass of TextReader and reads characters, not bytes. Messing with the encoding of StreamReader won't help.

The solution is to use BinaryReader and decode the text after reading all the bytes from the BinaryReader stream.

Cuyler Stuwe

unread,
Jun 13, 2024, 10:43:30 AMJun 13
to JohnnyCee, Chromium Extensions
I don’t use C#, but if I ever do, I’m sure I’ll come back to it. Someone searching Google will probably have a similar issue at some point and land here. So some good probably came of talking about it out loud here.

--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/2ae8c5e0-30d7-4198-b07d-ff3f40e80c6an%40chromium.org.

JohnnyCee

unread,
Jun 13, 2024, 1:49:32 PMJun 13
to Chromium Extensions, JohnnyCee
Given Cuyler's comment that someone might find this post down the road when encountering a similar issue, I thought I should expand on the issue and the solution.

Summary: You can get away with using StreamReader for the character data if you do not check the length via the StreamReader.Read() method call. In the Native Messaging protocol, the only binary contents are the first four bytes, and they must not be read as characters for obvious reasons. However, if you do check the length, the character decoding means the number of characters will be less than the number of bytes, and you'll get a mismatch. I was checking the length for reasons explained below.

In my code, I was reading the first 4 bytes directly from the stdin stream, so no decoding and no problem there:

         byte[] lengthBytes = new byte[4];
         stdin.Read(lengthBytes, 0, 4);
         int length = BitConverter.ToInt32(lengthBytes, 0);

I read somewhere that stream.Read() may not return all the bytes/chars requested. For that reason, I used a loop to read the Native Message:

         byte[] buffer = new byte[length];
         int actualLength = 0;
         using (var reader = new BinaryReader(stdin)) {
            while (actualLength < length) {
               actualLength += reader.Read(buffer, actualLength, length - actualLength);
         }

The code above is the new code that uses BinaryReader, so after the loop above:

         json = _utf8.GetString(buffer, 0, length);

My loop now looks like an endless loop waiting to happen, so I am going to investigate further and add a timeout or some other way to avoid the endless loop.

I hope this helps someone. While it is specific to C#, other languages/runtimes might present similar challenges.
Reply all
Reply to author
Forward
0 new messages