在服务实现中创建 System.Windows.Control 时,WCF 服务不起作用

Posted

技术标签:

【中文标题】在服务实现中创建 System.Windows.Control 时,WCF 服务不起作用【英文标题】:WCF service not working when a System.Windows.Control is created inside the service implementation 【发布时间】:2018-10-07 07:20:51 【问题描述】:

我有一个带有服务合同的 WCF 服务(我们称之为 IHelloWorldService)。在合约实现内部(不要问为什么......)构造函数创建一个 System.Windows.Control 实例。

好吧,使用这种配置,服务根本不起作用。它似乎启动得很完美,但是当我向服务发出请求时(即使我在浏览器中请求元数据),服务也没有响应。 有趣的是,如果我在另一个线程中创建 System.Windows.Form(例如,就在任务内部),该服务可以完美运行。该服务是一个单例,因此它不会在每次调用时创建新实例。控件创建一次。

我的示例的快速伪代码是:

服务主机创建:

   ServiceHost serviceHost = new ServiceHost(new HelloWorldService(), "callback");

服务:

   class HelloWorldService : IHelloWorldService
   
       public HelloWorldService() : this(new System.Windows.Control())
       

       public HelloWorldService(System.Windows.Control control)
   

前面的例子不起作用。但是,如果我在另一个线程中创建 HelloWorldService,例如在任务中,它会完美运行。 快速粗略的伪代码示例:

   ServiceHost serviceHost = new ServiceHost(CreateHelloWorldService(), "callback");

   HelloWorldService CreateHelloWorldService()
   
       HelloWorldService service;
       Task newTask = new Task service = new HelloWorldService() ;
       newTask.Run().Wait();
       return service;
   

这个例子效果很好。我的猜测是,主线程在创建控件时可能会发生某种变化,然后 WCF 堆栈无法处理请求。这个问题是一个更大的问题的简化,虽然我可以用线程解决它,但我想/需要了解发生了什么以及为什么不能正常工作,以便解释大问题真实环境程序。

为我的英语道歉。如果有什么不清楚的地方,请告诉我,我会尽力澄清。 提前致谢!

【问题讨论】:

您正在服务中制作表单? 我喜欢“不要问为什么”的部分。我不评判。真的。但有件事告诉我,“为什么”一定是我从未听过的最棒的故事之一。我们怎么能不问为什么?拜托,我必须知道。 我正在处理一个令人头疼的遗留代码。该服务充当声卡的控制器。此声卡由 directX 管理,Microsoft.DirectX.DirectSound.Device.SetCooperativeLevel 作为参数接收 System.Windows.Form ...这就是我所知道的。该软件已“弃用”,但遗留项目面临问题......我发现了这个可以解释发生了什么的东西,但我什至不知道这是否会导致错误。我只是想理解这个问题......长篇大论但并不有趣,这就是“不要问”xD的原因 【参考方案1】:

    服务并非旨在创建 Windows 窗体组件。即使你以某种方式成功地完成了创作,以后与它交互也会有问题。

    Windows 窗体控件应在后台线程中处理,因为创建它的线程必须与与之交互的线程相同。所有交互都需要使用 InvokeRequired,然后在 TPL 之前的时间内执行 BeginInvoke。使用 TPL,使用交互应该在 Task 有界操作中

    在您的代码 sn-p 中,由于正在使用的实际线程在方法调用之后被释放,因此表单将成为僵尸,因为没有参考来控制它..(未来可能出现问题的示例在服务中处理表格)

代码方面,只要我们使用 Invoke/BeginInvoke,就没有问题,如下面的示例所示

public delegate void myDel();

public class Service1 : MyService

    private WindowsFormsApp1.Form1 form;

    public Service1()
    
        this.form = new WindowsFormsApp1.Form1();
    

    private void SetFormData()
    
        this.form.Size = new System.Drawing.Size(100, 500);
    
    public void DoSomeFun()
    
        this.form.Show();
        myDel del = new myDel(SetFormData);
        this.form.Invoke(del);
    

【讨论】:

感谢您的回答! 1. 是的,我知道服务并不意味着托管表单。真正的开发是托管在服务中的 SoundPlayerController。这个 SoundPlayerController 使用 DirectX 设备,它需要一个表单来设置合作级别。也许可以以更好的方式完成,但这是我无法更改的遗留代码。 2.我不知道这是否会影响我的场景。如果我理解你,我的场景应该可以工作,然后我在同一个线程中创建表单,当我使用新的表单时失败.. 3. 是的,我的代码 sn-p 是一个简化,只是为了给你和想法。 单独的应用程序可以调用服务方法怎么样? 不可能。我们无法做出任何相关的改变。我们有一个生产中的软件,有时(我们无法在开发环境中重现它)在重新启动服务后端点无法访问。深入研究代码,我发现了关于服务内部控件的这种罕见的事情。这是在一个单独的线程中完成的(这就是它起作用的原因)。我把它放在主线程中,我观察到端点不可到达。我什至不知道这是否与真正的错误有关,这就是为什么我试图了解真正发生的事情。我真的很感谢你的帮助!谢谢! @MiguelAngelRodriguez 添加了一个示例代码,用于 WCF 在构造函数中设置一个表单,然后在服务方法中访问它。它工作正常,也许表单还有其他操作(比如忽略 BeginInvoke)导致异常,并且服务是单例,死了.. 是的,这可以解释真正的场景问题,因为我真的不知道如何在 DirectX 库中使用此表单...但在我的简单示例中,表单仅存储在一个字段中在服务实现内部。我调试了来自 wcf 堆栈的请求。它启动了该过程,但在某些地方(我无法找到)该过程卡住了。对不起,我觉得我在浪费你的时间,但我不能给你更多的信息。使用您的代码,它是否可以在不对表单执行任何操作的情况下工作?再次感谢!!

以上是关于在服务实现中创建 System.Windows.Control 时,WCF 服务不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Go 中创建 WebSocket 应用程序

在 Visual Studio Code 中创建自定义语言

是否可以在 GKE 的区域集群中创建仅限区域的节点池?

如何在 PHP 中创建子域? [复制]

在 actionscript flash 中创建登录

无法在 Azure 数据工厂中创建链接服务