Praveen Katiyar' Blog

Learning along the way . . . .

Understanding Late binding

in simplest words, late binding is a technique, in which you can create an instance of a given type, and can invoke its methods, at runtime, without having knowledge of its existence at the compile time.

System.Activator class plays a pivotal role in late binding process.

let us understand, it by creating

  • simple a Class library “MyMathLib” which exposes few methods let say Add, Subtract, Multiply.
  • Console bases client program , that will use this library using the conventional (early binding) method.
  • Console bases client program , that will use this library using the late binding method.

Creating the class library

Create a Class library, let’s name it “MyMathLib” choosing the “Class Library” template from the project wizard. Default project is created with a public class Class1 defined in Class1.cs.

Little house keeping.

rename the Class1 as “MathClass” using the refactor menu.

rename the class1.cs as MathClass.cs

Now define methods Add, Subtract and Multiply for MathClass as shown below.  as shown below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyMathLib
{
    public class MathClass
    {
        public double Add(double dblX, double dblY)
        {
            return (dblX + dblY);
        }

        public double Subtract(double dblX, double dblY)
        {
            return (dblX – dblY);
        }

        public double Multiply(double dblX, double dblY)
        {
            return (dblX * dblY);
        }
    }
}

 

Compile and Build library.

Creating the Application, that will use this library

Create a Console application, let’s name it “TestEarlyBinding” choosing the “Console Application” template from the project wizard. Default project is created.

Little house keeping.

Add Reference of MathLib class library project, created earlier.

Write code in the main function to use this library as shown below.

using System;
using System.Text;

using MyMathLib;

namespace TestEarlyBinding
{
    class Program
    {
        static void Main(string[] args)
        {
            double dblResult = 0;
            double dblX = 200; double dblY = 200;
           
            Console.WriteLine("Tesing early binding");  
           MathClass mathObj = new MyMathLib.MathClass ();
           
            dblResult = mathObj.Add(dblX, dblY);
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);

            dblResult = mathObj.Subtract (dblX, dblY);
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);
           
            dblResult = mathObj.Multiply(dblX, dblY);
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);
        }
    }
}

Compile and build and here is the output.

image

till now, it was default way, we used to do things, but this post is about late binding.

so using the same class library  with late binding, let’s create one more console based project let’s  call it TestLateBidning.

using System;
using System.Text;
using System.Reflection;

namespace TestLateBidning
{
    class Program
    {
        static void Main(string[] args)
        {
            double dblResult = 0;
            double dblX = 200; double dblY = 100;

            Console.WriteLine("Tesing late binding");
           
Assembly assem = null;
           
Console.WriteLine("Loading MyMathLib assembly . . .");
            try
            {
                assem = Assembly.Load("MyMathLib");
            }
            catch (Exception eX)
            {
                Console.WriteLine("Error while loading MyMathLib Error [{0}] \n\nMyMathLib.dll not found in application Folder", eX.Message);
                return;
            }

            Console.WriteLine("Creating Instance of MathClass object . . .");
            Type mathType = assem.GetType("MyMathLib.MathClass");
            object mathObjLB = Activator.CreateInstance(mathType);

            Console.WriteLine("Invoke method [Add] . . .");
            MethodInfo miAdd = mathType.GetMethod("Add");
            dblResult = (double)miAdd.Invoke(mathObjLB, new object[] { 100, 200 });
            Console.WriteLine("Adding {0} and {1} results to {2}", dblX, dblY, dblResult);

            Console.WriteLine("Invoke method [Subtract] . . .");
            MethodInfo miSub = mathType.GetMethod("Subtract");
            dblResult = (double)miSub.Invoke(mathObjLB, new object[] { 100, 200 });
            Console.WriteLine("Subtracting {0} and {1} results to {2}", dblX, dblY, dblResult);

            Console.WriteLine("Invoke method [Multiply] . . .");
            MethodInfo miMul = mathType.GetMethod("Multiply");
            dblResult = (double)miMul.Invoke(mathObjLB, new object[] { 100, 200 });
            Console.WriteLine("Multiply {0} and {1} results to {2}", dblX, dblY, dblResult);
        }
    }
}

Explanation:

Assembly assem = null;
Console.WriteLine("Loading MyMathLib assembly . . .");
try
{
    assem = Assembly.Load("MyMathLib");
}
catch (Exception eX)
{

    . . . . .    
    return

}

Assembly class represents an assembly, which is a valid building block of a common language runtime.

Load method simply loads an assembly, whose name has been provided.

Type mathType = assem.GetType("MyMathLib.MathClass");
object mathObjLB = Activator.CreateInstance(mathType);

Activator class contains methods to create types of object of the specified type, locally or remotely. 

MethodInfo miAdd = mathType.GetMethod("Add");

GetMethod is member function of Type class, that searches the given method name, and returns the MethodInfo, MethodInfo class discovers the attributes of a method access its metadata.

dblResult = (double)miAdd.Invoke(mathObjLB, new object[] { 100, 200 });(mathObjLB, new object[] { 100, 200 });

Invoke method of MethodInfo class simply calls the method, using the required parameters.

similarly other methods (Subtract, Multiply) are discovered and invoked.

build and execute.

image

oops, there was an error, as it could not found the MyMathLib.dll in the test application executable (TestLateBinding.exe) folder.

just copy the MyMathLib.dll to the test application’s executable folder and execute the client again. and you will see what was expected.

image

 

Hope it useful.

April 27, 2014 Posted by | .NET, CodeProject, General | , , , | Leave a comment

Understanding Instance Management in WCF

instance management is the technique WCF uses to bind client requests to service instances, governing which service instance handles which client request, and when. WCF Supports 3 types of instance activation

  • Per Call : per call services allocate and destroy a new service instance for each  client request.
  • Per Session: session full services allocate a service instance for each client  connection.
  • Singleton: singleton services share the same service instance for all clients, across all connections and activations.

by and large the service instance mode is strictly a server side implementation detail, hat should not manifest itself on the client side in any way.

now let’s define a service, that demonstrates the instance management in real life.

Creating the Class Library

let’s create a service, create a new project, take a Class Library template, name it InstanceLib, let’s do some house keeping with the newly created project.

  • Delete the file Class1.cs from the project workspace.
  • Add a new Interface named ISingleCallService to the project, a new file ISingleCallService.cs will be added to the project.
  • Add a new Class named SingleCallService, to the project. that will implement the ISingleCallService interface, a new file SingleCallService.cs will be added to the project.
  • Add a new Interface named ISessionService to the project, a new file ISessionService.cs will be added to the project.
  • Add a new Class named SessionService, to the project. that will implement the ISessionService interface, a new file SessionService.cs will be added to the project.
  • Add a new Interface named ISingletonService to the project, a new file ISingletonService.cs will be added to the project.
  • Add a new Class named SingletonService, to the project. that will implement the ISingletonService interface, a new file SingletonService.cs will be added to the project.

to keep the things simple, let’s assume that each interface defines only one method, which simply adds a passed value, to  its member variable.

Defining Interfaces

so let’s define interface for each service.

// Listing of ISingleCallService.cs

using System;
using System.Text;
using System.ServiceModel;

namespace InstanceLib
{
    [ServiceContract]
    interface ISingleCallService
    {
        [OperationContract]
        double AddValue (double dblNum);
    }
}

// Listing of ISessionService.cs

using System;
using System.Text;
using System.ServiceModel;

namespace InstanceLib
{
    [ServiceContract]
    interface ISessionService
    {
        [OperationContract]
        double AddValue (double dblNum);
    }
}

// Listing of ISingletonService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace InstanceLib
{
    [ServiceContract]
    interface ISingletonService
    {
        [OperationContract]
        double AddValue (double dblNum);
    }
}

Implementing Interfaces

Let’s implement each interface,as shown below.

// Listing of SingleCallService.cs

using System;
using System.Text;
using System.ServiceModel ;

namespace InstanceLib
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]  
    public class SingleCallService : ISingleCallService
    {
        private double m_dblTotal = 0 ;

        public double AddValue(double dblVal)
        {
            m_dblTotal += dblVal;
            return m_dblTotal;
        }
    }
}

Important

You can notice ServiceBehavior attribute of the class, which has been defined as InstanceContextMode.PerCall, it specifies that a new InstanceContext object is created prior to and recycled subsequent to each calll. if you omit this attribute or do not specify, then InstanceContextMode.PerCall is assumed by default.

// Listing of SessionService.cs

using System;
using System.Text;
using System.ServiceModel;

namespace InstanceLib
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class SessionService : ISessionService
    {
        private double m_dblTotal = 0 ;

        public double AddValue(double dblVal)
        {
            m_dblTotal += dblVal;
            return m_dblTotal;
        }
    }
}

Important

You can notice ServiceBehavior attribute of the class, which has been defined as InstanceContextMode.PerSession, it specifies that InstanceContext object is created for each session.

 // Listing of SingletonService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace InstanceLib
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class SingletonService : ISingletonService
    {
        private double m_dblTotal = 0 ;

        public double AddValue(double dblVal)
        {
            m_dblTotal += dblVal;
            return m_dblTotal;
        }
    }
}

Important

You can notice ServiceBehavior attribute of the class, which has been defined as InstanceContextMode.Singlel, it specifies that Only one InstanceContext object is used for all incoming calls and is not recycled to the calls. if the service object does not exist, one is created. 

if you notice the all 3 service implementation, you will notice the difference in ServiceBehavior class attribute of each class, apart from that, all interfaces has been implemented has the same implementation. they simply add a passed value to a member variable of the class.

Build the project and your Class Library is ready. 

Hosting the Service

for the sake of simplicity, i have have hosted this service as a self hosted service, to create the Host application, create a new Console based application.

Before we write any code for the Host application, let’s define the configuration file for the Service(s).

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>

      <!–************************ Single Call Service ************************ –>
      <service name="InstanceLib.SingleCallService" behaviorConfiguration="SingleCallServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="
http://localhost:9011/SingleCallService"/>
            <add baseAddress="net.tcp://localhost:9012/SingleCallService"/>
          </baseAddresses>
        </host>

        <endpoint address="http://localhost:9011/SingleCallService" binding="wsHttpBinding" contract="InstanceLib.ISingleCallService"/>
        <endpoint address="net.tcp://localhost:9012/SingleCallService" binding="netTcpBinding" contract="InstanceLib.ISingleCallService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>

      <!—************************ Single Session Service ************************–>
     <service name="InstanceLib.SessionService" behaviorConfiguration="SessionServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="
http://localhost:9013/SessionService"/>
            <add baseAddress="net.tcp://localhost:9014/SessionService"/>
          </baseAddresses>
        </host>

        <endpoint address="http://localhost:9013/SessionService" binding="wsHttpBinding" contract="InstanceLib.ISessionService"/>
        <endpoint address="net.tcp://localhost:9014/SessionService" binding="netTcpBinding" contract="InstanceLib.ISessionService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>

      <!—************************** Singleton Service **************************–>
     <service name="InstanceLib.SingletonService" behaviorConfiguration="SingletonServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="
http://localhost:9015/SingletonService"/>
            <add baseAddress="net.tcp://localhost:9016/SingletonService"/>
          </baseAddresses>
        </host>

        <endpoint address="http://localhost:9015/SingletonService" binding="wsHttpBinding" contract="InstanceLib.ISingletonService"/>
        <endpoint address="net.tcp://localhost:9016/SingletonService" binding="netTcpBinding" contract="InstanceLib.ISingletonService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>

    </services>

    <!– **************************** behaviors ************************** –>
    <behaviors>
      <serviceBehaviors>

        <!– Single Call Service Behavior –>
        <behavior name="SingleCallServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>

        <!–Single Session Service Behavior –>
        <behavior name="SessionServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>

        <!–Singleton Service Behavior –>
        <behavior name="SingletonServiceBehavior">

          <serviceMetadata httpGetEnabled="true"/>
        </behavior>

      </serviceBehaviors>
    </behaviors>

  </system.serviceModel>
</configuration>

Now let’s analyze the Configuration file.

Single Call service.

<!–******************* Single Call Service ************************** –>
      <service name="InstanceLib.SingleCallService" behaviorConfiguration="SingleCallServiceBehavior">
        <host>
         <baseAddresses>
            <add baseAddress="
http://localhost:9011/SingleCallService"/>
            <add baseAddress="net.tcp://localhost:9012/SingleCallService"/>
         </baseAddresses>
        </host>

        <endpoint address="http://localhost:9011/SingleCallService" binding="wsHttpBinding" contract="InstanceLib.ISingleCallService"/>
        <endpoint address="net.tcp://localhost:9012/SingleCallService" binding="netTcpBinding" contract="InstanceLib.ISingleCallService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>

Session service.

<!—************************* Single Session Service ************************* –>
      <service name="InstanceLib.SessionService" behaviorConfiguration="SessionServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="
http://localhost:9013/SessionService"/>
            <add baseAddress="net.tcp://localhost:9014/SessionService"/>
          </baseAddresses>
        </host>

        <endpoint address="http://localhost:9013/SessionService" binding="wsHttpBinding" contract="InstanceLib.ISessionService"/>
        <endpoint address="net.tcp://localhost:9014/SessionService" binding="netTcpBinding" contract="InstanceLib.ISessionService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>

Singleton service.

<!—************************** Singleton Service **************************–>
     <service name="InstanceLib.SingletonService" behaviorConfiguration="SingletonServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="
http://localhost:9015/SingletonService"/>
            <add baseAddress="net.tcp://localhost:9016/SingletonService"/>
          </baseAddresses>
        </host>

        <endpoint address="http://localhost:9015/SingletonService" binding="wsHttpBinding" contract="InstanceLib.ISingletonService"/>
        <endpoint address="net.tcp://localhost:9016/SingletonService" binding="netTcpBinding" contract="InstanceLib.ISingletonService"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
      </service>

 

Writing code to host the service.

// Listing of Program.cs

using System;
using System.Text;
using System.ServiceModel;

namespace InstanceLibHost
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost m_SingleCallHost = null;
            ServiceHost m_SingleSessHost = null;
            ServiceHost m_SingletonHost = null;

            Console.WriteLine("\nHosting Single Call Service at >> " );
            Console.WriteLine("   
http://localhost:9011/SingleCallService");
            Console.WriteLine("    net.tcp://localhost:9012/SingleCallService");
       
            try
            {
                m_SingleCallHost = new ServiceHost(typeof(InstanceLib.SingleCallService));
                m_SingleCallHost.Open();
            }
            catch (Exception eX)
            {
                Console.WriteLine("Failed while starting Single Call Service Service [" + eX.Message + "]");
                m_SingleCallHost = null;
            }

            if ( m_SingleCallHost!= null ) Console.WriteLine("Single Call Service hosted successfully . . .");

            Console.WriteLine("\nHosting Single Session Service at >> ");
            Console.WriteLine("   
http://localhost:9013/SessionService");
            Console.WriteLine("    net.tcp://localhost:9014/SessionService");
            try
            {
                m_SingleSessHost = new ServiceHost(typeof(InstanceLib.SessionService));
                m_SingleSessHost.Open();
            }
            catch (Exception eX)
            {
                Console.WriteLine("Failed while starting Single Session Service [" + eX.Message + "]");
                m_SingleSessHost = null;
            }

            if (m_SingleSessHost != null) Console.WriteLine("Single Session Service hosted successfully . . .");

            Console.WriteLine("\nHosting Singlton Service at >> " );
            Console.WriteLine("   
http://localhost:9015/SingletonService" );
            Console.WriteLine("    net.tcp://localhost:9016/SingletonService");
       
            try
            {
                m_SingletonHost = new ServiceHost(new InstanceLib.SingletonService());
                m_SingletonHost.Open();
            }
            catch (Exception eX)
            {
                Console.WriteLine("Failed while starting Service [" + eX.Message + "]");
                m_SingletonHost = null;
            }
            if (m_SingletonHost != null) Console.WriteLine("Singleton Service hosted successfully . . .");

            Console.WriteLine("Press any key to close . . ." );
            Console.ReadKey();

            m_SingleCallHost.Close ();
            m_SingleSessHost.Close ();
            m_SingletonHost.Close () ;

            m_SingleCallHost = null;
            m_SingleSessHost = null;
            m_SingletonHost = null;
        }
    }
}

Build and Execute the Service.

Open a command prompt and execute the host. here is the output.

image

Now when the service is hosted and running, let’s now create a client, which is actually going to use this service.

Creating Client

Creating the base project

Take a console based application.

Generating proxies

while the host application is running, right click on the client application project, click on

Generate Proxy for SingleCallService.

References –> Add Service Reference

in the address bar type the address of the mex endpoint address of SingleCallService as shown below.

image

in the namespace type some good name, let’s say SingleCallServiceReference for instance.

hit OK, this will simply add a new file app.config to the project. and a new item in the Service Reference node of the project.

Generate Proxy for SessionService.

References –> Add Service Reference

in the address bar type the address of the mex endpoint address of SessionService as shown below.

image

in the namespace type some good name, let’s say SessionServiceReference for instance.

Generate Proxy for SingletonService.

References –> Add Service Reference

in the address bar type the address of the mex endpoint address of SingletonService as shown below.

image

in the namespace type some good name, let’s say SingletonServiceReference for instance.

Now when you have added references of all 3 services, let’s write code for the client to use these services.

// Listing of Program.cs

using System;
using System.Text;

namespace InstanceClient
{
class Program
    {
        static void Main(string[] args)
        {
            SingleCallServiceReference.SingleCallServiceClient objSvc1 = new SingleCallServiceReference.SingleCallServiceClient("WSHttpBinding_ISingleCallService");
            SingleCallServiceReference.SingleCallServiceClient objSvc2 = new SingleCallServiceReference.SingleCallServiceClient("NetTcpBinding_ISingleCallService");

            Console.WriteLine("Client 1 Calling Single Call Service 3 times Using Http Binding");
            for (int nI = 0; nI < 3; nI++)
            {
                double dblValue1 = 10;
                double dblResult1 = objSvc1.AddValue(dblValue1);
                Console.WriteLine("Using HTTP Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue1, dblResult1);
            }

            Console.WriteLine("Client 2 Calling Single Call Service 3 times using TCP Binding");
            for (int nI = 0; nI < 3; nI++)
            {
                double dblValue2 = 10;
                double dblResult2 = objSvc2.AddValue(dblValue2);
                Console.WriteLine("Using TCP  Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue2, dblResult2);
            }

            SessionServiceReference.SessionServiceClient objSvc3 = new SessionServiceReference.SessionServiceClient("WSHttpBinding_ISessionService");
            SessionServiceReference.SessionServiceClient objSvc4 = new SessionServiceReference.SessionServiceClient("NetTcpBinding_ISessionService");

            Console.WriteLine("\n\nClient 1 Calling Single Session Service 3 times Using Http Binding");
            for (int nI = 0; nI < 3; nI++)
            {
                double dblValue1 = 10;
                double dblResult1 = objSvc3.AddValue(dblValue1);
                Console.WriteLine("Using HTTP Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue1, dblResult1);
            }

            Console.WriteLine("Client 2 Calling Single Session Service 3 times using TCP Binding");
            for (int nI = 0; nI < 3; nI++)
            {
                double dblValue2 = 10;
                double dblResult2 = objSvc4.AddValue(dblValue2);
                Console.WriteLine("Using TCP  Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue2, dblResult2);
            }

            SingletonServiceReference.SingletonServiceClient objSvc5 = new SingletonServiceReference.SingletonServiceClient("WSHttpBinding_ISingletonService");
            SingletonServiceReference.SingletonServiceClient objSvc6 = new SingletonServiceReference.SingletonServiceClient("NetTcpBinding_ISingletonService");

            Console.WriteLine("\n\nClient 1 Calling Singleton Service 3 times Using Http Binding");
            for (int nI = 0; nI < 3; nI++)
            {
                double dblValue1 = 10;
                double dblResult1 = objSvc5.AddValue(dblValue1);
                Console.WriteLine("Using HTTP Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue1, dblResult1);
            }

            Console.WriteLine("Client 2 Calling Singleton Service 3 times using TCP Binding");
            for (int nI = 0; nI < 3; nI++)
            {
                double dblValue2 = 10;
                double dblResult2 = objSvc6.AddValue(dblValue2);
                Console.WriteLine("Using TCP  Binding >> Input Value : {0:F2} Return value : {1:F2}", dblValue2, dblResult2);
            }

            Console.WriteLine("Press any key to close . . . ");
            Console.ReadKey();
        }
    }
}

 

Open command prompt and execute the client to see the output.

image

Open one more command prompt, and execute another instance of client.

image

as you can see from the output, all the instance mode in action.

September 29, 2013 Posted by | .NET, CodeProject, SOA, WCF | , , , | Leave a comment

Creating a Self Hosted WCF Service

In this post I am going to explain how to create a simple WCF Service, with Self Hosting and consume it, there will be 4 parts of the project.

  1. A Class Library (MyMathServiceLib.dll): which will implement our business logic.
  2. Service Host Application (MathServiceHost.exe): Application which will host this class library as a WCF Service.
  3. A Windows Client Application (MyMathServiceClient.exe): Client Application which will use this service.
  4. A Web Client, that will use this Service.

Note:

part 1 and 2 can be merged into one, if one like so, but, for the sake of portability, keep them as separate modules. let’s start with creating the Class Library.

Part 1: Creating the Class Library

Start the Visual Studio, and click File->New->Project . Select the project template ‘Class Library" click OK.

image

Now let’s analyse the project created by wizard for us, and do little house keeping, before we write any code.

  • Class1.cs (The default class, created by wizard).

Assumption

We are going to create a Service as per following assumptions.

  • Interface will named as IMyMathService (IMyMathService.cs)
  • Service will be implemented as MyMathService (MyMathService.cs)

Little house keeping. (just to keep things organized)

  • Delete Class1.cs the project workspace.
  • Add an Interface (IMyMathService) IMyMathService.cs to the project.
  • Add an Class (MyMathService) MyMathService.cs to the project.
  • Add reference of System.ServiceModel to the  project.

Now, let’s define the Interface and its implementation of our old familiar class Library.

//  Listing of IMyMathService.cs,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace MyMathServiceLib
{
    [ServiceContract]
    public interface IMyMathService
    {
        [OperationContract]
        double Add(double dblNum1, double dblNum2);

        [OperationContract]
        double Subtract(double dblNum1, double dblNum2);

        [OperationContract]
        double Multiply(double dblNum1, double dblNum2);

        [OperationContract]
        double Divide(double dblNum1, double dblNum2);
    }
}

//  Listing of MyMathService.cs,

using System;
using System.ServiceModel;
using System.Text;
using System.ServiceModel;

namespace MyMathServiceLib
{
    public class MyMathService : IMyMathService
    {
        public double Add(double dblNum1, double dblNum2)
        {
            return (dblNum1 + dblNum2);
        }

        public double Subtract(double dblNum1, double dblNum2)
        {
            return (dblNum1 – dblNum2);
        }

        public double Multiply(double dblNum1, double dblNum2)
        {
            return (dblNum1 * dblNum2);
        }

        public double Divide(double dblNum1, double dblNum2)
        {
            return ((dblNum2 == 0) ? 0 : (dblNum1 / dblNum2));
        }
    }
}

Part 2: Creating the Host Application

Our class library is ready, let’s write an Host application, which will expose this facility to the world as a WCF Service. lets’ Create a Console based application, for the sake of simplicity, name it MyMathServiceHost, as shown below.

image

a new Project MyMathServiceHost will be added to the workspace.

Assumption

The Host application will expose the following endpoints for the service.

  • Service will implement HTTP endpoint at Port 9001
  • Corresponding mex End Point  (IMetadatExchange) for the HTTP end point.  
  • Service will implement TCP endpoint at Port 9002
  • Corresponding mex End Point  (IMetadatExchange) for the TCP end point.  

Adding reference of the class library to the Host project

  • Add reference of the class library to the console project,as shown below.

image

  • Add reference of System.ServiceModel to the  project.

Creating the Service Configuration

Add a Application configuration file (App.config) to the Host Project. define the End Points and bindings according to the assumptions made at the start of the project.

  • will expose an end point with HTTP Binding at 9001
  • will expose an end point with TCP Binding at 9002
  • will expose, required mex (one for each binding) end points

below is he listing of the app.config file.

<?xml versio="1.0"?>
<configuration>

    <system.serviceModel>
      <services>
        <service name="MyMathServiceLib.MyMathService" behaviorConfiguration="myMathServiceBehave">
          <host>
            <baseAddresses>
              <add baseAddress="http://localhost:9001/MyMathService"/>
              <add baseAddress="net.tcp://localhost:9002/MyMathService"/>
            </baseAddresses>
          </host>
          <endpoint address="http://localhost:9001/MyMathService" binding="basicHttpBinding" contract="MyMathServiceLib.IMyMathService"/>
          <endpoint address="net.tcp://localhost:9002/MyMathService" binding="netTcpBinding" contract="MyMathServiceLib.IMyMathService"/>
          <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
          <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
        </service>
      </services>
      <behaviors>
        <serviceBehaviors>
          <behavior name="myMathServiceBehave">
            <serviceMetadata httpGetEnabled="true"/>
          </behavior>
        </serviceBehaviors>
      </behaviors>
    </system.serviceModel>
</configuration>

Description
  • Application configuration files defines 2 endpoints, one for TCP and one for HTTP. and 1 mex end point for each.
  • Service behavior define, <serviceMetadata httpGetEnabled="true"/> that meta data should be available through a HTTP get request.

Write code to Host the Service.

Write the code to Host the Service in the Main function of the Open Program.cs, and let’s write the code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace CalcServiceHost
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost svcHost = null;
            try
            {
                svcHost = new ServiceHost(typeof(MyMathServiceLib.MyMathService));
                svcHost.Open();
                Console.WriteLine("\n\nService is Running  at following address" );
                Console.WriteLine("\nhttp://localhost:9001/MyMathService");
                Console.WriteLine("\nnet.tcp://localhost:9002/MyMathService");
            }
            catch (Exception eX)
            {
                svcHost = null;
                Console.WriteLine("Service can not be started \n\nError Message [" + eX.Message + "]");
            }

            if (svcHost != null)
            {
                Console.WriteLine("\nPress any key to close the Service");
                Console.ReadKey();
                svcHost.Close();
                svcHost = null;
            }
        }
    }
}

explanation

as you can see from the above code,

  • Class Library is simply hosted inside a ServiceHost object, as all the End Points are defined in App.config file.

Part 3: Creating the Windows Client

Now when our service is self hosted and running, let us write a client Application, which can use this Service. lets’ Create a Console based application, for the sake of simplicity, name it MyMathServiceClient, as shown below.

image

newly created project will be added to the work space.

Generating proxy and config file.

Before we write the Client application, we need to generate the proxy for our Service, open a command prompt in Administrator mode. and execute the Host Application.

image

Open another command prompt, switch to the folder, where client project has been created, and generate the proxy and configuration file using the SVCUtil.exe utility. as shown below.

image

add both these files to the client project.

Calling the Service

Now write the code to use the proxy and call the service, here is the listing of the Program.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyMathClient
{
    class Program
    {
        static void Main(string[] args)
        {
            double dblX = 2000.0;
            double dblY = 100.0;
            double dblResult = 0;

            try
            {
                Console.WriteLine("Using TCP Binding", dblX, dblY, dblResult);

                MyMathServiceClient mathClient1 = new MyMathServiceClient("NetTcpBinding_IMyMathService");

                dblResult = mathClient1.Add(dblX, dblY);
                Console.WriteLine("Calling Add >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

                dblResult = mathClient1.Subtract(dblX, dblY);
                Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

                dblResult = mathClient1.Multiply(dblX, dblY);
                Console.WriteLine("Calling Mul >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

                dblResult = mathClient1.Divide(dblX, dblY);
                Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

                Console.WriteLine("Using Basic HTTP Binding", dblX, dblY, dblResult);

                MyMathServiceClient mathClient2 = new MyMathServiceClient("BasicHttpBinding_IMyMathService");

                dblResult = mathClient2.Add(dblX, dblY);
                Console.WriteLine("Calling Add >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

                dblResult = mathClient2.Subtract(dblX, dblY);
                Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

                dblResult = mathClient2.Multiply(dblX, dblY);
                Console.WriteLine("Calling Mul >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

                dblResult = mathClient2.Divide(dblX, dblY);
                Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);
            }
            catch (Exception eX)
            {
                Console.WriteLine("There was an error while calling Service [" + eX.Message + "]");
            }
        }
    }
}

and here is the output.

image

Part 4: Creating a Web Client

Create a New Web Site, using the File –> New Web Site as shown below. name it MyMathWebClient.

image

 

Click OK,

Now we need to add a reference to our service to this web site, right click on the newly created web site project, and click on Add Service Reference. a dialog box will pop up.

  • Enter any (TCP/HTTP) endpoint address in the address box.
  • Name the namespace, where Service Proxy will be added to the project.

image

click OK, some changes will be made to your web.config file, and App_WebReferences folder will be added, under which a Service reference "MathServiceReference" will be added.

  • a Service reference named MyMathServiceReference will be added in Service References node of the client project.
  • some changes will be made to the web.config file,

let’s analyse the changes made to web.config. open the web.config, you will find the changes at the end of the file, see the <system.ServiceModel> section, as shown below.

<system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IMyMathService"/>
      </basicHttpBinding>
      <netTcpBinding>
        <binding name="NetTcpBinding_IMyMathService"/>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="
http://localhost:9001/MyMathService&quot; binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyMathService" contract="MathServiceReference.IMyMathService" name="BasicHttpBinding_IMyMathService"/>
      <endpoint address="net.tcp://localhost:9002/MyMathService" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IMyMathService" contract="MathServiceReference.IMyMathService" name="NetTcpBinding_IMyMathService">
        <identity>

          <!—this value will be different on your machine –!>
          <userPrincipalName value="PRAVEEN-WIN7\BriskTech"/>
        </identity>
      </endpoint>
    </client>
</system.serviceModel>

Open the Main page of the site (Default.aspx page), double click, that will create the Page_Load  handler for the Page, lets call our service in the page load handler.

protected void Page_Load(object sender, EventArgs e)
    {
        double dblX = 10000.0 ;
        double dblY = 2000.0 ;
        Response.Write ( "<p>Value 1 : " + dblX.ToString ( "F2"));
        Response.Write ( "<br>Value 2 : " + dblY.ToString ( "F2"));

        try
        {
            Response.Write("<p>Using TCP Binding");

            MathServiceReference.MyMathServiceClient mathProxy1 = new MathServiceReference.MyMathServiceClient("NetTcpBinding_IMyMathService");

            double dblResult = mathProxy1.Add(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

            dblResult = mathProxy1.Subtract(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

            dblResult = mathProxy1.Multiply(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

            dblResult = mathProxy1.Divide(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

            Response.Write("<p>Using Basic HTTP Binding");

            MathServiceReference.MyMathServiceClient mathProxy2 = new MathServiceReference.MyMathServiceClient("BasicHttpBinding_IMyMathService");

            dblResult = mathProxy2.Add(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

            dblResult = mathProxy2.Subtract(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

            dblResult = mathProxy2.Multiply(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

            dblResult = mathProxy2.Divide(dblX, dblY);
            Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));
        }
        catch (Exception eX)
        {
            Response.Write("There was an error while calling Service <p> <b>[" + eX.Message + "] </b>");
        }
    }

Build and run the web site, following output will be displayed.

image

that’s all folks.

September 9, 2013 Posted by | CodeProject, SOA, WCF | , , | Leave a comment

Creating Simple WCF Service with IIS Hosting

In this post I am going to explain how to create a simple WCF Service, with IIS Hosting and consume it, there will be two parts of the project.

  • A WCF Service (MyMathService.svc): will be hosted by IIS.
  • Client Application (MyMathClient.exe): Client Application which will use this service.

let’s start with creating the WCF Service.

Creating the WCF Service

Start the Visual Studio 2010 in administrator Mode (Run as Administrator), and click File->New->Web Site. Select the project template ‘WCF Service’ and Location as http. This will directly host the service in IIS and click OK. select the template “WCF Service”. 

image

Now let’s analyse the project created by wizard for us, and do little house keeping, before we write any code.

  • IService.cs (the interface definition of the service)
  • Service.cs (the implementation of the service).
  • web.config (Service configuration file).
  • Service.svc

Assumption

We are going to create a Service as per following assumptions.

  • Service will be named as "MyMathService" (MyMathService.svc)
  • Interface will named as IMyMathService (IMyMathService.cs)
  • Service will be implemented as MyMathService (MyMathService.cs)
  • Service will implement Http endpoint at Port 8001.
  • Service will also expose IMetadataExchange endpoint.  IMetadataExchange interface Exposes methods used to return metadata about a service.

Little house keeping. (just to keep things organized)

  • Rename IService.cs as IMyMathService.cs.
  • Rename Service.cs as MyMathService.cs.
  • Rename Service.svc as MyMathService.svc, and change its content as

Previous Content

<%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

New Content

<%@ ServiceHost Language="C#" Debug="true" Service="MyMathService" CodeBehind="~/App_Code/MyMathService.cs" %>

Defining Configuration File

Changing web.config file.

Below is the listing of the configuration file, generated by visual studio wizard, as we know, that Services are defined in <System.servicemodel> section. Below is the listing of the configuration file, generated by visual studio wizard.  let’s define a service and endpoint, see the highlighted part.

<?xml version="1.0"?>
<configuration>

                 <configSections>
        . . . . . . . . .
         . . . . . . . . .
      </configSections>

    <system.web>
     . . . . . . . . .
     . . . . . . . . .
    </system.web>

  <system.serviceModel> 
<services>
     <service name="MyMathService" behaviorConfiguration="svcBehavior">  
        
<endpoint address=http://localhost:8001/MyMathService/MyMathService.svc" binding="wsHttpBinding" contract="IMyMathService">

          <identity> <dns value="localhost"/> </identity>
</endpoint>

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

   <behaviors>
      <serviceBehaviors>
        <behavior  name="svcBehavior">
          <!– To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment –>
         <serviceMetadata httpGetEnabled="true"/>
          <!– To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information –>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
   
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel> 

<system.codedom>
         . . . . . . . . .
         . . . . . . . . .
      </system.codedom>

   <system.webServer>
         . . . . . . . . .
         . . . . . . . . .
   </system.webServer>    

   <runtime>
         . . . . . . . . .
         . . . . . . . . .
   </runtime>

 
</configuration>

Explanation

See the highlighted text, as you can see service defines two endpoints, one is first end point defines an address(http://localhost:8001/MyMathService), binding(HTTP) and contract (IMyMathService), the other is MEX endpoint, MEX endpoint are defined to import Metadata (to be used by client applications) from a Service using utility SVCUtil.exe

<endpoint address ="http://localhost:8001/MyMathService&quot; binding ="basicHttpBinding" contract="IMyMathService" />

<endpoint address ="MEX" binding ="mexHttpBinding" contract="IMetadataExchange" />

In the serviceBehaviors, a behavior named "svcBehavior" defines following attributes about the service.

<serviceMetadata httpGetEnabled="true"/>

metadata information about the service can disclosed through a get request (if this is false, SVCUtil.exe would not be able to obtain metadata information about the service).

<serviceDebug includeExceptionDetailsInFaults="true"/>

can be set true, to receive exception details in faults for debugging purposes, should be set to false, before deployment, to avoid disclosing exception information.

Defining Service Interface (IMyMathService.cs)

using System;
using System.ServiceModel;
using System.Text;

[ServiceContract]
public interface IMyMathService
{
    [OperationContract]
    double Add(double dblNum1, double dblNum2);

    [OperationContract]
    double Subtract(double dblNum1, double dblNum2);

    [OperationContract]
    double Multiply(double dblNum1, double dblNum2);

    [OperationContract]
    double Divide(double dblNum1, double dblNum2);
}

explanation

as one can see, there is one service contract (IMyMathService), and 4 operation contract to perform 4 basic math operations.

Implementing Service (MyMathService.cs)

using System;
using System.ServiceModel;
using System.Text;

public class MyMathService : IMyMathService
{
    public double Add(double dblNum1, double dblNum2)
    {
        return (dblNum1 + dblNum2);
    }

    public double Subtract(double dblNum1, double dblNum2)
    {
        return (dblNum1 – dblNum2);
    }

    public double Multiply(double dblNum1, double dblNum2)
    {
        return (dblNum1 * dblNum2);
    }

    public double Divide(double dblNum1, double dblNum2)
    {
        return ((dblNum2 == 0) ? 0 : (dblNum1 / dblNum2));
    }
}

explanation

here simply those 4 methods has been implemented.

Creating Client for the Service

Add a new project to the workspace, say a Console based application, name it MyMathClient, for instance.

image

newly created project will be added to the work space.

Generating proxy and config file.

right click on the newly created (client) project, and click on "Add Service Reference", as shown below.

image

a dialog box will be displayed as shown below. in the address bar type the address of the service,

image

Click OK,

  • A app.config (client side configuration) will be added to the project.
  • a Service reference named MyMathServiceProxy will be added in Service References node of the client project.

Calling the Service

Now write the code to use the proxy and call the service, here is the listing of the Program.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyMathClient
{
    class Program
    {
        static void Main(string[] args)
        {
            double dblX = 2000.0 ;
            double dblY = 100.0 ;
            double dblResult = 0 ;
            MyMathServiceProxy.MyMathServiceClient mathClient = new MyMathServiceProxy.MyMathServiceClient();

            dblResult = mathClient.Add (dblX, dblY );
            Console.WriteLine("Calling Add >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);
           
            dblResult = mathClient.Subtract (dblX, dblY);
            Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

            dblResult = mathClient.Multiply(dblX, dblY);
            Console.WriteLine("Calling Mul >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);

            dblResult = mathClient.Divide(dblX, dblY);
            Console.WriteLine("Calling Sub >>  X : {0:F2}  Y : {1:F2}  Result : {2:F2}", dblX, dblY, dblResult);
        }
    }
}

and here is the output.

image

Creating a web Client for the Service

Add a new web site to the workspace, name it MyWebMathClient, for instance as shown below.

image

Add Service reference of the Math Service to the newly created website, as shown below

image

Adding this Service Reference, made a change in the web.config file of the Client Web site, if you open the web.config, you will find the following changes, at the bottom of the file.

<system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IMyMathService" />
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="
http://localhost/MyMathService/MyMathService.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IMyMathService"
                contract="MyMathServiceReference.IMyMathService" name="WSHttpBinding_IMyMathService">
                <identity>

                   <!—this name can be different on your machine. –!>
                    <servicePrincipalName value="host/PRAVEEN-WIN7" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>

 

Open the Page_load Handler of the Default.aspx in the newly created (Client Web Site) and put the code as shown below.

protected void Page_Load(object sender, EventArgs e)
   {
       double dblX = 10000.0;
       double dblY = 2000.0;
       double dblResult = 0;
       Response.Write("<p>Value 1 : " + dblX.ToString("F2"));
       Response.Write("<br>Value 2 : " + dblY.ToString("F2"));

       try
       {
           Response.Write("<p>Using WS HTTP Binding");

           MyMathServiceReference.MyMathServiceClient mathProxy2 = new MyMathServiceReference.MyMathServiceClient("WSHttpBinding_IMyMathService");

           dblResult = mathProxy2.Add(dblX, dblY);
           Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

           dblResult = mathProxy2.Subtract(dblX, dblY);
           Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

           dblResult = mathProxy2.Multiply(dblX, dblY);
           Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));

           dblResult = mathProxy2.Divide(dblX, dblY);
           Response.Write("<br>Calling Add >>  X : " + dblX.ToString("F2") + "  Y : " + dblY.ToString("F2") + " Result : " + dblResult.ToString("F2"));
       }
       catch (Exception eX)
       {
           Response.Write("There was an error while calling Service <p> <b>[" + eX.Message + "] </b>");
       }
   }

and when you run this web site, you will get following output.

image

September 6, 2013 Posted by | .NET, CodeProject, SOA, WCF | , , , | 1 Comment

Introduction to WCF

What is WCF

In a nutshell, WCF is a SDK for developing and deploying services on Windows, although services can be build even without WCF.  Developing services with WCF is easier. WCF is Microsoft’s implementation of a set of industry standards defining service interactions, type conversions, marshaling, and management of various protocols. consequently, WCF provides interoperability between services.    

What is SOA

SOA is the next evolutionary step in the long journey from functions to object to components to service orientated applications. Service orientation (SO) is an abstract set of methodologies, principles and best practices for building service oriented applications(SOA). Service oriented application aggregates services into a single logical application.

What is a Service

A service is a unit of functionality exposed to the world. the services can be local or remote, can be deployed by multiple parties using any technology.

What is a WCF Service

WCF Service typically exposes metadata describing the available functionality and possible ways of communicating with service. Metadata is published in a predefined, technology-neutral way such as using WSDL (Web Services Description Language) over HTTP-GET or an industry standard for metadata exchange over any protocol. WCF services may communicate over a variety of transports (not just HTTP). A non WCF client can import the metadata to its native environment as native types. similarly, a WCF client can import the metadata of a non WCF service and  consume it as a native CLR class/interface. 

Anatomy of WCF Service/ A B C of WCF Service

(A) Address

in WCF every service is associated with a unique address. the address provides two important elements:

  • location of the service: the location portion of the address indicates the name of the target machine, site, or network; a communication port, pipe or queue; and an optional specific path, or URI. URI can be any unique string, such as service name or a globally unique identifier (GUID).

  • transport protocol/transport scheme: WCF supports following transport protocols/schemes.

    • HTTP/HTTPS

    • TCP

    • IPC

    • Peer network

    • MSMQ

    • Service Bus

the address always have the following format

[base address] / [optional URI]

the base address is always in the format

[transport]://[machine or domain][optional port]

here are few examples of  sample addresses.

http://localhost:8001                              

http://localhost:8001/MyService           

net.tcp://localhost:8002/MyService      

net.pipe://localhost/MyPipe                   

net.msmq://localhost/MyQueue             

here are few examples of  sample addresses.

(B) Binding

A binding is merely, a consistent, canned set of choices regarding the transport protocol, message encoding, communication pattern, reliability, security, transaction propagation and interoperability. A single service can support multiple bindings on separate addresses. Binding allow you to use the same service logic over drastically different plumbing. all you need to do is determine the target scenario for your service, and WCF makes a correct multidimensional decision for you regarding all the aspects of the communication. One can use the WCF-provided bindings out of the box, can tweak their properties, or can write their own custom bindings from scratch. WCF defines 5 frequently used bindings.

  • Basic Binding: offered by the BasicHttpBinding class, basic binding is designed to expose a WCF services as a legacy ASMX web service, so that old clients can work with the new services. this binding makes your service look like a legacy web service that communicates over basic web service profile. when used by clients, this enables new WCF clients to work with old ASMX services.
  • TCP Binding:  offered by NetTcpBinding class, TCP binding uses TCP for cross machine communication on the intranet. it supports variety of features, including reliability, transactions and security, and is optimized for WCF to WCF communication, hence it requires both the client and the service to use WCF.
  • IPC Binding: offered by NetNamedPipeBinding class, IPC binding uses named pipes as a transport for same-machine communication. it is the most secure binding, since it can not accept calls outside of the machine boundary. It is also the most performant binding, since IPC is a lighter protocol than TCP.
  • Web Service (WS) Binding: offered by WSHttpBinding class, the WS binding uses HTTP or HTTPS for transport and offers a variety of features(such as reliability, transactions, and security) over the Internet, all using the WS-* standards.
  • MSMQ Binding: offered by the NetMsmqBinding class, the MSMQ binding uses MSMQ for transport and offers support for disconnected queued calls. 

each frequently used binding  uses different transport scheme and encoding as shown below.

Name Transport Encoding Interoperable
BasicHttpBdinding HTPP/HTTPS TEXT, MTOM Yes
NetTcpBinding TCP Binary No
NetNamedPipeBinding IPC Binary No
WsHttpBinding HTTP/HTTPS TEXT, MTOM Yes
NetMsmqBidning MSMQ Binary No

having text based encoding typically enables a WCF service (or client) to communicate over HTTP with any other service (or client), regardless of its technology and across firewalls. Binary encoding over TCP, IPC or MSMQ yields the best performance, but it does so at the cost of interoperability because it mandates the WCF-WCF communication.

Choosing a Binding

When choosing a binding the following diagram can help.

image

in addition to the five frequently used bindings described above, WCF defines 6 infrequently used bindings. these bindings are each designed for a specific target scenario, which normally can not be used out of that scenario. 

  • WS dual binding:  offered by WSDualHttpBinding class, similar to the WS binding, except it also supports bidirectional duplex communication from the service to the client. This binding nothing more than just two WsHttpBinding bindings wired up against each other to support callbacks, as there is no industry standard for setting up callback, thus WSDualHttpBinding is not interoperable.
  • Peer Network binding: offered by NetPeerTcpBinding class, this uses peer networking as a transport: the peer network enabled client and services all subscribe to the same grid and broadcast messages to it.
  • Fedrated WS binding: offered by WSFedrationHttpBinding class, a specialization of WS binding that offers support for federated security, not a main stream binding. 
  • Fedrated WS 2007 binding: offered by WS2007FedrationHttpBinding class, this is an update of a WSFedrationHttpBinding.
  • MSMQ integration binding: offered by MSMQIntegrationBinding class, this is the analogous queued –world binding to the basic binding. The integration binding converts WCF messages to and from MSMQ messages and is designed to interoperate with legacy MSMQ clients.
  • WS 2007 binding: offered by WS2007HttpBinding class, this binding is derived from the WSHttpBinding class; it adds support for the emerging co-ordination standard and updates for the transaction, security, and reliability standards.

(C) Contracts

A WCF contract is a platform neutral and standard way of describing what a service does. in WCF all services expose contracts. WCF defines 4 types of contracts.

  • Service Contracts: Describes which related operations can be tied together as a single functional unit that the client can perform on the service. Service contracts can be defined as shown below.

    [ServiceContract]
    public interface IMyService
    {

    }

  • Operation Contract: An operation contract defines the parameters and return type of an operation.

    [ServiceContract]
    public interface IMyService
    {
       [OperationContract]
        double MultiplyNumber(double dblX, double dblY );   
       [OperationContract]
        double DivideNumber(double dblX, double dblY );  
    }

  • Data Contracts: Defines which data types are passed to and from the service. WCF defines implicit contracts for built in types such as int and string, but you can easily define explicit opt in data contracts for custom types.

    [DataContract]
    public class ErrorInfo
    {
        private string m_strError;
        private int m_nErrorCode;
       
       [DataMember]
        public string ErrorMessage
        {
            get { return m_strError; }
            set { m_strError = value; }
        }

       [DataMember]
        public int ErrorCode
        {
            get { return m_nErrorCode }
            set { m_nErrorCode = value; }
        }
    }

  • Fault Contracts: Defines which errors are raised by the service and how the service handles and propagates errors to the clients. Fault Contract provides documented view for error occurred in the service to client. This helps to easily identity the error.

    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        double MultiplyNumber(double dblX, double dblY );   
        [OperationContract]
       [FaultContract(typeof(DevideByZeroException))]
        double DivideNumber(double dblX, double dblY );  
    }

  • Message Contracts: Allow the service to interact directly with messages. Message contracts can be types or un typed and are useful in interoperability cases when another party has already dictated some explicit (mostly proprietary) message format. 

    [MessageContract]
    public class ErrorDetails
    {
        [MessageHeader]
        public int  ErrorCode;
       
       [MessageBodyMember]
        public string ErrorMessage;
       [MessageBodyMember]
        public DateTime ErrorTime;
    }

Hosting

The WCF service class can not exist in void. every WCF service must be hosted in a Windows process called host process. a single host process can host multiple services, and the same service type can be hosted in multiple services.

Hosting Environment and Supported Bindings

Hosting Environment

Supported protocol

Windows console and form application

http, net.tcp, net.pipe, net.msmq

Windows service application (formerly known as NT services)

http, net.tcp, net.pipe, net.msmq

Web server IIS6

http, wshttp

Web server IIS7 – Windows Process Activation Service (WAS)

http, net.tcp, net.pipe, net.msmq

 

A summary of hosting options and supported features.

Feature

Self-Hosting

IIS Hosting

WAS Hosting

Executable Process/ App Domain

Yes

Yes

Yes

Configuration

App.config

Web.config

Web.config

Activation

Manual at startup

Message-based

Message-based

Idle-Time Management

No

Yes

Yes

Health Monitoring

No

Yes

Yes

Process Recycling

No

Yes

Yes

Management Tools

No

Yes

Yes

Endpoints

Every service is associated with an address that defines where the service is, a binding defines how to communicate with the service, and the contract defines what the service does. WCF formalizes this relationship in the form of endpoint. The endpoint is the coalition of address, binding and contract. So every endpoint must have all 3 elements, and the host exposes the endpoint. logically, endpoint is the interface of the service to the outside world. Every service must expose at least one business Endpoint, and each endpoint has exactly one contract. All endpoints on a service has unique addresses, and a single service can expose multiple endpoints.

Configuration of Endpoint

It is important to note nothing in the service code has to do with its service points. and they are always external to the service code. you can configure endpoints either administratively or programmatically.

Administrative Endpoint Configuration

<system.ServiceModel>
    <services>
        <service name="MyService">
            <endpoint address="http://localhost:8000/MyService&quot; binding ="wsHttpBinding" contract="IMyContract">
            <endpoint address="http://localhost:8001/MyService&quot; binding ="netTcpBinding" contract="IMyContract">
            <endpoint address="http://localhost:8002/MyService&quot; binding ="netTcpBinding" contract="IOtherContract">
        </service>
    </services>
</system.ServiceModel>

Programmatic Endpoint Configuration

ServiceHost host = new ServiceHost(typeof(MyService));
Binding httpBinding = new WSHttpBinding();
Binding tcpBinding = new NetTcpBinding();
host.AddServiceEndPoint( typeof (IMyContract), httpBinding, "
http://localhost:8000/MyService");
host.AddServiceEndPoint( typeof (IMyContract), tcpBinding, "net.tcp://localhost:8001/MyService");
host.AddServiceEndPoint( typeof (IMyOtherContract), tcpBinding, "net.tcp://localhost:8002/MyService");

Default Endpoints

If the service host does not define any end points (neither in configuration nor programmatically) but does provide at least one base address. WCF will by default add endpoints to the service. These are called default endpoints. WCF will add an endpoint per base address per contract, using the base address as the endpoint’s address. for HTTP WCF will use the basic binding. Note that default binding will affect the default endpoints. WCF will also name the endpoint by concatenating the binding name and the contract name.

consider the following example.

[ServiceContract]
interface IMyContract {
    //Interface Body
    … 
}

[ServiceContract]
interface IMyOtherContract {
    //Interface Body
    … 
}

class MyService : IMyContract, IMyOtherContract {
    //Class Body
    … 
}

//    Host Application
Uri httpbaseAdr = new Uri ( "http://localhost:8000/");
Uri tcpbaseAdr = new Uri ( "net.tcp://localhost:9000/");
Uri pipebaseAdr = new Uri ( "net.pipe://localhost/");
ServiceHost host = new ServiceHost ( typeof (MyService), httpbaseAdr, tcpbaseAdr, pipebaseAdr);
host.Open ();

Assuming that no config file is used to define any additional endpoints, WCF will add these default endpoints, as they were defined in config file.

<service name="MyService">


    <endpoint name="BasicHttpBinding_IMyContract" address="http://localhost:8000/" binding="basicHttpBinding" contract="IMyContract" />
    <endpoint name="NetTcpBinding_IMyContract" address="net.tcp://localhost:9000/" binding="netTcpBinding" contract="IMyContract" />
    <endpoint name="NetNamedPipeBinding_IMyContract" address="net.pipe://localhost/" binding="netNamedPipeBinding" contract="IMyContract" />
   
    <endpoint name="BasicHttpBinding_IMyOtherContract" address="http://localhost:8000/" binding="basicHttpBinding" contract="IMyOtherContract" />
    <endpoint name="NetTcpBinding_IMyOtherContract" address="net.tcp://localhost:9000/" binding="netTcpBinding" contract="IMyOtherContract" />
    <endpoint name="NetNamedPipeBinding_IMyOtherContract" address="net.pipe://localhost/" binding="netNamedPipeBinding" contract="IMyOtherContract" />

</service>   

Metadata Exchange

By default, the service will not publish its metadata. publishing you service meta data will need some efforts. , luckily, the host can do it for you, if you instruct it to do so, as host knows everything, it needs to know about your service and its endpoints.

Enabling Metadata exchange administratively

<system.ServiceModel>
<services>
    <service name="MyService" behaviorConfiguration="MEXGET">
        <host>
        <baseAddresses>
            <add baseAddress = "net.pipe://localhost/" /> 
        </baseAddresses>
        
         <endpoint address="MEX" binding="mexNamedPipeBinding" contract="IMetadataExchange"/>

        <!– Here we have defined the complete address, we do not use the base address. -–>
        <endpoint address="http://localhost:8000/MEX" binding="mexHttpBinding" contract="IMetadataExchange"/>

        <endpoint address="net.tcp://localhost:8001/MEX" binding="mexTcpBinding" contract="IMetadataExchange"/>

    </service>   
</services>
</system.ServiceModel>

in the vast majority of cases, a MEX endpoint always has the same 3 elements: the contract is always IMetadataExchange, the binding is always the reserved binding element, and the only variable is the address (and that too, just the base address)

Adding MEX endpoints programmatically

like any other endpoint, you can also add metadata exchange endpoint programmatically before opening the host. WCF does not offer a dedicated binding type for the metadata exchange endpoint. you can use the following methods of MetadataExchangeBindings  class defined in System.ServiceModal.Description

Uri tcpbaseAdr = new Uri ( “net.tcp://localhost:9000/”);
ServiceHost host = new ServiceHost ( typeof (MyService), tcpbaseAdr );
ServiceMetadataBehavior metaBehave ;
metaBehave = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if ( metaBehave == null )
{
    metaBehave = new ServiceMetadataBehavior();
    host.Description.Behaviors.Add (metaBehave);
}
Binding binding = MetadataExchangeBindings.CreateMexTcpBinding ();
host.AddServiceEndpoint (typeof (IMetadataExchange), binding, "MEX");
host.Open ();

you can also add a MEX endpoint using the standard MEX endpoint.

ServiceHost host = new ServiceHost (typeof(MyService));
host.Description.Behaviors.Add(new ServiceMetadataBehavior());
EndpointAddress address = new EndpointAddress ( "
http://localhost:8000/MEX");
ServiceEndPoint endpoint = new ServiceMetadataEndPoint(address) ;
host.AddServiceEndpoint (endpont);
host.Open ();

Client side Programming

To invoke operations on a service, a client first need to import the service contract to client’s native (environ.) .

Client side Calling Service using Code

Just define the interface definition in the client application in a file, say IMyService.cs.

[ServiceContract]
public interface IMyService
{
   [OperationContract]
    double AddNumber(double dblX, double dblY );   
   [OperationContract]
    double MultiplyNumber(double dblX, double dblY );   
}

assuming that Service is hosted at address “http://localhost:8001/MyService/”

To invoke operations on a service, if you do not want to use a configuration file. a client need to create everything by hand.

private void CallServiceFunctionOnHTTP ()
{
    string strAdr =
http://localhost:8001/MyService/”;
    Uri adrbase = new Uri(strAdr);
    BasicHttpBinding httpb = new BasicHttpBinding();
    ChannelFactory<IMyService> channelFactory = new ChannelFactory<IMyService>(httpb);
   
    strEPAdr = "
http://localhost:8001/MyService" ;
    EndpointAddress ep = new EndpointAddress(strEPAdr );
    IMyService myObj = m_ChannelFactory.CreateChannel(ep);
    double dblResult = 0;
    dblResult = chatObj.AddNumber(dblX, dblY);
}

If the same Service also exposes another EndPoint (let say at "net.tcp://localhost:9000/MyService” ) then you can call the service, through this end point as shown below.

private void CallServiceFunctionOnTCP ()
{
    string strAdr =
net.tcp://localhost:9000/MyService/”;
    Uri adrbase = new Uri(strAdr);
    NetTcpBinding tcpb = new NetTcpBinding();
    ChannelFactory<IMyService> channelFactory = new ChannelFactory<IMyService>(tcpb);
   
    strEPAdr = "
net.tcp://localhost:9000/MyService
" ;
    EndpointAddress ep = new EndpointAddress(strEPAdr );
    IMyService myObj = m_ChannelFactory.CreateChannel(ep);
    double dblResult = 0;
    dblResult = chatObj.AddNumber(dblX, dblY);
}

August 20, 2013 Posted by | .NET, CodeProject, SOA, WCF | , , , , | Leave a comment

Modifying array of structures (default marshaling)

Let’s assume you have a user defined structure “EMPLOYEE” in a win32 DLL (let say it “Win32Native.dll”) as shown below.

typedef struct _EMPLOYEE
{
    int Age ;
    int Sex;
    double Salary ;
    char* FirstName ;
    char* LastName ;
} EMPLOYEE;

Define some functions in native dll (let say it “Win32Native.dll”)  as shown below. 

extern "C" __declspec(dllexport) void ModifyArrayOfEmployeeStruct(int nSize, EMPLOYEE* pArray);

Implementing Functions

extern "C" __declspec(dllexport) void ModifyArrayOfEmployeeStruct( int nSize, EMPLOYEE* pArray)
{
    int result = 0;
    EMPLOYEE* pCur = pArray;
    STRSAFE_LPSTR temp1, temp2 ;
    for ( int i = 0; i < nSize; i++ )
    {
        size_t nLen1 = 0;
        size_t nLen2 = 0;
        StringCchLengthA( pCur->FirstName, STRSAFE_MAX_CCH, &nLen1 );
        StringCchLengthA( pCur->LastName, STRSAFE_MAX_CCH, &nLen2 );
       
        nLen1 = sizeof(char) * ( nLen1 + 11 );    //    To accomodate "<Modified>"
        nLen2 = sizeof(char) * ( nLen2 + 11 );    //    To accomodate "<Modified>"
        temp1 = (STRSAFE_LPSTR)CoTaskMemAlloc( nLen1 );
        temp2 = (STRSAFE_LPSTR)CoTaskMemAlloc( nLen2 );

        StringCchCopyA( temp1, nLen1, (STRSAFE_LPCSTR)"<Modified>" );
        StringCbCatA( temp1, nLen1, (STRSAFE_LPCSTR)pCur->FirstName);

        StringCchCopyA( temp2, nLen2, (STRSAFE_LPCSTR)"<Modified>" );
        StringCbCatA( temp2, nLen2, (STRSAFE_LPCSTR)pCur->LastName);
               
        // CoTaskMemFree must be used instead of delete to free memory.
        CoTaskMemFree( pCur->FirstName  );
        CoTaskMemFree( pCur->LastName  );

        pCur->FirstName = (char *)temp1;
        pCur->LastName = (char *)temp2;

        pCur->Age += 1 ;
        pCur->Salary  += 1000.0 ;
        pCur++;
   }
}

Point of Interest

  • CoTaskMemAlloc is used to allocated the memory required.
  • CoTaskMemFree is used to free any previously allocated buffer, if null is passed then, CoTaskMemFree is not called.
  • StringCchCopyA copies an ANSI string to a string buffer.
  • StringCbCatA Appends an ANSI string to a string buffer.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

  • On the native side use CoTaskMemAlloc() and CoTaskMemFree().

     

    Writing the client code (the managed part)

    one can simple create a console base application which can use this dll. let’s name it MarshallingTest.

    see the code snippet below.

    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace MarshallingTest
    {

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct Employee
        {
            public int Age;
            public int Sex;
            public double Salary;
            public String FirstName;
            public String LastName;
        }
        class Program
        {
            [DllImport("Win32Native.dll")]

            public static extern int ModifyArrayOfEmployeeStruct(

                                                        int nSize,

                                                        [In, Out] Employee[] empArr);
            static void Main(string[] args)
            {
                int nCnt = 5; // Number of Items in Structure Array

                Employee[] emp = new Employee[nCnt];
                emp[0].FirstName = "Ramesh"; emp[0].LastName = "Sharma"; emp[0].Age = 42; emp[0].Salary = 40000; emp[0].Sex = 0;
                emp[1].FirstName = "Shalini"; emp[1].LastName = "Verma"; emp[1].Age = 30; emp[1].Salary = 25000; emp[1].Sex = 1;
                emp[2].FirstName = "Ramesh"; emp[2].LastName = "Sharma"; emp[2].Age = 51; emp[2].Salary = 35000; emp[2].Sex = 0;
                emp[3].FirstName = "Aarushi"; emp[3].LastName = "Shukla"; emp[3].Age = 25; emp[3].Salary = 20000; emp[3].Sex = 0;
                emp[4].FirstName = "Malini"; emp[4].LastName = "Kapoor"; emp[4].Age = 33; emp[4].Salary = 30000; emp[4].Sex = 1;

                Console.WriteLine("\nEmployee Array Before Call");
                for (int nI = 0; nI < nCnt; nI++)
                {
                    StringBuilder sb = new StringBuilder( "First Name=[" );
                    sb.Append(emp[nI].FirstName);
                    sb.Append("]  Last Name=[");
                    sb.Append(emp[nI].LastName );
                    sb.Append("]  Age=[");
                    sb.Append(emp[nI].Age .ToString ());
                    sb.Append("]  Salary=[");
                    sb.Append(emp[nI].Salary.ToString ("F2"));
                    sb.Append("]  Sex=[");
                    sb.Append(((emp[nI].Sex == 0) ? "Male" : "Female"));
                    sb.Append("]");
                    Console.WriteLine(sb.ToString ());
                }

                // Call the Function.

                ModifyArrayOfEmployeeStruct(emp.Length, emp);

                Console.WriteLine("\nEmployee Array After Call");
                for (int nI = 0; nI < nCnt; nI++)
                {
                    StringBuilder sb = new StringBuilder("First Name=[");
                    sb.Append(emp[nI].FirstName);
                    sb.Append("]  Last Name=[");
                    sb.Append(emp[nI].LastName);
                    sb.Append("]  Age=[");
                    sb.Append(emp[nI].Age.ToString());
                    sb.Append("]  Salary=[");
                    sb.Append(emp[nI].Salary.ToString("F2"));
                    sb.Append("]  Sex=[");
                    sb.Append(((emp[nI].Sex == 0) ? "Male" : "Female"));
                    sb.Append("]");
                    Console.WriteLine(sb.ToString());
                }

            }
        }
    }

    Point of Interest

  • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
  • DllImport defines the DLL entry point.

    compile and execute you will get following output.

    image

  • August 6, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , , , | Leave a comment

    Modifying array of string values (default Marshaling)

    Define some functions in native dll (let say it “Win32Native.dll”)  as shown below. this function receives array of char pointers (string).

    extern "C" __declspec(dllexport) void ModifyStringArrayValues( int nCount, char* ppStrArray[] )

    Implementing Functions

    extern "C" __declspec(dllexport) void ModifyStringArrayValues( int nCount, char* ppStrArray[] )
    {
        size_t cchDest = 40;
        const size_t alloc_size = sizeof(char) * 40;
        for ( int nI = 0; nI < nCount; nI++ )
        {
            char *pszFormat = "<<from DLL>> [Modified String %2d]";
            STRSAFE_LPSTR temp = (STRSAFE_LPSTR)CoTaskMemAlloc( alloc_size );
            StringCchPrintfA(temp, cchDest, pszFormat, nI);
            CoTaskMemFree( ppStrArray[nI] );
            ppStrArray[nI] = (char *) temp;
       }
    }

    Point of Interest

    • CoTaskMemAlloc is used to allocated the memory required.
    • CoTaskMemFree is used to free any previously allocated buffer, if null is passed then, CoTaskMemFree is not called.
    • StringCchPrintfA printf used to print a formatted output to a string buffer.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

    Writing the client code (the managed part)

    one can simple create a console base application which can use this dll. let’s name it MarshallingTest.

    see the code snippet below.

    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace MarshallingTest
    {
        class Program
        {
             [DllImport("Win32Native.dll")]
             public static extern double ModifyStringArrayValues(int nSize, [In, Out] String[] strArr);
        
            static void Main(string[] args)
            {
                int nSize = 5;
                string[] arrStr = new string[nSize];
                for (int nI = 0; nI < nSize; nI++) arrStr[nI] = "String " + (nI + 1).ToString();

               
                Console.WriteLine("String Values in Array, before calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1}", i, arrStr[i]));
                }

                ModifyStringArrayValues(nSize, arrStr);

                Console.WriteLine("String Values in Array, after calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1}", i, arrStr[i]));
                }
            }
        }
    }

    Point of Interest

    • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
    • DllImport defines the DLL entry point.

    compile and execute you will get following output.

    image

    August 6, 2013 Posted by | .NET, CodeProject, Interoperability, Win32 | , , , , | Leave a comment

    Modifying array of numeric types (default Marshaling)

    Define some functions in native dll (let say it “Win32Native.dll”)  as shown below. These 2 functions receives integer buffer, and double buffer respectively.

    extern "C" __declspec(dllexport) int ModifyIntegerArrayValues ( int nSize, int* pArray)

    extern "C" __declspec(dllexport) double ModifyDoubleArrayValues ( int nSize, double* pArray)

    Implementing Functions

    extern "C" __declspec(dllexport) int ModifyIntegerArrayValues ( int nSize, int* pArray)
    {
        int nResult = 0 ;
        for ( int i = 0; i < nSize; i++ )
        {
            nResult += pArray[ i ];
            pArray[i] += 100;
        }
        return nResult;
    }

    extern "C" __declspec(dllexport) double ModifyDoubleArrayValues ( int nSize, double* pArray)
    {
        double dblResult = 0 ;
        for ( int i = 0; i < nSize; i++ )
        {
            dblResult += pArray[ i ];
            pArray[i] += 200 ;
        }
        return dblResult ;
    }

    Point of Interest

    • CoTaskMemAlloc is used to allocated the memory required.
    • CoTaskMemFree is used to free any previously allocated buffer, if null is passed then, CoTaskMemFree is not called.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

    Writing the client code (the managed part)

    one can simple create a console base application which can use this dll. let’s name it MarshallingTest.

    see the code snippet below.

    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace MarshallingTest
    {
        class Program
        {
             [DllImport("Win32Native.dll")]
            public static extern void ModifyIntegerArrayValues(int nCount, [In, Out] int[] arrStr);

             [DllImport("Win32Native.dll")]
             public static extern int ModifyDoubleArrayValues(int nSize, [In, Out] double[] nArr);
        
            static void Main(string[] args)
            {
                int nSize = 5 ;
                int[] arrInt = new int[nSize];
                for (int nI = 0; nI < nSize; nI++) arrInt[nI] = (nI + 1) * 10;

                Console.WriteLine("\nValues of Integer array, before calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:D3}", i, arrInt[i]));
                }

                ModifyIntegerArrayValues(nSize, arrInt);

                Console.WriteLine("\nValues of Integer array, after calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:D3}", i, arrInt[i]));
                }

                double [] arrDbl= new double [nSize];
                for (int nI = 0; nI < nSize; nI++) arrDbl[nI] = (nI + 1) * 5.0 ;

                Console.WriteLine("\nValues of Double array, before calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:F2}", i, arrDbl[i]));
                }

                ModifyDoubleArrayValues(nSize, arrDbl);

                Console.WriteLine("\nValues of double array, after calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:F2}", i, arrDbl[i]));
                }
            }
        }
    }

    Point of Interest
    • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
    • DllImport defines the DLL entry point.
    • Marshal.Copy function used to copy buffer from managed buffer to unmanaged buffer and vice versa.
    • Marshal.FreeCoTaskMem frees the memory allocated by native DLL.

    compile and execute you will get following output.

    image

    August 5, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , , | Leave a comment

    Fetching an array of strings from a Win32 DLL

    Define  a function in native dll (let say it “Win32Native.dll”)  as shown below.

    extern "C" __declspec(dllexport) void FetchStringArray( int nCount, char* ppStrArray[])
    {
        int result = 0;
        STRSAFE_LPSTR temp;
        size_t cchDest = 40;

        const size_t alloc_size = sizeof(char) * 40;

        for ( int nI = 0; nI < nCount; nI++ )
        {
           char *pszFormat = "from DLL >> [returned String %d]";
           STRSAFE_LPSTR temp = (STRSAFE_LPSTR)CoTaskMemAlloc( alloc_size );
           StringCchPrintfA(temp, cchDest, pszFormat, nI);

           CoTaskMemFree( ppStrArray[nI] );
           ppStrArray[nI] = (char *) temp;
        }
    }

    Point of Interest

    • CoTaskMemAlloc is used to allocated the memory required.
    • CoTaskMemFree is used to free any previously allocated buffer, if null is passed then, CoTaskMemFree is not called.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

    Writing the client code (the managed part)

    one can simply create a console based application

    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace MarshallingTest
    {
       class Program
       {
           [DllImport("Win32Native.dll")]
           public static extern void FetchStringArray(int nCount, [In, Out] String[] arrStr);

           static void Main(string[] args)
           {
                int nSize = 10;
                String[] arrStr = new String[nSize];
                FetchStringArray(nSize, arrStr);
                Console.WriteLine("Returned String Array");
                for (int nI = 0; nI < nSize; nI++)
                {
                     Console.WriteLine(arrStr[nI]);
                }
           }

       }

    }

    Point of Interest

    • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
    • DllImport defines the DLL entry point.

    compile and execute you will get following output.

    image

    August 4, 2013 Posted by | .NET, CodeProject, Interoperability, Win32 | , , , | Leave a comment

    Fetching buffer of numeric types from a Win32 DLL

    Define some functions in native dll (let say it “Win32Native.dll”)  as shown below. These 3 functions receives integer buffer, and double buffer respectively.

    extern "C" __declspec(dllexport) int FetchIntegerArray ( int nNewSize, int** ppnArray );

    extern "C" __declspec(dllexport) double  FetchDoubleArray ( int nNewSize, double ** ppnArray );

    Implementing Functions

    extern "C" __declspec(dllexport) int FetchIntegerArray ( int nNewSize, int** ppnArray )
    {
          int result = 0;
          //    CoTaskMemAlloc must be used because code on the managed side will call
          //    Marshal.FreeCoTaskMem to free this memory.
          int* newArray = (int*)CoTaskMemAlloc( sizeof(int) * nNewSize);
          for ( int j = 0; j < nNewSize ; j++ )
          {
               newArray[j] = ( j + 1 ) * 10 ;
               result += newArray[j];
           }

          if ( *ppnArray != NULL ) CoTaskMemFree( *ppnArray );
          *ppnArray = newArray;
          return result;
    }

    extern "C" __declspec(dllexport) double  FetchDoubleArray ( int nNewSize, double ** ppnArray )
    {
         double result = 0;
         //    CoTaskMemAlloc must be used because code on the managed side will call
         //    Marshal.FreeCoTaskMem to free this memory.
         double* newArray = (double*)CoTaskMemAlloc( sizeof(double) * nNewSize );
         for ( int j = 0; j < nNewSize ; j++ )
         {
             newArray[j] = 10 + ( j+1 ) * 30 ;
             result += newArray[j];
         }

         if ( *ppnArray != NULL ) CoTaskMemFree( *ppnArray );
         *ppnArray = newArray;
         return result;
    }

    Point of Interest

    • CoTaskMemAlloc is used to allocated the memory required.
    • CoTaskMemFree is used to free any previously allocated buffer, if null is passed then, CoTaskMemFree is not called.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

    Writing the client code (the managed part)

    one can simple create a console base application which can use this dll. let’s name it MarshallingTest.

    see the code snippet below.

    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace MarshallingTest
    {
       class Program
       {
           [DllImport("Win32Native.dll")]
           public static extern int FetchIntegerArray(int nSize, ref IntPtr arrInt);

           [DllImport("Win32Native.dll")]
           public static extern double FetchDoubleArray(int nSize, ref IntPtr arrInt);

           static void Main(string[] args)
           {
               int nSize = 10;
               IntPtr ptrArr = IntPtr.Zero;

               int nSum = FetchIntegerArray(nSize, ref ptrArr);
               int [] arrInt = new int [nSize];
               Marshal.Copy(ptrArr, arrInt, 0, nSize);

               Console.WriteLine("\nReturned Integer Buffer\n");

               for (int i = 0; i < nSize; i++)
               {
                   Console.Write("{0:}  ", arrInt[i]);
               }

               Console.Write("\nSum of Integer Buffer : {0}\n", nSum );
               Marshal.FreeCoTaskMem(ptrArr);

               ptrArr = IntPtr.Zero;
              double dblSum = FetchDoubleArray(nSize, ref ptrArr);
              double[] arrDbl = new double[nSize];
              Marshal.Copy(ptrArr, arrDbl, 0, nSize);

              Console.WriteLine("\nReturned Double Buffer\n");
              for (int i = 0; i < nSize; i++)
              {
                  Console.Write("{0:F2}  ", arrDbl[i]);
              }

              Console.Write("\nSum of Double Double Buffer : {0}\n", dblSum);
              Marshal.FreeCoTaskMem(ptrArr);
          }
       }
    }

    Point of Interest

    • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
    • DllImport defines the DLL entry point.
    • Marshal.Copy function used to copy buffer from managed buffer to unmanaged buffer and vice versa.
    • Marshal.FreeCoTaskMem frees the memory allocated by native DLL.

    compile and execute you will get following output.

    image

    August 3, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , , | Leave a comment