从 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_t
和 idn_result_t
是什么。请注意,linux 和 windows 之间的另一个大区别可能是在 C 中使用 long
类型。在 linux 64 位上,sizeof(long) == 8
,而在Windows 取决于使用的编译器(VC++ 是 sizeof(long) == 4
)
我被困在一个问题上,我已经尝试调试好几天了,现在还没有解决方案。基本上,我们有一个 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_t
和 idn_result_t
是什么。请注意,linux 和 windows 之间的另一个大区别可能是在 C 中使用 long
类型。在 linux 64 位上,sizeof(long) == 8
,而在Windows 取决于使用的编译器(VC++ 是 sizeof(long) == 4
)