C#命名管道而不从控制台发出命令? [英] C# Named Pipes without issuing commands from the Console?

查看:604
本文介绍了C#命名管道而不从控制台发出命令?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用命名管道与进程通信。我已经能够使它与下面的代码工作。 (原始代码位于此处: http://www.dijksterhuis.org/ using-named-pipes-in-c-windows /

  class ProgramPipeTest 
{

public void ThreadSenderStartClient(object obj)
{
//确保我们只在服务器创建管道后启动客户端
ManualResetEvent SyncClientServer =(ManualResetEvent)obj;

使用(NamedPipeClientStream pipeStream = new NamedPipeClientStream(。,ToSrvPipe,PipeDirection.Out,PipeOptions.None))
{
//连接函数将无限期等待为管道成为可用
//如果不可接受指定最大等待时间(以毫秒为单位)
pipeStream.Connect();

Console.WriteLine([Client]管道连接已建立);
using(StreamWriter sw = new StreamWriter(pipeStream))
{
sw.AutoFlush = true;
string temp;
Console.WriteLine(请键入一个消息,然后按[Enter],或键入'quit'退出程序);
while((temp = Console.ReadLine())!= null)
{
if(temp ==quit)break;
sw.WriteLine(temp);
}
}
}
}

public void ThreadStartReceiverClient(object obj)
{
//在服务器创建管道后启动客户端
ManualResetEvent SyncClientServer =(ManualResetEvent)obj;

使用(NamedPipeClientStream pipeStream = new NamedPipeClientStream(。,FromSrvPipe,PipeDirection.In,PipeOptions.None))
{
// connect函数将无限期等待为管道成为可用
//如果不可接受指定最大等待时间(以毫秒为单位)
pipeStream.Connect();

Console.WriteLine([ClientReceiver]管道连接已建立);

使用(StreamReader sr = new StreamReader(pipeStream))
{
//显示读取的文本到控制台
string temp;
while((temp = sr.ReadLine())!= null)
{
Console.WriteLine(Received from server:{0},temp);
}
}
}
}

static void Main(string [] args)
{

//为了简化调试,我们将创建一个进程,并有两个任务
//彼此交谈。 (这有点像我给我的同事发送电子邮件)

ProgramPipeTest Client = new ProgramPipeTest();

Thread ClientThread = new Thread(Client.ThreadSenderStartClient);
Thread ReceivedThread = new Thread(Client.ThreadStartReceiverClient);

ClientThread.Start();
ReceivedThread.Start();


}
}

意图。我能够发出命令到我的目标进程(大胆)。



我的问题是,我基本上想要围绕这个代码包装一个C#的GUI,但不知道如何修改它,使通信完成,而不必使用控制台,因为命令将通过GUI或代码发出。



我试着将streamWriter sw转换为类变量,通过属性暴露它并调用sw。 WriteLine()with一个方法,但是这似乎不工作。



所以我不确定如何封装流在一个对象内来回良好。 p>

我发现这篇文章似乎是当地的,使用命名管道将GUI连接到Windows中的控制台应用程序,但不幸的是,它似乎没有任何代码,是一种在我的头,没有任何参考。



那么如何使用命名管道而不必使用控制台来发出命令呢?

解决方案

你想做的是获取作为发送者的逻辑的主要部分,接收者代码并将其重写为可重用的类,可以像特定目的的包装类一样使用。



也许下面的代码可以作为一个指南(我没有检查,看看这是否工作,它可能需要小的更改)

  public sealed class ResponseReceivedEventArgs:EventArgs 
{
public ResponseReceivedEventArgs(string id,string response)
{
Id = id;
Response = response;
}

public string Id
{
private set;
get;
}

public string响应
{
private set;
get;
}
}

public delegate void ResponseReceived(object sender,ResponseReceivedEventArgs e);

public sealed class NamedPipeCommands
{
private readonly Queue< Tuple< string,string>> _queuedCommands = new Queue< Tuple< string,string>>();
private string _currentId;

private readonly Thread _sender;
private readonly Thread _receiver;

//相当于在控制台上接收quit
private bool _cancelRequested;

//等待收到请求的响应,然后继续
private readonly AutoResetEvent _waitForResponse = new AutoResetEvent(false);

//锁定以安全地修改命令队列
private readonly object _commandQueueLock = new object();

//收到响应时产生一个事件
private void RaiseResponseReceived(string id,string message)
{
if(ResponseReceived!= null)
ResponseReceived(this,new ResponseReceivedEventArgs(id,message));
}

//向输出命令的队列添加命令
//返回入队命令的id
//因此用户可以将其与相应的响应
public string EnqueueCommand(string command)
{
var resultId = Guid.NewGuid()。ToString();
lock(_commandQueueLock)
{
_queuedCommands.Enqueue(Tuple.Create(resultId,command));
}
return resultId;
}

//构造函数。请传递两个管道需要的任何参数
//下面的列表可能不完整
public NamedPipeCommands(string servername,string pipeName)
{
_sender = new Thread(syncClientServer = >
{
//线程的主体
var waitForResponse =(AutoResetEvent)syncClientServer;

using(var pipeStream = new NamedPipeClientStream(servername,pipeName,PipeDirection。 Out,PipeOptions.None))
{
pipeStream.Connect();

using(var sw = new StreamWriter(pipeStream){AutoFlush = true})
//这样做直到Cancel()被调用
while(!_cancelRequested)
{
//没有命令?保持等待
//这是一个紧的循环, .Yield或某事?
if(_queuedCommands.Count == 0)
continue;

Tuple< string,string> _currentCommand = null;

//我们要修改命令队列,锁定它
lock(_commandQueueLock)
//检查是否有人窃取了我们的命令
/ / before we got here
if(_queuedCommands.Count> 0)
_currentCommand = _queuedCommands.Dequeue();

//是上面出队的命令吗?
if(_currentCommand!= null)
{
_currentId = _currentCommand.Item1;
sw.WriteLine(_currentCommand.Item2);

//等待对此命令的响应
waitForResponse.WaitOne();
}
}
}
});

_receiver = new Thread(syncClientServer =>
{
var waitForResponse =(AutoResetEvent)syncClientServer;

using(var pipeStream = new NamedPipeClientStream servername,pipeName,PipeDirection.In,PipeOptions.None))
{
pipeStream.Connect();

using(var sr = new StreamReader(pipeStream))
//这样做直到Cancel()被调用
//再次,这是一个紧的循环,可能是一个Thread.Yield或某事?
while(!_cancelRequested)
//如果有流中的任何内容
if(!sr.EndOfStream)
{
//读它
var response = sr.ReadLine();
//提高事件用于处理
//注意这个事件是从
//接收者线程中产生的,你不能访问UI
//你需要Control.BeginInvoke或者一些这样的
RaiseResponseReceived(_currentId,response);

//继续发送后续命令
waitForResponse.Set();
}
}
});
}

public void Start()
{
_sender.Start(_waitForResponse);
_receiver.Start(_waitForResponse);
}

public void Cancel()
{
_cancelRequested = true;
}

public event ResponseReceived ResponseReceived;
}



你可以看到我已经为Console.ReadLine创建了抽象队列)和Console.WriteLine(事件)。 quit也是一个布尔变量,由Cancel()方法设置。显然,这不是最好的/正确的做法 - 我只是告诉你一种方式将上面的命令式代码关联到一个可以重用的包装类。


I am using Named Pipes to communicate with a process. I have been able to make it work with the following code. (Original code found here : http://www.dijksterhuis.org/using-named-pipes-in-c-windows/ )

class ProgramPipeTest
    {

        public void ThreadSenderStartClient(object obj)
        {
            // Ensure that we only start the client after the server has created the pipe
            ManualResetEvent SyncClientServer = (ManualResetEvent)obj;

            using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".","ToSrvPipe",PipeDirection.Out,PipeOptions.None))
            {
                // The connect function will indefinately wait for the pipe to become available
                // If that is not acceptable specify a maximum waiting time (in ms)
                pipeStream.Connect();

                Console.WriteLine("[Client] Pipe connection established");
                using (StreamWriter sw = new StreamWriter(pipeStream))
                {
                    sw.AutoFlush = true;
                    string temp;
                    Console.WriteLine("Please type a message and press [Enter], or type 'quit' to exit the program");
                    while ((temp = Console.ReadLine()) != null)
                    {
                        if (temp == "quit") break;
                        sw.WriteLine(temp);
                    }
                }
            }
        }

        public void ThreadStartReceiverClient(object obj)
        {
            // Ensure that we only start the client after the server has created the pipe
            ManualResetEvent SyncClientServer = (ManualResetEvent)obj;

            using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "FromSrvPipe", PipeDirection.In, PipeOptions.None))
            {
                // The connect function will indefinately wait for the pipe to become available
                // If that is not acceptable specify a maximum waiting time (in ms)
                pipeStream.Connect();

                Console.WriteLine("[ClientReceiver] Pipe connection established");

                using (StreamReader sr = new StreamReader(pipeStream))
                {
                    // Display the read text to the console
                    string temp;
                    while ((temp = sr.ReadLine()) != null)
                    {
                        Console.WriteLine("Received from server: {0}", temp);
                    }
                }
            }
        }

        static void Main(string[] args)
        {

            // To simplify debugging we are going to create just one process, and have two tasks
            // talk to each other. (Which is a bit like me sending an e-mail to my co-workers)

            ProgramPipeTest Client = new ProgramPipeTest();

            Thread ClientThread = new Thread(Client.ThreadSenderStartClient);
            Thread ReceivedThread = new Thread(Client.ThreadStartReceiverClient);

            ClientThread.Start();
            ReceivedThread.Start();


        }
    }

Everything works as intended. I am able to issue commands to my target process (audacity).

My issue is, I basically want to wrap a C# GUI around this code, but am not sure how to modify it so that the communication is done without having to use the console, as commands would be issued via the GUI or from the code.

I have tried turning the streamWriter sw into a class variable, exposing it via property and calling sw.WriteLine() with a method, but that doesn't seem to work.

So I am unsure how to encapsulate the stream back and forth nicely within an object.

I found this article which seemed like it was spot on, Using Named Pipes to Connect a GUI to a Console App in Windows, but unfortunately it does not seem to come with any code and is kind of over my head without any to refer to.

So how can I use named pipes without having to use the console to issue the commands ?

解决方案

What you want to do is take the main pieces of logic which are the sender, the receiver out of that code and rewrite it into a re-usable class that can be used like a purpose-specific wrapper class.

Perhaps the code below could serve as a guideline (I have NOT checked to see if this works, it might require minor changes)

public sealed class ResponseReceivedEventArgs : EventArgs
{
    public ResponseReceivedEventArgs(string id, string response)
    {
        Id = id;
        Response = response;
    }

    public string Id
    {
        private set;
        get;
    }

    public string Response
    {
        private set;
        get;
    }
}

public delegate void ResponseReceived(object sender, ResponseReceivedEventArgs e);

public sealed class NamedPipeCommands
{
    private readonly Queue<Tuple<string, string>> _queuedCommands = new Queue<Tuple<string,string>>();
    private string _currentId;

    private readonly Thread _sender;
    private readonly Thread _receiver;

    // Equivalent to receiving a "quit" on the console
    private bool _cancelRequested; 

    // To wait till a response is received for a request and THEN proceed
    private readonly AutoResetEvent _waitForResponse = new AutoResetEvent(false);

    // Lock to modify the command queue safely
    private readonly object _commandQueueLock = new object();

    // Raise an event when a response is received
    private void RaiseResponseReceived(string id, string message)
    {
        if (ResponseReceived != null)
            ResponseReceived(this, new ResponseReceivedEventArgs(id, message));
    }

    // Add a command to queue of outgoing commands
    // Returns the id of the enqueued command
    // So the user can relate it with the corresponding response
    public string EnqueueCommand(string command)
    {
        var resultId = Guid.NewGuid().ToString();
        lock (_commandQueueLock)
        {
            _queuedCommands.Enqueue(Tuple.Create(resultId, command));
        }
        return resultId;
    }

    // Constructor. Please pass in whatever parameters the two pipes need
    // The list below may be incomplete
    public NamedPipeCommands(string servername, string pipeName)
    {
        _sender = new Thread(syncClientServer =>
        {
            // Body of thread
            var waitForResponse = (AutoResetEvent)syncClientServer;

            using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.Out, PipeOptions.None))
            {
                pipeStream.Connect();

                using (var sw = new StreamWriter(pipeStream) { AutoFlush = true })
                    // Do this till Cancel() is called
                    while (!_cancelRequested)
                    {
                        // No commands? Keep waiting
                        // This is a tight loop, perhaps a Thread.Yield or something?
                        if (_queuedCommands.Count == 0)
                            continue;

                        Tuple<string, string> _currentCommand = null;

                        // We're going to modify the command queue, lock it
                        lock (_commandQueueLock)
                            // Check to see if someone else stole our command
                            // before we got here
                            if (_queuedCommands.Count > 0)
                                _currentCommand = _queuedCommands.Dequeue();

                        // Was a command dequeued above?
                        if (_currentCommand != null)
                        {
                            _currentId = _currentCommand.Item1;
                            sw.WriteLine(_currentCommand.Item2);

                            // Wait for the response to this command
                            waitForResponse.WaitOne();
                        }
                    }
            }
        });

        _receiver = new Thread(syncClientServer =>
        {
            var waitForResponse = (AutoResetEvent)syncClientServer;

            using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.In, PipeOptions.None))
            {
                pipeStream.Connect();

                using (var sr = new StreamReader(pipeStream))
                    // Do this till Cancel() is called
                    // Again, this is a tight loop, perhaps a Thread.Yield or something?
                    while (!_cancelRequested)
                        // If there's anything in the stream
                        if (!sr.EndOfStream)
                        {
                            // Read it
                            var response = sr.ReadLine();
                            // Raise the event for processing
                            // Note that this event is being raised from the
                            // receiver thread and you can't access UI here
                            // You will need to Control.BeginInvoke or some such
                            RaiseResponseReceived(_currentId, response);

                            // Proceed with sending subsequent commands
                            waitForResponse.Set();
                        }
            }
        });
    }

    public void Start()
    {
        _sender.Start(_waitForResponse);
        _receiver.Start(_waitForResponse);
    }

    public void Cancel()
    {
        _cancelRequested = true;
    }

    public event ResponseReceived ResponseReceived;
}

You can see that I have created abstractions for the Console.ReadLine (the command queue) and Console.WriteLine (the event). The "quit" is also a boolean variable that is set by the "Cancel()" method now. Obviously this isn't the most optimal/correct way of doing it - I am just showing you one way to relate the imperative code from above into a wrapper class that can be re-used.

这篇关于C#命名管道而不从控制台发出命令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆