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