在 Windows 中枚举命名管道

Posted

技术标签:

【中文标题】在 Windows 中枚举命名管道【英文标题】:Enumerating Named Pipes in Windows 【发布时间】:2009-01-26 23:05:40 【问题描述】:

我无法连接到命名管道(在本例中为快速 cgi 命名管道) 根据 MSDN,我应该使用 CreateFile() 或 CallNamedPipe() (平面 C API,同步 - 无重叠 I/O) http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx

但我得到 INVALID_HANDLE_VALUE 并且当我 GetLastError() 时它为零!?

我还想知道是否可以使用某种类型的 . 调用枚举所有命名管道,然后解析出我正在寻找的管道: "\.\pipe\FastCGI\"

有没有人有使用这些 cmets 的经验: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/225878

【问题讨论】:

【参考方案1】:

问题出在这里:


    TmpInfo = DirInfo;   
    while(1)   
       
       if(TmpInfo->NextEntryOffset==0)   
         break;   

       TmpInfo->FileDirectoryInformationClass.FileName[TmpInfo->FileNameLength/sizeof(WCHAR)] = NULL;   

       wprintf(L"%s (%d, %d)\n",TmpInfo->FileDirectoryInformationClass.FileName,   
                                TmpInfo->EndOfFile.LowPart,   
                                TmpInfo->AllocationSize.LowPart );   

       TmpInfo = (PFILE_QUERY_DIRECTORY)((DWORD)TmpInfo+TmpInfo->NextEntryOffset);   
       

就在“while(1)”之后,您检查 NextEntryOffset == 0 这意味着最后一个条目永远不会被报告,移动“if(...) break;”在“wprintf(...)”调用之后,您应该能够枚举所有管道。EDIT 对于那些想要完整源代码(不需要 DDK)的人来说,这里是。请注意,这不是我的代码,而是找到了here。此代码与原始代码之间的唯一变化是上面详述的错误修复。EDIT v2.0 在下面的代码中发现了另一个错误。当它打印有关它正在迭代的当前项目的信息时,它会在名称的末尾放置一个空字符。这个空字符实际上覆盖了下一个条目的前 2 个字节,这恰好覆盖了该条目中“NextEntryOffset”变量的 2 个最低有效字节(通常导致它等于 0),因此只有前 2 个项目是每个从每个“NtQueryDirectoryFile”调用中枚举。 我在下面的代码中添加了一个修复程序,应该可以解决这个问题(存储 WCHAR 被清除,然后在打印后恢复它。有点黑客,但这只是一些示例代码,为了正确实现,要么避免使用 wprintf打印名称,或将其复制到另一个缓冲区,您可以安全地将其结尾 NULL)。


// pipelist.cpp (Windows NT/2000) // // This example will show how you can enumerate all named pipes // active on a system. // // (c)2000 Ashot Oganesyan K, SmartLine, Inc // mailto:ashot@aha.ru, http://www.protect-me.com, http://www.codepile.com #include <windows.h> #include <stdio.h> #define FileDirectoryInformation 1 #define STATUS_NO_MORE_FILES 0x80000006L typedef struct USHORT Length; USHORT MaximumLength; PWSTR Buffer; UNICODE_STRING, *PUNICODE_STRING; typedef struct LONG Status; ULONG Information; IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; typedef struct ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; union struct WCHAR FileName[1]; FileDirectoryInformationClass; struct DWORD dwUknown1; WCHAR FileName[1]; FileFullDirectoryInformationClass; struct DWORD dwUknown2; USHORT AltFileNameLen; WCHAR AltFileName[12]; WCHAR FileName[1]; FileBothDirectoryInformationClass; ; FILE_QUERY_DIRECTORY, *PFILE_QUERY_DIRECTORY; // ntdll!NtQueryDirectoryFile (NT specific!) // // The function searches a directory for a file whose name and attributes // match those specified in the function call. // // NTSYSAPI // NTSTATUS // NTAPI // NtQueryDirectoryFile( // IN HANDLE FileHandle, // handle to the file // IN HANDLE EventHandle OPTIONAL, // IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, // IN PVOID ApcContext OPTIONAL, // OUT PIO_STATUS_BLOCK iostatusBlock, // OUT PVOID Buffer, // pointer to the buffer to receive the result // IN ULONG BufferLength, // length of Buffer // IN FILE_INFORMATION_CLASS InformationClass,// information type // IN BOOLEAN ReturnByOne, // each call returns info for only one file // IN PUNICODE_STRING FileTemplate OPTIONAL, // template for search // IN BOOLEAN Reset // restart search // ); typedef LONG (WINAPI *PROCNTQDF)( HANDLE,HANDLE,PVOID,PVOID,PIO_STATUS_BLOCK,PVOID,ULONG, UINT,BOOL,PUNICODE_STRING,BOOL ); PROCNTQDF NtQueryDirectoryFile; void main(void) LONG ntStatus; IO_STATUS_BLOCK IoStatus; HANDLE hPipe; BOOL bReset = TRUE; PFILE_QUERY_DIRECTORY DirInfo, TmpInfo; NtQueryDirectoryFile = (PROCNTQDF)GetProcAddress( GetModuleHandle("ntdll"), "NtQueryDirectoryFile" ); if (!NtQueryDirectoryFile) return; hPipe = CreateFile("\\\\.\\Pipe\\",GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,OPEN_EXISTING,0,NULL); if(hPipe == INVALID_HANDLE_VALUE) return; DirInfo = (PFILE_QUERY_DIRECTORY) new BYTE[1024]; printf("Pipe name (Number of instances, Maximum instances)\n\n"); while(1) ntStatus = NtQueryDirectoryFile(hPipe,NULL,NULL,NULL,&IoStatus,DirInfo,1024, FileDirectoryInformation,FALSE,NULL,bReset); if (ntStatus!=NO_ERROR) if (ntStatus == STATUS_NO_MORE_FILES) break; return; TmpInfo = DirInfo; while(1) // Store old values before we mangle the buffer const int endStringAt = TmpInfo->FileNameLength/sizeof(WCHAR); const WCHAR oldValue = TmpInfo->FileDirectoryInformationClass.FileName[endStringAt]; // Place a null character at the end of the string so wprintf doesn't read past the end TmpInfo->FileDirectoryInformationClass.FileName[endStringAt] = NULL; wprintf(L"%s (%d, %d)\n",TmpInfo->FileDirectoryInformationClass.FileName, TmpInfo->EndOfFile.LowPart, TmpInfo->AllocationSize.LowPart ); // Restore the buffer to its correct state TmpInfo->FileDirectoryInformationClass.FileName[endStringAt] = oldValue; if(TmpInfo->NextEntryOffset==0) break; TmpInfo = (PFILE_QUERY_DIRECTORY)((DWORD)TmpInfo+TmpInfo->NextEntryOffset); bReset = FALSE; delete DirInfo; CloseHandle(hPipe);

【讨论】:

代码在 x64 模式下不起作用。到目前为止,我发现了 2 个问题:(DWORD)TmpInfoDWORD 应替换为 64 位值)。 IO_STATUS_BLOCK 应该有 LONG* Status 字段而不是 LONG Status【参考方案2】:

如果您想要一个可以为您执行此操作的编译工具,请查看 SysInternals(Microsoft 拥有)的“PipeList”。

Download Here

【讨论】:

【参考方案3】:

您是否正确转义了管道名称?它应该看起来像:\\\\.\\pipe\\FastCGI

请参阅Named Pipe Client Demo 了解更多信息。

【讨论】:

【参考方案4】:

使用未记录的函数:

// NtQueryDirectoryFile( // IN HANDLE FileHandle, // 文件句柄 // 在 HANDLE EventHandle 可选, // 在 PIO_APC_ROUTINE ApcRoutine 可选, // 在 PVOID ApcContext 中是可选的, // OUT PIO_STATUS_BLOCK IoStatusBlock, // OUT PVOID Buffer, // 指向接收结果的缓冲区的指针 // IN ULONG BufferLength, // 缓冲区长度 // IN FILE_INFORMATION_CLASS InformationClass,// 信息类型 // IN BOOLEAN ReturnByOne, // 每次调用只返回一个文件的信息 // IN PUNICODE_STRING FileTemplate OPTIONAL, // 搜索模板 // IN BOOLEAN 重置 // 重新开始搜索 // );

【讨论】:

您仍然需要转义反斜杠。该演示是用 C 编写的,它也逃脱了它们。 正确。我使用 C 是因为它是事实上的语言,但我使用另一种不需要转义反斜杠的语言来实现。【参考方案5】:

管道名称的第一个反斜杠被论坛软件截断。管道名称为:

\\.\pipe\test

(不需要在我用于测试的语言中进行转义)

我写了两个应用程序,一个是管道服务器,一个是管道客户端来测试阻塞等 他们完美地工作。

我使用以下方法创建管道:

Pipe_Name      = "\\.\pipe\test"
MaxInstances   = 1
OutBufferSize  = 1024
InBufferSize   = 1024

hPipe = CreateNamedPipe(_
Pipe_Name, _                                     ' Name of the Pipe
PIPE_ACCESS_DUPLEX, _                            ' Specifies the pipe access/overlapped/write-through/security access modes 
PIPE_TYPE_MESSAGE OR PIPE_READMODE_MESSAGE, _    ' Specifies the type, read, and wait modes of the pipe handle
MaxInstances, _                                  ' Specifies the maximum number of instances that can be created for this pipe
OutBufferSize, _                                 ' Specifies the number of bytes to reserve for the output buffer
InBufferSize, _                                  ' Specifies the number of bytes to reserve for the input buffer
0, _                                             ' Specifies the default time-out value, in milliseconds
Security_Declaration)                            ' Pointer to a SECURITY_ATTRIBUTES structure 

它不返回 INVALID_HANDLE_VALUE,而是一个有效的句柄,我随后使用它并且完美地工作 他们按预期阻止并正常通信。

【讨论】:

【参考方案6】:

好的,我在用于生成管道列表的代码中发现了另一个错误(关于第一个错误的帖子中的详细信息)。

至于“以及是否有人对这些 cmets 有经验”之后的链接中的信息,我了解他们在说什么,您能否更具体地说明您不了解或好奇的内容(顺便说一句,关于无法执行非阻塞操作的部分有点谎言,尽管它不是以 unix 系统的“传统”方式完成的)。

【讨论】:

感谢格兰特。你收到我的电子邮件了吗? 是的,这帮助我朝着正确的方向前进,看到它们都错了,但都没有返回相同的值,这让我尝试了一些最终揭示了问题的值。【参考方案7】:

感谢您了解这一点。我将此代码转换为另一种类似 C 的语言并使用: FILE_NAMES_INFORMATION 因为我只是在寻找名字

然后我用另一个应用程序创建了一个命名管道:

 \\.\pipe\test

【讨论】:

以上是关于在 Windows 中枚举命名管道的主要内容,如果未能解决你的问题,请参考以下文章

windows命名管道

Windows 8 命名管道创建

Docker Windows 容器挂载命名管道

Windows进程间通信—命名管道

Java中命名管道的并发读/写(在Windows上)

适用于 Windows 和 Linux 的 Go 中的命名管道