我正在尝试围绕现有的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电话时,我可能有什么遗漏,或者还有其他事情发生在这里?
更新:我添加了第二个更简单的示例函数,该函数与第一个函数具有相同的问题。
更新:我删除了第一个示例,并提供了一个简单但完整的问题实现。我之前的测试没有让我用值初始化向量。我已经尝试了几种初始化向量的方法,但没有一种方法可行。每当为向量中的新项分配内存时,包括初始化期间,就会发生段错误。
答案 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中发现了错误的二进制文件。