.NET 6 encoding issue on Linux returning char * buf from shared lib

Viewed 146

I have a .NET 6 aspcore app that loads an unmanaged dll. The dll returns C strings encoded as ISO8859-1.

Everything works fine with the Windows version but on Linux, any accented characters that come back are not translated into Unicode.

I've re-created the issue using a minimal dll & .NET console app.

The shared library code is a simple C file

#include <stdio.h>
#include <string.h>

//file is ansi encoded 8859-1 é = xE9
static char* msg = "Montréal"; 

extern int Test(char* buf, int buf_size)
{
    if (buf != NULL && buf_size > (int)strlen(msg)) {
        strcpy(buf, msg);
        return ((int)strlen(buf));
    }
    return -1;
}

This is the .NET console app code. Note that I used StringBuilder to handle the char* buffer. I believe this is the typical way to do this.

using System.Runtime.InteropServices;
using System.Text;

Console.WriteLine($"Hello,Montréal"); //utf8 encoded source file

var buf = new StringBuilder(512);
int ret = TestImport.Test(buf, 500);
Console.WriteLine($"Hello again, {buf.ToString()}");

public static class TestImport
{
    public const string _dll = "libTestLib.so";

    [DllImport(_dll, CharSet = CharSet.Ansi)]
    public static extern int Test( StringBuilder buf, int buf_size);

}

I'm running the app from WSL (Ubuntu 20.04) as:

$ dotnet run -c Debug -r linux-x64 --no-self-contained

Output shows as:

Hello, Montréal
Hello again, Montr�al

So how do I tell DllImport what the source encoding is? (ie CharSet = CharSet.Ansi)

Is there some .NET environment setting I'm missing?

Is there something I need to set up in the Linux environment? (I've tried changing the locale to LANG=en_US.iso88591 - which doesn't seem to do anything)

I do have build ownership over the unmanaged dll, but it isn't going to return utf-8.

1 Answers

Well taking @JeroenMostert's advice I'm going to marshal the params myself using a Byte[] buffer. Perhaps a custom marshaler would work - if I could figure it out.

using System.Runtime.InteropServices;
using System.Text;

Console.WriteLine($"Hello,Montréal"); //utf8 encoded source file

var buf = new Byte(512);
int ret = TestImport.Test(buf, 500);
var s = Encoding.Latin1.GetString(buf); // Latin1 = 8859-1

Console.WriteLine($"Hello again, {s}");

public static class TestImport
{
    public const string _dll = "libTestLib.so";

    [DllImport(_dll, CharSet = CharSet.Ansi)]
    public static extern int Test( Byte[] buf, int buf_size);

}

output looks like

Hello,Montréal
Hello again, Montréal
Related