草庐IT

c# - 在 Linux 64 位上按值将结构传递给 P/Invoked 库?

coder 2023-06-21 原文

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

但是,在第一次尝试使用已编译的依赖项在 Linux 上运行我的项目后,我开始从库中得到奇怪的结果,随后出现段错误。经过一些调查后,P/Invoke 函数的参数似乎没有以正确的顺序传递。看起来好像它们正在向后传递。

我尝试过以几种不同的方式编译 native 依赖项并明确定义不同的调用约定。似乎没有任何效果。

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);

相关C++定义

#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

在下面的输出中,dtqFindPath 行清楚地显示了乱序参数。 maxPath 应为 100 (0x64),但实际为 1298。 1298 是 startPos 结构中的第一个 int。 100 是 path 的值。

部分 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 ?? ()

我已经比较了两端的rcnNavmeshPointNavmeshPoint 结构体的大小,它们是相同的。进入 P/Invoke 调用的参数顺序正确,已使用调试器进行检查。

不妨包括我正在尝试使用的库是 CritterAI .

所以我的问题是:我应该更改什么以使这两段代码之间的调用约定匹配?


更新

我隔离了这个问题。这是未正确传递的结构。我创建了一个 SSCCE 来证明这一点:

互操作.cpp

#include <cstdio>

#if _MSC_VER
#define EXPORT_API __declspec(dllexport)
#else
#define EXPORT_API 
#endif

struct s
{
    unsigned int a;
    float b[3];
};

extern "C"
{
    EXPORT_API void testStruct(s str)
    {
        printf("STRUCT NATIVE\n");
        printf("SIZE: %u\n", sizeof(s));
        printf("%u, (%f, %f, %f)\n", str.a, str.b[0], str.b[1], str.b[2]);
    }
}

cs.cs

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 上只有一种调用约定。它被称为 System V AMD64 ABI .无论不匹配是什么,它肯定不在调用约定中。可能结构声明不匹配,或者存在我们看不到的其他错误。

我接下来要做的事情是,站在您的角度,编写一些简单的测试代码。我会编写一个 C++ 函数来接收一对 int 参数。检查它们是否以正确的顺序通过。说服自己调用约定不是问题所在,然后深入挖掘以找出问题的真正原因。

关于c# - 在 Linux 64 位上按值将结构传递给 P/Invoked 库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17331418/

有关c# - 在 Linux 64 位上按值将结构传递给 P/Invoked 库?的更多相关文章

  1. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  2. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  3. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  4. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  5. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  6. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  7. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. ruby - 如何将 Puma::Configuration 传递给 Sinatra? - 2

    这是我的网络应用:classFront我是这样开始的(请不要建议使用Rack):Front.start!这是我的Puma配置对象,我不知道如何传递给它:require'puma/configuration'Puma::Configuration.new({log_requests:true,debug:true})说真的,怎么样? 最佳答案 配置与您运行的方式紧密相关puma服务器。运行的标准方式puma-pumaCLI命令。为了配置puma配置文件config/puma.rb或config/puma/.rb应该提供(参见examp

  10. jquery - 如何将 AJAX 变量从 jQuery 传递到他们的 Controller ? - 2

    我有一个电子邮件表格。但是我正在制作一个测试电子邮件表单,用户可以在其中添加一个唯一的电子邮件,并让电子邮件测试将其发送到该特定电子邮件。为了简单起见,我决定让测试电子邮件通过ajax执行,并将整个内容粘贴到另一个电子邮件表单中。我不知道如何将变量从我的HAML发送到我的Controllernew.html.haml-form_tagadmin_email_blast_pathdoSubject%br=text_field_tag'subject',:class=>"mass_email_subject"%brBody%br=text_area_tag'message','',:nam

随机推荐