Problem
The most reproducible scenario is starting the desktop application from a link on the tray application and then requesting the desktop application via a callback to do something. That virtually always throws a timeout error as other subsequent errors.
WCF Environment:
Server: A System Tray applet
Client: WinForms desktop application
Resources:
Method to Launch the Desktop Application from the Tray
// Start the process.
ProcessStartInfo oProcessInfo = new ProcessStartInfo()
{
FileName = regValue.ToString(),
WindowStyle = ProcessWindowStyle.Normal,
UseShellExecute = true,
CreateNoWindow = false,
};
Process oProcess = new Process()
{
StartInfo = oProcessInfo,
};
oProcess.Start();
I make several calls, but interaction does not necessarily fail on the first call. Here is a sample call:
var callback = IpcToTray.Callback;
if (null == callback)
return 0;
UInt16 idIsLaunched = callback.IsAppLaunched();
if (Id_AppIsLaunched == idIsLaunched)
return true;
I am using PerCall at the moment, but was using PerSession. Both did not help the cause any.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class IpcToTray : IIpcToTray
{
}
Here are some of the errors:
System.TimeoutException
HResult=0x80131505
Message=This request operation sent to http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous did not receive a reply within the configured timeout (00:00:10). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.
Source=mscorlib
StackTrace:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at MyAppIpc.IIpcCallbackToTray.GetMyAppMode()
at MyAppTray.Communication.IpcFromTray.GetMyAppMode() in ...\IpcFromTray.cs:line 194
System.ObjectDisposedException
HResult=0x80131622
Message=Cannot access a disposed object.
Object name: 'System.ServiceModel.ServiceHost'.
Source=System.ServiceModel
StackTrace:
at System.ServiceModel.Channels.CommunicationObject.ThrowIfDisposedOrImmutable()
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open()
at MyAppTray.CustomApplicationContext.StartPipeServer() in ...\MyAppTray\CustomApplicationContext.cs:line 105
System.ServiceModel.CommunicationObjectAbortedException
HResult=0x80131501
Message=The operation 'IsAppLaunched' could not be completed because the sessionful channel timed out waiting to receive a message. To increase the timeout, either set the receiveTimeout property on the binding in your configuration file, or set the ReceiveTimeout property on the Binding directly.
Source=mscorlib
StackTrace:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at MyAppIpc.IIpcCallbackToTray.IsAppLaunched()
at MyAppTray.Communication.IpcFromTray.IsAppLaunched() in ...\MyAppTray\Communication\IpcFromTray.cs:line 142
As to my code, it basically looks like the WCF Interprocess Communication sample with changed class names, but otherwise similar, so no need placing here.
I am trying to restart after a failure, but .Close()
followed by .Open()
does not always work, as might be disposed, creating a new one gets a URI endpoint already existing. I tried many ways to get things stable to no avail.
I have time out to 10-seconds at the moment. The original time out was 1-minute, the default by Microsoft. I thought my application hung at first. Session stuff does not help the cause. I tried adding a delay, Thread.Sleep(4000), but that is annoying and does nothing. The application might be open, but, again, sleeping 4000 or 4000000000000 makes no difference whatsoever.
The bottom line is that for whatever reason I cannot stabilize the callback. Yes, the callback is static. I do not see any other way but that.
Thoughts? I am mega frustrated at the moment.
I had two problems with my code, which I post here for posterity.
The main problem was because I made callbacks too close (not one or two) to each other. I noticed, while debugging that the failure was inside my do-while
loop. I had kept track of the number of iterations inside the loop looking for the application's state to change and noticed that the counter was at 1, not 0. That made me realize that I needed a delay. I set the delay first to 25ms between calls and still had the problem. I then tried 50ms, which solved the problem.
I had to add a 3-second delay after several different calls in order for the link to not fault out. I was reading a bit and that is by design. Microsoft has a DOS (Denial of Service) scheme in place in WCF as best that I understand. It is possible that I ran into that or just there needs a bit of clean-up time for the channel to reset before another call, though that argument does not explain the longer one.
Closing down the channel at app closing
this.HostIpcToTray.CloseCallback();
this.IpcPipeFactory.Close();
private void IpcPipeFactory_Closing(object sender, EventArgs e)
{
this.IsConnectedPipeToTray = false;
this.HostIpcToTray = null;
this.IpcPipeFactory.Closing -= IpcPipeFactory_Closing;
}
The first call, sets the static Callback variable to null, so that the service cannot use a disposed object until the next time the application comes up.
I had to unsubscribe myself from the Closing
event, which I do in the Closing
event.
I call the Close() channel, which I did not do. I guess garbage collection does not close channels properly. I noticed the problem, when I attempted to close the main application, the client, and then launch the application again and use the tray to communicate that I threw a fault. A bit of research led to the code above.
Many samples show the basic framework for WCF IPC, but the devil is in the details for production code.
User contributions licensed under CC BY-SA 3.0