COM加载项:解决WinWord.exe中的错误DisconnectedContext

时间:2016-10-19 02:05:21

标签: c# multithreading visual-studio com

我构建了Microsoft Word的附加组件。当用户单击某个按钮时,它会运行许多将Microsoft Word文档列表导出为Filtered HTML的进程。这很好。

代码失败的地方在于处理大量文件。文件转换完成后我调用下一个函数,应用程序崩溃,我从Visual Studio获取此信息:

  

托管调试助手' DisconnectedContext'已在C:\ Program Files \ Microsoft Office \ root \ Office16 \ WINWORD.EXE'中检测到问题。

     

附加信息:转换到COM上下文0x56255b88 for   此RuntimeCallableWrapper失败,出现以下错误:System   呼叫失败。 (HRESULT的例外情况:0x80010100   (RPC_E_SYS_CALL_FAILED))。这通常是因为COM上下文   0x56255b88创建了这个RuntimeCallableWrapper   断开连接或忙于做其他事情。释放   来自当前COM上下文的接口(COM上下文0x56255cb0)。这个   可能会导致损坏或数据丢失。请避免这个问题   确保所有COM上下文/公寓/线程保持活跃状态   可用于上下文转换,直到应用程序完全   使用代表COM组件的RuntimeCallableWrappers完成   住在他们里面。

经过一些测试,我意识到如果我只是在文件转换后删除所有代码,就没有问题。要解决此问题,我将剩下的代码放在另一个按钮中。

问题是我不想给用户两个按钮。阅读各种其他线程后,听起来我的代码有内存或线程问题。我正在阅读的答案并没有帮助我真正理解下一步该做什么。

我觉得这就是我想做的事情:

1-运行转换。 2-转换中关闭线程/清理内存问题。 3-继续运行代码。

不幸的是,我真的不知道怎么做#2或者甚至可能。非常感谢您的帮助。

1 个答案:

答案 0 :(得分:6)

  

或正在忙着做别的事情

您获得的托管调试助手诊断非常糟糕,但这是准确描述真实问题的消息部分。您有一个 firehose 问题,这是与线程相关的第三个最常见的问题。这个事故难以诊断,因为这在Word管道中出错了,而不是你的代码。

试着不要自己犯同样的gobbledygook罪,出了什么问题,你在Office程序中进行的互操作调用是排队的,等待轮到他们执行。基础"系统调用"错误代码提示的是PostMessage()。只要有队列,队列就有可能变得太大。当生产者(您的程序)正在添加项目时,发生的事情远远快于消费者(Office程序)删除它们。消防问题。除非生产者放慢速度,否则队列将无限制地增长,如果允许无限增长,某些将失败,至少进程会耗尽内存。

不允许接近这个问题。 PostMessage()使用的基础队列受操作系统保护。当队列已包含10,000条消息时,Windows无法通过呼叫。这是一个致命的错误,RPC不知道如何从中恢复,或者更确切地说不应该尝试从中恢复。有些不对劲并且不是很好。它会向您的程序返回一个错误代码,以告诉您相关信息。这是RPC_E_SYS_CALL_FAILED。在您的程序中没有更好的事情发生,CLR也不知道如何从中恢复,您的代码也不知道。所以节目结束了,你所做的互操作丢失了,而且没有被Word执行。

为这个棘手的问题找到一个完全可靠的解决方法并不是那么简单。请注意,这可能发生在任何互操作调用上,因此捕获异常并再次尝试是非常不切实际的。但请记住,Q + D修复非常简单。一个明显的问题是你的程序运行得太快,使用Thread.Sleep()或Task.Delay()调用减慢它的速度非常粗糙,但总会解决问题。好吧,假设你有足够的延迟。

我想,但不知道一个事实,因为没有人发布过repro代码,这个问题也与你的程序中使用控制台模式应用程序或工作线程有关。如果它是控制台模式应用程序,则尝试将[STAThread]属性应用于Main()方法。如果它是工作线程,则在启动线程之前调用Thread.SetApartmentState(),但要注意,在该工作线程上创建Application接口也非常重要。否则不是加载项的解决方法。

如果这些变通办法都没有效果或太不实用,那么请考虑您可以自动减慢程序速度,并通过偶尔从Office程序中读取内容来确保队列清空。有点傻,任何财产吸气者都会这么做。在Office程序赶上之前,您无法获得属性值。这可能仍然失败,互操作呼叫也有60秒的超时。但是你可以解决这个问题,你可以在你的程序中调用CoRegisterMessageFilter()来安装一个在超时跳闸时运行的回调。非常gobbledygooky,但剪切和粘贴代码是readily available