从C#而不是C ++调用时,非托管库函数失败

时间:2013-09-18 04:53:32

标签: c# c++ mono pinvoke dllimport

我正在尝试围绕现有的C ++共享库创建一个Mono / .Net包装类,但是在非托管代码执行时我遇到了问题。虽然我成功调用了库,但是非托管代码会引发分段错误。当我从用C ++编写的非托管应用程序调用库函数时,这不会发生。

C ++标题代码:

extern "C" void some_function();

C ++源代码:

void some_function()
{
    std::vector<uint8_t> v = std::vector<uint8_t> { 0x00 };
}

C#P /调用代码:

[DllImport("somelib.so", EntryPoint = "some_function")]
public static extern void some_function();

正如您所看到的,没有参数需要编组,所以问题不在于传入的数据。我在这个库中有几个函数存在这个问题,但我也能够打几个没有任何问题。通常,在尝试分配内存时会发生段错误(至少在一种情况下,它是std :: vector),但并非总是如此。我试图在gdb中调试,并注意到从C#调用时有5个线程,但从C ++调用时只有1个。我也在Ubuntu上使用Mono这样做,如果这会产生重大影响。

在实施我的P / Invoke电话时,我可能有什么遗漏,或者还有其他事情发生在这里?

更新:我添加了第二个更简单的示例函数,该函数与第一个函数具有相同的问题。

更新:我删除了第一个示例,并提供了一个简单但完整的问题实现。我之前的测试没有让我用值初始化向量。我已经尝试了几种初始化向量的方法,但没有一种方法可行。每当为向量中的新项分配内存时,包括初始化期间,就会发生段错误。

2 个答案:

答案 0 :(得分:3)

据我所知,Unix平台上Mono的默认调用约定是cdecl。这可能与您的C ++代码相匹配。所以我猜这不是问题所在。值得仔细检查。也许在你的pinvoke中明确指定调用约定。

我不明白你为什么使用UnmanagedType.Struct作为返回值。据我所知,这是不正确的。在.net上会导致结构被编组为VARIANT,这绝对不是你想要的。我不确定UnmanagedType.Struct对于Linux上的Mono pinvoke是否有任何意义,但无论如何它都不应该存在,所以你应该删除它。

也没有必要为入口点命名。所以我简化了p / invoke。

[DllImport("somelib.so")]
public static extern some_struct some_function();

另一个明显的潜在不匹配将是您的本机和托管结构定义之间。你说:

  

正如您所看到的,没有参数需要编组,因此问题不在于传入的数据。

这是真的,但传递的返回值又如何呢?这也需要编组,你必须把它弄好。由于无法看到结构类型的定义(本机和托管),我们无法判断。如果你不能在这里展示它们,那么你必须检查它们是否匹配。

最后,可以想象你的编译器和pinvoke marshaller假定结构返回值有不同的ABI。这总是一片灰色地带。我总是建议使用简单类型作为返回值,并将结构作为out参数返回。

如果我不得不下注,我会说问题出在结构定义中,遗憾的是你还没有表现出来。


<强>更新

您的问题编辑表示您遇到void返回类型的函数问题。在这种情况下,唯一明智的结论是问题出在您的本机代码中,而不是在p / invoke中。要进一步跟踪此问题,您需要查看本机代码。

答案 1 :(得分:0)

我最近遇到了这个问题。在从托管可执行文件调用非托管DLL中的函数时,我无法分配$cmdparameters = @( 'fileDate = "17/06/2017"', 'filePath = "c:\temp\test.txt"', 'sqlLoadErrors = "yourcontent"' ) $mySqlCmd = "sqlcmd -S $server -U $username -P $pwd -d $dbname -o $lis -i $sqlScript -v $cmdparameters" Invoke-Expression $mySqlCmd std::vector而不会崩溃访问冲突。同样,直接从非托管可执行文件调用时一切正常。

在我们的例子中,问题结果是从托管上下文调用时在运行时加载了一个错误的(可能已损坏的)std DLL,只是从PATH中删除了这个DLL的位置修复了这个问题。

这很难识别,因为错误消息最初没有提示可能是什么原因,但是在动态链接标准库(而不是默认的静态链接)之后,访问冲突识别出有问题的std DLL,导致在PATH中发现了错误的二进制文件。