Click here to Skip to main content
15,887,596 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
This is my first post here and I'm brand new to .NET. I'm creating a Windows Forms application using C++/CLI that needs to have bidirectional communication with several worker threads. I read the tutorials on the background threads and created a simple Form with a TextBox that starts several background workers (bw's). Each of the bw runs a do-forever loop that occasionally sends a data structure to the Form using bw->ReportProgress(0, my_data_structure), and the Form's ProgressChanged() event formats the data into a string that it Appends to the TextBox.

All this was working well but I needed a way for the Form to send data to the bw's. Named Pipes seemed to be a reasonable way to do this, but i can't get it to work.

In Form1.h, I added private variables:
C++
private:
    System::IO::Pipes::NamedPipeServerStream ^pipeServer;
    array<byte> ^msgOut;

Then in Form1 c'tor:
C++
        Form1(void)
        {
            InitializeComponent();
            // create named pipe to each subsystem
            this->msgOut = (gcnew array<byte>(256));
            this->pipeServer = (gcnew NamedPipeServerStream("pipeStream1", PipeDirection::Out));

            // start each subsystem (background worker) thread
           this->Arm1Thd->RunWorkerAsync();
        }
and
    private:
        System::Void ProgressChanged(System::Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e) {
            //this value can be anything, not used here 
            int Value = e->ProgressPercentage;
            this->pipeServer->WaitForConnection();    // not sure this is needed
            this->pipeServer->Write(msgOut, 0, msgOut->GetLength(0));
            ...
        }

In the background worker thread:
C++
void Form1::TestCycle( BackgroundWorker^ bw, int SubSysID )
{
    array<byte> ^inMsg = (gcnew array<byte>(256));
    unsigned    msg_cnt = 0;
    int            len;

    System::IO::Pipes::NamedPipeClientStream  ^pipeClient =
        gcnew NamedPipeClientStream(".", "pipeStream1", PipeDirection::In);
    pipeClient->Connect();        // block until connected
    bw->ReportProgress(0, msg_cnt++);

    while (!bw->CancellationPending)
    {
        len = pipeClient->Read(inMsg, 0, inMsg->GetLength(0));
        ....
        System::Threading::Thread::Sleep(2000);           // set breakpoint here
        bw->ReportProgress(0, msg_cnt++);
    }
}

So the order of execution is:

1) Form1 loads, creates the NamedPipe, starts the background worker TestCycle
2) TestCycle sends a ProgressReport then blocks on NamedPipe Read()
3) Form1::ProgressChanged() gets the report, writes it to TextBox then Writes to NamedPipe.
4) TestCycle NamedPipe Read() returns, so he sleeps awhile then goes to step 2.

The first ProgressReport from TestCycle startup code shows up in Form1::TextBox.
Then the first pipeClient->Read completes, and the second ProgressReport is displayed.
But just after that, I get:

An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll

Additional information: Exception has been thrown by the target of an invocation.

Can anyone tell me what's going wrong, or even how to find what's going wrong?
Posted
Updated 17-Jul-12 6:20am
v2
Comments
Sergey Alexandrovich Kryukov 17-Jul-12 12:23pm    
Why on Earth would you use named pipes, if this is the same process?!
--SA

1 solution

Using named pipe directly is too lo-level for .NET. I mean, you could use them, but everything you are trying to do is already done for you with remoting. You could use classical remoting or self-hosted WCF. This way is quite good for your purpose but is also much more universal. The communication channel is replaceable: for example, the code you use today for named pipe based communications you can use tomorrow with sockets. And it will be much less development on your side with more reliable results.

But more importantly, if this is in the same process, between a UI thread and some other thread of the same process, you should not use IPC communications at all. You simply need a blocking queue to carry data, or, much more flexible twist — to carry delegate instances.

Please see my article complete with full source code and usage samples:
Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^].

—SA
 
Share this answer
 
v2
Comments
RayZ 17-Jul-12 16:45pm    
Sergey - your points are well taken. I was specifically looking for a relatively low level mechanism because I have some extreme performance requirements and very little time to absorb enough .NET to be productive. But your use of queues makes more sense if I can keep the final app in the same process as you have solved a nearly identical problem. I will study your example and see if I can incorporate it into my project. Thank you for taking time to help!
Sergey Alexandrovich Kryukov 17-Jul-12 16:57pm    
Right. Whatever you do with pipes, the blocking queue will work faster because pipes is an IPC mechanism, but I use only in-proc things. As processes are well isolated, nearly every inter-process facility is considerably slower. With delegate instance as queue elements, you can also get a lot more flexibility.

If you agree that it makes sense, please accept this answer formally (green button) -- thanks.

--SA
Albert Holguin 17-Jul-12 16:50pm    
+5
Sergey Alexandrovich Kryukov 17-Jul-12 16:58pm    
Thank you, Albert.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900