Posts Tagged WCF

WCF: Creating Custom Headers, How To Add and Consume Those Headers

When creating a C# WCF service (version .Net 3.0 and above) there may be a value in identifying the clients (consumers) which a web service is providing operational support to. This article demonstrates in C# and config Xml how to have clients identify themselves and pass pertinent information within the soap message’s header. That information in turn will be processed by the Web Service accordingly.

Client Identifies Itself

The goal here is to have the client provide some sort of information which the server can use to determine who is sending the message. The following C# code will add a header named ClientId:

var cl = new ActiveDirectoryClient();

var eab = new EndpointAddressBuilder(cl.Endpoint.Address);

eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientId",       // Header Name
                                                   string.Empty,     // Namespace
                                                    "OmegaClient")); // Header Value
cl.Endpoint.Address = eab.ToEndpointAddress();

// Now do an operation provided by the service.
cl.ProcessInfo("ABC");

What that code is doing is adding an endpoint header named ClientId with a value of OmegaClient to be inserted into the soap header without a namespace.

Custom Header in Client’s Config File

There is an alternate way of doing a custom header. That can be achieved in the Xml config file of the client where all messages sent by specifying the custom header as part of the endpoint as so:

<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IActiveDirectory" />
            </basicHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:41863/ActiveDirectoryService.svc"
              binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IActiveDirectory"
              contract="ADService.IActiveDirectory" name="BasicHttpBinding_IActiveDirectory">
            <headers>
              <ClientId>Console_Client</ClientId>
            </headers>
          </endpoint>
        </client>
    </system.serviceModel>
</configuration>

The above config file is from a .Net 4.5 client.

Server Identifies Client Request

Finally the web service will read the custom header and distinquish between any WCF client and process it accordingly.

var opContext = OperationContext.Current; // If this is null, is this code in an async block? If so, extract it before the async call.

var rq = opContext.RequestContext; 

var headers = rq.RequestMessage.Headers;

int headerIndex = headers.FindHeader("ClientId", string.Empty);

var clientString = (headerIndex < 0) ? "UNKNOWN" : headers.GetHeader<string>(headerIndex);
Share

Tags: ,

C# Silverlight WCF: Thread Safe Multiple Async Call Strategy With Final Operation To Join Data.

Seven Pointing Arrows ending at different points except one arror leaping of the page to show the final Async call.This article describes one way to handle asynchronous or async calls in .Net 3.5 or .Net 4.0 when using WCF in Silverlight in C#. The goal is to have a final operation wait for all the calls to finish so to combine all the data gathered, all in a thread safe way. A secondary goal is to minimize the consumer code required to perform this operation from what is currently available in straight WCF async calls.

This article has a short shelf life because after .Net 4 (see What’s Next in C#? Get Ready for Async!) one will use the built in Asynchrony methodology developed. Until that time if one is using any version of Silverlight and WCF then this article describes how to handle those multiple async calls and join the data in a final method call.

Final Result Example

Before delving into the solution, here is how the consumer will use the methodology. Below a user is getting account information of user requests, departments and accounts to join all the data on the view model in the final method for display on a Silverlight page. Thesee operations as shown are setup when the view model class (MyViewModel) is created and no blocking occurs keeping UI thread clear.

public MyViewModel()
{
    DataContext = new MyServiceClient();

    FinalAsync(CombineDataAndDisplay); // When all the data is retrieved, do this method to combine the data From the 3 async callls below

    // Provide the operation as a lambda (could be a method call) to assign data to our target backing store property
    // and if all has gone well (no errors in other async calls) and it is the final completing operation. Do the final
    // Processing call automatically.
    DataContext.GetUserRequestsCompleted   += (s, e) => { AssignResultCheckforAllAsyncsDone(e, e.Result, ref _UserRequests, "Acquisition failure for User Requests");};
    DataContext.GetAccountsCompleted       += (s, e) => { AssignResultCheckforAllAsyncsDone(e, e.Result, ref _Accounts, "Acquisition Failure for Accounts"); };
    DataContext.GetDepartments             += (s, e) => { AssignResultCheckforAllAsyncsDone(e, e.Result, ref _Departments, "Failed to get Department Info."); };   

    // Start the Async processes 
    MultipleAsyncRun(DataContext.GetUserRequestsAsync);
    MultipleAsyncRun(DataContext.GetAccountsAsync);
    MultipleAsyncRun(DataContext.GetDepartmentsAsync);

    // Exit out and return the UI thread to the user operations.
}

// Once all the data is done, combine and assign into our
// PagedCollectionView property for display on the screen.
public void CombineDataAndDisplay()
{
    // Combine missing data on the calls
   _UserRequests.ToList()
                .ForEach(ur =>
                {
                ur.BillingName     = _Accounts.First(ac => ur.AccountID == ac.AccountID).Name;
                ur.DepartmentName  = _Departments.First(dp => dp.DepartmentID == ur.DepartmentID).Name;
                });


    UserRequests = new PagedCollectionView( _UserRequests);
    UserRequests.GroupDescriptions.Add(new PropertyGroupDescription("DepartmentName"));

}
Explanation
Line  5: The final async call is where this process specifies a method to use after all async calls are completed. On line 24 of the example is our final operation method to do that which is reported to FinalAsync.
Line 11-13: Just like the normal async process we subscribe to the anync handler but we handle it in a anonymous lambda.
Line 11-13: AssignResultCheckForAllAsyncsDone method will check the result returned, code show later in the article. All one needs to know now is that the respective _UserRequests, _Accounts and _Departments, all backing store variables (not properties) will be loaded with the data if the result information ( e ) operation contains no errors. This method does all the heavy lifiting by checking error state, loading the data variable, decrementing the async count and firing off the final method if all asynchronous operations are completed.
Line 16-18: Just like the normal WCF async process we must launch the asnc calls and these statements do just that. We supply the method to be launched inside the MulipleAsync method.
Line 24: Here is our final method to be executed once the asynchrony process is complete. This method combines all the async data internally into the _UserRequest object at which it becomes complete and can be used.
Line 34: Finally a paged collection view is created from our data to be bound to Silverlight Xaml items which is our end result.
24-37: Note: The section below has a variable entitled AsyncErrors. The above example did not check that for null or an error situation. I have left out the check of AsyncErrors for brevity of the example. See the last section for an example of proper error handling.

Multiple Async Methodology Plumbing

Below is what will needed to be brought into your View Model, or placed on your page if not using MVVM. This is where the .Net thread safe Silverlight asynchronous operations will occur.

// Lock Objects 0 for false, 1 for true.
private int AsyncErrorResource = 0;
private int AsyncFinal = 0;

private List<Exception> AsyncErrors;  // If not null errors have been encountered.

private int PendingAsyncOperations; // Holds the counted total of ongoing async operations. Zero means do users final operation.

private Action FinalOperation;      // The user's final operation.

public void MultipleAsyncRun(Action Operation)
{
    Interlocked.Increment(ref PendingAsyncOperations);
    Operation();
}

public void FinalAsync(Action method)
{
    FinalOperation = method;
}

public void AssignResultCheckforAllAsyncsDone<T>(AsyncCompletedEventArgs ea, T receivedData, ref T assignTo, string ErrorMessage)
   where T : class
{
    bool valid = !((ea.Error != null) || (receivedData == null));

    if (valid == false)
    {
        if (0 == Interlocked.Exchange(ref AsyncErrorResource, 1))
        {
            if (AsyncErrors == null)
                AsyncErrors = new List<Exception>();

            AsyncErrors.Add(ea.Error);

            //Release the lock
            Interlocked.Exchange(ref AsyncErrorResource, 0);
        }
    }
    else
    {
        assignTo = receivedData;
    }

    Interlocked.Decrement(ref PendingAsyncOperations);
    if (PendingAsyncOperations == 0)
    {
        if (0 == Interlocked.Exchange(ref AsyncFinal, 1))
        {
            FinalOperation();
        }

        Interlocked.Exchange(ref AsyncFinal, 0);
        Interlocked.Decrement(ref PendingAsyncOperations); // Move to -1
    }
}
Explanation
Line  2/3: These variables are used as a lock targets for thread safety. Value zero means its open and a value of 1 means a lock is in place.
Line  5: Any errors encountered are placed into AsyncErrors. The final operation must check this variable. If it is not null errors have occurred and data is incomplete see the final section of this article on how to handle errors.
Line 7: This variable will hold the running total async operations. Once it gets to zero the final operation will be executed.
Line 11: This method counts and stores our async operations as they come in and launches the async method as well.
Line 13: This process uses the Interlocked class found in the System.Threading namespace. To quote Microsoft, “Provides atomic operations for variables that are shared by multiple threads. “. We simply count up the PendingAsyncOperations variable. Later when the operations complete this will be decremented.
Line 18: This method is called by the consumer so we know what method to launch when all async operations have completed. We assign the method to a holder variable as found on line 9.
Line 22: This method will take in the event arguments and check if there is a problem. It will store the error if there is one and also decrement our count. If our count hits zero it will launch the users final operation.
25-39: Checking errors reported by the async call. If there are any we log them and do not assign the variable. Note whether an error exists or not the operation continues below where the final operation is checked for and executed. Its up to the final operation to check if errors exists and handle them appropriately.
Line 42: If the event reports a success this is where we assign the value to the passed in reference to the template T assignTo. If one doesn’t use a variable for assignTo reference an exception will be generated on this line. See the following section as to why. 
Line 46: We safely decrement our operations count variable. Zero means we are done!
Line 47-57: Here is where we execute the final operation when the count is zero. Note there is an extra lock if operations unlikely hit a zero at the same time. The extra lock ensures only one will execute the final method.

That is it, simply use the above code and you can have all your operations work done asynchronously. Smile Now for the disclaimers…

Why Can’t Properties be Used by AssignResultCheckforAllAsyncsDone?

We must use direct variables like in our example

private ObservableCollection<UserRequestDTO> _UserRequests;
private ObservableCollection<Account> _Accounts;
private ObservableCollection<Department> _Deparments;

// Not Private ObservableCollection<Department> _Departments { get; set; }

If one used a property this exception is thrown.

A property, indexer or dynamic member access may not be passed as an out or ref parameter

That is because we are using the generic method AssignResultCheckforAllAsyncsDone to assign a value, and that assignment has to have an exact object instance for the template (T) object and not a Property to assign the value. If your end result is in a property, simply assign it from the variable to the property in the final async call.

Handling Errors

Simply check the error variable if it is not null it has encountered errors.

if (AsyncErrors != null)
{
    MessageBox.Show("Errors: " + string.Join(string.Format("{0}{0}", Environment.NewLine), AsyncErrors.Select(ex => ex.Message)));
}
else
{
    // Success Handle accordingly.
}
Share

Tags: , , , ,

WCF .Net WSDL Generation and Consumption

One of the semi-hidden features of WCF found both as a consumer and a producer of webservices is the industry standard Web Services Description Language (WSDL) which can be consumed and exposed by the tools and code of a .Net web service. This article talks about the Tribal Knowledge that optionally allows one to expose a WSDL as a publisher and can consume a foreign WSDL to be used by code by a generated proxy. To access a wsdl simply ad a ?wsdl to the address and see if it comes back as xml such as http://localhost:8000/Omega?wsdl.

Expose a WSDL using MetaDataExchange (MEX)

The ability to expose a WSDL is not a default when creating a webservice. One has two options to expose the WSDL. The first is programmatically in code such as this when creating the service host


using (ServiceHost host = new ServiceHost(typeof(NSTest.HelloNS),
   new Uri("http://localhost:8000/Omega")))
{
    host.AddServiceEndpoint(
        typeof(NSTest.IMy_NS),
        new BasicHttpBinding(),
        "Primary");

    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
    behavior.HttpGetEnabled = true;

    host.Description.Behaviors.Add(behavior);

    host.AddServiceEndpoint(
            typeof(IMetadataExchange),
            MetadataExchangeBindings.CreateMexHttpBinding(),
            "mex");

    host.Open();

The other way is to place similar information in the config file, either web.config or app.config depending on how it is being hosted.  This xml is copied from How to: Publish Metadata for a Service Using a Configuration File

<system.serviceModel>
   <services>
     <service
         name="Metadata.Samples.SimpleService"
         behaviorConfiguration="SimpleServiceBehavior">

       <host>
         <baseAddresses>
           <add baseAddress="http://localhost:8001/MetadataSample"/>
         </baseAddresses>
       </host>

       <endpoint address=""
                 binding="wsHttpBinding"
                 contract="Metadata.Samples.ISimpleService" />

       <endpoint address="mex"
                 binding="mexHttpBinding"
                 contract="IMetadataExchange" />
     </service>
   </services>

   <behaviors>
     <serviceBehaviors>
       <behavior name="SimpleServiceBehavior">
         <serviceMetadata httpGetEnabled="True" policyVersion="Policy12" />
         <serviceDebug includeExceptionDetailInFaults="False" />
       </behavior>
     </serviceBehaviors>
   </behaviors>

 </system.serviceModel>

Consume a WSDL to create a Proxy

If a foreign webservice has exposed a WSDL, one can consume that webservice methods by a generated proxy. Again there are two ways to generate that proxy. Both actually use the same tool. That tool is Svcutil.exe which does its magic on the command line or one can browse for it in VS2008 by using the Add Service Reference command. That command simply encapsulates the SVCUtil.exe to generate a proxy class from the exposed wsdl. To see more on SVCUtil.exe see Retrieving Metadata.

Share

Tags: , ,

Create a Intra-Application Named Pipe using WCF in .Net using C#; IPC Communications.

Update 07/2009: Update to not use HTTP transport methodology but NetNamedPipeBinding. Updated downloadable code

Update 10/2008: Code downloadable…see comments for link.
A named pipe is a communications method between two disparate applications. This article will show how to use WCF and .Net 3+ with C# to create such named pipe(s). This article is a how-to article showing every step needed and has the code downloadable.
Premise
I have a need to have one application to provide a messages to another application (InterProcess Communication (IPC)) using a unique named pipe between them using WCF.

Named Pipe Library

We will be creating three projects. The first will be a library project (shown below as NamedPipe) and two test projects (not shown in the picture below). The test projects will be the consumers of the library which will be a receiver and a sender talking to each other. The Library will contain the guts of the operations in WCF and once done will look like this in VS2008 Solution Explorer:
image
This library will be all encompassing and contain the code necessary to perform the named pipe between applications. It handles all the core WCF functionality setup/endpoints. All one has to do is have the receiver and sender use the library to communicate by just specifying a name for the pipe.
The receiver and sender will simply consume the classes in Receiver.cs and Sender.cs respectively to achieve the named pipe communication.
Steps:
  1. Create a library project named NamedPipe.
  2. Add two references to the System namespaces Runtime.Serialization and ServiceModel.
  3. Create a folder within the project named Communication, which will be a sub-namespace to NamedPipe. We will place the low level operations and specifications there.
  4. Within the Communication folder add an interface class named IPipeService (as iPipeService.cs in the downloadable code). Within that file is where we will begin to define the service contract which the sending and receive code will adhere to. The interface code will look like this:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    
    namespace NamedPipe.Communication
    {
     [ServiceContract
      (Namespace =
      "http://www.OmegaCoder.com/NamedPipe1")]
     public interface IPipeService
     {
      [OperationContract]
      void PipeIn(List<string> data);
     }
    }
    • Line 04: We will need to add this using so our attributes will be recognized on the interface.
    • Line 08: This is the attribute which will show our interface to be a specialized WCF contract.
    • Line 10: We setup a generic namespace to avoid naming collisions with other WCF services. This namespace does not have anything to do with opening up the ports, which will have a different URL altogether. This url is kind of a throw away url, we are not publishing to this location.
    • Line 13: We will be opening up one port which we will communicate between the processes and this attribute defines that.
    • Line 14: We will be passing a list of strings between the processes and this method is the vector of the operation.
  5. Again within the Communication folder create the second part of the contract, the actual class which will be the catalyst for the data transfer on the receiving side named PipeService (as PipeService.cs in the download code). Let me show the code and explain each line:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    
    namespace NamedPipe.Communication
    {
    
    [ServiceBehavior
            (InstanceContextMode = InstanceContextMode.Single)]
     public class PipeService : IPipeService
     {
         public static string URI
             = "net.pipe://localhost/Pipe";
    
         // This is when we used the HTTP bindings.
         // = "http://localhost:8000/Pipe";
    
         #region IPipeService Members
    
         public void PipeIn(List<string> data)
         {
             if (DataReady != null)
                 DataReady(data);
         }
    
         public delegate void DataIsReady(List<string> hotData);
         public DataIsReady DataReady = null;
    
         #endregion
     }
    }
    • Line 04: This is the required using for WCF operations.
    • Line 09: This attribute is self explanatory, for this class will be generated for each endpoint we define. In our case only one endpoint for our pipe.
    • Line 10: This is not the default behavior. In standard WCF processing this object would be created through reflection. We don’t want that to happen, because we have a delegate (call back) which will transfer data to the receiver. Due to that fact, we will be creating the object by hand later and allowing the consumer to subscribe to the delegate. Because of that, we have to specify that this context will be a singleton instance.
    • Line 14: Here is the actual url and port which the operations will be operating on. The named part of the port will be added to this string to give us a unique pipe.  If we want to expand this to other machines on the intranet, we could use an HTTP protocol as shown below in Line 17.
    • Line 20: Here is where the data will be passed in/out.
    • Line 22: Check for a subscriber to the delegate.
    • Line 23: Finally pass the data on to the consumer(s) of the delegate after we have received data from the ether.
    • Line 25/26: Here is where we will transfer the data back and forth by allowing a consumer to subscribe to this delegate.

Receiver Class

  1. Now we will create the top level receiver class (as Receiver.cs in the zip) which will be consumed by the target of the named pipe. That class is created in the top level of the NamedPipe namespace. Here is the code and the line by line explanation.
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using NamedPipe.Communication;

namespace NamedPipe
{
 public class Receiver :  IDisposable
 {

     #region Constructors
     // Default constructor, use this if
     // the default name of the pipe can
     // be used instead of one provided.
     public Receiver() : this(DefaultPipeName) { }

     // Use this constructor to create
     // a pipe with a unique name.
     public Receiver(string PipeName)
     {
         _PipeName = PipeName;
     }
#endregion

     #region Public Operations
     // If the service host can be started,
     // true will be returned. If false is returned
     // another process owns the pipe.
     public bool ServiceOn()
     {
         return ( _operational = HostThisService());
     }

     public void ServiceOff()
     {
      if (_host != null)
          if (_host.State != CommunicationState.Closed)
              _host.Close();

      _operational = false;

     }
#endregion

     #region WCF Operations
    // The thread which will listen for communications
    // from the other side of the pipe.
    private bool HostThisService()
    {

     try
     {

         _host = new ServiceHost(_ps, new Uri(PipeService.URI));

         // Usage BasicHttpBinding can be used if this is
         // not going to be on the local machine.
         _host.AddServiceEndpoint( typeof( IPipeService ),
              new NetNamedPipeBinding(),
              _PipeName );
         _host.Open();

         _operational = true;
     }
     catch (Exception ex)
     {
         error = ex;
         _operational = false;
     }

     return _operational;

    }
#endregion

    #region Public Properties
    // The consumer of this class will subscribe to this delegate to
    // receive data in a callback fashion.
    public PipeService.DataIsReady Data
    {
        get
        {
           return _ps.DataReady;
        }

        set
        {
           _ps.DataReady += value;
        }
    }

    public string CurrentPipeName
    {
        get { return _PipeName; }
    }

    // Any error text will be placed here.
    public Exception error              = null;

    // See the actual name of the pipe for
    // any default operations.
    public const string DefaultPipeName = "Pipe1";
#endregion

#region Private Variables
    private PipeService _ps   = new PipeService();
    private bool _operational = false;
    private ServiceHost _host = null;
    private string _PipeName  = string.Empty;
#endregion

#region IDisposable Members

    // A basic dispose.
    public void Dispose()
    {
        this.ServiceOff();

        if (_ps != null)
            _ps = null;
    }

 #endregion
   }
}
  • Line 04: Even though we have lower level WCF definitions, we will be initiating the WCF functionality in this class.
  • Line 05: Pull in the lower level WCF contract and operations.
  • Line 16: If you know that this is the only named pipe on the system, use a default name for the pipe.
  • Line 20: Otherwise use this constructor to create a named pipe to your specifications.
  • Line 30: Call this after creating the object and after subscribing to the delegate, to start the process of listening. Don’t forget to check for a running status. (Note if the pipe is in use it will throw an exception, so catch any exceptions).
  • Line 35: Turn service off, or it will be done in the dispose automatically.
  • Line 49: This is the function which will do the magic of the WCF port processing.
  • Line 55: The service host will not be creating a generic end point. It will use our singleton pipe service.
  • Line 58: We will open up this URI which is specified in the URI static of PipeService.
  • Line 59: We will have one endpoint and we let it know what type it is.
  • Line 60: We are doing not http communications but netnamed pipe operations.
  • Line 62: Here is the uniqueness of this endpoint. The pipe name.
  • Line 62: Finally we begin listening operations.
  • Line 68: Any errors will be on the public error object for the consumer to check.
  • Line 85: Subscribe to this delegate to pass the information back to the creator of this receiver named pipe.
  • Line 99: Check here for any errors.
  • Line 103: This is the default name of the pipe, if one is not given by the consumer.
  • Line 107: Here we create our singleton WCF service for the one endpoint we will specify.
  • Line 115: Clean up any operations and member variables.

Sender Class

    1. Here is the class which will be consumed by the application which will send the information over. Note this class is not instantiated but used by simply calling the static which will send the data on over:
using System;
using System.Collections.Generic;
using System.Text;

using System.ServiceModel;
using NamedPipe.Communication;

namespace NamedPipe
{
// Create this class to send a message on a
// named pipe.
public class Sender
{
    // Use a default pipe name. Be careful not to reuse
    // this on other applications.
    public static void SendMessage(List<string> messages)
    {
        SendMessage(messages, Receiver.DefaultPipeName);

    }

    // Use this method when we have an actual pipe name.
    public static void SendMessage(List<string> messages, string PipeName)
    {
        EndpointAddress ep
            = new EndpointAddress(
                string.Format("{0}/{1}",
                   PipeService.URI,
                   PipeName));

//      IPipeService proxy = ChannelFactory<IPipeService>.CreateChannel( new BasicHttpBinding(), ep );
        IPipeService proxy = ChannelFactory<IPipeService>.CreateChannel( new NetNamedPipeBinding(), ep );
        proxy.PipeIn( messages );

    }
}
}
  • Line 05: Need to pull in the WCF information to initiate the message.
  • Line 06: We have all the connection contract information which we will need to initiate a message.
  • Line 16: The class and method is a static class so just call this static. No muss no fuss. Note this uses the default named pipe name. If our receiver has specified a name for the pipe do not call this one.
  • Line 21: Call this method with a unique name in for the pipe so a message to us can be sent.
  • Line 23: We will combine the URL and port with the name of the pipe which gives us the uniqueness.
  • Line 29: We create a proxy service to do the transfer and expose the method we need to send the info to the receiver application.
  • Line 30: We send in the list of strings and we are done!

Test Projects

Finally we will create two test projects. A winform which will be the receiver and a console application which will be the sender. All we need to do is reference the Named Pipe Library!

image

Test Receiver

  1. Create a winform project and reference the NamedPipe Project.
  2. Add a richtext box to the form. Here is the code inside:
    using NamedPipe;
    
    public partial class Form1 : Form
    {
        Receiver pipe = new Receiver();
        public Form1()
        {
            InitializeComponent();
            pipe.Data += new NamedPipe.Communication.PipeService.DataIsReady(DataBeingRecieved);
            if (pipe.ServiceOn() == false)
                MessageBox.Show(pipe.error.Message);
    
        }
    
        void DataBeingRecieved(List<string> data)
        {
            this.Invoke(new MethodInvoker(delegate()
            {
                rtb_Status.Text += string.Join(Environment.NewLine, data.ToArray());
                rtb_Status.Text += Environment.NewLine;
            }));
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            if (pipe != null)
                rtb_Status.Text += "Not Null" + Environment.NewLine;
    
            if (pipe.error != null)
                rtb_Status.Text += pipe.error + Environment.NewLine;
    
        }
    }
  • Line 01: We pull in the Named Pipe namespace.
  • Line 05: Since we will be continuously monitoring, we will instantiate the receiver class on the class, so its lifetime is the same as that of the application.
  • Line 09: Here is where we will subscribe to the delegate which will provide us with the text sent from the sender.
  • Line 11: We have gotten an error, show the exception.
  • Line 15: Here is our subscriber target which will receive the data!
  • Line 17: Since the WCF process is on another thread, we invoke back to the GUI thread to populate the richtext box.
  • Line 19: We add the data to the rich text box!
  • Line 24: I added a button, not in the steps to test out the object. This code is optional.

Test Sender

  1. Create a console application and reference the NamedPipe project.
  2. Copy this code:
    static void Main(string[] args)
    {
        List<string> messages = new List<string>();
    
        messages.Add("Line 1");
        messages.Add("Second Line");
        try
        {
            NamedPipe.Sender.SendMessage(messages);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    
    }
  • Line 09: We simply send our message. We are using the default pipe name, so we just use the single argument SendMessage. Otherwise we would use the constructor which would allow us to send data on the unique named pipe!

Summary

With that we are finished. Run the winform receiver first. Then run the console sender. If everything has been coded you should see the text on the screen. With the NamedPipe library it is now a toolbox item which we can take with us from project to project.

image

Share

Tags: , , ,