0

我正在尝试围绕现有的 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 调用时我可能遗漏了什么,或者这里还有其他事情吗?

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

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

4

2 回答 2

3

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

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

也不需要命名入口点。所以我会简化 p/invoke。

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

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

如您所见,没有需要封送处理的参数,因此问题不在于传入的数据。

没错,但是传递出去的返回值呢。这也需要编组,你必须把它做好。由于无法查看结构类型(本机和托管)的定义,我们无法判断。如果您无法在此处显示它们,那么您必须检查它们是否匹配。

最后,可以想象你的编译器和 pinvoke 编组器为结构返回值假设不同的 ABI。这始终是一个灰色地带。我总是推荐简单类型作为返回值,并将你的结构作为输出参数返回。

如果我不得不打赌,我会说问题出在结构定义上,遗憾的是你没有展示出来。


更新

您的问题编辑表明您对具有void返回类型的函数有问题。在这种情况下,唯一合理的结论是问题出在您的本机代码中,而不是在 p/invoke 中。要进一步追踪这一点,您需要查看本机代码。

于 2013-09-18T06:42:03.180 回答
1

我最近遇到了这个确切的问题。从托管可执行文件调用非托管 DLL 中的函数时,我无法分配std::vectorstd::map不崩溃访问冲突。同样,当直接从非托管可执行文件调用时,一切正常。

在我们的案例中,问题原来是在运行时从托管上下文调用时加载了错误的(并且可能损坏的)std DLL,并且只需从 PATH 中删除此 DLL 的位置即可解决问题。

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

于 2017-06-17T19:01:05.103 回答