从C#.Net Core项目正确调用外部C库方法*在Linux上*

时间:2018-07-12 23:01:13

标签: c# c linux native marshalling

我陷入了一个问题,该问题我已经尝试调试了好几天,但还没有解决方案。基本上,我们有一个c#.Net应用程序,该应用程序在Windows上运行,并且使用本机库dll。一切都很好。

现在,问题是我们正在转向.Net Core和Docker(Linux),而我在使其工作方面遇到困难。具体来说,我能够将C库编译为.so,并且能够修改C#DLLImport代码以从Linux Docker计算机上的已编译库中读取。但是,似乎在Linux上调用本机方法的行为有所不同,因为错误被抛在了以前没有的地方。

我们将this C library的精简版用于IDN。

此库包含一个C方法,我将在此处的示例中使用该方法:

idn_result_t
idn_encodename(idn_action_t actions, const char *from, char *to, size_t tolen) {...}

我将首先描述我们当前在Linux上(失败)的实现,但是请在下面查看我们在Windows上(成功)的实现。

在“ IDNKitLite.cs”类中,我们具有以下摘录:

[DllImport("idnkitlite", EntryPoint = "idn_encodename", CallingConvention = CallingConvention.Cdecl)]
public static extern idn_result_t idn_encodename(idn_action_t actions, string from, StringBuilder to, uint tolen);

public static idn_result_t idn_encodename(idn_action_t actions, string from, StringBuilder to, uint tolen)
{
   return idn_encodename(actions, from, to, tolen);
}

然后在助手类中,我们将这些方法称为:

public static string GetPunycode(string original)
{
    var to = new StringBuilder(256);
    var rval = IDNKitLite.idn_encodename(idn_action_t.IDN_ENCODE_REGIST, original, to, (uint)to.Capacity);

    //do validation
    return to.ToString();
}

但是,无论何时执行上述代码,idn_encodename方法都会返回一个invalid_action结果,而不管输入是什么,也就是说,这是在测试应该通过的情况下发生的,也可能是由于其他原因导致失败的结果(具有不同的错误结果) )。我应该提到,我们也首先尝试使用IntPtrs和Marshaling来实现它,因为那是我们在Windows上实现的方式,但是从指南here中我不确定是否有必要(并且它没有用)无论如何,仍然会引发与该实现相同的错误)

最初,当它在Windows上运行时,我们的C#代码在“ IDNKitLite.cs”类中如下所示:

[DllImport("idnkitlite64.dll", EntryPoint = "idn_encodename", CallingConvention = CallingConvention.Cdecl)]
public static extern idn_result_t idn_encodename_64(idn_action_t actions, IntPtr from, StringBuilder to, Int32 tolen);


public static idn_result_t idn_encodename(idn_action_t actions, IntPtr from, StringBuilder to, Int32 tolen)
{
   return idn_encodename_64(actions, from, to, tolen);
}

这将通过以下方式调用:

public static string GetPunycode(string original)
{
    var to = new StringBuilder(256);
    var p = Utf8FromString(original);
    var rval = IDNKitLite.idn_encodename(idn_action_t.IDN_ENCODE_REGIST, p, to, to.Capacity);

    Marshal.FreeHGlobal(p);
    //do validation with rval
    return to.ToString();
}

最后是用于获取IntPtr的Utf8FromString方法:

private static IntPtr Utf8FromString(string from)
{
    var chars = from.ToCharArray();
    var enc = Encoding.UTF8.GetEncoder();
    var count = enc.GetByteCount(chars, 0, chars.Length, true);
    var bytes = new byte[count + 1];
    count = enc.GetBytes(chars, 0, chars.Length, bytes, 0, true);
    bytes[count] = 0;
    var p = Marshal.AllocHGlobal(1024);
    Marshal.Copy(bytes, 0, p, bytes.Length);
    return p;
}

我尝试使用c库的strncpy方法沿示例here进行操作,但是如图所示实现它时,即使对我们的库执行相同类型的实现仍然无法正常工作,它也对我来说很好。

1 个答案:

答案 0 :(得分:1)

size_t tolen应该为IntPtr tolen,因为通常为64位sizeof(size_t) == 8。并且不清楚idn_action_tidn_result_t是什么。请注意,Linux和Windows之间的另一大区别可能是如果在C中使用了long类型。在Linux 64位上,sizeof(long) == 8则在Windows上取决于所使用的编译器(对于VC ++,是sizeof(long) == 4