I'm playing around with hooking up an in-game console to a WCF interface, so an external application can send console commands and receive console output. To accomplish this I created the following service contracts:
public interface IConsoleNetworkCallbacks
{
[OperationContract(IsOneWay = true)]
void NewOutput(IEnumerable<string> text, string category);
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IConsoleNetworkCallbacks))]
public interface IConsoleInterface
{
[OperationContract]
void ProcessInput(string input);
[OperationContract]
void ChangeCategory(string category);
}
On the server I implemented it with:
public class ConsoleNetworkInterface : IConsoleInterface, IDisposable
{
public ConsoleNetworkInterface()
{
ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler);
}
public void Dispose()
{
ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
}
public void ProcessInput(string input)
{
ConsoleManager.Instance.ProcessInput(input);
}
public void ChangeCategory(string category)
{
ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler, category);
}
protected void OutputHandler(IEnumerable<string> text, string category)
{
var callbacks = OperationContext.Current.GetCallbackChannel<IConsoleNetworkCallbacks>();
callbacks.NewOutput(text, category);
}
}
On the client I implemented the callback with:
public class Callbacks : IConsoleNetworkCallbacks
{
public void NewOutput(IEnumerable<string> text, string category)
{
MessageBox.Show(string.Format("{0} lines received for '{1}' category", text.Count(), category));
}
}
Finally, I establish the service host with the following class:
public class ConsoleServiceHost : IDisposable
{
protected ServiceHost _host;
public ConsoleServiceHost()
{
_host = new ServiceHost(typeof(ConsoleNetworkInterface), new Uri[] { new Uri("net.pipe://localhost") });
_host.AddServiceEndpoint(typeof(IConsoleInterface), new NetNamedPipeBinding(), "FrbConsolePipe");
_host.Open();
}
public void Dispose()
{
_host.Close();
}
}
and use the following code on my client to establish the connection:
protected Callbacks _callbacks;
protected IConsoleInterface _proxy;
protected void ConnectToConsoleServer()
{
_callbacks = new Callbacks();
var factory = new DuplexChannelFactory<IConsoleInterface>(_callbacks,
new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/FrbConsolePipe"));
_proxy = factory.CreateChannel();
_proxy.ProcessInput("Connected");
}
So what happens is that my ConnectToConsoleServer() is called and then it gets all the way to _proxy.ProcessInput("Connected");. In my game (on the server) I immediately see the output caused by the ProcessInput call, but the client is still stalled on the _proxy.ProcessInput() call.
After a minute my client gets a JIT TimeoutException however at the same time my MessageBox message appears.
So obviously not only is my command being sent immediately, my callback is being correctly called. So why am I getting a timeout exception?
Note: Even removing the MessageBox call, I still have this issue, so it's not an issue of the GUI blocking the callback response.