protected Ipc.ResponseCode SendReceive(Ipc.Command commandId, byte[] data, int dataLength, out byte[] response)
        {
            CheckConnected();
            ushort messageId = _nextMessageId;

            _nextMessageId = (ushort)(_nextMessageId == UInt16.MaxValue ? 1 : _nextMessageId + 1);

            BitConverter.GetBytes((ushort)commandId).CopyTo(data, 0);
            BitConverter.GetBytes(messageId).CopyTo(data, 2);
            BitConverter.GetBytes((ushort)(dataLength - 6)).CopyTo(data, 4);

            _pipe.Write(data, 0, dataLength);
            _pipe.Flush();
            if (((ushort)commandId & Ipc.NO_RESPONSE_COMMAND) != 0)
            {
                response = null;
                return(Ipc.ResponseCode.Ok);
            }
            var header = new byte[6];
            var tEnd   = DateTime.Now.AddMilliseconds(ResponseTimeout);

            while (DateTime.Now <= tEnd)
            {
                // read 6 byte header
                int cnt = 0;
                while (cnt < header.Length)
                {
                    var       readDoneEvent = new ManualResetEvent(false);
                    int       newCnt        = 0;
                    Exception endReadEx     = null;
                    _pipe.BeginRead(header, cnt, header.Length - cnt, ar =>
                    {
                        try
                        {
                            newCnt = _pipe.EndRead(ar);
                        }
                        catch (Exception ex)
                        {
                            endReadEx = ex;
                        }
                        readDoneEvent.Set();
                    }, null);
                    if (!readDoneEvent.WaitOne(ResponseTimeout))
                    {
                        Disconnect();
                        Connect();
                        throw new TimeoutException(cnt == 0
               ? String.Format("No response for IPC message from {0} for command {1}", AppName, commandId)
               : String.Format("Timeout for IPC message from {0} for command {1}", AppName, commandId));
                    }
                    if (endReadEx != null)
                    {
                        throw new IpcException(endReadEx.Message, endReadEx);
                    }
                    if (newCnt == 0) // end of stream
                    {
                        throw new IpcException("Connection closed unexpected");
                    }
                    cnt += newCnt;
                }
                Ipc.ResponseCode responseCode      = (Ipc.ResponseCode)BitConverter.ToUInt16(header, 0);
                ushort           responseMessageId = BitConverter.ToUInt16(header, 2); // for future use to allow parallel messages from a single client
                ushort           responseLength    = BitConverter.ToUInt16(header, 4);

                response = new byte[responseLength];
                if (responseLength > 0)
                {
                    cnt = 0;
                    while (cnt < response.Length)
                    {
                        var       readDoneEvent = new ManualResetEvent(false);
                        int       newCnt        = 0;
                        Exception endReadEx     = null;
                        _pipe.BeginRead(response, cnt, response.Length - cnt, ar =>
                        {
                            try
                            {
                                newCnt = _pipe.EndRead(ar);
                            }
                            catch (Exception ex)
                            {
                                endReadEx = ex;
                            }
                            readDoneEvent.Set();
                        }, null);
                        if (!readDoneEvent.WaitOne(ResponseTimeout))
                        {
                            Disconnect();
                            Connect();
                            throw new TimeoutException(String.Format("Timeout for IPC message from {0} for command {1}", AppName, commandId));
                        }
                        if (endReadEx != null)
                        {
                            throw new IpcException(endReadEx.Message, endReadEx);
                        }
                        if (newCnt == 0) // end of stream
                        {
                            throw new IpcException("Connection closed unexpected");
                        }
                        cnt += newCnt;
                    }
                }
                if (responseMessageId == messageId)
                {
                    if (responseCode == Ipc.ResponseCode.ServerException)
                    {
                        int offset   = 0;
                        var message  = Ipc.BytesToString(response, ref offset);
                        var typeName = Ipc.BytesToString(response, ref offset);
                        throw new IpcException(String.Format("A IPC server exception of type {0} was thrown:\n{1}", typeName, message));
                    }
                    return(responseCode);
                }
            }
            throw new TimeoutException(String.Format("No response for IPC message from {0} for command {1}", AppName, commandId));
        }
        private void WaitForConnectionCallback(IAsyncResult ar)
        {
            try
            {
                // finish async wait
                var pipe = (NamedPipeServerStream)ar.AsyncState;
                pipe.EndWaitForConnection(ar);

                // start another pipe for the next client
                OpenNewPipe();

                try
                {
                    var header = new byte[6];
                    while (true)
                    {
                        // read 6 byte header
                        int cnt = 0;
                        while (cnt < header.Length)
                        {
                            int newCnt = pipe.Read(header, cnt, header.Length - cnt);
                            if (newCnt == 0) // end of stream
                            {
                                return;
                            }
                            cnt += newCnt;
                        }
                        Ipc.Command commandId  = (Ipc.Command)BitConverter.ToUInt16(header, 0);
                        ushort      messageId  = BitConverter.ToUInt16(header, 2); // for future use to allow parallel messages from a single client
                        ushort      dataLength = BitConverter.ToUInt16(header, 4);

                        var data = new byte[dataLength];
                        if (dataLength > 0)
                        {
                            cnt = 0;
                            while (cnt < data.Length)
                            {
                                int newCnt = pipe.Read(data, cnt, data.Length - cnt);
                                if (newCnt == 0) // end of stream
                                {
                                    return;
                                }
                                cnt += newCnt;
                            }
                        }

                        var response       = new byte[1024];
                        int responseOffset = 6;
                        Ipc.ResponseCode responseCode;
                        try
                        {
                            responseCode = ProcessCommand(commandId, data, 0, dataLength, ref response, ref responseOffset);
                        }
                        catch (Exception ex)
                        {
                            responseCode   = Ipc.ResponseCode.ServerException;
                            responseOffset = 6;
                            Ipc.StringToBytes(ex.Message, response, ref responseOffset);
                            Ipc.StringToBytes(ex.GetType().FullName, response, ref responseOffset);
                        }
                        if (((ushort)commandId & Ipc.NO_RESPONSE_COMMAND) == 0)
                        {
                            // send response
                            BitConverter.GetBytes((ushort)responseCode).CopyTo(response, 0);
                            BitConverter.GetBytes(messageId).CopyTo(response, 2);
                            BitConverter.GetBytes((ushort)(responseOffset - 6)).CopyTo(response, 4);
                            pipe.Write(response, 0, responseOffset);
                        }
                    }
                }
                finally
                {
                    lock (_serverPipes)
                    {
                        _serverPipes.Remove(pipe);
                        pipe.Close();
                    }
                }
            }
            catch (Exception)
            {
            }
        }