runtime: Dotnet 6.x looks weird if I see screen_number of X11 - I think that DLLImport has problem...

Description

Hello everyone

Linux [X} Windows MacOS WASM ANDROID

I found bug like it looks crazy. If you write in C++/C and result of printf shows example:

screen_number = XDefaultScreen(display);
if (screen_number == -1)
{
	printf("Error: passing default screen number!\n");
}
else
{
	printf("Success: default screen number passed %i!\n", screen_number);
}

Result: image

I try to C# but it seems weird because it doesn’t like same 1:1 just 1:meshed Why does C# not work same to C++/C - I thought C# is faster then C++/C but for X11 via DLLImport Example:

int screen_number = DefaultScreen(display);
if (screen_number == -1)
{
    Console.WriteLine("Error: passing default screen number!\n");
}
else
{
    Console.WriteLine("Screen Number is " + screen_number);
}

Result: image

I have tried to resolve. No success…

Reproduction Steps

Problem with wrong number of Screen Number from X11

Expected behavior

For C++/C shows correct number of screen_number with X11’s DefaultScreen()

Actual behavior

Only C++/C looks successfully With C++/C programming is very impressive then C#

Regression?

No…

Known Workarounds

Workaround with C++/C = success But C# looks weird like C# makes crazy.

Configuration

LINUX , Dotnet 6.x latest version

Other information

With C++/C and C# should be same…

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16 (10 by maintainers)

Most upvoted comments

TerraFX.Interop.Xlib provides raw/blittable interop bindings for Xlib that are generated directly from the underlying C headers using dotnet/ClangSharp

You can see the P/Invoke binding for XDefaultScreen here: https://source.terrafx.dev/#TerraFX.Interop.Xlib/X11/Xlib/Xlib.cs,00dc5c4648e5207c

[DllImport("libX11", ExactSpelling = true)]
public static extern int XDefaultScreen(Display* param0);

The library also provides a DllImportResolver (https://source.terrafx.dev/#TerraFX.Interop.Xlib/Xlib.cs) to help look up the other well-known library names and to allow users to customize that resolution themselves as well.

You could use this directly or as a reference for your own bindings (whether manually copying out just what you need or likewise using ClangSharp to generate bindings suitable for your project).

@Wraith2 My complete Program.cs

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

namespace Example;

unsafe class Program
{
    public static int UTF8Size(string name)
    {
        if (name == null)
        {
            return 0;
        }
        return (name.Length *4) + 1;
    }

    public static byte *UTF8ToString(string name, byte *data, int size)
    {
        if (name == null)
        {
            return (byte*) 0;
        }

        fixed (char *name_ptr = name)
        {
            Encoding.UTF8.GetBytes(name_ptr, name.Length + 1, data, size);
        }

        return data;
    }

    public static string UTF8FromBytePointer(byte *pointer)
    {
        byte *ptr = pointer;
        while (*ptr != 0)
        {
            ptr++;
        }

        return Encoding.UTF8.GetString(pointer, (int)(ptr - pointer));
    }

    const string libX11 = "libX11.so.6";

    [StructLayout(LayoutKind.Sequential)]
    public struct Display
    {
    }

    [DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr XOpenDisplay(byte* display_name);
    public static Display OpenDisplay(string display_name)
    {
        IntPtr ret = Marshal.AllocHGlobal(Marshal.SizeOf<Display>());
        int display_name_size = UTF8Size(display_name);
        byte* display_name_data = stackalloc byte[display_name_size];
        ret = XOpenDisplay(UTF8ToString(display_name, display_name_data, display_name_size));
        return Marshal.PtrToStructure<Display>(ret);
    }

    [DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
    private static extern void XCloseDisplay(IntPtr display);
    public static void CloseDIsplay(Display display)
    {
        IntPtr ret = Marshal.AllocHGlobal(Marshal.SizeOf<Display>());
        Marshal.StructureToPtr<Display>(display, ret, false);
        if (ret != IntPtr.Zero)
        {
            XCloseDisplay(ret);
        }
    }

    [DllImport(libX11, CallingConvention = CallingConvention.Cdecl)]
    private static extern int XDefaultScreen(IntPtr display);
    public static int DefaultScreen(Display display)
    {
        IntPtr ret = Marshal.AllocHGlobal(Marshal.SizeOf<Display>());
        Marshal.StructureToPtr<Display>(display, ret, false);
        return XDefaultScreen(ret);
    }

    static void Main(string[] args)
    {
        Display display = OpenDisplay(string.Empty);
        IntPtr disPtr = Marshal.AllocHGlobal(Marshal.SizeOf<Display>());
        Marshal.StructureToPtr<Display>(display, disPtr, false);

        if (disPtr == IntPtr.Zero)
        {
            Console.WriteLine("Error: opening display!\n");
        }
        else
        {
            Console.WriteLine("Success Display opened.\n");

            int screen_number = DefaultScreen(display);
            if (screen_number == -1)
            {
                Console.WriteLine("Error: passing default screen number!\n");
            }
            else
            {
                Console.WriteLine("Screen Number is " + screen_number);
            }
        }

        CloseDIsplay(display);
        Console.WriteLine("Success: Display closed.\n");
    }
}

And type dotnet restore && dotnet run -c Release Then result looks wrong… in C++/C main.cpp looks like same but I tried

#include <X11/Xlib.h>
#include <iostream>

bool Initialize();
void Destroy();

Display *display;
int screen_number;

int main()
{
	if (!Initialize())
	{
		printf("Error initializing!\n");
	}
	else
	{
		printf("Success!\n");
	}
	
	Destroy();
	return 0;
}

bool Initialize()
{
	display = XOpenDisplay((char*)0);
	if (!display)
	{
		printf("Error: opening display!\n");
	}
	else
	{
		screen_number = XDefaultScreen(display);
		if (screen_number == -1)
		{
			printf("Error: passing default screen number!\n");
		}
		else
		{
			printf("Success: default screen number passed %i!\n", screen_number);
		}

	}

	return true;
}

void Destroy()
{
	if (display != NULL)
	{
		XCloseDisplay(display);
	}
}

And you should type in terminal: g++ main.cpp -o myapp -lX11 -no-pie

It shows for test with screen number from C# and C++/C. If you expect that why does C# not same to C++/C for result 1:1 like example screen number passed “0” but C# shows wrong or impossible.