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.

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

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

Fetching a byte buffer from a Win32 DLL

 

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

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

    // release the previous buffer, if any allocated.
    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.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace MarshallingTest
{
    class Program
    {
        [DllImport("Win32Native.dll")]
        public static extern int FetchByteArray(int nSize, ref IntPtr arrInt);
    
        static void Main(string[] args)
        {
            int nSize = 10;
            IntPtr ptrArr = IntPtr.Zero;

            int nSum = FetchByteArray(nSize, ref ptrArr);
            byte[] arrByte = new byte[nSize];
            Marshal.Copy(ptrArr, arrByte, 0, nSize);

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

            for (int i = 0; i < nSize; i++)
            {
                Console.Write ( "{0:D2}  ", arrByte[i] );
            }
           
            Console.Write("\nSum of Buffer : {0}\n", nSum );
            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

Interoperability with Winodws API (1 of N)

In this article I will explain, how one can call windows API, from managed world. let’s define the signatures of windows API for managed world.

Calling a Win32 MessageBox function from Managed Word

//****************************

// MyProgram.cs

namespace MyManagedProgram   {  //1

class Program   { // 2
static void Main(string[] args)   { //3

Win32DllWrapper.MsgBox(0, "This Text has been sent from  Managed World",          "Win32 Messsage Box", 0);

} //3

}  //2

///defining the helper class

///define a helper class which will contain a functions for

public class Win32DllWrapper   {    //4
//  Standard Functions from Windows API.
[DllImport("User32.dll", EntryPoint = "MessageBox", CharSet = CharSet.Auto)]
public static extern int MsgBox(int hWnd, String text, String caption, uint type);

}  //4

}  // 1

compile and execute

Marshaling Introduction (1 of N)

What is Marshaling?

Marshaling (similar to serialization) is the process of transforming the memory representation of an object to a data format suitable for storage or transmission. It is typically used when data must be moved between different parts of a computer program or from one program to another.

in the .NET scenario, Marshaling is the process of creating a bridge between managed code and unmanaged code; it is the homer that carries messages from the managed to the unmanaged environment and reverse. It is one of the core services offered by the CLR (Common Language Runtime.) Because much of the types in unmanaged environment do not have counterparts in managed environment, you need to create conversion routines that convert the managed types into unmanaged and vice versa; and that is the marshaling process. As a refresher, we call .NET code “managed” because it is controlled (managed) by the CLR. Other code that is not controlled by the CLR is called

When we need Marshaling

You already know that there is no such compatibility between managed and unmanaged environments. In other words, .NET does not contain such the types HRESULT, DWORD, and HANDLE that exist in the realm of unmanaged code. Therefore, you need to find a .NET substitute or create your own if needed. That is what called marshaling.

An example is the unmanaged DWORD; it is an unsigned 32-bit integer, so we can marshal it in .NET as System.UInt32. Therefore, System.UInt32 is a substitute for the unmanaged DWORD.

Marshaling comes handy when you are working with unmanaged code, whether you are working with Windows API or COM components. It helps you interoperating (i.e. working) correctly with these environments by providing a way to share data between the two environments. 

image

Interop Marshaling

Interop marshaling governs how data is passed in method arguments and return values between managed and unmanaged memory during calls. Interop marshaling is a run-time activity performed by the common language runtime’s marshaling service.

Most data types have common representations in both managed and unmanaged memory. The interop marshaler handles these types for you. Other types can be ambiguous or not represented at all in managed memory.

An ambiguous type can have either multiple unmanaged representations that map to a single managed type, or missing type information, such as the size of an array. For ambiguous types, the marshaler provides a default representation and alternative representations where multiple representations exist. You can supply explicit instructions to the marshaler on how it is to marshal an ambiguous type.

Default Marshaling

  • Platform invoke, which enables managed code to call functions exported from an unmanaged library.
  • COM interop, which enables managed code to interact with COM objects through interfaces.

Marshaling data with platform Invoke (PInvoke)

To call functions exported from an unmanaged library, a .NET Framework application requires a function prototype in managed code that represents the unmanaged function. To create a prototype that enables platform invoke to marshal data correctly, you must do the following:

  • Apply the DLLImportAttribute attribute to the static function or method in managed code.
  • Substitute managed data types for unmanaged data types.

You can use the documentation supplied with an unmanaged function to construct an equivalent managed prototype by applying the attribute with its optional fields and substituting managed data types for unmanaged types.

Memory Management

The interop marshaler always attempts to free memory allocated by unmanaged code. This behavior complies with COM memory management rules, but differs from the rules that govern native C++.

Confusion can arise if you anticipate native C++ behavior (no memory freeing) when using platform invoke, which automatically frees memory for pointers. 

see the following example

BSTR MyMethod (BSTR b)

{

            return b;

}

However, if you define the method as a platform invoke prototype, replace each BSTR type with a String type, and call Method, the common language runtime attempts to free variable b twice in the above example. You can change the marshaling behavior by using IntPtr types rather than String types.

The runtime always uses the CoTaskMemFree method to free memory. If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method. Similarly, you can avoid automatic memory freeing in situations where memory should never be freed.

Directional attribute

Directional attributes are optional. You apply them to method parameters when you want to alter the default behavior of the marshaler. If you omit directional attributes from a method parameter, the marshaler determines the directional flow based on the type of the parameter (value or reference) and its modifiers, if any.

Some languages provide keywords that enable you to modify the directional flow of method parameters. The following table lists the direction-related keywords provided by Visual Basic .NET and C# and shows the equivalent IDL interface attribute.

Default marshaling of method arguments to unmanaged code

ByRef, ref, and out parameter modifiers cause method arguments to be marshaled by reference rather than by value. Method arguments passed by value are marshaled to unmanaged code as values on the stack; arguments passed by reference are marshaled as pointers on the stack. The figure below shows the default marshaling behavior of value types and reference types with parameter modifiers.

image

By default, reference types (classes, arrays, strings, and interfaces) passed by value are marshaled as In parameters for performance reasons. You do not see changes to these types unless you apply InAttribute and OutAttribute (or just OutAttribute) to the method parameter. The StringBuilder class, which is an exception to this rule, is marshaled as an In/Out parameter.

The interop marshaler guarantees the following behavior with regard to directional attributes:

  • The interop marshaler never generates a write operation to an In parameter passed from unmanaged code. Thus, unmanaged code can safely pass a pointer to a read-only page, or a pointer to concurrently accessed data.
  • When the copied object contains an allocated object, such as a BSTR, the marshaler always executes the correct sequence of allocations and destructions demanded by the In/Out settings.

Default Marshaling

To call functions exported from an unmanaged library, a .NET Framework application requires

Blittable Types

Most data types have a common representation in both managed and unmanaged memory and do not require special handling by the interop marshaler. These types are called blittable types because they do not require conversion when passed between managed and unmanaged code.

  • System.Byte
  • System.SByte
  • System.Int16
  • System.UInt16
  • System.Int32
  • System.UInt32
  • System.Int64
  • System.IntPtr
  • System.UIntPtr
  • One-dimensional arrays of blittable types, such as an array of integers
  • Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types).

The following table lists non-blittable types from the System namespace. Delegates, which are data structures that refer to a static method or to a class instance, are also non-blittable.

  • System.Array
  • System.Boolean
  • System.Char
  • System.Object
  • System.Mdarray
  • System.String
  • System.Valuetype
  • System.SzArray