1. 起源:
VCU10之视频下载模块,采用纯python实现,c++代码调用pythonrun.h配置python运行环境启动python模块,以使界面UI能够使用其中功能。
棘手问题!用去一天时间反复打印日志,验证所传字串区别,以期望发现问题定位问题,直至下班前始有灵感。
验证发现,非中文字符可以正常下载,中文字符下载解析失败,当时即想到可能是字串不统一所致,就在python代码中做字串转换处理,均不奏效。
原接口封装代码如下:
[DllImport("VideoDownloader", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static bool VDDownload(IntPtr instance,string savePath,string imageSavePath, string quality, string ext, string subtitleLang);
比如传savePath为[d:\vcu新],dll调用python模块,则打印日志为:
dir: d:\kvd新, code: ‘d:\\kvd\xd0\xc2‘
而直接运行python代码,其输出却是:
dir: d:\kvd新, code: ‘d:\\kvd\xe6\x96\xb0‘
用c#建Demo验知\xd0\xc2为[新]字的gb2312编码,\xe6\x96\xb0为[新]字的utf-8编码。
2、字串编码
Win7 64位简体中文版中c#的默认编码:
做如此简单验证,发现其默认编码为gb2312;而所用python代码,为兼顾中文等多字节字符,默认采用utf-8编码。
问题大抵就在这里了,dll接口字串,改为utf-8字串传递。但c#内置没有utf-8封装器,自定义实现它。
3、UTF8Marshaler
//接口数据为utf-8编码所设置 public class UTF8Marshaler : ICustomMarshaler { public void CleanUpManagedData(object managedObj) { } public void CleanUpNativeData(IntPtr pNativeData) { Marshal.FreeHGlobal(pNativeData); } public int GetNativeDataSize() { return -1; } public IntPtr MarshalManagedToNative(object managedObj) { if (object.ReferenceEquals(managedObj, null)) return IntPtr.Zero; if (!(managedObj is string)) throw new InvalidOperationException(); byte[] utf8bytes = Encoding.UTF8.GetBytes(managedObj as string); IntPtr ptr = Marshal.AllocHGlobal(utf8bytes.Length + 1); Marshal.Copy(utf8bytes, 0, ptr, utf8bytes.Length); Marshal.WriteByte(ptr, utf8bytes.Length, 0); return ptr; } public object MarshalNativeToManaged(IntPtr pNativeData) { if (pNativeData == IntPtr.Zero) return null; List<byte> bytes = new List<byte>(); for (int offset = 0; ; offset++) { byte b = Marshal.ReadByte(pNativeData, offset); if (b == 0) break; else bytes.Add(b); } return Encoding.UTF8.GetString(bytes.ToArray(), 0, bytes.Count); } private static UTF8Marshaler instance = new UTF8Marshaler(); public static ICustomMarshaler GetInstance(string cookie) { return instance; } }
4、更新接口字串封送样式
[DllImport("VideoDownloader", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private extern static bool VDDownload(IntPtr instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))] string savePath, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))] string imageSavePath, string quality, string ext, string subtitleLang);
验证工作OK!运行效果如下:
至此,一个技术难点解决,记录于此。
时间: 2024-10-05 06:16:12