在 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)TmpInfo
(DWORD
应替换为 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 中枚举命名管道的主要内容,如果未能解决你的问题,请参考以下文章