The end goal is to have a WCF server that calls a callback function, and each subscribed WCF client displays a new instance of a form on the callback.
To accomplish this, I am using using a pub/sub pattern where the clients connect to the server via a named pipe and call Subscribe(), then receive a callback, EventReceived() when an event is read from the event log. In the implementation for the callback, the clients instantiate a new instance of a form and show it.
The problem is that the forms that are shown by the client freeze immediately. Any suggestions would be helpful. A simplified version of the code which exhibits the same behavior is below.
Client Code (Form1 is just a blank, default form with no additional code/decorators. Form2's only purpose in life is to record the SynchronizationContext.Current for the callbacks to use later.)
static class Program
{
public static SynchronizationContext SynchronizationContext;
[STAThread]
static void Main()
{
ClientContract callbacks = new ClientContract();
DuplexChannelFactory<IEventService> pipeFactory = new DuplexChannelFactory<IEventService>(
callbacks, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/EventPipe"));
IEventService pipeProxy = pipeFactory.CreateChannel();
pipeProxy.Subscribe();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form2());
}
}
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
class ClientContract : IClientContract
{
public void EventReceived(int eventId)
{
Form1 newForm = new Form1();
newForm.Show();
}
}
Client Code - Form2 Constructor
public Form2()
{
InitializeComponent();
Program.SynchronizationContext = SynchronizationContext.Current;
}
Server Code
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(EventService), new Uri[] { new Uri("net.pipe://localhost") }))
{
host.AddServiceEndpoint(typeof(IEventService), new NetNamedPipeBinding(), "EventPipe");
host.Open();
Console.WriteLine("Service started");
do
{
Console.WriteLine("Enter \"callback\" to trigger a callback, or enter to quit");
string input = Console.ReadLine();
if (input == "callback")
{
EventService.PublishEvent(3);
}
else if (input == "")
{
break;
}
} while (true);
host.Close();
}
}
}
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.PerCall)]
class EventService : IEventService
{
public static event NewEventHandler NewEventAvailableHandler;
public delegate void NewEventHandler(EventLogEventArgs e);
private NewEventHandler newEventHandler;
private IClientContract callback = null;
public static void PublishEvent(int eventId)
{
EventLogEventArgs e = new EventLogEventArgs();
e.eventId = eventId;
if (NewEventAvailableHandler != null)
{
NewEventAvailableHandler(e);
}
}
public void Subscribe()
{
callback = OperationContext.Current.GetCallbackChannel<IClientContract>();
newEventHandler = new NewEventHandler(MagazineService_NewIssueAvailableEvent);
NewEventAvailableHandler += newEventHandler;
}
public void Unsubscribe()
{
NewEventAvailableHandler -= newEventHandler;
}
public void MagazineService_NewIssueAvailableEvent(EventLogEventArgs e)
{
callback.EventReceived(e.eventId);
}
}
public class EventLogEventArgs : EventArgs
{
public int eventId;
}
Shared Code (implemented via a class library DLL file)
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientContract))]
public interface IEventService
{
[OperationContract(IsOneWay = true, IsInitiating = true)]
void Subscribe();
[OperationContract(IsOneWay = true, IsInitiating = true)]
void Unsubscribe();
}
public interface IClientContract
{
[OperationContract(IsOneWay = true)]
void EventReceived(int eventId);
}
** edit **
As pointed out in the comments, the callback was received on a worker thread, but UI events need to happen on the main thread. I was able to get it to work, but it feels like a hack because I have a second, unused form, simply to get the SynchronizationContext for the callback to use.