Author: gschneider Date: Sun Oct 12 16:55:50 2008 New Revision: 36737
URL: http://svn.reactos.org/svn/reactos?rev=36737&view=rev Log: RosDbg Part 3/3: - Named pipe implementation based on .net namespace IO.Pipes with support of threads - Previous win32 test version would strip random characters from debug messages and crash on entering kdbg (related to .net ReadFile approach) - Works with QEMU (Client mode), VirtualBox (Client and Server), VMware Server (Client and Server) - Usually faster than pure serial connection mode
Known bugs: - Input to kdbg using /KDSERIAL is not displayed until enter pressed (but input works) - Reconnect issues, depending on virtual machine behaviour
Modified: trunk/tools/reactosdbg/Pipe/namedpipe.cs trunk/tools/reactosdbg/Pipe/serialpipe.cs
Modified: trunk/tools/reactosdbg/Pipe/namedpipe.cs URL: http://svn.reactos.org/svn/reactos/trunk/tools/reactosdbg/Pipe/namedpipe.cs?... ============================================================================== --- trunk/tools/reactosdbg/Pipe/namedpipe.cs [iso-8859-1] (original) +++ trunk/tools/reactosdbg/Pipe/namedpipe.cs [iso-8859-1] Sun Oct 12 16:55:50 2008 @@ -1,227 +1,205 @@ using System; +using System.Collections.Generic; +using System.ComponentModel; using System.IO; -using System.ComponentModel; +using System.IO.Pipes; using System.Runtime.InteropServices; - +using System.Text; +using System.Threading;
namespace AbstractPipe { - class Kernel32 + public enum ConnectionMode { - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr CreateNamedPipe( - String lpName, - OpenMode dwOpenMode, - uint dwPipeMode, - uint nMaxInstances, - uint nOutBufferSize, - uint nInBufferSize, - DefaultTimeout nDefaultTimeOut, - IntPtr pipeSecurityDescriptor - ); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ConnectNamedPipe( - IntPtr hHandle, - IntPtr lpOverlapped - ); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ReadFile( - IntPtr hHandle, - byte[] lpBuffer, - uint nNumberOfBytesToRead, - ref uint lpNumberOfBytesRead, - IntPtr lpOverlapped - ); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool WriteFile( - IntPtr hHandle, - byte[] lpBuffer, - uint nNumberOfBytesToWrite, - ref uint lpNumberOfBytesWritten, - IntPtr lpOverlapped - ); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool CloseHandle( - IntPtr hHandle); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern uint GetLastError(); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool DisconnectNamedPipe( - IntPtr hHandle); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool FlushFileBuffers(IntPtr handle); + MODE_CLIENT = 0x00000000, + MODE_SERVER = 0x00000001, + MODE_AUTO = 0x00000002 }
- [StructLayout(LayoutKind.Sequential)] - public class Overlapped + public class NamedPipe : Pipe { - } - - public enum OpenMode : uint - { - PIPE_ACCESS_INBOUND = 0x00000001, - PIPE_ACCESS_OUTBOUND = 0x00000002, - PIPE_ACCESS_DUPLEX = 0x00000003, - FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000, - FILE_FLAG_OVERLAPPED = 0x40000000, - FILE_FLAG_WRITE_THROUGH = 0x80000000 - } - - public enum DefaultTimeout : uint - { - NMPWAIT_USE_DEFAULT_WAIT = 0x00000000, - NMPWAIT_NOWAIT = 0x00000001, - NMPWAIT_WAIT_FOREVER = 0xffffffff - } - - [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_ATTRIBUTES - { - public int nLength; - public IntPtr lpSecurityDescriptor; - public int bInheritHandle; - } - - public class NamedPipe// : Pipe - { - private const Int32 INVALID_HANDLE_VALUE = -1; - private const ulong ERROR_PIPE_CONNECTED = 535; - - private IntPtr handle; - //FileAccess mode; + public const int PIPE_SIZE = 1024; + + private PipeStream ioStream; /* stream for io operations */ + private String wCommand; /* buffer of a single command line */ + private List<String> cmdList; /*list of commands pending to be written */ + + public event PipeReceiveEventHandler PipeReceiveEvent; + public event PipeErrorEventHandler PipeErrorEvent; + + private static ManualResetEvent newWriteData = new ManualResetEvent(false);
public NamedPipe() { - handle = IntPtr.Zero; - } - - public IntPtr Create(string name) - { - handle = Kernel32.CreateNamedPipe(name, - OpenMode.PIPE_ACCESS_DUPLEX | OpenMode.FILE_FLAG_OVERLAPPED, - 0, - 1, - 100, - 100, - DefaultTimeout.NMPWAIT_WAIT_FOREVER, - IntPtr.Zero); - if (handle.ToInt32() == INVALID_HANDLE_VALUE) - { - throw new Win32Exception("Error creating named pipe " + name + " . Internal error: " + Marshal.GetLastWin32Error().ToString()); - } - - return handle; - } - - public void Disconnect() - { - Kernel32.DisconnectNamedPipe(handle); + cmdList = new List<string>(); + } + + public bool CreateServerPipe(string name) + { + /* create a pipe and wait for a client */ + NamedPipeServerStream sStream = new NamedPipeServerStream(name, PipeDirection.InOut, 1, + PipeTransmissionMode.Byte, PipeOptions.Asynchronous, PIPE_SIZE, PIPE_SIZE); + sStream.WaitForConnection(); + + if (sStream.IsConnected) + { + ioStream = sStream; + return true; + } + else + { + return false; + } + } + + public bool CreateClientPipe(string name) + { + /* try to connect as a client */ + /* (QEMU -serial pipe or VMware in pipe server mode) */ + try + { + NamedPipeClientStream cStream = new NamedPipeClientStream(".", name, PipeDirection.InOut, PipeOptions.Asynchronous); + cStream.Connect(100); + + if (cStream.IsConnected) + { + ioStream = cStream; + return true; + } + else + { + return false; + } + } + catch (Exception) + { + return false; + } + } + + public bool CreatePipe(string name, ConnectionMode mode) + { + if (name == "" || name == null) + { + return false; + } + switch (mode) + { + case ConnectionMode.MODE_AUTO: + //check if pipe exists, if not create server pipe, wait certain time, check if pipe... + //TODO: server-client lookup should time out + while (true) + { + if (CreateClientPipe(name)) + { + break; + } + if (CreateServerPipe(name)) + { + break; + } + } + return true; + + case ConnectionMode.MODE_CLIENT: + while (!CreateClientPipe(name)) ; + + /* pipe open, everything fine */ + return true; + + case ConnectionMode.MODE_SERVER: + if (CreateServerPipe(name)) + { + /* wait for a client*/ + + return true; + } + break; + } + return false; }
public void Close() { - Kernel32.CloseHandle(handle); - handle = IntPtr.Zero; - } - - public void Flush() - { - if (handle == IntPtr.Zero) - throw new ObjectDisposedException("NamedPipeStream", "The stream has already been closed"); - Kernel32.FlushFileBuffers(handle); - } - - public bool Listen() - { - Disconnect(); - if (Kernel32.ConnectNamedPipe(handle, IntPtr.Zero) != true) - { - uint lastErr = (uint)Marshal.GetLastWin32Error(); - if (lastErr == ERROR_PIPE_CONNECTED) - return true; - return false; + if (ioStream != null) + ioStream.Close(); + } + + public void WriteLoop() + { + try + { + while (true) + { + if (cmdList.Count > 0) + { + byte[] wBuf = new byte[cmdList[0].Length]; + UTF8Encoding.UTF8.GetBytes(cmdList[0], 0, cmdList[0].Length, wBuf, 0); + + ioStream.Write(wBuf, 0, cmdList[0].Length); + + /* remove written data from commandlist */ + cmdList.RemoveAt(0); + } + else if (cmdList.Count == 0) + { + /* wait until new data is signaled */ + newWriteData.Reset(); + newWriteData.WaitOne(); + } + } + } + catch (Exception e) + { + if (PipeErrorEvent != null) + PipeErrorEvent.Invoke(this, new PipeErrorEventArgs(e.Message)); + } + + } + + public void ReadLoop() + { + byte[] buf = new byte[PIPE_SIZE]; + int read = 0; + + try + { + while(true) + { + read = ioStream.Read(buf, 0, PIPE_SIZE); + if (read > 0) + { + if (PipeReceiveEvent != null) + PipeReceiveEvent.Invoke(this, new PipeReceiveEventArgs(UTF8Encoding.UTF8.GetString(buf, 0, read))); + } + else + { + /* connecion closed */ + break; + } + } + } + catch (Exception e) + { + if (PipeErrorEvent != null) + PipeErrorEvent.Invoke(this, new PipeErrorEventArgs(e.Message)); + } + } + + public bool Write(string str) + { + /* only forward a complete line */ + wCommand += str; + + if (str[str.Length-1] == '\r') + { + cmdList.Add(wCommand); + wCommand = null; + + /* wake up the write thread */ + newWriteData.Set(); } return true; - } - - public 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 (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 = buffer; - if (offset != 0) - { - buf = new byte[count]; - } - bool f = Kernel32.ReadFile(handle, buf, (uint)count, ref read, IntPtr.Zero); - if (!f) - { - throw new Win32Exception(Marshal.GetLastWin32Error(), "ReadFile failed"); - } - if (offset != 0) - { - for (int x = 0; x < read; x++) - { - buffer[offset + x] = buf[x]; - } - - } - return (int)read; - } - - public 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 (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 - if (offset != 0) - { - byte[] buf = new Byte[count]; - for (int x = 0; x < count; x++) - { - buf[x] = buffer[offset + x]; - } - buffer = buf; - } - uint written = 0; - bool result = Kernel32.WriteFile(handle, buffer, (uint)count, ref written, IntPtr.Zero); - - if (!result) - { - int err = (int)Marshal.GetLastWin32Error(); - throw new Win32Exception(err, "Writing to the stream failed"); - } - if (written < count) - throw new IOException("Unable to write entire buffer to stream"); } } }
Modified: trunk/tools/reactosdbg/Pipe/serialpipe.cs URL: http://svn.reactos.org/svn/reactos/trunk/tools/reactosdbg/Pipe/serialpipe.cs... ============================================================================== --- trunk/tools/reactosdbg/Pipe/serialpipe.cs [iso-8859-1] (original) +++ trunk/tools/reactosdbg/Pipe/serialpipe.cs [iso-8859-1] Sun Oct 12 16:55:50 2008 @@ -80,7 +80,7 @@
public void SerialPortErrorReceived(object sender, SerialErrorReceivedEventArgs args) { - mSerialPort.Close(); + /* port will be closed by debug connection */ if (PipeErrorEvent != null) PipeErrorEvent.Invoke(this, new PipeErrorEventArgs(args.EventType.ToString())); }