C# 是否将struct by值传递给Linux 64位上的P/调用库?

C# 是否将struct by值传递给Linux 64位上的P/调用库?,c#,linux,mono,g++,pinvoke,C#,Linux,Mono,G++,Pinvoke,我正在尝试获取用于在Linux x86#U 64上编译的C#库的本机依赖项。代码本身与平台无关,并且易于编译 然而,在第一次尝试使用编译的依赖项在Linux上运行我的项目之后,我开始从库中得到奇怪的结果,后来出现了一个segfault。经过一些调查,似乎P/Invoke函数上的参数没有以正确的顺序传递。看起来他们好像是在倒退 我尝试过以几种不同的方式编译本机依赖项,并明确定义了不同的调用约定。似乎什么都不管用 C#外部方法定义 [DllImport(InteropUtil.PLATFORM_DL

我正在尝试获取用于在Linux x86#U 64上编译的C#库的本机依赖项。代码本身与平台无关,并且易于编译

然而,在第一次尝试使用编译的依赖项在Linux上运行我的项目之后,我开始从库中得到奇怪的结果,后来出现了一个segfault。经过一些调查,似乎P/Invoke函数上的参数没有以正确的顺序传递。看起来他们好像是在倒退

我尝试过以几种不同的方式编译本机依赖项,并明确定义了不同的调用约定。似乎什么都不管用

C#外部方法定义

[DllImport(InteropUtil.PLATFORM_DLL)]
public static extern NavStatus dtqFindPath(IntPtr query
    , NavmeshPoint startPosition
    , NavmeshPoint endPosition
    , IntPtr filter
    , [In, Out] uint[] resultPath
    , ref int pathCount
    , int maxPath);
#if _MSC_VER    // TRUE for Microsoft compiler.
#define EXPORT_API __declspec(dllexport) // Required for VC++
#else
#define EXPORT_API // Otherwise don't define.
#endif

extern "C"
{

    EXPORT_API dtStatus dtqFindPath(dtNavMeshQuery* query 
        , rcnNavmeshPoint startPos
        , rcnNavmeshPoint endPos
        , const dtQueryFilter* filter
        , dtPolyRef* path
        , int* pathCount
        , const int maxPath)
    {
        return query->findPath(startPos.polyRef
            , endPos.polyRef
            , &startPos.point[0]
            , &endPos.point[0]
            , filter
            , path
            , pathCount
            , maxPath);
    }
}

<强>相关C++定义>P/>

[DllImport(InteropUtil.PLATFORM_DLL)]
public static extern NavStatus dtqFindPath(IntPtr query
    , NavmeshPoint startPosition
    , NavmeshPoint endPosition
    , IntPtr filter
    , [In, Out] uint[] resultPath
    , ref int pathCount
    , int maxPath);
#if _MSC_VER    // TRUE for Microsoft compiler.
#define EXPORT_API __declspec(dllexport) // Required for VC++
#else
#define EXPORT_API // Otherwise don't define.
#endif

extern "C"
{

    EXPORT_API dtStatus dtqFindPath(dtNavMeshQuery* query 
        , rcnNavmeshPoint startPos
        , rcnNavmeshPoint endPos
        , const dtQueryFilter* filter
        , dtPolyRef* path
        , int* pathCount
        , const int maxPath)
    {
        return query->findPath(startPos.polyRef
            , endPos.polyRef
            , &startPos.point[0]
            , &endPos.point[0]
            , filter
            , path
            , pathCount
            , maxPath);
    }
}
g++编译器设置

g++ -shared -o cai-nav-rcn.so.1 -g -fPIC -I Detour/Include -I DetourCrowd/Include -I Nav/Include Detour/Source/*.cpp DetourCrowd/Source/*.cpp Nav/Source/*.cpp
using System;
using System.Runtime.InteropServices;

namespace InteropTest
{
    [StructLayout(LayoutKind.Sequential)]
    public struct v
    {
        public float X;
        public float Y;
        public float Z;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct s
    {
        public uint A;
        public v B;
    }

    public class Test
    {
        [DllImport("./test.so")]
        public static extern void testStruct(s str);

        unsafe static void Main(string[] args)
        {
            s mStr;
            mStr.A = 22;
            mStr.B.X = 33f;
            mStr.B.Y = 44f;
            mStr.B.Z = 55f;
            Console.WriteLine("STRUCT MANAGED");
            Console.WriteLine("SIZE: " + sizeof(s));
            Console.WriteLine(mStr.A + ", (" + mStr.B.X + ", " + mStr.B.Y + ", " + mStr.B.Z + ")");
            testStruct(mStr);
        }
    }
}
在下面的输出中,
dtqFindPath
行清楚地显示了无序参数
maxPath
应为100(
0x64
),但应为1298。1298是
startPos
结构中的第一个
int
。100是路径的值

部分GDB输出

Thread 1 (Thread 0x7fef64330740 (LWP 3923)):
#0  0x00007fef63823ce9 in waitpid () from /usr/lib/libpthread.so.0
#1  0x00000000004ae448 in ?? ()
#2  0x0000000000503b8b in ?? ()
#3  0x00000000004226b2 in ?? ()
#4  <signal handler called>
#5  0x00007feef052339c in dtNavMeshQuery::findPath (this=0x5405610, startRef=88101520, endRef=4203419680, startPos=0x7fff5fd3975c, endPos=0x7fff5fd3974c, filter=0x7fef64176ec0, path=0x64, pathCount=0x44d6595341be38e0, maxPath=1298) at Detour/Source/DetourNavMeshQuery.cpp:958
#6  0x00007feef0534d19 in dtqFindPath (query=0x5405610, startPos=..., endPos=..., filter=0x7fef64176ec0, path=0x64, pathCount=0x44d6595341be38e0, maxPath=1298) at Nav/Source/DetourNavMeshQueryEx.cpp:234
#7  0x0000000041ec2140 in ?? ()
...
#17 0x0000000005405610 in ?? ()
#18 0x0000000000000000 in ?? ()
cs.cs

g++ -shared -o cai-nav-rcn.so.1 -g -fPIC -I Detour/Include -I DetourCrowd/Include -I Nav/Include Detour/Source/*.cpp DetourCrowd/Source/*.cpp Nav/Source/*.cpp
using System;
using System.Runtime.InteropServices;

namespace InteropTest
{
    [StructLayout(LayoutKind.Sequential)]
    public struct v
    {
        public float X;
        public float Y;
        public float Z;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct s
    {
        public uint A;
        public v B;
    }

    public class Test
    {
        [DllImport("./test.so")]
        public static extern void testStruct(s str);

        unsafe static void Main(string[] args)
        {
            s mStr;
            mStr.A = 22;
            mStr.B.X = 33f;
            mStr.B.Y = 44f;
            mStr.B.Z = 55f;
            Console.WriteLine("STRUCT MANAGED");
            Console.WriteLine("SIZE: " + sizeof(s));
            Console.WriteLine(mStr.A + ", (" + mStr.B.X + ", " + mStr.B.Y + ", " + mStr.B.Z + ")");
            testStruct(mStr);
        }
    }
}

g++ -shared -o test.so -g -fPIC interop.cpp && mcs /unsafe cs.cs && ./cs.exe
我的系统上的输出

STRUCT MANAGED
SIZE: 16
22, (33, 44, 55)
STRUCT NATIVE
SIZE: 16
22, (33.000000, 0.000000, 3.179688)

其他一些测试显示结构被“跳过”,其中打印
str.A
将打印出下一个非结构参数的值。结构的其余部分似乎是垃圾。

注意:下面的答案解决了问题的原始版本

Linux x86_64上只有一个调用约定。它被称为。不管不匹配是什么,它肯定不在调用约定中。可能结构声明不匹配,或者存在我们看不到的其他错误


我接下来要做的事情是,站在你的立场上,编写一些简单的测试代码。我会编写一个C++函数,接收一对<代码> int <代码>参数。检查他们是否按正确的顺序通过考试。说服自己呼叫约定不是问题所在,然后深入挖掘以找出问题的真正原因。

我用一个隔离问题的SSCCE更新了问题。我保持结构两侧的定义方式相同,尽管它们应该具有相同的内存布局。而且,自从我开始这么做以来,我没有问过很多问题。更改标题以反映孤立的问题是否明智?好的,您在SSCCE方面做得很好,做得好!现在的问题有点不同了。关于调用约定的问题现在是关于结构的传递。不管怎样,我看看能不能帮上忙。然而,我是一个Windows用户,而不是Linux用户,不懂Mono。所以,我的帮助可能不是很有效!在.net上,我将三个浮点声明为
float[]
,并使用
[MarshalAs(UnmanagedType.ByValArray,SizeConst=3)]
。我还向结构传递指针,而不是按值传递。您能否将本机变量更改为
const s*str
和托管变量更改为
ref s str
?使用
float[]
切换
v
结构没有太大变化,但通过引用传递确实会产生影响,所以是的,这确实有效。唯一的问题是库的很多API都是按值传递小结构的,这需要做很多工作。在这一点上,我认为向Mono提交一份bug报告,看看他们有什么要说的是一个好主意。这看起来确实像Mono bug。或者可能是ABI的一个角落,没有一个明确的约定。在Windows上,按值传递结构肯定容易产生混乱。也许Linux上也是如此。我总是强迫他们通过ref。我不确定这里还有多少可以做的。好的,在这里提交一个bug报告:我会等一等,看看会发生什么。最坏的情况是,我只会出于自己的目的修改库。