代码之家  ›  专栏  ›  技术社区  ›  The_Fox

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

  •  2
  • The_Fox  · 技术社区  · 14 年前

    我正在致力于Word自动化,为了消除“被呼叫者拒绝呼叫”/“消息过滤器显示应用程序正忙”错误,我实现了一个IMessageFilter。当我直接将Word自动化时,messagefilter就像一个魔咒一样工作:

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

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

    2 回复  |  直到 14 年前
        1
  •  8
  •   Marjan Venema    14 年前

    当Word处于交互状态(即显示对话框)时,您总是会看到“被呼叫者拒绝了呼叫”。这并不局限于文字。Excel也会发生这种情况,例如,当用户编辑单元格时。而且在用户界面上也不一定很明显。当您开始编辑单元格,将焦点移到另一个应用程序并返回Excel时,UI不会给您任何提示,但它仍处于“交互式”模式,并将拒绝自动调用,出现“Call was rejected by callee”错误。

    因此,基本上,当您将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是否已准备就绪:(你必须进行实验,才能找到一个这样做的。

        2
  •  2
  •   Pat Mustard    12 年前

    IMessageFilter不会处理所有异常,例如,在某些情况下,office应用程序“挂起”其对象模型,此时无法调用并抛出: 0x800AC472 (VBA_E_IGNORE)

    为了避免这种情况,你必须把你的电话放在一个循环中,等待它成功:

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

    看见 here 更多细节。