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

Calling external C library method correctly from C# .Net Core project *On Linux*

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

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

我们正在为 IDN 使用 this C library 的精简版。

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

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

我将首先描述 Linux 的当前(失败)实施情况,但请参阅下面的 Windows(成功)实施情况。

在 class "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);
}

然后在助手 class 中我们调用这些方法如下:

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 方法 returns 一个 invalid_action 结果与输入无关,即这发生在测试应该通过但还有当它应该因其他原因而失败时(具有不同的错误结果)。我应该提一下,我们也首先尝试使用 IntPtrs 和 Marshaling 来实现它,因为我们在 Windows 上就是这样做的,但是根据指南 here,我不确定它是否有必要(而且它无论如何都没有用,仍然抛出与此实现相同的错误)

最初,当它在 Windows 上工作时,我们的 C# 代码在 'IDNKitLite.cs' class:

中如下所示
[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 进行操作,但是当我如图所示实现它时,它对我来说工作正常,即使在使用我们的库执行相同类型的实现时仍然没有正在工作。

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