3 votes

Pourquoi mon IMessageFilter ne fonctionne-t-il pas toujours ?

Je travaille sur l'automatisation de Word et pour me débarrasser des erreurs "Call was rejected by callee" / "the message filter indicated that the application is busy" j'ai implémenté un IMessageFilter. Le filtre de message fonctionne comme un charme lorsque j'automatise Word directement comme :

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

Mais lorsque j'appelle TOleContainer.DoVerb(ovPrimary), j'obtiens toujours des erreurs lorsque Word affiche une boîte de dialogue modale. Pourquoi le MessageFilter ne fonctionne-t-il pas avec la méthode DoVerb de TOleContainers ?

8voto

Marjan Venema Points 14626

Le message "Call was rejected by callee" (l'appel a été rejeté par l'appelant) est ce que vous obtenez toujours lorsque Word est en mode interactif, c'est-à-dire lorsqu'il affiche un dialogue. Ce phénomène n'est pas limité à Word. Cela se produit également avec Excel, par exemple lorsque l'utilisateur modifie une cellule. Et il n'est pas nécessaire que cela soit évident dans l'interface utilisateur. Lorsque vous commencez à éditer une cellule, que vous déplacez le focus vers une autre application et que vous revenez à Excel, l'interface utilisateur ne vous donne aucun indice, mais elle est toujours en mode "interactif" et rejettera les appels d'automatisation avec l'erreur "Call was rejected by callee" (l'appel a été rejeté par le destinataire).

Ainsi, lorsque vous automatisez Word en conjonction avec l'interaction de l'utilisateur (et pas seulement avec Word dans un processus d'arrière-plan), vous devez être prêt à recevoir et à gérer ces erreurs.

Éditer Si vous voulez savoir si Excel ou Word est en mode interactif avant d'appeler une autre méthode COM, demandez simplement au serveur COM s'il est "prêt" :

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');

Mise à jour Apparemment, Word n'a pas de propriété "Ready" (qui a dit que Microsoft était cohérent ?). Dans ce cas, vous devez déterminer vous-même son état de préparation en appelant une propriété simple (et rapide) avant l'appel proprement dit, et en supposant que si cette propriété lève une exception, c'est que Word n'est pas prêt. Dans l'exemple ci-dessus, la version est récupérée avant la propriété Ready. Si cette dernière lève une exception, nous supposons que l'application (Excel dans ce cas) n'est pas prête et nous procédons en conséquence.

Quelque chose comme ça :

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

Note Word.Version fait n lancent une exception lorsqu'une boîte de dialogue est ouverte, ce qui ne permet pas de savoir si Word est prêt :( Vous devrez expérimenter pour en trouver un qui le fasse.

2voto

Pat Mustard Points 394

IMessageFilter ne gère pas toutes les exceptions. Par exemple, à certains moments, les applications bureautiques "suspendent" leur modèle d'objet, ce qui fait qu'il ne peut pas être invoqué et qu'il est rejeté : 0x800AC472 (VBA_E_IGNORE)

Pour contourner ce problème, vous devez placer votre appel dans une boucle et attendre qu'il aboutisse :

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

// continue after successful call

Voir aquí pour plus de détails.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X