// ByteFX.Data data access components for .Net // Copyright (C) 2002-2003 ByteFX, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.IO; using System.Runtime.InteropServices; namespace ByteFX.Data.Common { /// /// Summary description for API. /// internal class NamedPipeStream : Stream { [DllImport("kernel32.dll", EntryPoint="CreateFile", SetLastError=true)] private static extern IntPtr CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32.dll", EntryPoint="PeekNamedPipe", SetLastError=true)] private static extern bool PeekNamedPipe( IntPtr handle, byte[] buffer, uint nBufferSize, ref uint bytesRead, ref uint bytesAvail, ref uint BytesLeftThisMessage); [DllImport("kernel32.dll", SetLastError=true)] private static extern bool ReadFile( IntPtr handle, byte[] buffer, uint toRead, ref uint read, IntPtr lpOverLapped); [DllImport("kernel32.dll", SetLastError=true)] private static extern bool WriteFile( IntPtr handle, byte[] buffer, uint count, ref uint written, IntPtr lpOverlapped ); [DllImport("kernel32.dll", SetLastError=true)] private static extern bool CloseHandle( IntPtr handle ); [DllImport("kernel32.dll", SetLastError=true)] private static extern bool FlushFileBuffers( IntPtr handle ); //Constants for dwDesiredAccess: private const UInt32 GENERIC_READ = 0x80000000; private const UInt32 GENERIC_WRITE = 0x40000000; //Constants for return value: private const Int32 INVALID_HANDLE_VALUE = -1; //Constants for dwFlagsAndAttributes: private const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000; private const UInt32 FILE_FLAG_NO_BUFFERING = 0x20000000; //Constants for dwCreationDisposition: private const UInt32 OPEN_EXISTING = 3; IntPtr _handle; FileAccess _mode; public NamedPipeStream(string host, FileAccess mode) { _handle = IntPtr.Zero; Open(host, mode); } public void Open( string host, FileAccess mode ) { _mode = mode; uint pipemode = 0; if ((mode & FileAccess.Read) > 0) pipemode |= GENERIC_READ; if ((mode & FileAccess.Write) > 0) pipemode |= GENERIC_WRITE; _handle = CreateFile( host, pipemode, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero ); } public bool DataAvailable { get { uint bytesRead=0, avail=0, thismsg=0; bool result = PeekNamedPipe( _handle, null, 0, ref bytesRead, ref avail, ref thismsg ); return (result == true && avail > 0); } } public override bool CanRead { get { return (_mode & FileAccess.Read) > 0; } } public override bool CanWrite { get { return (_mode & FileAccess.Write) > 0; } } public override bool CanSeek { get { throw new NotSupportedException("NamedPipeStream does not support seeking"); } } public override long Length { get { throw new NotSupportedException("NamedPipeStream does not support seeking"); } } public override long Position { get { throw new NotSupportedException("NamedPipeStream does not support seeking"); } set { } } public override void Flush() { if (_handle == IntPtr.Zero) throw new ObjectDisposedException("NamedPipeStream", "The stream has already been closed"); FlushFileBuffers(_handle); } public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) throw new ArgumentNullException("buffer", "The buffer to read into cannot be null"); if (buffer.Length < (offset + count)) throw new ArgumentException("Buffer is not large enough to hold requested data", "buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset", offset, "Offset cannot be negative"); if (count < 0) throw new ArgumentOutOfRangeException("count", count, "Count cannot be negative"); if (! CanRead) throw new NotSupportedException("The stream does not support reading"); if (_handle == IntPtr.Zero) throw new ObjectDisposedException("NamedPipeStream", "The stream has already been closed"); // first read the data into an internal buffer since ReadFile cannot read into a buf at // a specified offset uint read=0; byte[] buf = new Byte[count]; ReadFile( _handle, buf, (uint)count, ref read, IntPtr.Zero ); for (int x=0; x < read; x++) { buffer[offset+x] = buf[x]; } return (int)read; } public override void Close() { CloseHandle(_handle); _handle = IntPtr.Zero; } public override void SetLength(long length) { throw new NotSupportedException("NamedPipeStream doesn't support SetLength"); } public override void Write(byte[] buffer, int offset, int count) { if (buffer == null) throw new ArgumentNullException("buffer", "The buffer to write into cannot be null"); if (buffer.Length < (offset + count)) throw new ArgumentException("Buffer does not contain amount of requested data", "buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset", offset, "Offset cannot be negative"); if (count < 0) throw new ArgumentOutOfRangeException("count", count, "Count cannot be negative"); if (! CanWrite) throw new NotSupportedException("The stream does not support writing"); if (_handle == IntPtr.Zero) throw new ObjectDisposedException("NamedPipeStream", "The stream has already been closed"); // copy data to internal buffer to allow writing from a specified offset byte[] buf = new Byte[count]; for (int x=0; x < count; x++) { buf[x] = buffer[offset+x]; } uint written=0; bool result = WriteFile( _handle, buf, (uint)count, ref written, IntPtr.Zero ); if (! result) throw new IOException("Writing to the stream failed"); if (written < count) throw new IOException("Unable to write entire buffer to stream"); } public override long Seek( long offset, SeekOrigin origin ) { throw new NotSupportedException("NamedPipeStream doesn't support seeking"); } } }