In order to communicate between two processes, we uses named pipes. Named pipes provide one-way or duplex pipes for communication between a pipe server and one or more pipe clients. Named pipes can be used for inter process communication locally or over network. Any process can act as either a named piped server or client or both.
Let’s see an example where a string value will be shared between two processes. .NET framework 3.5 has released a new class for creating named pipes server. The name of the class is NamedPipeServerStream. After creating the named pipes server, it will wait for a client process to connect to it. There is a new class NamedPipeClientStream in .NET framework3.5 which is used for creating the named pipes client. Let’s create the named pipes server:
//create the server instance
NamedPipeServerStream server = new NamedPipeServerStream("SetuInstance", PipeDirection.InOut);
//the following code snippet make the server always in a listening mode.
while (true)
{
server.WaitForConnection();
using (StreamReader reader = new StreamReader(server))
{
string stringValue = reader.ReadLine();
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Invoker)delegate
{
MessageBox.Show(stringValue);
});
}
server = new NamedPipeServerStream("SetuInstance", ipeDirection.InOut);
}
As you can see in the above code snippet, after creating the named pipes server, it waits for the client connection. As soon as it connects to the named pipe client, it reads the string value.
Now, let’s create the named pipes client:
using (NamedPipeClientStream client = new NamedPipeClientStream(".", "SetuInstance", PipeDirection.InOut))
{
try
{
//try to connect to IPC Server
client.Connect(500);
//write data to the pipe so that IPC server can get the string.
using (StreamWriter writer = new StreamWriter(client))
{
writer.WriteLine("An easy way to communicate between two processes!");
}
}
catch
{
}
}
In the above code snippet, you can see, how easily you can pass string to named pipes server.
NamedPipeClientStream and NamedPipeServerStream are very helpful class added in .NET framework 3.5. Now the processes can easily communicate between each others.
Hope this help!
Monday, June 22, 2009
Friday, June 19, 2009
Disconnect the client from WCF service and notifiy the client before disconnecting.
A colleague of mine was asking me to create a Client-Server demo application using WCF. The following are the requirements:
1>Service instance mode is “PerSession”.
2>Session mode has to be “Required”.
3>WCF Server will be able to terminate a session at any time. In other words, WCF Server can disconnect a WCF client.
4>Before terminating the session, the WCF client will be notified.
In order to meet the above requirement, I have used the WCF Callback for notifying the Client and disconnect the client by closing the incoming channel. Let’s see step by step of the implementation.
Step 1> Create the WCF Contact:
[ServiceContract(CallbackContract=typeof(INotifyClientCallback), SessionMode=SessionMode.Required)]
public interface IService1
{
[OperationContract]
bool StartingService();
}
public interface INotifyClientCallback
{
[OperationContract(IsOneWay = true)]
void Disconnecting();
}
INotifyClientCallback interface for Callback.
Step 2> Implementation of the Contact:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
private static readonly Dictionary subscribers = new Dictionary();
public static event EventHandler onClientAdded;
///
/// Returns the IP Address of the Client
///
///
public string GetAddressAsString()
{
if (!OperationContext.Current.IncomingMessageProperties.ContainsKey(RemoteEndpointMessageProperty.Name))
{
return "127.0.0.1";
}
RemoteEndpointMessageProperty clientEndpoint =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return clientEndpoint.Address;
}
public bool StartingService()
{
//Get the callback reference
INotifyClientCallback callback = OperationContext.Current.GetCallbackChannel();
string IPAddress = GetAddressAsString();
lock (subscribers)
{
if (!subscribers.ContainsKey(IPAddress))
{
subscribers[IPAddress] = new CommunicationStore() { NotifyCallback = callback, IService = OperationContext.Current.InstanceContext };
if (onClientAdded != null)
{
onClientAdded(IPAddress, null);
}
}
}
return true;
}
public static void Disconnect(string ipAddress)
{
if (subscribers.ContainsKey(ipAddress))
{
CommunicationStore com = subscribers[ipAddress];
if (((ICommunicationObject)com.NotifyCallback).State == CommunicationState.Opened)
{
try
{
//fires the callback method
com.NotifyCallback.Disconnecting();
com.IService.IncomingChannels.FirstOrDefault().Close();
}
catch (Exception)
{
throw;
}
}
}
}
}
public class CommunicationStore
{
public InstanceContext IService { get; set; }
public INotifyClientCallback NotifyCallback { get; set; }
}
Some important points of the service implementation:
a>Use a static dictionary to keep the Client’s IP and callback channel. Before writing on the share object, lock the object.
b>Gets the IP address of the client using the GetAddressAsString method. You can get the IP of the client from the incoming message. The following statement shows how can we get the IP adddress of the Client in WCF:
RemoteEndpointMessageProperty clientEndpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
String ipAddress = clientEndpoint.Address;
If you are using the namepipe binding, you will not get the RemoteEndpointMessageProperty.
c>When the client creates the proxy of the service, it will call StartingService method immediately. Inside the StartingService method, I am keeping the callback channel of the client and current instance into the dictionary.
d>When the user of WCF service wants to disconnect a client, he/she will call the Disconnect method with the IP Address of the client.
e>The Disconnect method uses the IP Address to get the callback channel of the client and associate service instance of the client from the dictionary. Eventually, it notifies the client by using callback channel and close the incoming channel.
I am done with the service implementation.
Step 3> Implementation of the callback and create the proxy of the service:
public class Proxy
{
public static DuplexChannelFactory factory;
static IService1 _IService1;
private static bool Connect()
{
try
{
WSDualHttpBinding binding = new WSDualHttpBinding();
EndpointAddress address1 = new EndpointAddress(@"http://localhost:9008/Service1");
EndpointAddress address = new EndpointAddress(@"net.tcp://localhost:9002/");
NetTcpBinding nettcp = new NetTcpBinding();
nettcp.ReliableSession.Enabled = true;
nettcp.ReliableSession.InactivityTimeout = new TimeSpan(0, 0, 30);
NetNamedPipeBinding namepipe = new NetNamedPipeBinding();
EndpointAddress addressPipe = new EndpointAddress(@"net.pipe://localhost/");
InstanceContext context = new InstanceContext(new NotifyCallback());
binding.ClientBaseAddress = new Uri(@"http://localhost:9010/");
factory = new DuplexChannelFactory(context, binding, address1);
_IService1 = factory.CreateChannel();
_IService1.StartingService();
}
catch (Exception ex)
{
}
return true;
}
}
public static IService1 ServiceProxy
{
get
{
if (_IService1 == null)
{
Connect();
}
return _IService1;
}
}
}
public class NotifyCallback : INotifyClientCallback
{
#region INotifyClientCallback Members
public void Disconnecting()
{
MessageBox.Show("You are about to disconnect from the service");
}
#endregion
}
Points to ponder of the above code snippet:
a>When you use the WCF callback, you have to use the DuplexChannelFactory rather then ChannelFactory to create the proxy.Othewise, you can generate the proxy.
b>WCF callback supports the following binding: WSDualHttpBinding, NetTcpBinding and NetNamedPipeBinding. When you use the WSDualHttpBinding, don’t forget to set the ClientBaseAddress.
c>Implement the callback interface.
That’s it.
If you need the source code of this demo, you can email me.
Happy Programming!
1>Service instance mode is “PerSession”.
2>Session mode has to be “Required”.
3>WCF Server will be able to terminate a session at any time. In other words, WCF Server can disconnect a WCF client.
4>Before terminating the session, the WCF client will be notified.
In order to meet the above requirement, I have used the WCF Callback for notifying the Client and disconnect the client by closing the incoming channel. Let’s see step by step of the implementation.
Step 1> Create the WCF Contact:
[ServiceContract(CallbackContract=typeof(INotifyClientCallback), SessionMode=SessionMode.Required)]
public interface IService1
{
[OperationContract]
bool StartingService();
}
public interface INotifyClientCallback
{
[OperationContract(IsOneWay = true)]
void Disconnecting();
}
INotifyClientCallback interface for Callback.
Step 2> Implementation of the Contact:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
private static readonly Dictionary
public static event EventHandler onClientAdded;
///
/// Returns the IP Address of the Client
///
///
public string GetAddressAsString()
{
if (!OperationContext.Current.IncomingMessageProperties.ContainsKey(RemoteEndpointMessageProperty.Name))
{
return "127.0.0.1";
}
RemoteEndpointMessageProperty clientEndpoint =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return clientEndpoint.Address;
}
public bool StartingService()
{
//Get the callback reference
INotifyClientCallback callback = OperationContext.Current.GetCallbackChannel
string IPAddress = GetAddressAsString();
lock (subscribers)
{
if (!subscribers.ContainsKey(IPAddress))
{
subscribers[IPAddress] = new CommunicationStore() { NotifyCallback = callback, IService = OperationContext.Current.InstanceContext };
if (onClientAdded != null)
{
onClientAdded(IPAddress, null);
}
}
}
return true;
}
public static void Disconnect(string ipAddress)
{
if (subscribers.ContainsKey(ipAddress))
{
CommunicationStore com = subscribers[ipAddress];
if (((ICommunicationObject)com.NotifyCallback).State == CommunicationState.Opened)
{
try
{
//fires the callback method
com.NotifyCallback.Disconnecting();
com.IService.IncomingChannels.FirstOrDefault().Close();
}
catch (Exception)
{
throw;
}
}
}
}
}
public class CommunicationStore
{
public InstanceContext IService { get; set; }
public INotifyClientCallback NotifyCallback { get; set; }
}
Some important points of the service implementation:
a>Use a static dictionary to keep the Client’s IP and callback channel. Before writing on the share object, lock the object.
b>Gets the IP address of the client using the GetAddressAsString method. You can get the IP of the client from the incoming message. The following statement shows how can we get the IP adddress of the Client in WCF:
RemoteEndpointMessageProperty clientEndpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
String ipAddress = clientEndpoint.Address;
If you are using the namepipe binding, you will not get the RemoteEndpointMessageProperty.
c>When the client creates the proxy of the service, it will call StartingService method immediately. Inside the StartingService method, I am keeping the callback channel of the client and current instance into the dictionary.
d>When the user of WCF service wants to disconnect a client, he/she will call the Disconnect method with the IP Address of the client.
e>The Disconnect method uses the IP Address to get the callback channel of the client and associate service instance of the client from the dictionary. Eventually, it notifies the client by using callback channel and close the incoming channel.
I am done with the service implementation.
Step 3> Implementation of the callback and create the proxy of the service:
public class Proxy
{
public static DuplexChannelFactory
static IService1 _IService1;
private static bool Connect()
{
try
{
WSDualHttpBinding binding = new WSDualHttpBinding();
EndpointAddress address1 = new EndpointAddress(@"http://localhost:9008/Service1");
EndpointAddress address = new EndpointAddress(@"net.tcp://localhost:9002/");
NetTcpBinding nettcp = new NetTcpBinding();
nettcp.ReliableSession.Enabled = true;
nettcp.ReliableSession.InactivityTimeout = new TimeSpan(0, 0, 30);
NetNamedPipeBinding namepipe = new NetNamedPipeBinding();
EndpointAddress addressPipe = new EndpointAddress(@"net.pipe://localhost/");
InstanceContext context = new InstanceContext(new NotifyCallback());
binding.ClientBaseAddress = new Uri(@"http://localhost:9010/");
factory = new DuplexChannelFactory
_IService1 = factory.CreateChannel();
_IService1.StartingService();
}
catch (Exception ex)
{
}
return true;
}
}
public static IService1 ServiceProxy
{
get
{
if (_IService1 == null)
{
Connect();
}
return _IService1;
}
}
}
public class NotifyCallback : INotifyClientCallback
{
#region INotifyClientCallback Members
public void Disconnecting()
{
MessageBox.Show("You are about to disconnect from the service");
}
#endregion
}
Points to ponder of the above code snippet:
a>When you use the WCF callback, you have to use the DuplexChannelFactory rather then ChannelFactory to create the proxy.Othewise, you can generate the proxy.
b>WCF callback supports the following binding: WSDualHttpBinding, NetTcpBinding and NetNamedPipeBinding. When you use the WSDualHttpBinding, don’t forget to set the ClientBaseAddress.
c>Implement the callback interface.
That’s it.
If you need the source code of this demo, you can email me.
Happy Programming!
Sunday, June 14, 2009
Create a CheckListBox in WPF
Many developers are frustrated with lack of basic WPF controls. The WPF and WPF toolkit have some important and cool controls. However, some basic and necessary controls are still missing. But it is easy to create the basic controls in WPF. The article will show you how to create a checklistbox in WPF.
Here, I will create a custom control which will inherit from Listbox. The following code snippet shows the entire source code for the CheckListBox.
namespace MyControlLibrary
{
public class CheckListBox:ListBox
{
public CheckListBox()
{
}
///
/// Item Source of the check list box
///
public new IEnumerable ItemsSource
{
get
{
return this.ItemsSource;
}
set
{
//set the template
this.ItemTemplate = GetTemplate();
base.ItemsSource = value;
}
}
///
/// Gets or sets the property of source object. The property value will be displayed as a content of the checkbox.
///
public string DisplayMember { get; set; }
///
///
///
public string ValueMember { get; set; }
///
/// returns the template for the checkbox.
///
///
private DataTemplate GetTemplate()
{
DataTemplate template = new DataTemplate();
Binding binding = new Binding(this.DisplayMember);
FrameworkElementFactory fwfactory = new FrameworkElementFactory(typeof(CheckBox));
fwfactory.SetValue(CheckBox.NameProperty, "chk");
fwfactory.SetValue(CheckBox.ContentProperty, binding);
fwfactory.SetValue(CheckBox.TagProperty, new Binding(this.ValueMember));
template.VisualTree = fwfactory;
return template;
}
///
/// returns the checked list items.
///
public IEnumerable GetCheckListItems
{
get
{
List
Here, I will create a custom control which will inherit from Listbox. The following code snippet shows the entire source code for the CheckListBox.
namespace MyControlLibrary
{
public class CheckListBox:ListBox
{
public CheckListBox()
{
}
///
/// Item Source of the check list box
///
public new IEnumerable ItemsSource
{
get
{
return this.ItemsSource;
}
set
{
//set the template
this.ItemTemplate = GetTemplate();
base.ItemsSource = value;
}
}
///
/// Gets or sets the property of source object. The property value will be displayed as a content of the checkbox.
///
public string DisplayMember { get; set; }
///
///
///
public string ValueMember { get; set; }
///
/// returns the template for the checkbox.
///
///
private DataTemplate GetTemplate()
{
DataTemplate template = new DataTemplate();
Binding binding = new Binding(this.DisplayMember);
FrameworkElementFactory fwfactory = new FrameworkElementFactory(typeof(CheckBox));
fwfactory.SetValue(CheckBox.NameProperty, "chk");
fwfactory.SetValue(CheckBox.ContentProperty, binding);
fwfactory.SetValue(CheckBox.TagProperty, new Binding(this.ValueMember));
template.VisualTree = fwfactory;
return template;
}
///
/// returns the checked list items.
///
public IEnumerable GetCheckListItems
{
get
{
List