C# 调用WNetGetUniversalName后Marshal.PtrToStringAuto出现问题
下面是我获取通用路径的函数。我在这个主题上发现了各种相互冲突的帖子,这已经经过了多次迭代,但是这个函数通常是有效的。但是,大约有5%的时间它会导致应用程序崩溃。错误消息表明它可能已损坏内存。我找不到理由。在调试器下运行c#应用程序时,问题永远不会发生,但是如果我在错误发生后附加到进程,那么我可以查看WNetGetUniversalName返回的数据,它看起来与没有崩溃时完全相同。我想知道是什么原因造成的C# 调用WNetGetUniversalName后Marshal.PtrToStringAuto出现问题,c#,marshalling,C#,Marshalling,下面是我获取通用路径的函数。我在这个主题上发现了各种相互冲突的帖子,这已经经过了多次迭代,但是这个函数通常是有效的。但是,大约有5%的时间它会导致应用程序崩溃。错误消息表明它可能已损坏内存。我找不到理由。在调试器下运行c#应用程序时,问题永远不会发生,但是如果我在错误发生后附加到进程,那么我可以查看WNetGetUniversalName返回的数据,它看起来与没有崩溃时完全相同。我想知道是什么原因造成的 public static string GetUncPath (strin
public static string GetUncPath (string localPath)
{
if (localPath.IndexOf ('\\')== 0)
return localPath; // already a unc path
int bufferSize = 8000;
IntPtr buffer = Marshal.AllocHGlobal (bufferSize);
int ret = WNetGetUniversalName (localPath, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref bufferSize);
if (ret == 2250)
return localPath;
string result = "";
if (ret == 0) {
IntPtr ptr = Marshal.ReadIntPtr (buffer);
result = Marshal.PtrToStringAuto (ptr, bufferSize);
result = result.Substring (0, result.IndexOf ('\0'));
}
Marshal.FreeHGlobal (buffer);
return result;
}
[DllImport ("mpr.dll", EntryPoint="WNetGetUniversalName",
CharSet=CharSet.Unicode, SetLastError=false)]
[return: MarshalAs (UnmanagedType.U4)]
static extern int WNetGetUniversalName (string lpLocalPath,
[MarshalAs (UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs (UnmanagedType.U4)] ref int lpBufferSize);
崩溃总是发生在对Marshal.PtrToStringAuto的调用中。同样,当崩溃发生时,缓冲区中的数据看起来绝对正确
请注意,我已经设置了8000字节的缓冲区。我从2000开始,认为这足够了,因为返回的字符串都少于200字节。但使用8000似乎可以减少崩溃。这有点主观,因为崩溃是完全随机的,有时长时间不发生,而有时几乎每次应用程序启动时都会发生
这是一个64位应用程序。这些错误发生在Windows 7 Pro和Windows 10上
有什么想法吗?
谢谢,罗斯
好的,多亏了本,我得到了答案。这是我最后的工作代码:
public static string GetUncPath (string localPath)
{
if (localPath.IndexOf ('\\')== 0)
return localPath; // already a unc path
int bufferSize = 512;
IntPtr buffer = Marshal.AllocCoTaskMem (bufferSize);
int ret = WNetGetUniversalName (localPath, UNIVERSAL_NAME_INFO_LEVEL,
buffer, ref bufferSize);
if (ret == 2250) { // if localpath is on the local computer
Marshal.FreeCoTaskMem (buffer);
return localPath;
}
string result = "";
if (ret == 0) {
IntPtr ptr = Marshal.ReadIntPtr (buffer);
result = Marshal.PtrToStringAuto (ptr);
}
Marshal.FreeCoTaskMem (buffer);
return result;
}
以下行中有三个错误:
result = Marshal.PtrToStringAuto (ptr, bufferSize);
@tolanj在评论中指出,返回时bufferSize
是写入的字节总数,包括\u UNIVERSAL\u NAME\u INFOW
结构本身的大小。不是字符串中的字节数
第二个参数是Marshal.PtrToStringAuto
,它的第二个参数是要从指定指针读取的字符数,但传递的是字节数
第三,您使用CharSet.Unicode
导入了函数,因此您知道您总是会得到WNetGetUniversalNameW
、\u UNIVERSAL\u NAME\u INFOW
和UTF-16字符串数据。因此,您应该使用PtrToStringUni
notPtrToStringAuto
1。上述两个问题也适用于PtrToStringUni
PtrToStringAuto
行为记录为
在Unicode平台上,此方法调用PtrToStringUni
;在ANSI平台上,它调用PtrToStringAnsi
。在调用这些方法之前不进行任何转换
有时候你不想要一个,有时候你不想要另一个。p/invoke声明正在所有平台上强制使用Unicode。我注意到您没有处理缓冲区不够长并且返回错误数据的情况。在这里查看一个修复它的实现:问题与字符串中的编码类型有关。PtrToStringAuto试图自动检测字符串中的编码。字符串是否更改(UTF-8和/或Unicode)?。如果字符串始终为8位,则使用UTF-8(LpStr)。请参阅:除了检查错误\u更多\u数据外,我从文档中了解到,缓冲区中填充的是通用\u名称\u信息(IntPtr size),然后是实际字符,而bufferSize将是这些大小的总和(写入缓冲区的实际数量),因此,您似乎应该将小于bufferSize的字节拉入Marshal.PtrToStringAuto。我会尝试Marshal.PtrToStringAuto的重载,它不会占用较长的时间,这不是问题的原因,但会丢失
Marshal.AllocHGlobal
和Marshal.FreeHGlobal
,只是暂时固定一个普通的C#数组。手动内存管理总是乱七八糟的,手动内存管理与函数名不符的情况几乎是一样糟糕。@jdweng:“PtrToStringAuto
试图自动检测字符串中的编码”与文档相矛盾。你从哪里得到这个主意的?