Reputation: 14233
I'm trying to port some of my old .NET Remoting IPC code over to WCF Named Pipes and I know I'm probably doing something stupid here...
I have a Windows Service using a ServiceHost/NetNamedPipeBinding endpoint.
I have a WinForms UI connecting to it with the ChannelFactory/NetNamedPipeBinding.
Regardless of if the Windows Server is running or not, after I do CreateChannel
on the client, the ChannelFactory object State is Opened. I would expect it to be something other than 'Opened' if the service isn't running. It's not until I try to call a remote method that I receive an exception. What is the best way to detect if the connection was successfully made or not?
My code works fine if the service is actually running, but when it's not (or the connection gets dropped) I can't seem to make the client State changed from Opened.
Here is my Service Helper code:
public class WcfServerHelper
{
public static ServiceHost RemoteServiceHost { get; set; }
public static void Start(Type remotingType)
{
RemoteServiceHost = new ServiceHost(remotingType, new Uri[] {
new Uri("net.pipe://localhost")
});
RemoteServiceHost.AddServiceEndpoint(typeof(ICardPrinterRemoting), new NetNamedPipeBinding(), "LifeMedPrinter");
RemoteServiceHost.Open();
}
public static void Stop()
{
if (RemoteServiceHost != null && RemoteServiceHost.State == CommunicationState.Opened)
{
RemoteServiceHost.Close();
}
}
}
Windows Service:
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
WcfServerHelper.Start(typeof(CardPrinterRemoting));
}
protected override void OnStop()
{
WcfServerHelper.Stop();
}
}
Client Helper Code:
public class WcfClientHelper
{
public static ICardPrinterRemoting RemotingObject { get; set; }
public static ChannelFactory<ICardPrinterRemoting> PipeFactory { get; set; }
public static void ConnectIpc()
{
PipeFactory = new ChannelFactory<ICardPrinterRemoting>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/LifeMedPrinter"));
RemotingObject = PipeFactory.CreateChannel();
}
}
Windows UI (Service Client) Code:
private void btnConnect_Click(object sender, EventArgs e)
{
WcfClientHelper.ConnectIpc();
// The event handlers never get called, even if a successfully connection is made and then the Windows Service is stopped
WcfClientHelper.PipeFactory.Closing += PipeFactory_Closing;
WcfClientHelper.PipeFactory.Faulted += PipeFactory_Faulted;
WcfClientHelper.PipeFactory.Closed += PipeFactory_Closed;
if (WcfClientHelper.PipeFactory.State == CommunicationState.Opened)
{
LifeMedPrinterSettings lifeMedPrinterSettings = new LifeMedPrinterSettings();
lifeMedPrinterSettings.PrinterName = "Hello World";
// It always falls into this IF statement, but will blow up on this method call if the service isn't actually running:
WcfClientHelper.RemotingObject.SaveSettings(lifeMedPrinterSettings);
}
else
{
MessageBox.Show("No can-do");
}
}
And here is the exception I receive when I call the remote method when the service isn't running:
System.ServiceModel.EndpointNotFoundException was unhandled
HResult=-2146233087
Message=There was no endpoint listening at net.pipe://localhost/LifeMedPrinter that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
Source=mscorlib
StackTrace:
Server stack trace:
at System.ServiceModel.Channels.PipeConnectionInitiator.GetPipeName(Uri uri, IPipeTransportFactorySettings transportFactorySettings)
at System.ServiceModel.Channels.NamedPipeConnectionPoolRegistry.NamedPipeConnectionPool.GetPoolKey(EndpointAddress address, Uri via)
at System.ServiceModel.Channels.ConnectionPoolHelper.TakeConnection(TimeSpan timeout)
at System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(TimeSpan timeout)
at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at LifeMed.CardPrinter.Remoting.Interfaces.ICardPrinterRemoting.SaveSettings(LifeMedPrinterSettings printerSettings)
InnerException: System.IO.PipeException
HResult=-2146232800
Message=The pipe endpoint 'net.pipe://localhost/LifeMedPrinter' could not be found on your local machine.
ErrorCode=-2146232800
InnerException:
Thank you!
Upvotes: 2
Views: 1565
Reputation: 14233
After much research I found an answer, but not exactly what I wanted. Apparently the only way to see if a service is available is to call it - such as through a generic Ping method and then handle the exception (like the exception I have in my question).
So I guess this is the answer.
My only other question would be: what is the purpose of the State property? I would expect State == Opened to indicate that there is an open connection with the service.
Anyway, I have my solution.
Thanks!
Upvotes: 2