6

Getting the list of named pipes is in ideal case pretty simple and can be found here: How can I get a list of all open named pipes in Windows?

But mentioned solution

var namedPipes = Directory.GetFiles(@"\\.\pipe\");

has occasionally unpredictable results. One of them was mentioned in the link above (Invalid character in path exception). Today I met my own exception:

ArgumentException "The second path fragment must not be a drive or UNC name. Parameter name: path2".

Question is whether there is any really working solution in .net to get the list of all opened named pipes? Thanks

Ashkan Mobayen Khiabani
  • 33,575
  • 33
  • 102
  • 171
user2126375
  • 1,594
  • 12
  • 29
  • 2
    Using pipelist.exe (http://technet.microsoft.com/en-us/sysinternals/dd581625.aspx) I found that pipe which break my code is named: "D:\Virtuals\Windows 8.1 x64 Professional\Windows 8.1 x64 Professional.vmx". If I shut down my virtual testing machine everything is ok. But still, that kind of name for pipe can be used by anyone else. Thus my question is still opened – user2126375 Aug 04 '14 at 05:08
  • Has this been fixed in .NET in the meantime? I can only reproduce it when targeting .NET 3.5 or earlier, even not on Windows 7. – Martin Jun 14 '19 at 09:28

2 Answers2

11

I dug into Directory class source code and found an inspiration. Here is a working solution which gives you list of all opened named pipes. My result does not contain \\.\pipe\ prefix as it can be seen in result of Directory.GetFiles. I tested my solution on 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();
     }
user2126375
  • 1,594
  • 12
  • 29
  • This code lacks any error handling when calling `FindFirstFile()` and `FindNextFile()`. At the very least, make sure `FindFirstFile()` does not return `INVALID_HANDLE_VALUE` before doing anything with `lpFindFileData` or `FindNextFile()`/`FindClose()`, eg `var ptr = FindFirstFile(@"\\.\pipe\*", out lpFindFileData); if (ptr != -1) { do { namedPipes.Add(lpFindFileData.cFileName); } while (FindNextFile(ptr, out lpFindFileData)); FindClose(ptr); }` – Remy Lebeau Sep 08 '20 at 16:43
2

Using one of the .NET 4 APIs returning IEnumerable, you can catch those exceptions:

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;
        }
    }
}

The badly named pipes are not enumerated in the end, of course. So, you cannot use this solution if you really want to list all of the pipes.

tm1
  • 1,180
  • 12
  • 28