我正在实现自定义的Marshaler,以便将utf8字符串从本机发送到本机,或从本机发送到本机。
{
[ComVisible(true)]
public class UTF8StringMarshaler : ICustomMarshaler
{
private static ICustomMarshaler marshalerInstance = new UTF8StringMarshaler();
public static ICustomMarshaler GetInstance(string optionalCookie)
{
return marshalerInstance;
}
public void CleanUpManagedData(object ManagedObj)
{
//Managed Data will be deleted by the garbage collector
}
public void CleanUpNativeData(IntPtr pNativeData)
{
Marshal.FreeCoTaskMem(pNativeData);
}
public int GetNativeDataSize()
{
//Not used in our case
return -1;
}
public IntPtr MarshalManagedToNative(object ManagedObj)
{
if (ManagedObj == null || ManagedObj as string == null)
return IntPtr.Zero;
if (!(ManagedObj is string))
throw new MarshalDirectiveException("UTF8StringMarshaler can only be used on String.");
UTF8Encoding utf8Encoder = new UTF8Encoding();
string utf8string = ManagedObj as string;
byte[] stringBuffer = utf8Encoder.GetBytes(utf8string);
IntPtr buffer = Marshal.AllocCoTaskMem(stringBuffer.Length + 1);
Marshal.Copy(stringBuffer, 0, buffer, stringBuffer.Length);
Marshal.WriteByte(buffer + stringBuffer.Length, 0);
return buffer;
}
public unsafe object MarshalNativeToManaged(IntPtr pNativeData)
{
if (pNativeData == IntPtr.Zero)
return null;
string temp = null;
UTF8Encoding utf8Encoder = new UTF8Encoding(true, true);
byte* buffer = (byte*)pNativeData;
while (*buffer != 0)
{
buffer++;
}
int length = (int)(buffer - (byte*)pNativeData);
byte[] stringbuffer = new byte[length];
Marshal.Copy(pNativeData, stringbuffer, 0, length);
try
{
temp = utf8Encoder.GetString(stringbuffer);
}
catch (EncoderFallbackException e)
{
Console.WriteLine("Encoding Exception type {0}, Error {1}", e.GetType().Name, e.Message);
}
return temp;
}
}
此实现有效,除非C#字符串来自Marshal.PtrToStringAnsi
函数。
因此,在MarshalNativeToManaged
函数中,我需要验证字符串是否是从Marshal.PtrToStringAnsi
开始的正确编码
从Microsoft Doc,Marshal.PtrToStringAnsi
将每个ANSI字符扩展为Unicode:
Copies all characters up to the first null character from an unmanaged ANSI string to a managed String, and widens each ANSI character to Unicode.
所以问题是,Marshal.PtrToStringAnsi
函数中的字符串的编码是什么?
是否有更简单的方法来验证字符串是否来自该函数?
Marshal.PtrToStringAnsi函数的字符串的编码是什么?
没有一种“ ANSI”编码。无论系统当前的代码页是什么。这将取决于用户的区域设置。这应该对应于CharSet
enum:
Ansi:将字符串编组为多字节字符串:Windows上的系统默认Windows(ANSI)代码页,Unix上的UTF-8。
但是请注意,在Unix(以及我认为是Linux)上的特殊处理。
是否有更简单的方法来验证字符串是否来自该函数?
在我看来,这和似乎是主要问题是一个完全不同的问题。特别是:在我看来,了解从“ ANSI”转换为UTF-16(.NET使用的内部文本编码)时函数将使用哪种编码似乎并不导致一种“验证字符串是否为通过该功能”。一旦有了C#CharSet
对象,它就已经被编码为UTF-16。它实际上可能源自any编码。
从您的问题中也不清楚“的含义,除了C#字符串来自string
函数时,”是有效的“。也就是说,在这种情况下precisely不能以什么方式工作?您的封送处理程序似乎只负责向本地代码传递UTF-8字节或从本地代码传递UTF-8字节。给定一个C#Marshal.PtrToStringAnsi
对象,该string
的创建方式无关紧要。现在它是一串UTF-16字符,可以可靠地将其重新编码为UTF-8。如果“ ANSI”文本存在问题,则在您的封送处理程序介入之前就发生了该问题。您的封送整理者不必为此担心。
最后:为什么不只使用string
而不是在每个封送处理操作上实例化一个新的Encoding.UTF8
对象?至少,您应该缓存该对象,但是由于UTF8Encoding
和GetBytes()
对于GetString()
的任何实例都可以工作,因此实际上,您只应使用.NET已经为您创建的对象,并让.NET处理对象的缓存。