Mercurial > hg > ucis.core
comparison Windows/WindowsNamedPipe.cs @ 55:ec222fb577dd
Added Windows Named Pipe stream wrapper
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Wed, 02 Oct 2013 21:34:09 +0200 |
parents | |
children | 5e717aac4c1d |
comparison
equal
deleted
inserted
replaced
54:ba4e2cb031e0 | 55:ec222fb577dd |
---|---|
1 using System; | |
2 using System.ComponentModel; | |
3 using System.IO; | |
4 using System.Runtime.InteropServices; | |
5 using System.Threading; | |
6 using Microsoft.Win32.SafeHandles; | |
7 using UCIS.Util; | |
8 | |
9 namespace UCIS.Windows { | |
10 public class WindowsNamedPipe : PacketStream { | |
11 delegate Byte[] ReadPacketDelegate(); | |
12 ReadPacketDelegate ReadPacketDelegateInstance = null; | |
13 SafeFileHandle PipeHandle; | |
14 | |
15 [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] | |
16 static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, IntPtr lpSecurityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile); | |
17 [DllImport("kernel32.dll", SetLastError = true)] | |
18 static extern unsafe bool ReadFile(SafeFileHandle hFile, Byte* lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, NativeOverlapped* lpOverlapped); | |
19 [DllImport("kernel32.dll", SetLastError = true)] | |
20 static extern unsafe bool WriteFile(SafeFileHandle hFile, Byte* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, NativeOverlapped* lpOverlapped); | |
21 [DllImport("kernel32.dll", SetLastError = true)] | |
22 static extern unsafe bool GetOverlappedResult(SafeFileHandle hFile, NativeOverlapped* lpOverlapped, out uint lpNumberOfBytesTransferred, Boolean bWait); | |
23 [DllImport("kernel32.dll", SetLastError = true)] | |
24 static extern bool SetNamedPipeHandleState(SafeFileHandle hNamedPipe, ref UInt32 lpMode, IntPtr lpMaxCollectionCount, IntPtr lpCollectDataTimeout); | |
25 [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] | |
26 static extern SafeFileHandle CreateNamedPipe([MarshalAs(UnmanagedType.LPStr)] String lpName, UInt32 dwOpenMode, UInt32 dwPipeMode, UInt32 nMaxInstances, UInt32 nOutBufferSize, UInt32 nInBufferSize, UInt32 nDefaultTimeOut, IntPtr lpSecurityAttributes); | |
27 [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] | |
28 static extern unsafe bool ConnectNamedPipe(SafeFileHandle hNamedPipe, NativeOverlapped* lpOverlapped); | |
29 [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] | |
30 static extern unsafe bool GetNamedPipeInfo(SafeFileHandle hNamedPipe, out UInt32 lpFlags, IntPtr lpOutBufferSize, IntPtr lpInBufferSize, IntPtr lpMaxInstances); | |
31 | |
32 const UInt32 PIPE_ACCESS_DUPLEX = 0x00000003; | |
33 const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000; | |
34 const UInt32 PIPE_TYPE_BYTE = 0x00000000; | |
35 const UInt32 PIPE_TYPE_MESSAGE = 0x00000004; | |
36 const UInt32 PIPE_READMODE_BYTE = 0x00000000; | |
37 const UInt32 PIPE_READMODE_MESSAGE = 0x00000002; | |
38 | |
39 public int InitialMessageBufferSize { get; set; } | |
40 | |
41 private WindowsNamedPipe(SafeFileHandle handle) { | |
42 InitialMessageBufferSize = 1024; | |
43 this.PipeHandle = handle; | |
44 } | |
45 public static WindowsNamedPipe Create(String name, Boolean messageMode, uint maxClients, uint readBuffer, uint writeBuffer, uint defaultTimeout) { | |
46 SafeFileHandle handle = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, | |
47 messageMode ? (PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE) : (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE), | |
48 maxClients, writeBuffer, readBuffer, defaultTimeout, IntPtr.Zero); | |
49 if (handle.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error()); | |
50 return new WindowsNamedPipe(handle); | |
51 } | |
52 public static WindowsNamedPipe Connect(String name) { | |
53 SafeFileHandle handle = CreateFile(name, 0x40000000 | 0x80000000, FileShare.None, IntPtr.Zero, FileMode.Open, 0x40000000, IntPtr.Zero); | |
54 if (handle.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error()); | |
55 UInt32 flags; | |
56 if (!GetNamedPipeInfo(handle, out flags, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); | |
57 UInt32 lpMode = (flags & PIPE_TYPE_MESSAGE) != 0 ? PIPE_READMODE_MESSAGE : PIPE_READMODE_BYTE; | |
58 if (!SetNamedPipeHandleState(handle, ref lpMode, IntPtr.Zero, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); | |
59 return new WindowsNamedPipe(handle); | |
60 } | |
61 | |
62 public unsafe void WaitForClient() { | |
63 uint nread; | |
64 using (ManualResetEvent evt = new ManualResetEvent(false)) { | |
65 NativeOverlapped overlapped = new NativeOverlapped(); | |
66 overlapped.EventHandle = evt.SafeWaitHandle.DangerousGetHandle(); | |
67 if (!ConnectNamedPipe(PipeHandle, &overlapped)) { | |
68 int err = Marshal.GetLastWin32Error(); | |
69 if (err != 997) throw new Win32Exception(err); | |
70 evt.WaitOne(); | |
71 if (!GetOverlappedResult(PipeHandle, &overlapped, out nread, false)) throw new Win32Exception(Marshal.GetLastWin32Error()); | |
72 } | |
73 } | |
74 } | |
75 | |
76 private unsafe int ReadInternal(Byte[] buffer, int offset, int count, out int error) { | |
77 if (offset < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("offset", "Specified buffer is outside of array bounds"); | |
78 uint nread; | |
79 using (ManualResetEvent evt = new ManualResetEvent(false)) { | |
80 NativeOverlapped overlapped = new NativeOverlapped(); | |
81 overlapped.EventHandle = evt.SafeWaitHandle.DangerousGetHandle(); | |
82 fixed (Byte* bufferptr = buffer) { | |
83 if (ReadFile(PipeHandle, bufferptr + offset, (uint)count, out nread, &overlapped)) { | |
84 error = 0; | |
85 } else { | |
86 error = Marshal.GetLastWin32Error(); | |
87 if (error == 997) { | |
88 evt.WaitOne(); | |
89 if (GetOverlappedResult(PipeHandle, &overlapped, out nread, false)) { | |
90 error = 0; | |
91 } else { | |
92 error = Marshal.GetLastWin32Error(); | |
93 } | |
94 } | |
95 } | |
96 return (int)nread; | |
97 } | |
98 } | |
99 } | |
100 public override unsafe int Read(byte[] buffer, int offset, int count) { | |
101 int error; | |
102 int nread = ReadInternal(buffer, offset, count, out error); | |
103 if (error == 109) return 0; | |
104 if (error == 0) return nread; | |
105 throw new Win32Exception(error); | |
106 } | |
107 public override unsafe void Write(byte[] buffer, int offset, int count) { | |
108 if (offset < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("offset", "Specified buffer is outside of array bounds"); | |
109 uint nread; | |
110 using (ManualResetEvent evt = new ManualResetEvent(false)) { | |
111 NativeOverlapped overlapped = new NativeOverlapped(); | |
112 overlapped.EventHandle = evt.SafeWaitHandle.DangerousGetHandle(); | |
113 fixed (Byte* bufferptr = buffer) { | |
114 if (!WriteFile(PipeHandle, bufferptr + offset, (uint)count, out nread, &overlapped)) { | |
115 int err = Marshal.GetLastWin32Error(); | |
116 if (err != 997) throw new Win32Exception(err); | |
117 evt.WaitOne(); | |
118 if (!GetOverlappedResult(PipeHandle, &overlapped, out nread, false)) throw new Win32Exception(Marshal.GetLastWin32Error()); | |
119 } | |
120 } | |
121 } | |
122 if (nread != count) throw new IOException("Not all data could be written"); | |
123 } | |
124 public override void Close() { | |
125 base.Close(); | |
126 PipeHandle.Close(); | |
127 } | |
128 | |
129 public unsafe override byte[] ReadPacket() { | |
130 int offset = 0; | |
131 Byte[] buffer = new Byte[InitialMessageBufferSize]; | |
132 while (true) { | |
133 int error; | |
134 int nread = ReadInternal(buffer, offset, buffer.Length - offset, out error); | |
135 offset += nread; | |
136 if (error == 109) { | |
137 return null; | |
138 } else if (error == 0) { | |
139 if (offset != buffer.Length) Array.Resize(ref buffer, offset); | |
140 return buffer; | |
141 } else if (error == 234) { | |
142 offset = buffer.Length; | |
143 Array.Resize(ref buffer, buffer.Length * 2); | |
144 } else { | |
145 throw new Win32Exception(error); | |
146 } | |
147 } | |
148 } | |
149 public override IAsyncResult BeginReadPacket(AsyncCallback callback, object state) { | |
150 if (ReadPacketDelegateInstance == null) ReadPacketDelegateInstance = (ReadPacketDelegate)ReadPacket; | |
151 return ReadPacketDelegateInstance.BeginInvoke(callback, state); | |
152 } | |
153 public override byte[] EndReadPacket(IAsyncResult asyncResult) { | |
154 return ReadPacketDelegateInstance.EndInvoke(asyncResult); | |
155 } | |
156 | |
157 public override bool CanRead { | |
158 get { return !PipeHandle.IsClosed; } | |
159 } | |
160 public override bool CanWrite { | |
161 get { return !PipeHandle.IsClosed; } | |
162 } | |
163 | |
164 public override void Flush() { } | |
165 public override bool CanSeek { get { return false; } } | |
166 | |
167 public override long Length { get { throw new NotSupportedException(); } } | |
168 public override long Position { | |
169 get { throw new NotSupportedException(); } | |
170 set { throw new NotSupportedException(); } | |
171 } | |
172 | |
173 public override long Seek(long offset, System.IO.SeekOrigin origin) { throw new NotSupportedException(); } | |
174 | |
175 public override void SetLength(long value) { throw new NotSupportedException(); } | |
176 | |
177 } | |
178 } |