在理想情况下,获取命名管道列表非常简单,可以在这里找到: 如何获取 Windows 中所有打开的命名管道的列表?
但是提到了解决方案
var namedPipes = Directory.GetFiles(@"\\.\pipe\");
有时会产生不可预测的结果。上面的链接中提到了其中之一(路径异常中的无效字符)。今天我遇到了自己的例外:
ArgumentException“第二个路径片段不能是驱动器或 UNC 名称。参数名称:path2”。
问题是.net中是否有任何真正有效的解决方案来获取所有打开的命名管道的列表?谢谢
我深入研究了 Directory 类源代码并找到了灵感。这是一个可行的解决方案,它为您提供所有打开的命名管道的列表。我的结果不包含 \.\pipe\ 前缀,因为它可以在 Directory.GetFiles 的结果中看到。我在 WinXp SP3、Win 7、Win 8.1 上测试了我的解决方案。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA
lpFindFileData);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FindClose(IntPtr hFindFile);
private static void Main(string[] args)
{
var namedPipes = new List<string>();
WIN32_FIND_DATA lpFindFileData;
var ptr = FindFirstFile(@"\\.\pipe\*", out lpFindFileData);
namedPipes.Add(lpFindFileData.cFileName);
while (FindNextFile(ptr, out lpFindFileData))
{
namedPipes.Add(lpFindFileData.cFileName);
}
FindClose(ptr);
namedPipes.Sort();
foreach (var v in namedPipes)
Console.WriteLine(v);
Console.ReadLine();
}
使用返回
IEnumerable
的 .NET 4 API 之一,您可以捕获这些异常:
static IEnumerable<string> EnumeratePipes() {
bool MoveNextSafe(IEnumerator enumerator) {
// Pipes might have illegal characters in path. Seen one from IAR containing < and >.
// The FileSystemEnumerable.MoveNext source code indicates that another call to MoveNext will return
// the next entry.
// Pose a limit in case the underlying implementation changes somehow. This also means that no more than 10
// pipes with bad names may occur in sequence.
const int Retries = 10;
for (int i = 0; i < Retries; i++) {
try {
return enumerator.MoveNext();
} catch (ArgumentException) {
}
}
Log.Warn("Pipe enumeration: Retry limit due to bad names reached.");
return false;
}
using (var enumerator = Directory.EnumerateFiles(@"\\.\pipe\").GetEnumerator()) {
while (MoveNextSafe(enumerator)) {
yield return enumerator.Current;
}
}
}
当然,最后并没有列出命名错误的管道。因此,如果您确实想列出所有管道,则不能使用此解决方案。
对于具有更具体用例的开发人员:如果您只需要检查给定管道是否存在,并且您知道它不包含任何无效字符,则可以简单地使用
Directory.EnumerateFiles
: 中的枚举器
public static bool PipeExists(string pipePath)
{
var e = Directory.EnumerateFiles(@"\\.\pipe\");
using (var enumerator = e.GetEnumerator())
{
while (true)
{
try
{
if (!enumerator.MoveNext())
{
break;
}
if (enumerator.Current != null && enumerator.Current.Contains(pipePath))
{
return true;
}
}
catch (ArgumentException)
{
// ignore, the pipe's name contains invalid characters
}
}
}
return false;
}