【问题标题】:How are STA COM components handled when used in a WCF service hosted in IIS (7+)?在 IIS (7+) 中托管的 WCF 服务中使用时如何处理 STA COM 组件?
【发布时间】:2011-04-22 06:50:39
【问题描述】:

据我了解,当从MTA 线程使用标记为使用STA 的COM 组件时,应该将调用编组到STA 线程并从该专用线程执行。对于 Windows 客户端应用程序,这意味着它将在 UI 线程上执行(如果标记为 STA),并且从 COM 组件到我的回调将由发送到隐藏窗口的 Windows 消息处理并在Windows 消息循环。

如果我在 IIS 中托管的 WCF 服务中使用 STA COM 组件会发生什么?工作进程是否会在 STA 线程上有 Windows 消息循环?我可以使用自己的消息循环启动自己的 STA 线程吗?

【问题讨论】:

    标签: wcf iis com sta


    【解决方案1】:

    COM 运行时负责对 STA 内的 COM 对象上的方法调用的分派:您说得对,这基于用于分派 Windows 消息的相同操作系统机制,但您不必担心发生这种情况 - COM 在后台为您执行此操作。

    要做需要担心的是您的 COM 对象将驻留在哪个 STA。如果您使用 WCF 服务中的 COM 互操作实例化单元线程 COM 对象,则需要小心.

    如果执行此操作的线程不是 STA 线程,则所有进程内 COM 对象都将存在于 IIS 工作进程的默认 Host STA 中。您不希望发生这种情况:所有服务操作的所有 COM 对象都将在同一个 STA 中结束。线索就在名称中——所有对象只有一个线程——并且对其方法的所有调用都将被序列化,等待公寓中唯一的线程来执行它们。您的服务将无法扩展以处理多个并发客户端。

    您需要确保为服务特定 WCF 请求而实例化的 COM 对象位于它们自己的 STA 中,与为其他请求创建的对象分开。大致有两种方法可以做到这一点:

    • 启动您自己的线程,在启动之前在SetApartmentState() 中指定ApartmentState.STA,在其上为特定请求实例化COM 对象。这是 Scott Seely 在the link in Kev's answer 中详述的方法:他确保在新的 STA 初始化线程上调用每个服务操作调用。沿着这些思路,一个更难但更可扩展的解决方案是实现一个可重用的 STA 初始化线程池。
    • 在 COM+ 应用程序中托管您的 COM 对象,以便它们存在于单独的 DllHost 进程中,其中 COM+(通过其称为 the Activity 的抽象)可以负责将不同请求的对象放入不同的 STA。

    当您提到回调时,我不确定您的确切含义。也许您的意思是在托管代码中实现的某个 COM 接口上的 COM 方法调用,通过作为 COM 对象方法之一的参数传递给 COM 对象的引用:如果是这样,这应该可以工作。但也许你的意思是别的,在这种情况下,也许你可以修改问题以澄清。

    【讨论】:

      【解决方案2】:

      我发现您需要在 WCF 服务中的 STA 线程上发送消息,否则您会错过来自 COM 对象的回调。

      以下代码有效,但需要您通过 Dispatcher 调用 COM 对象。

      ComWrapper comWrapper;
      Thread localThread;
      Dispatcher localThreadDispatcher;
      
      public Constructor()
      {
         localThread = new Thread(ThreadProc)
         {
             Name = "test"
         };
         localThread.SetApartmentState(ApartmentState.STA);
      
         AutoResetEvent init = new AutoResetEvent(false);
      
         localThread.Start(init);
      
         init.WaitOne();
      }
      
      private void ThreadProc(object o)
      {
          localThreadDispatcher = Dispatcher.CurrentDispatcher;
          ((AutoResetEvent)o).Set();
      
          comWrapper = new ComWrapper()
      
          Dispatcher.Run();
      
          localThreadFinished.Set();
       }
      

      然后进行如下调用。

      public void UsefulComOperation()
      {
          localThreadDispatcher.Invoke(new Action( () => comWrapper.UsefulOperation);
      }
      

      【讨论】:

      • localThreadFinished 没有在任何地方定义,据我所知......您是否打算在ThreadProc 的顶部声明它,如AutoResetEvent localThreadFinished = (AutoResetEvent)o
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      • 2010-11-15
      • 2011-10-24
      相关资源
      最近更新 更多