Search code examples
c++linux.net-corepinvokemarshalling

.NET Core on Linux - Marshal structures


I have a .NET Core console app that calls some functions from C++ library. The function I'm trying to call simpty taken in some settings and outputs the result into result.

C++:

struct settings
{
    char* input_path;
    char* first_file;
    char* second_file;
    char* mask;
    char* log_path;
    int count_accepted;
    double confidence;
    char* device;
};

struct result
{
    int count;
    foo* foos;
    bool is_preprocessed;
    double duration;
};

bool process_input(const settings& settings, result* result);

C#:

[StructLayout(LayoutKind.Sequential)]
public struct Settings
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string input_path;
    [MarshalAs(UnmanagedType.LPStr)]
    public string first_file;
    [MarshalAs(UnmanagedType.LPStr)]
    public string second_file;
    [MarshalAs(UnmanagedType.LPStr)]
    public string mask;
    [MarshalAs(UnmanagedType.LPStr)]
    public string log_path;
    [MarshalAs(UnmanagedType.I4)]
    public int count_accepted;
    [MarshalAs(UnmanagedType.R8)]
    public double confidence;
    [MarshalAs(UnmanagedType.LPStr)]
    public string device;
}

[StructLayout(LayoutKind.Sequential)]
public struct Result
{
    [MarshalAs(UnmanagedType.I4)]
    public int count;
    [MarshalAs(UnmanagedType.SysInt)]
    public IntPtr foos;
    [MarshalAs(UnmanagedType.I1)]
    public bool is_preprocessed;
    [MarshalAs(UnmanagedType.R8)]
    public double duration;
}

[DllImport("myLib", EntryPoint = "process_input", CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool ProcessInput(Settings settings, out Result result);

This all works well on Windows but does not work on Linux. When I print the settings on a C++ side (from process_input), I get completely different values in int and double properties, and a Segmentation Fault when try to access char* property.

I also tried to call this library from C++ code (Windows and Linux) and it works as expected. As I understand, this is a marshaling problem, but I can't pinpoint it myself. I'm a C# developer and don't have much experience with C++ or PInvoke or Linux.

I work with Windows 10 (x64) and Ubuntu 16.04 (x64).


Solution

  • As David Heffernan suggested in the comments, I added an explicit ref keyword and it worked.

    Here's my function's signature before:

    public static extern bool ProcessInput(Settings settings, out Result result);
    

    and after:

    public static extern bool ProcessInput(ref Settings settings, out Result result);