I'd originally refrained from doing it this way, using pinvoke instead to keep .net
2.0 compatibility.
Using classes such as NamedPipeServerStream forces a .net 3.5 dependency. Is this ok with
everyone?
Ged.
-----Original Message-----
From: ros-diffs-bounces(a)reactos.org [mailto:ros-diffs-bounces@reactos.org] On Behalf Of
gschneider(a)svn.reactos.org
Sent: 12 October 2008 22:56
To: ros-diffs(a)reactos.org
Subject: [ros-diffs] [gschneider] 36737: 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
.n
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.c…
==============================================================================
--- 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()));
}