为什么我的IMessageFilter并不总是有效?

时间:2010-10-12 10:03:47

标签: delphi ms-word automation ole imessagefilter

我正在进行Word自动化并且摆脱“被叫被拒绝的呼叫”/“消息过滤器指示应用程序正忙”错误我实现了IMessageFilter。当我直接自动化Word时,messagefilter就像魅力一样:

Word.Documents.Open(...)
Document.SaveAs(...)

但是当我调用TOleContainer.DoVerb(ovPrimary)时,Word在显示模式对话框时仍然会出错。为什么MessageFilter不能与TOleContainers DoVerb方法一起使用?

2 个答案:

答案 0 :(得分:7)

“调用被被调用者拒绝”是Word处于交互状态时总是得到的,即显示对话框。这不仅限于Word。它也发生在Excel中,例如当用户编辑单元格时。而且在用户界面中也不一定非常明显。当您开始编辑单元格,将焦点移动到另一个应用程序并返回到Excel时,UI不会给您一些线索,但它仍处于“交互”模式,并且将拒绝自动调用“呼叫被被调用者拒绝”错误。

所以基本上当你将Word与用户交互一起自动化时(而不仅仅是在后台进程中使用Word),你应该准备好处理和处理这些错误。

修改 如果您想在调用任何其他COM方法之前知道Excel或Word是否处于交互模式:只需询问COM服务器是否为“Ready”:

Result := _GetActiveOleObject('Excel.Application');

try
  aSharedInstance := not VarIsClear(Result);
  if aSharedInstance then
    Version := Result.Version;  // If this produces an exception, then use a dedicated instance.

  // In case checking the version does not produce an exception, but Excel still isn't
  // ready, we'll check that as well.
  // By the way, for some unclear reason, partial evaluation does not work on .Ready, 
  // so we'll do it like this:
  if aSharedInstance and (StrToIntDef(StringBefore('.', Version), 0) >= EXCEL_VERSION_2002) then
    aSharedInstance := Result.Ready;
except
  aSharedInstance := False;
end;

if not aSharedInstance then
  Result := CreateOleObject('Excel.Application');

<强>更新 显然Word没有“Ready”属性(谁说微软是一致的?)。在这种情况下,你需要通过在实际调用之前调用一个简单(和快速)属性来自己确定它的准备情况,并假设当它抛出一个异常时,Word还没有准备好。在上面的示例中,在Ready属性之前检索Version。如果它抛出异常,我们只是假设应用程序(在这种情况下为Excel)没有准备好并相应地继续。

有些事情:

while Tries <= MaxTries do
  try
    Version := Word.Version;
    Tries := MaxTries + 1; // Indicate success
    Word.TheCallYouReallyWantToDo;
  except
    Inc(Tries);
    sleep(0);
  end;

注意 Word.Version会在对话框打开时执行抛出异常,因此无法确定Word是否已准备就绪。 :(你将不得不尝试找到一个。

答案 1 :(得分:1)

IMessageFilter不会处理所有异常,例如,在某些时候,办公室应用程序“暂停”它们的对象模型,此时它无法被调用并抛出:0x800AC472 (VBA_E_IGNORE)

为了解决这个问题,您必须将呼叫置于循环中并等待它成功:

while(true)
{
    try
    {
        office_app.DoSomething();
        break;
    }
    catch(COMException ce)
    {
        LOG(ce.Message);
    }
}

// continue after successful call

有关详细信息,请参阅here