Blue Theme Orange Theme Green Theme Red Theme
 
Home | Forums | ASP.NET 2.0 Tutorials | Web Services | How Do I...? | Class Browser | WPF Quick Starts | Advertise with Us
 | Consulting  
Submit an Article Submit a Blog 
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
Team Foundation Server Hosting
Search :       Advanced Search »
Home » Visual C# » Inter-Process Data Exchange using .NET 2.0

Inter-Process Data Exchange using .NET 2.0

A simple way to allow Inter-Process Data Exchange using .NET 2.0 Mutex and temporary files.

Page Views : 18061
Downloads : 339
Rating :
 Rate it
Level : Intermediate
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
SharedStorages.zip
 
 
Nevron Gauge for SharePoint
Become a Sponsor
Become a Sponsor
 Tag Cloud
 Latest Jobs
More ... 
 Latest Interview Questions
More ... 

Introduction:

The purpose of this class is to allow exchange data between different processes on the same machine.

The idea is to use temporary files, in a common directory, that are shared to all processes (i.e. c:\windows\temp) and synchronize access to these files through System.Threading.Mutex objects.

Prerequisites:

  • The processes must reside on the same machine;
  • There must be a local or remote directory accessible to all processes
  • Objects that can be exchanged must be serializable (at least in binary format).

Limitations:

It is not possible to exchange data on a cluster system (active-active) or on back-end machines behind a load balancer such as NLB.

Possible uses:

  • Inter Process Communication and data exchange of primitive types and/or complex types (custom classes that are serializable);
  • Data exchange between Two or more applications, COM <==> NET, ASP <==> ASP.NET, and so on. [Shared Storage class is COM visible]

Mutex and Temporary files:

When two or more threads/processes need to access a shared resource at the same time, the system needs a synchronization mechanism to ensure that only one thread/process at a time uses the resource.

Mutex is a synchronization primitive that grants exclusive access to the shared resource to only one thread. If a thread/process acquires a mutex, the second thread (in the same or another process) that wants to acquire that mutex is suspended until the first thread/process releases the mutex.

Exclusive file write:

using System.IO;

using System.Threading;
public bool AddOrUpdate(string objectName, objectobjectValue)
{
    
bool createdNew = false;   
    
(Mutex mLocked = new Mutex(true,String.Format(SharedStorage.
     
 MUTEX_LOCKED_FORMAT, this.storageName), out createdNew))

     {

          try

          {

               if (!createdNew)

               mLocked.WaitOne(); //Another thread/process is using it

                bool fileCreated = this.writeFile(objectName, objectValue);

                //false if   overwrite

                if (fileCreated && this.destroyWhenDisposed)

                this.createdFiles.Add(this.getStorageFileName(objectName));

                return fileCreated;

         }

         finally

         {

             mLocked.ReleaseMutex();

         }

    }

}


Exclusive file read:

using System.IO;

using System.Threading;
public object this[string objectName]
{
     get

     {

          bool createdNew = false;

using (Mutex mLocked = new Mutex(true,String.Format(SharedStorage.MUTEX_LOCKED_FORMAT, this.storageName), out createdNew))

          {

               try

              {

         if (!createdNew)mLocked.WaitOne();   //Another thread/process is using it    

        

                   if (this.Exists(objectName))return this.readFile(objectName);

                   else throw new ArgumentOutOfRangeException ("objectName",  

 String.Format("'{0}' object not found.",objectName));

               }

               finally

               {

                   mLocked.ReleaseMutex();

               }

          }

     }

}

 

Using SharedStorage from .NET:

public partial class Form1 : Form

{

    SharedStorage ss = new SharedStorage();

    public Form1()

    {

         InitializeComponent();

    }

 

     private void btnWrite_Click(object sender, EventArgs e)

     {

         ss.StorageName = this.txtStorageName.Text;

         ss.AddOrUpdate(this.txtObjectName.Text, this.txtObjectValue.Text);

     }

     private void btnRead_Click(object sender, EventArgs e)

     {

         ss.StorageName = this.txtStorageName.Text;

         this.txtObjectValue.Text = (string)ss[this.txtObjectName.Text];

    }

}

Using SharedStorage from VB6:

Option Explicit On

Private Sub cmdWrite_Click()

    Dim ss As New SharedStorages.SharedStorage

    ss.StorageName = Me.txtStorageName.Text

    ss.AddOrUpdate_2(Me.txtObjectName.Text, Me.txtObjectValue.Text)

    ss = Nothing

End Sub

Private Sub cmdRead_Click()

    Dim ss As New SharedStorages.SharedStorage

    ss.StorageName = Me.txtStorageName.Text

    Me.txtObjectValue.Text = ss(Me.txtObjectName.Text)

    ss = Nothing

End Sub


Using SharedStorage from ASP:

(Remember to give first IIS_WPG group "modifiy" permission to c:\windows\temp folder.)

<html>

<head></head>

<body>

<%

Dim ss

Dim storageName

Dim objectName

Dim objectValue

 

if Request("txtStorageName")="" Then

    storageName = "My Storage Name"

Else  

    storageName = Request("txtStorageName")

End If

  

if Request("objectName")="" Then

    objectName = "Who I am ?"

Else

    objectName = Request("objectName")

End If

 

if Request("objectValue")="" Then

    objectValue = "ASP"

Else

    objectValue = Request("objectValue")

End If

 

Set ss = Server.CreateObject("SharedStorages.SharedStorage")

ss.StorageName="My Storage Name"

 

if Request("hAction")="Write" Then

    ss.AddOrUpdate objectName, objectValue

End If   

 

if Request("hAction")="Read" Then

    objectValue = ss(objectName)

End If

   

Set ss = Nothing

%>

 

<p>

<b>ASP World !</b>

</p>

<form id="form1" method="post" action="asp.asp">

<p>Storage Name:<input id="txtStorageName" name="txtStorageName" type="text" value="<% = storageName %>" /></p>

<p>Storage Name:<input id="objectName" name="objectName" type="text" value="<% = objectName %>" /></p>

<p>Storage Name:<input id="objectValue" name="objectValue" type="text" value="<% = objectValue %>" /></p>

<br />

<p><input id="btnWrite" type="submit" value="Write" onclick="document.getElementById('hAction').value='Write';" />

<input id="btnRead" type="submit" value="Read" onclick="document.getElementById('hAction').value='Read';"/></p>

<input id="hAction" name="hAction" type="hidden" />

</form>

</body>

</html>


Testing Inter-process data exchange:

Step 1: Write from a .NET Windows Application into the Shared Storage "My Storage Name".

Step 2: Read this value from an ASP.NET Application and from an ASP Application.

Step 3: Write from ASP and read value from ASP.NET (press first Write below, then Read above).

Step 4: Read same value from a .NET Windows Application.

Following is SharedStorage.cs:

(When compile remember to register SharedStorages.dll assembly into the Win registry to expose this class to COM Project Properties Build Register for COM Interop)

using System;

using System.Diagnostics;

using System.Runtime.Serialization;

using System.Runtime.Serialization.Formatters.Binary;

using System.IO;

using System.Collections;

using System.Collections.Specialized;

using System.Collections.Generic;

using System.Text;

using System.Threading;

namespace SharedStorages

{

    /// <summary>

    /// SharedStorage object that use Mutex and Files to share data between processes in the same Machine.

    /// </summary>

    /// <remarks>COM visibile.</remarks>

    [System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDual)]

public sealed class SharedStorage : IDisposable

{

    #region Constants

    private const string MUTEX_LOCKED_FORMAT = "{0} locked";

    #endregion Constants

    #region Static Fields

    private static volatile string sharedFolder;

    private static volatile bool destroyWhenDisposedDefault;

    #endregion Static Fields

    #region Fields

    private bool destroyWhenDisposed;

    private List<string> createdFiles;

    private string storageName;

    #endregion Fields

    #region Static Constructors

    static SharedStorage()

    {

SharedStorage.sharedFolder = Environment.GetEnvironmentVariable   ("temp", EnvironmentVariableTarget.Machine);

          SharedStorage.destroyWhenDisposedDefault = true;

    }

    #endregion Static Constructors

    #region Constructors

    /// <summary>

    /// Initializes a new instance of the <see cref="SharedStorage"/> class.

    /// </summary>

    public SharedStorage()

       : this(String.Empty)

   {}

   /// <summary>

   /// Initializes a new instance of the <see cref="SharedStorage"/> class.

   /// </summary>

   /// <param name="storageName">Name of the storage.</param>

   public SharedStorage(string storageName)

   : this(storageName, SharedStorage.destroyWhenDisposedDefault)

   {}

   /// <summary>

   /// Initializes a new instance of the <see cref="SharedStorage"/> class.

   /// </summary>

   /// <param name="storageName">Name of the storage.</param>

  /// <param name="destroyWhenDisposed">if set to <c>true</c> [destroy when disposed].</param>

   public SharedStorage(string storageName, bool destroyWhenDisposed)

   {

       this.createdFiles = new List<string>();

            this.destroyWhenDisposed = destroyWhenDisposed;

            this.storageName = storageName;

   }

   #endregion Constructors

   #region Static Properties

   /// <summary>

   /// Gets or sets the shared folder.

   /// </summary>

   /// <value>The shared folder.</value>

   public static string SharedFolder

   {

       get { return SharedStorage.sharedFolder; }

       set { SharedStorage.sharedFolder = value; }

   }

   /// <summary>

   /// Gets or sets a value indicating whether [destroy when disposed default].

   /// </summary>

   /// <value>

   ///   <c>true</c> if [destroy when disposed default]; otherwise, <c>false</c>.

   /// </value>

   public static bool DestroyWhenDisposedDefault

   {

       get { return SharedStorage.destroyWhenDisposedDefault; }

       set { SharedStorage.destroyWhenDisposedDefault = value; }

   }

   #endregion Static Properties

   #region Properties

   /// <summary>

   /// Gets or sets the name of the storage.

   /// </summary>

   /// <value>The name of the storage.</value>

   public string StorageName

   {

       get { return this.storageName; }

       set { this.storageName = value; }

   }

   #endregion Properties

   #region Public Methods

   /// <summary>

   /// Adds the or update.

   /// </summary>

   /// <param name="objectName">Name of the object.</param>

   /// <param name="objectValue">The object value.</param>

   /// <returns></returns>

   public bool AddOrUpdate(string objectName, object objectValue)

   {

       bool createdNew = false;

       using (Mutex mLocked = new Mutex(true, String.Format(SharedStorage.MUTEX_LOCKED_FORMAT, this.storageName), out createdNew))

       {

           try

           {

               if (!createdNew)

                   mLocked.WaitOne(); //Another thread/process is using it

               bool fileCreated = this.writeFile(objectName, objectValue); //false if overwrite

               if (fileCreated && this.destroyWhenDisposed)

    this.createdFiles.Add(this.getStorageFileName(objectName));

               return fileCreated;

           }

           finally

           {

               mLocked.ReleaseMutex();

           }

       }

   }

   /// <summary>

   /// Removes the specified object name.

   /// </summary>

   /// <param name="objectName">Name of the object.</param>

   /// <returns></returns>

   public bool Remove(string objectName)

   {

       bool createdNew = false;

       using (Mutex mLocked = new Mutex(true,String.Format (SharedStorage.MUTEX_LOCKED_FORMAT, this.storageName), out createdNew))

       {

           try

           {

               if (!createdNew)mLocked.WaitOne(); //Another thread/process is using it

               return this.deleteFile(objectName);

           }

            finally

            {

                mLocked.ReleaseMutex();

            }

        }

    }

        /// <summary>

        /// Existses the specified object name.

        /// </summary>

        /// <param name="objectName">Name of the object.</param>

        /// <returns></returns>

        public bool Exists(string objectName)

        {

            bool createdNew = false;

            using (Mutex mLocked = new Mutex(true, String.Format(SharedStorage.MUTEX_LOCKED_FORMAT, this.storageName), out createdNew))

            {

                try

                {

                    if (!createdNew)

                        mLocked.WaitOne(); //Another thread/process is using it

                    return this.fileExists(objectName);

                }

                finally

                {

                    mLocked.ReleaseMutex();

                }

            }

        }

        #endregion Public Methods

        #region Indexers

        /// <summary>

        /// Gets the <see cref="System.Object"/> with the specified object name.

        /// </summary>

        /// <value></value>

        public object this[string objectName]

        {

            get

            {

                bool createdNew = false;

                using (Mutex mLocked = new Mutex(true, String.Format(SharedStorage.MUTEX_LOCKED_FORMAT, this.storageName), out createdNew))

                {

                    try

                    {

                        if (!createdNew)

                            mLocked.WaitOne(); //Another thread/process is using it

                        if (this.Exists(objectName))

                            return this.readFile(objectName);

                        else

                            throw new ArgumentOutOfRangeException("objectName", String.Format("'{0}' object not found.", objectName));

                    }

                    finally

                    {

                        mLocked.ReleaseMutex();

                    }

                }

            }

        }

        #endregion Indexers

        #region Private Methods

        /// <summary>

        /// Gets the name of the storage file.

        /// </summary>

        /// <param name="objectName">Name of the object.</param>

        /// <returns></returns>

        private string getStorageFileName(string objectName)

        {

            string fileName = Path.Combine(SharedStorage.sharedFolder, String.Format("ss{0}_obj{1}.tmp", this.storageName.GetHashCode(), objectName.GetHashCode()));

            Debug.WriteLine("getStorageFileName: " + fileName);

            return fileName;

        }

        /// <summary>

        /// Writes the file.

        /// </summary>

        /// <param name="objectName">Name of the object.</param>

        /// <param name="objectValue">The object value.</param>

        /// <returns></returns>

        private bool writeFile(string objectName, object objectValue)

        {

            string fileName = this.getStorageFileName(objectName);

            bool exists = File.Exists(fileName);

            using (FileStream fs = File.Create(fileName))

            {

                IFormatter formatter = new BinaryFormatter();

                formatter.Serialize(fs, objectValue);

            }

            return !exists;

        }

        /// <summary>

        /// Reads the file.

        /// </summary>

        /// <param name="objectName">Name of the object.</param>

        /// <returns></returns>

        private object readFile(string objectName)

        {

            using (FileStream fs = File.Open(this.getStorageFileName(objectName), FileMode.Open))

            {

                IFormatter formatter = new BinaryFormatter();

                return formatter.Deserialize(fs);

            }

        }

        /// <summary>

        /// Deletes the file.

        /// </summary>

        /// <param name="objectName">Name of the object.</param>

        /// <returns></returns>

        private bool deleteFile(string objectName)

        {

            if (this.fileExists(objectName))

            {

                File.Delete(this.getStorageFileName(objectName));

                return true;

            }

            else

            {

                return false;

            }

        }

        /// <summary>

        /// Files the exists.

        /// </summary>

        /// <param name="objectName">Name of the object.</param>

        /// <returns></returns>

        private bool fileExists(string objectName)

        {

            return File.Exists(this.getStorageFileName(objectName));

        }

        #endregion Private Methods

        #region IDispose members

        /// <summary>

        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

        /// </summary>

        public void Dispose()

        {

            using (Mutex mLocked = new Mutex(true, String.Format(SharedStorage.MUTEX_LOCKED_FORMAT, this.storageName)))

            {

                try

                {

                    if (this.destroyWhenDisposed)

                    {

                        foreach (string createdFile in this.createdFiles)

                        {

                            if (File.Exists(createdFile))

                                File.Delete(createdFile);

                        }

                    }

                }

                finally

                {

                    mLocked.ReleaseMutex();

                }

 

            }

        }

        #endregion IDispose members

        #region Destructor

        /// <summary>

        /// Releases unmanaged resources and performs other cleanup operations before the /// <see cref="SharedStorage"/> is reclaimed by garbage collection.

        /// </summary>

        ~SharedStorage()

        {

            this.Dispose();

            GC.SuppressFinalize(this);

        }

        #endregion Destructor

    }

}

 

Remarks:

This is the easiest way to share data between two processes without using Remoting or more complex techniques.

If you set the static property DestroyWhenDisposedDefault to false you can get that instance of classes SharedStorage does not destroy the objects added to storage when they cease to be refernziate. In this way, data added to the Storage can be permanent.

Source Code:

Attached source code contains Shared Storage project, test web project, test win project and VB6 project which use SharedStorage class.

Comment Request!
Thank you for reading this post. Please post your feedback, question, or comments about this post Here.
Login to add your contents and source code to this article
 [Top] Rate this article
 
 About the author
 
Andrea Ferendeles
Andrea Ferendeles is working as Software Consultant for a IT company based in Rome. He is MCSD of .NET, MCDBA, MCT. Andrea has been working in the software industry for over 16 years. He is focused more in building server architecture and in building reusable developer components. His current area of expertise is in Microsoft technologies such as Smart Clients, .NET, C#, Web services, ASP.NET, VB, COM/DCOM, ASP/IIS, .NET Security, Data Access Architecture. Andrea is the owner of 2 two famous open source project written in .NET 2.0: Tdo - Typed Data Object 2.0: http://tdo.sourceforge.net and NetSqlAzMan - .NET SQL Authorization Manager (AzMan for .NET 2.0 for SQL Server): http://netsqlazman.sourceforge.net.
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
ASP.NET 4 Hosting
Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites – Click Here!
 
 Post a Feedback, Comment, or Question about this article
Subject:
Comment:
Nevron Gauge for SharePoint
Become a Sponsor
 Comments
Team Foundation Server Hosting
 © 2012  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.