2

I have a program, that has to comunicate with another program that is called from the first. I have managed to send the required data from the first to the second program using NamedPipes. When the second program closes, i need to send some data back to the first program. I set up a NamedPipeServerStream in the first program and from the Closing event of the second program a NamedPipeClientStream. Now when i try to write from ClientStream i get a broken pipe error.

This is the code from the first program:

private void CreateOverlay(object sender, EventArgs e)
    {
        if (oinstallfolder != null)
        {
            string processfilename = oinstallfolder.ToString() + "\\SecondProgram.exe";*/

        string processfilename = "SecondProgram.exe";

        if (File.Exists(processfilename))
            {

                foreach (Process clsProcess in Process.GetProcesses())
                {
                    if (clsProcess.ProcessName.Equals("SecondProgram"))
                    {
                        this.threadHandleOverlayBounds = new Thread(new ThreadStart(this.ExchangeMapOverlayBoundsServer));
                        this.threadHandleOverlayBounds.Start();
                        this.threadHandleOverlayFile = new Thread(new ThreadStart(this.ExchangeMapOverlayFileServer));
                        this.threadHandleOverlayFile.Start();
                        ShowWindow(clsProcess.MainWindowHandle, SW_SHOWNORMAL);
                        SetForegroundWindow(clsProcess.MainWindowHandle);
                        return;
                    }
                }
            try
            {
                this.threadHandleOverlayBounds = new Thread(new ThreadStart(this.ExchangeMapOverlayBoundsServer));
                this.threadHandleOverlayBounds.Start();
                Logger.Logger.Write("Log.txt", "Starting Filethread serverside");

                this.threadHandleOverlayFile = new Thread(new ThreadStart(this.ExchangeMapOverlayFileServer));
                this.threadHandleOverlayFile.Start();

                Process.Start(processfilename);
            }
            catch { }

            }
        }
    }


 private void ExchangeMapOverlayFileServer()
        {
            try
            {
                using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("OverlayFilePipe", PipeDirection.In, 1, PipeTransmissionMode.Byte))
                {
                    string line;
                    namedPipeServer.WaitForConnection();
                    StreamReader sr = new StreamReader(namedPipeServer);
                    line = sr.ReadLine();
                    namedPipeServer.Disconnect();
                    namedPipeServer.Close();
                }
            }
            catch(Exception e)
            {
                Logger.Logger.Write("OverlayGenerator.txt", "namedPipeServer exception: " + e.Message + "\n" + e.StackTrace);
            }

        }

this is the code from the second program

this.Closing += (s, a) =>
        {
            Logger.Logger.Write("Log.txt", "Window Closing");
            this.threadHandleOverlayFile = new Thread(new ThreadStart(this.HandleWindowClosing));
            this.threadHandleOverlayFile.Start();
            while (!done)
            {
                Thread.Sleep(100);
            }

        };


  private void HandleWindowClosing()
    {
        if (!String.IsNullOrEmpty(this.latestSavedOverlayPath))
        {
            try
            {
                using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "OverlayFilePipe", PipeDirection.Out))
                {
                    namedPipeClient.Connect();
                    StreamWriter sw = new StreamWriter(namedPipeClient);
                    sw.AutoFlush = true;
                    sw.WriteLine("testphrase");
                    namedPipeClient.Close();
                    this.done = true;
                }

            }
            catch (Exception e) { Logger.Logger.Write("OverlayGenerator.txt", "Exception in Client: " + e.Message + "\n" + e.StackTrace); }
        }
        else
        {
            Logger.Logger.Write("OverlayGenerator.txt", "Latest saved OverlayPath = " + this.latestSavedOverlayPath);
        }
    }

I tried this with Autoflush=true and without. With it, i get a broken pipe exception at the line

sw.WriteLine("testphrase");

and without it, the client side just runs to the end and nothing happens at all.

Where is my mistake? Thank you.

EDIT

OK, i just don't get it. I made a test program, that is build the same way as the actual one. Two applications, the first starting the second. I start the pipe server in a thread in the first application and the client, also in a thread, in the second. The only difference is, that i run this test project in debug mode, which i can't do for the actual program. Now, in the test program, this matter is realy simple and it works. When i use the exact same approach in the actual program, it doesn't work. In the testprogram i need to use flush on the streamwriter and i don't get an exception. The syncronization of the threads gets handled by the pipe itself, as it should be if i understood this correctly.

The Master looks like this:

private const string CLIENTPROC = "C:\\Users\\Maik\\Source\\Repos\\PipeTest\\PipeClient\\obj\\x86\\Release\\PipeClient.exe";
        private ManualResetEvent threadResetEvent;
        private Thread threadHandlePipeSendLast;
        private Process procClient;
        private string msgPipeSend;
        private volatile bool bMsgPipeSend;
        public MainWindow()
        {
            InitializeComponent();
            this.threadResetEvent = new ManualResetEvent(false);
            System.Diagnostics.Debug.WriteLine("### starting thread");
            this.threadHandlePipeSendLast = new Thread(new ThreadStart(this.ExchangeMapOverlayFileServer));
            this.threadHandlePipeSendLast.Start();
        }

        private void ExchangeMapOverlayFileServer()
        {
            System.Diagnostics.Debug.WriteLine("server thread started");
            Thread.CurrentThread.Name = "apocalypse";
            try
            {
                using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("ClosingPipe", PipeDirection.In, 1, PipeTransmissionMode.Byte))
                {
                    string line;

                    namedPipeServer.WaitForConnection();
                    StreamReader sr = new StreamReader(namedPipeServer);
                    Thread.Sleep(100);
                    line = sr.ReadLine();
                    handleRecvMsgFromPipe(line);
                    namedPipeServer.Disconnect();
                    namedPipeServer.Close();
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine("### " + e.Message + "\n" + e.StackTrace);
            }
        }

        private void handleRecvMsgFromPipe(string line)
        {
            this.outbox.Text = line;
        }

        private void buttonOpenFormClient_Click(object sender, EventArgs e)
        {
#if DEBUG

#else
            if (this.procClient == null)
            {
                try
                {
                    this.procClient = Process.Start(new ProcessStartInfo(CLIENTPROC));
                }
                catch (Exception exc)
                {
                    MessageBox.Show(exc.Message, "Error");
                }
            }
#endif
        }
    }

And that's the client side:

private ManualResetEvent threadResetEvent;
        private Thread threadHandlePipeSendLast;

        private string msgPipeSend;
        private volatile bool bMsgPipeSend;
        private bool done;

        public MainWindow()
        {
            InitializeComponent();
            this.threadResetEvent = new ManualResetEvent(false);
            this.Closing += (s, a) =>
            {
                System.Diagnostics.Debug.WriteLine("+++ FormClosing started.");
                this.threadHandlePipeSendLast = new Thread(new ThreadStart(this.HandleWindowClosing));
                this.threadHandlePipeSendLast.Start();
                while (!done)
                {
                    Thread.Sleep(1000);
                }
            };
        }

        private void HandleWindowClosing()
        {
            try
            {
                using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "ClosingPipe", PipeDirection.Out))
                {
                    namedPipeClient.Connect();
                    StreamWriter sw = new StreamWriter(namedPipeClient);
                    //sw.AutoFlush = true;
                    sw.WriteLine("last message");
                    namedPipeClient.WaitForPipeDrain();
                    namedPipeClient.Close();
                    this.done = true;
                }

            }
            catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.Message + "\n" + e.StackTrace); }
        }

Can anyone tell me, why this works in the test app and not in the actual one? I also tried various things from Thread.Sleep(x) at different points in the programs over Thread.Join() to pipe methods like WaitForPipeDrain (which is just ignored).

Myrkjartan
  • 166
  • 3
  • 16
  • Which instruction give exception? You are getting a Closing Exception which is before the connection actually closed. From the exception is looks like you may be flushing a pipe that is closed. If the application is closed why flush? There is no app to write to. – jdweng Aug 17 '19 at 12:14
  • sw.WriteLine(this.latestSavedOverlayPath); gives the exception. As far as my intention goes, i want to open the NamedPipeServerStream when program 2 is opened and the NamedPipeClientStream when program 2 is closing. Then i want to connect to the ServerStream and write the string to the pipe and read it at the Serverside. I don't get why the pipe is closed, when i succesfully conect a few lines before and namedPipeClient.IsConnected is true. – Myrkjartan Aug 17 '19 at 13:06
  • You are writing as the form is closing which will close the pipe. – jdweng Aug 17 '19 at 13:40
  • I understand. So where would i have to write into the pipe? – Myrkjartan Aug 17 '19 at 13:50
  • Before the second app closes. The first application is the client( Master) and second application is the server (slave). The slave should never close without the client knowing so the client doesn't try to send after the slave closes. The slave should send a message when completed, the client stops sending, and then sends a message to slave to close. – jdweng Aug 17 '19 at 16:09
  • But it is actually the other way around. The first application is the Server and the second application is the client. And the only way i can think of detecting when the second app is closing, is by using the Closing event. But, if i understand this correctly, you said, that i can't do that, because the pipe closes with with that. I also can't exchange server and client side, because i don't have any way to go back to the first application and establish the connection. – Myrkjartan Aug 17 '19 at 16:33
  • I didn't say "Can't". If you do not following a protocol there are cases where you may get an exception and be able to handle the exception. What is happening is explainable. I do not think the flush is necessary since the other end is already closed. – jdweng Aug 17 '19 at 19:51
  • I took out the flush and yes, the exception is gone. But now the code on serverside is not executed anymore. The client just runs through the code. Before Writing as well as after, the Client says it is connected (.IsConnected() returns true) but WaitForPipeDrain seems to be ignored and the pipe closes and then the first application closes (which it shouldn't). – Myrkjartan Aug 18 '19 at 08:31
  • Do the code on the server require a command? Does command need a return and or flush? – jdweng Aug 18 '19 at 09:10
  • It shouldn't. Before application 2 is called, i start a new Thread in which the Server runs. With the Server i use WaitForConnection. In application 2 i start a thread, running the client, inside the Closing event. As i understand it, the server side code shoul be continued once it gets a connection. But that does not happen. – Myrkjartan Aug 18 '19 at 10:20
  • I edited th question and added the code (slightly changed) again without all the log lines. – Myrkjartan Aug 18 '19 at 10:33
  • You have two threads. How do you synchronized the two threads? Start by drawing a state diagram and make sure you do not have a race condition. – jdweng Aug 18 '19 at 11:10
  • after working the last 7 hours on thread synchronization and managing to write a dummy program that's simpler to test, i'm just very confused. I edited my initial post with an explanation of my confusion. Sorry. Please be patuient with me. It is the first time i'm working with pipes and threads. – Myrkjartan Aug 18 '19 at 18:14
  • _"I made a test program...and it works"_ -- then you are 80% of the way to a solution. All you have to do now is refine your test program a little bit at a time, introducing implementation details from the program that doesn't work. At some point, the test program will stop working. Once it does, you've found your problem: it's whatever you just added. – Peter Duniho Aug 18 '19 at 19:03
  • Do you know what the flush() method actually does? In windows the stream methods uses a timer when writing data and waits for a minimum number of bytes before actually doing the write. Before closing the stream there could still be some bytes in the stream that never get written. So a flush performs the writing even when the number of bytes is less than the minimum. – jdweng Aug 18 '19 at 23:27
  • OK, the deciding difference seems to be, that the actual program is WPF and the test was using windows forms. I made another testprogram using WPF and it also does not work.... Any ideas? – Myrkjartan Aug 19 '19 at 10:12
  • Just for my info: How should i continue with this question? The broken pipe exception is no more since i took out the flush, but my actual problem is not solved. Schoul i change the caption or start a new question? – Myrkjartan Aug 19 '19 at 12:32

0 Answers0