【问题标题】:Why does my IMessageFilter not always work?为什么我的 IMessageFilter 并不总是有效?
【发布时间】:2011-04-24 05:31:40
【问题描述】:

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

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

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

【问题讨论】:

    标签: delphi ms-word automation ole imessagefilter


    【解决方案1】:

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

    为了解决这个问题,您必须循环调用并等待它成功:

    while(true)
    {
        try
        {
            office_app.DoSomething();
            break;
        }
        catch(COMException ce)
        {
            LOG(ce.Message);
        }
    }
    
    // continue after successful call
    

    更多详情请见here

    【讨论】:

      【解决方案2】:

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

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

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

      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 没有“就绪”属性(谁说微软是一致的?)。在这种情况下,您需要在实际调用之前调用一个简单(且快速)的属性,并假设当它引发异常时,Word 尚未准备好,从而自己确定它的就绪状态。在上面的示例中,版本是在 Ready 属性之前检索的。如果这引发了异常,我们只是假设应用程序(在本例中为 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 是否已准备好是没有用的。 :( 你必须试验才能找到一个。

      【讨论】:

      • 当然,我知道,这就是我实现 IMessageFilter 的原因。这样,当 Word 处于模态状态(如显示字体对话框)时,我可以显示“服务器正忙”对话框。问题是,当在 TOleContainer 上调用 DoVerb 时,我的 IMessageFilter 实现不起作用。
      • @The_Fox:您是说即使在 Word 显示对话或以其他方式“参与”时,在 TOleContainer 上调用 DoVerb?如果是这样的话,我从你的问题中并不清楚。
      • 不,我的意思是:当我在 Word 显示模型对话框时调用 DoVerb 时,尽管注册了 IMessageFilter,但我收到了上述错误。
      • 正如预期的那样,因为 Word 处于交互模式。如果我错了,请纠正我,但 IMessageFilter 不是旨在过滤进入您的应用程序的消息,而不是过滤从您的应用程序传出到其他应用程序的消息吗? MSDN:msdn.microsoft.com/en-us/library/…。因此,实施 IMessageFilter 可能无助于 DoVerb 的“无操作”。您最好在调用 DoVerb 之前检查 Word/Excel 的“Ready”方法。出于这个原因,此调用是专门存在的,并且从 Office2002 开始可用。
      • 您也可以将 IMessageFilter 用于拨出电话,我已经可以使用:social.msdn.microsoft.com/forums/en-US/vsto/thread/…。不幸的是,Word 没有 Ready 属性。
      猜你喜欢
      • 2019-08-02
      • 2016-11-11
      • 2020-08-10
      • 1970-01-01
      • 2018-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-02
      相关资源
      最近更新 更多