简体   繁体   English

WCF NamedPipes服务和WPF客户端

[英]WCF NamedPipes Service and WPF Client

I'm working with a windows service where i want to add a GUI too it. 我正在使用Windows服务,我也想在其中添加GUI。 I made a proof of concept with my service creating a ServiceHost object and hosting the WCF Named pipes service and then i Console application to consume the WCF service and also getting callback responses from the service (messages sent from server to connected clients). 我通过服务创建ServiceHost对象并托管WCF命名管道服务进行了概念验证,然后通过i Console应用程序使用了WCF服务,还从该服务获取了回调响应(从服务器发送到连接的客户端的消息)。 This works great my console application runs and get responses from the service with no interruption or delays. 我的控制台应用程序可以很好地运行,并且可以不中断或延迟地从服务获得响应。

However when doing the same thing in my WPF GUI application when clicking a button that then calls the WCF service it freezes the whole UI thread and then after a couple of minutes throws an exception and then the UI is updated with the message callback (server sends message to connected clients) but any return values from service is lost since the exception was thrown. 但是,当在我的WPF GUI应用程序中做同样的事情时,单击一个按钮然后调用WCF服务,它将冻结整个UI线程,然后在几分钟后引发异常,然后用消息回调更新UI(服务器发送消息发送给已连接的客户端),但由于引发了异常,因此服务的所有返回值都将丢失。

The two exception messages i have gothen is theses (the most common is the first): 我收到的两条异常消息是论文(最常见的是第一条):

1: The requesting action sent to net.pipe :/ / localhost / PipeGUI did not receive a response within the specified timeout (00:00:59.9989999). 1: 发送到net.pipe:/// localhost / PipeGUI的请求操作在指定的超时时间(00:00:59.9989999)内未收到响应。 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. 这可能是因为该服务仍在处理该操作,或者是因为该服务无法发送回复消息。 Raise the deadline for action (by entering the channel / proxy to Icon Text Channel and set the property Operation Timeout) and verify that the service can connect to the client. 提高操作期限(通过将通道/代理输入到“图标文本通道”并设置属性“操作超时”),并验证服务是否可以连接到客户端。

2: Communication object System.ServiceModel.Channels.ServiceChannel, can not be used for communication because it has been canceled. 2: 通信对象System.ServiceModel.Channels.ServiceChannel,由于已被取消,因此无法用于通信。

Anyone got any ideas why this is happeing ? 有人知道为什么会这样吗? I can post more code if neccessary. 如果需要,我可以发布更多代码。

UPDATE , added code for reference UPDATE,添加了代码以供参考

public interface IClientCallback
{
    [OperationContract(IsOneWay = true)]
    void MessageRecived(string message);
}

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
public interface IPipeServiceContract
{
    [OperationContract]
    string Hello();

    [OperationContract]
    void Message(string msg);

    [OperationContract(IsInitiating = true)]
    void Connect();

    [OperationContract(IsTerminating = true)]
    void Disconnect();
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, UseSynchronizationContext = false)]
public class PipeService : IPipeServiceContract
{
    List<IClientCallback> _clients = new List<IClientCallback>();

    public string Hello()
    {
        PublishMessage("Hello World.");
        return "Return from method!";
    }

    public void Connect()
    {
        _clients.Add(OperationContext.Current.GetCallbackChannel<IClientCallback>());
    }

    public void Disconnect()
    {
        IClientCallback callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
        _clients.Remove(callback);
    }

    void PublishMessage(string message)
    {
        for (int i = _clients.Count - 1; i > 0; i--)
        {
            try
            {
                _clients[i].MessageRecived(message);
            }
            catch (CommunicationObjectAbortedException coae)
            {
                _clients.RemoveAt(i);
            }
            catch(CommunicationObjectFaultedException cofe)
            {
                _clients.RemoveAt(i);
            }
        }
    }


    public void Message(string msg)
    {
        PublishMessage(msg);
    }
}


/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged, IClientCallback
{
    public ServiceController Service { get; set; }

    protected IPipeServiceContract Proxy { get; set; }
    protected DuplexChannelFactory<IPipeServiceContract> PipeFactory { get; set; }

    public ObservableCollection<ServerActivityNotification> Activity { get; set; }

    public override void BeginInit()
    {
        base.BeginInit();
        PipeFactory = new DuplexChannelFactory<IPipeServiceContract>(this, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/PipeGUI"));
    }

    public MainWindow()
    {
        InitializeComponent();
        Activity = new ObservableCollection<ServerActivityNotification>();
        Service = ServiceController.GetServices().First(x => x.ServiceName == "Server Service");
        NotifyPropertyChanged("Service");
        var timer = new DispatcherTimer();
        timer.Tick += new EventHandler(OnUpdate);
        timer.Interval = new TimeSpan(0, 0, 0, 0, 850);
        timer.Start();

        if (Service.Status == ServiceControllerStatus.Running)
        {
            Proxy = PipeFactory.CreateChannel();
            Proxy.Connect();
        }
    }

    void OnUpdate(object sender, EventArgs e)
    {
        Service.Refresh();
        NotifyPropertyChanged("Service");

        StartButton.IsEnabled = Service.Status != ServiceControllerStatus.Running ? true : false;
        StopButton.IsEnabled = Service.Status != ServiceControllerStatus.Stopped ? true : false;

        if (PipeFactory != null && Service.Status == ServiceControllerStatus.Running)
        {
            Proxy = PipeFactory.CreateChannel();
            Proxy.Connect();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

    private void OnStart(object sender, RoutedEventArgs e)
    {
        try
        {
            Service.Start();
        }
        catch
        {
            Service.Refresh();
        }
    }

    private void OnStop(object sender, RoutedEventArgs e)
    {
        try
        {
            if (Proxy != null)
            {
                Proxy.Disconnect();
                PipeFactory.Close();
            }
            Service.Stop();
        }
        catch
        {
            Service.Refresh();
        }
    }

    public void MessageRecived(string message)
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
        {
            ServerActivityNotification log = new ServerActivityNotification { Activity = message, Occured = DateTime.Now };
            Activity.Add(log);
            ListBoxLog.ScrollIntoView(log);
            NotifyPropertyChanged("Activity");
        }));
    }

    private void OnHello(object sender, RoutedEventArgs e)
    {
        try
        {
            Proxy.Message(txtSendMessage.Text);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

} }

Try setting the UseSynchronizationContext property of the service behaviour to false : 尝试将服务行为的UseSynchronizationContext属性设置为false

[ServiceBehavior(UseSynchronizationContext = false)]
class MyService
{

}

[ServiceContract]
public interface IMyService
{

}

I believe that by default this is set to true , so you are currently attempting to consume and run the WCF service on the same thread resulting in a deadlock. 我相信默认情况下将其设置为true ,因此您当前正在尝试在同一线程上使用和运行WCF服务,从而导致死锁。

In any case, it sounds like you are trying to consume the WCF service on the UI thread of the WPF application. 在任何情况下,听起来您都想在WPF应用程序的UI线程上使用WCF服务。 Generally it is recommended that you perform potentially long running tasks on the background thread as this keeps the interface responsive even if your service call takes a few seconds/minutes. 通常,建议您在后台线程上执行可能会长时间运行的任务,因为即使您的服务呼叫需要花费几秒钟/分钟,也可以使界面保持响应状态。

EDIT: 编辑:

I tried and succeeded in replicating your problem. 我尝试并成功地复制了您的问题。 Trying to call the service on the UI thread results in the UI freezing. 尝试在UI线程上调用服务会导致UI冻结。 However, when I changed my code to call the service on a background task (see below), I was able to call the service and receive the callback: 但是,当我更改代码以在后台任务上调用服务(见下文)时,我能够调用服务并接收回调:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Task.Factory.StartNew(() =>
            {
                var myService = DuplexChannelFactory<IMyService>.CreateChannel(new CallbackImplementation(),
                                                                               new WSDualHttpBinding(),
                                                                               new EndpointAddress(
                                                                                   @"http://localhost:4653/myservice"));
                myService.CallService();
                string s = "";
            });
    }

I have to confess, I am not entirely sure why this is so, and any clarification on exactly how WCF manages the thread hosting the service instance would be great in working out why this works. 我不得不承认,我并不完全确定为什么会这样,关于WCF如何确切地管理托管服务实例的线程的任何澄清都将有助于弄清其工作原理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM