COM出站调用出现“由于应用程序正在分派输入同步调用,因此无法进行输出调用。”的错误信息。

12
我有一个COM服务器(基于C++/STA(MFC为基础的应用程序))和一个COM客户端(基于C# / MTA)。由于它是一个MFC应用程序(我在这个问题上没有选择),因此COM服务器必须驻留在STA中。客户端发出对服务器的调用,服务器向客户端发出回调。这就是错误发生的地方(RPC_E_CANTCALLOUT_ININPUTSYNCCALL)。我猜想如果服务器是MTA的话,这个问题就不会出现,但遗憾的是,MFC的文档明确拒绝将公寓初始化为MTA。

有什么解决这个问题的想法吗?

我一直在考虑让服务器对象(通过运行对象表公开的对象)位于其自己的公寓(MTA)中。这是个好主意吗?还是有更简单的尝试方法?

更新

服务器对象只是指向应用程序中某些函数的轻量级接口点。大部分时间它只读取和写入内存位置,但也有时候会给应用程序中的各个窗口生成窗口消息。服务器对象本身并不是整个应用程序。


这是违反公寓线程要求的直接后果。不太清楚它是如何发生的,但如果服务器需要单线程公寓,则最好在C#端在STA线程上创建RCW。 - Hans Passant
服务器所在的应用程序需要STA。服务器对象本身不需要STA。对服务器的某些调用将生成发送到应用程序内各个窗口的窗口消息。 - Jörgen Sigvardsson
在C#方面创建RCW是什么意思? - Jörgen Sigvardsson
3个回答

20

RPC_E_CANTCALLOUT_ININPUTSYNCCALL 的意思是你试图在通过SendMessage发送的Windows消息的处理程序中进行一个封送的COM调用。这是为了避免某些死锁情况。你有许多选项,总结起来就是“避免在SendMessage处理程序中进行COM调用”:

  • 你可以使用PostMessage来排队发送一条消息给自己,在该“已发布”消息处理程序中调用COM回调。
  • 你可以使用异步DCOM,避免在消息处理程序中阻塞调用结果。
  • 你可以封送回调接口,然后从线程池工作项中调用它。由于这与主应用程序的消息循环无关,因此不会在SendMessage调用中,甚至可以在MTA中。
  • 你可以放弃MFC COM支持,直接从另一个线程调用CoRegisterClassObject。这意味着任何对服务器COM对象的调用都将从COM线程池(或者如果你使用STA线程,则从该线程)调用,而不是从MFC UI线程调用,因此你需要使用Windows消息跨线程通信;但如果需要向客户端进行同步调用,则可能是最好的方法。

我最终采用了第一点,因为这意味着处理手头问题的最简单方式,因为我没有太多的回调(目前只有三个,而且我不认为这个数字会很快改变)。我正在开发的这个自动化代码并不是优先考虑的工作,我不能在上面花费太多时间和精力。特别是当它是我正在处理的“内部”事情时。然而,您提出的观点非常有启发性,如果我将来必须在生产代码中再次处理此类事情,我会记住这些观点。谢谢! - Jörgen Sigvardsson
1
在WPF中,我只需将我的代码包装在new Thread(new ThreadStart(() => {})).Start();之间,然后就完成了! - ioan
@ioanb7 你太棒了,不确定是男是女!这也解决了WinForms中OP提到的错误。 - Michael Z.

2
无论我如何扭曲和翻转,当我从客户端被调用时,我都无法拔掉应用程序中的STA上下文。即使我将服务器对象托管到MTA中,我仍然必须遵守COM的规则,STA在这种情况下是一个非常讨厌的“矫正设施”。这让我走上了一条相当丑陋的路,但它确实可行。我不使用COM与客户端进行通信,而是手动创建自己的通信路径到托管服务器对象和回调引用的MTA。我基本上通过设置调用队列(STL容器与要发送的参数)来创建自己的编组代码,MTA线程会捡起并传递给客户端。然后将响应返回给响应初始调用的代码。同步使用Win32事件对象完成。幸运的是,我没有太多要处理的回调,并且机制的性质是静态的,仅用于我的目的(不会在生产环境中运行)。真是太辛苦了……有时我想知道如果我选择成为木匠会怎样。

0

当你收到消息时,可以创建一个计时器,然后在WM_TIMER下的TimerProc / WinProc中执行COM调用和进一步处理。这对我有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接