Pages

Search

Tuesday, February 28, 2012

Transferring File Using Name Pipe and Serialization


Named Pipe is usually used for inter process communication. The example below demonstrates the same. Below sample helps to transfer file content from NamedPipeServer to NamedPipeClient.
Creating an instance to “NamedPipeServerStream” class providing a pipe name will register a named pipe server. A Named Pipe client can receive the stream from a specific Named Pipe Server based on the name.
This is something like a dedicated channel through which processes can communicate using NamedPipe Name. 
Using NamedPipe a process can also transfer messages (string).

In the below example, NamedPipeServer is started or registered with name “File Transfer”.
The NamedPipeServer reads the content of a source file and creates an instance for “TransferFile” class setting the attributes such as source file name and file content (stream).
Server then serializes the "TransferFile" object and writes to the stream.


NamedPipeClient connects to the NamedPipeServer based whose server name is “File Transfer”.
and reads the stream. After reading the stream, Client deserializes to get the “TransferFile” object.
from the “FileTransfer” object, client creates the target file in specific directory with name from “FileName” attribute and content(byte[]) from “FileContent”.

TransferFile.cs

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

namespace FileTransfer
{
    //file transfer class (serializable)
    [Serializable]
    public class TransferFile
    {
        public string FileName;
        public Stream FileContent;
    }
}

Named Pipe Server

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Runtime.Serialization;
using FileTransfer;

namespace ServerNamedPipe
{
    class Program
    {
        static void Main()
        {
            //creating object for file transfer
            using (NamedPipeServerStream pipeServer =
                new NamedPipeServerStream("File Transfer", PipeDirection.Out))
            {
                Console.WriteLine("File Transfer Named Pipe Stream is ready...");
                Console.Write("Waiting for client connection...");//waiting for any client connections
                pipeServer.WaitForConnection();
                Console.WriteLine("Client connected.");
                try
                {
                    string strFile = @"c:\Test\1\Srinivas.txt";

                    //creating FileTransfer Object ans setting the file name
                    TransferFile objTransferFile = new TransferFile() { FileName = new FileInfo(strFile).Name };
                    objTransferFile.FileContent = new MemoryStream();

                    //opening the source file to read bytes
                    using (FileStream fs = File.Open(strFile, FileMode.Open, FileAccess.Read))
                    {
                        byte[] byteBuffer = new byte[1024];
                        int numBytes = fs.Read(byteBuffer, 0, 1024);

                        //writing the bytes to file transfer content stream
                        objTransferFile.FileContent.Write(byteBuffer, 0, 1024);

                        //below code is to write the bytes directly to the pipe stream
                        //pipeServer.Write(byteBuffer, 0, 1024);

                        //Reading each Kbyte and writing to the file content
                        while (numBytes > 0)
                        {
                            numBytes = fs.Read(byteBuffer, 0, numBytes);
                            objTransferFile.FileContent.Write(byteBuffer, 0, 1024);

                            //below code is to write the bytes to pipe stream directly
                            //pipeServer.Write(byteBuffer, 0, numBytes);
                        }

                        //setting the file content (stream) position to begining
                        objTransferFile.FileContent.Seek(0, SeekOrigin.Begin);

                        //serializing the file transfer object to a stream
                        IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                        try
                        {
                            //serialzing and writing to pipe stream
                            formatter.Serialize(pipeServer, objTransferFile);
                        }
                        catch (Exception exp)
                        {
                            throw exp;
                        }
                    }
                }
                // Catch the IOException that is raised if the pipe is
                // broken or disconnected.
                catch (IOException e)
                {
                    Console.WriteLine("ERROR: {0}", e.Message);
                }
            }
        }

    }

}

 Named Pipe Client

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.Runtime.Serialization;

namespace ClientNamedPipe
{
    class Program
    {
        static void Main(string[] args)
        {
            //connecting to the known pipe stream server which runs in localhost
            using (NamedPipeClientStream pipeClient =
                new NamedPipeClientStream(".", "File Transfer", PipeDirection.In))
            {


                // Connect to the pipe or wait until the pipe is available.
                Console.Write("Attempting to connect to File Transfer pipe...");
                //time out can also be specified
                pipeClient.Connect();

                Console.WriteLine("Connected to File Transfer pipe.");

                IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                //deserializing the pipe stream recieved from server
                FileTransfer.TransferFile objTransferFile = (FileTransfer.TransferFile)formatter.Deserialize(pipeClient);

                //creating the target file with name same as specified in source which comes using
                //file transfer object
                byte[] byteBuffer = new byte[1024];

                using (FileStream fs = new FileStream(@"c:\Test\2\" + objTransferFile.FileName, FileMode.Create, FileAccess.Write))
                {
                    //writing each Kbyte to the target file
                    int numBytes = objTransferFile.FileContent.Read(byteBuffer, 0, 1024);
                    fs.Write(byteBuffer, 0, 1024);
                    while (numBytes > 0)
                    {
                        numBytes = objTransferFile.FileContent.Read(byteBuffer, 0, numBytes);
                        fs.Write(byteBuffer, 0, numBytes);
                    }
                }
                Console.WriteLine("File, Received from server: {0}", objTransferFile.FileName);
            }
            Console.Write("Press Enter to continue...");
            Console.ReadLine();
        }
    }
}



Monday, February 27, 2012

Starting a Process (application) sending arguments and reading the Output/Error



Any application like notepad or calculator or user applications (console or windows) can be invoked using “Process” class which falls in “System.Diagnostics” package or namespace.

Below code demonstrates, creating a console application “Divide2Numbers” which accepts 2 arguments and writes the division of the given numbers to console.

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

namespace Divide2Numbers
{
    class Program
    {
        static void Main(string[] args)
        {
            //getting the input arguments and writing the result of the division
            double a = double.Parse(args[0]);
            double b = double.Parse(args[1]);
            Console.Write("Division of {0} and {1} is {2}", a, b, (a / b));
        }
    }
}


A windows application is created which has 2 textboxes to enter value for “a” and “b”.
User will enter the values and click on “Divide” button.

The .exe created from above console application is copied to the current windows application directory.

In the button “Divide” click event, application will create a process object which will invoke the above console application exe and sends the a, b arguments.

private void Divide_Click(object sender, EventArgs e)
        {
            try
            {
                //new process
                System.Diagnostics.Process p1 = new System.Diagnostics.Process();

                //sending the arguments (a,b) to divide application
                p1.StartInfo.Arguments = txtA.Text + " " + txtB.Text;

                //CreateNoWindow=false, to not open the process ui.
                p1.StartInfo.CreateNoWindow = false;

                //The application file path
                p1.StartInfo.FileName = Application.StartupPath + "/Divide2Numbers.exe";

                //UseShellExecute must be set to false, to redirect IO streams
                p1.StartInfo.UseShellExecute = false;

                //to redirect the error that occurs in the process
                p1.StartInfo.RedirectStandardError = true;

                //to redirect the output from the process
                p1.StartInfo.RedirectStandardOutput = true;

                //to start the process
                p1.Start();

                //reading the error and output from stream
                StreamReader oR = p1.StandardOutput;
                StreamReader eR = p1.StandardError;
                string strError = eR.ReadToEnd();
                string strOutput = oR.ReadToEnd();

                //displaying the error or result
                lblResult.Text = (strOutput.Trim().Length > 0) ? "Result : " + strOutput : "Error : " + strError;
            }
            catch (Exception exp)
            {
                MessageBox.Show("Error : " + exp.Message);
            }
        }

When user enters the value of a, b and click on “Divide” the application gets the result from console application through output stream.


Incase of Error, applications gets the error via Error Stream reader.


Friday, February 24, 2012

Custom Attributes


Custom attribute enables the capability of creating user defined attributes (types) extending “Attribute” class. Custom attribute helps to tag a property or class or method to identify the respective members at run time based on type.
Below Example helps to understand better.
In below example, the same business object can be used in 2 different entities to invoke actions specific to entity.
Class which has methods to read property values based on type.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace BusinessAttributes
{
    public class Attribute
    {
        //To read the value of a given property and type
        public object ReadPropertyValue(Object argBusinessObject, Type atrType)
        {
            Type objType = argBusinessObject.GetType();
            var properties = (from memInfo in objType.FindMembers(MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, null, null)
                              where memInfo.GetCustomAttributes(atrType, true).Count() > 0 &&
                              memInfo.GetCustomAttributes(atrType, true).All((o) =>
                              { if (o.ToString().Trim().ToUpper() == atrType.ToString().Trim().ToUpper()) { return true; } return false; })
                              select memInfo);
            if (properties.Count() == 0)
                throw new Exception("Business Property of type \"" + atrType.ToString() + "\" not found");
            if (properties.Count() > 1)
                throw new Exception("Business Property of type \"" + atrType.ToString() + "\"  found more than one");
            return objType.InvokeMember(properties.First().Name, BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public, null, argBusinessObject, null);

        }

        //To return custom attribute objects list
        public IList<Object> GetCustomAttribute(Object argBusinessObject, Type atrType)
        {
            Type objType = argBusinessObject.GetType();

            var properties = (from memInfo in objType.FindMembers(MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, null, null)
                              where memInfo.GetCustomAttributes(atrType, true).Count() > 0 &&
                              memInfo.GetCustomAttributes(atrType, true).All((o) =>
                              { if (o.ToString().Trim().ToUpper() == atrType.ToString().Trim().ToUpper()) { return true; } return false; })
                              select memInfo);
            if (properties.Count() == 0)
                throw new Exception("Business Property of type \"" + atrType.ToString() + "\" not found");
            if (properties.Count() > 1)
                throw new Exception("Business Property of type \"" + atrType.ToString() + "\"  found more than one");
            var customAttribute =
            (from att in properties.First().GetCustomAttributes(atrType, true)
             where att.GetType().ToString().Trim().ToUpper() == atrType.ToString().Trim().ToUpper()
             select att);
            return customAttribute.ToList();
        }
    }
}


Logger (Entity) class extending the BusinessAttributes.Attribute class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace DBLogger
{
    public class Logger : BusinessAttributes.Attribute
    {
        public Object LogObject
        {
            set;
            get;
        }
        //reading the employee details from object based on custom attribute type
        public string ReadDetails()
        {
            return "Log Information - " + ReadPropertyValue(LogObject, typeof(DBLogger.LogID)) + " - " + ReadPropertyValue(LogObject, typeof(DBLogger.LogDesc));
        }
    }
    public class LogID : Attribute { };
    public class LogDesc : Attribute { };
}


Employee  (Entity) class extending the BusinessAttributes.Attribute class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace EmployeeLib
{
    public class Employee : BusinessAttributes.Attribute
    {
        public Object EmployeeObject
        {
            set;
            get;
        }
        //reading the employee details from object based on custom attribute type
        public string ReadDetails()
        {
            IList<Object> dt = GetCustomAttribute(EmployeeObject, typeof(EmpID));
            EmpID objEmpID = dt[0] as EmpID;
            if (objEmpID.isActive)
                return "Employee Information (Active) - " + ReadPropertyValue(EmployeeObject, typeof(EmpID)) + " - " + ReadPropertyValue(EmployeeObject, typeof(EmpName));
            else
                return "Employee Information (Inactive) - " + ReadPropertyValue(EmployeeObject, typeof(EmpID)) + " - " + ReadPropertyValue(EmployeeObject, typeof(EmpName));
        }
    }

    public class EmpID : Attribute
    {
        public bool isActive { get; set; }

        public EmpID(bool blnActive)
        {
            isActive = blnActive;
        }
    };
    public class EmpName : Attribute
    { };
}

User Interface (Windows form)

//The common business object which can be used in 2 entities
        CommonBA objCommonBA = null;

        //when used clicks on "Log" button
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                //creating an object for Log entity and setting the shared business object as Log Object
                DBLogger.Logger objLogger = new DBLogger.Logger() { LogObject = objCommonBA };
                //displaying the details of the log object
                MessageBox.Show(objLogger.ReadDetails());
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.Message);
            }
        }

        //when used clicks on "Employee" button
        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                //creating an object for Employee entity and setting the shared business object as Employee object
                EmployeeLib.Employee objEmp = new EmployeeLib.Employee() { EmployeeObject = objCommonBA };
                //displaying the employee details.
                MessageBox.Show(objEmp.ReadDetails());
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.Message);
            }
        }

        //on form load
        private void Form1_Load(object sender, EventArgs e)
        {
            //creating the shared business object
            objCommonBA = new CommonBA() { ID = 1234, Desc = "srinivas" };
        }
    }

    //The Business class
    public class CommonBA
    {
        [DBLogger.LogID]
        [EmployeeLib.EmpID(true)]
        //ID attribute and tagged that this property can be used as Log ID in case of
        //log entity and the same as employee id in employee entity
        public int ID
        {
            get;
            set;
        }

        [DBLogger.LogDesc]
        [EmployeeLib.EmpName]
        //Desc attribute and tagged that this property can be used as Log Description in case of
        //log entity and the same as employee name in employee entity
        public string Desc
        {
            get;
            set;
        }
    }