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

在Linux上从C#.Net核心项目*正确调用外部C库方法*,c#,c,linux,native,marshalling,C#,C,Linux,Native,Marshalling,我被困在一个问题上,我已经尝试调试了好几天了,现在还没有解决方案。基本上,我们有一个在Windows上运行的c#.Net应用程序,它使用本机库dll。这一切都很顺利 现在的问题是,我们正在转向.NETCore和Docker(Linux),而我很难让它正常工作。具体来说,我能够将C库编译成.so,并且我能够修改C#DLLImport代码,以便在Linux Docker机器上从编译后的库中读取。然而,在Linux上调用本机方法的行为似乎有所不同,因为错误会在以前没有的地方抛出 我们正在使用IDN的L

我被困在一个问题上,我已经尝试调试了好几天了,现在还没有解决方案。基本上,我们有一个在Windows上运行的c#.Net应用程序,它使用本机库dll。这一切都很顺利

现在的问题是,我们正在转向.NETCore和Docker(Linux),而我很难让它正常工作。具体来说,我能够将C库编译成.so,并且我能够修改C#DLLImport代码,以便在Linux Docker机器上从编译后的库中读取。然而,在Linux上调用本机方法的行为似乎有所不同,因为错误会在以前没有的地方抛出

我们正在使用IDN的Lite版本

此库包含一个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方法都会返回无效的_操作结果,而不管输入是什么,也就是说,当测试应该通过,但由于其他原因(具有不同的错误结果)应该失败时,这种情况也会发生。我应该提到的是,我们也尝试先用IntPtrs和封送来实现它,因为这是我们在Windows上实现的方式,但从指南中我不确定这是否必要(而且它无论如何都不起作用,仍然抛出与此实现相同的错误)

最初,当它在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方法来遵循这个示例,但是当我实现它时,它对我来说很好,即使在使用我们的库执行相同类型的实现时,它仍然不工作。

size\u t tolen应该是
IntPtr tolen
,因为在64位时通常
sizeof(size\u t)==8
。不清楚
idn\u action\u t
idn\u result\u t
是什么。请注意,linux和windows之间的另一大区别可能是,如果在C中使用
long
类型。在linux 64位上,
sizeof(long)==8
,而在windows上则取决于使用的编译器(在VC++中是
sizeof>)(长)==4