为何NamedPipes在64位环境下无法正常工作?
64位构建下NamedPipe原生API调用返回无效句柄(错误码6)
我正在开发一个C#项目,通过原生C函数调用实现NamedPipes,完成主应用与Windows服务的进程间通信。32位构建下运行正常,但切换到64位构建后,客户端读取管道时GetLastError()始终返回错误码6(无效句柄)。
服务端核心代码
public const uint PIPE_ACCESS_DUPLEX = 0x00000003; public const uint FILE_FLAG_OVERLAPPED = 0x40000000; public const uint PIPE_WAIT = 0x00000000; public const uint PIPE_READMODE_MESSAGE = 0x00000002; public const uint PIPE_TYPE_MESSAGE = 0x00000004; public const uint PIPE_UNLIMITED_INSTANCES = 255; public const uint NMPWAIT_WAIT_FOREVER = 0xffffffff; public const uint GENERIC_READ = (0x80000000); public const uint GENERIC_WRITE = (0x40000000); public const uint OPEN_EXISTING = 3; public const uint WAIT_OBJECT_0 = 0x00000000; public const uint ERROR_BROKEN_PIPE = 109; public const uint ERROR_MORE_DATA = 234; public const uint ERROR_PIPE_CONNECTED = 535; public const uint ERROR_IO_PENDING = 997; public const Int32 INVALID_HANDLE_VALUE = -1; public const uint OUTPUT_BUFFER_SIZE_IN_BYTES = 32000; public const uint INPUT_BUFFER_SIZE_IN_BYTES = 32000; public const Int32 MAX_MESSAGE_SIZE_IN_BYTES = 1000000; base.handle = CreateNamedPipe(@"\\.\pipe\" + name, NamedPipe.PIPE_ACCESS_DUPLEX | NamedPipe.FILE_FLAG_OVERLAPPED, NamedPipe.PIPE_TYPE_MESSAGE | NamedPipe.PIPE_READMODE_MESSAGE | NamedPipe.PIPE_WAIT, NamedPipe.PIPE_UNLIMITED_INSTANCES, NamedPipe.OUTPUT_BUFFER_SIZE_IN_BYTES, NamedPipe.INPUT_BUFFER_SIZE_IN_BYTES, NamedPipe.NMPWAIT_WAIT_FOREVER, secAttr);
客户端核心代码
base.handle = CreateFile(@"\\.\pipe\" + name, NamedPipe.GENERIC_READ | NamedPipe.GENERIC_WRITE, 0, null, NamedPipe.OPEN_EXISTING, 0, 0);
错误信息
客户端读取管道时,调用GetLastError()返回错误码6:
[DllImport("kernel32.dll", SetLastError = true)] public static extern uint GetLastError();
ERROR: Invalid Handle Specified (错误6:无效句柄)
已确认kernel32.dll会自动路由到对应位数版本,排除这个因素。
问题原因与解决方法
核心原因:句柄类型的位数不匹配
64位系统中,Windows API返回的句柄是8字节的指针类型,代码中大概率存在以下问题:
base.handle类型错误:如果定义为Int32(32位整数),在64位环境下存储8字节句柄会被截断,直接导致无效句柄。- DllImport函数签名错误:
CreateNamedPipe和CreateFile的返回值必须是IntPtr(适配32/64位的指针类型),而非固定位数的int或uint。
具体修复步骤
- 修正句柄类型:将
base.handle的类型改为IntPtr,避免位数截断。 - 修正API函数签名:
CreateNamedPipe的正确签名:[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr CreateNamedPipe( string pipeName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, IntPtr lpSecurityAttributes);CreateFile的正确签名:[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr CreateFile( string fileName, uint desiredAccess, uint shareMode, IntPtr securityAttributes, uint creationDisposition, uint flagsAndAttributes, IntPtr templateFile);
- 修正无效句柄判断:将
INVALID_HANDLE_VALUE替换为new IntPtr(-1),64位环境下只有IntPtr类型的-1才是标准的无效句柄值。
额外注意事项
- 所有涉及句柄传递的API调用(如
ReadFile、WriteFile),参数中的句柄必须统一使用IntPtr类型。 - 64位环境下禁止将
IntPtr强制转换为int或uint,避免数据截断。
内容的提问来源于stack exchange,提问作者Spiky05
相关产品推荐
相关产品推荐

