Почему мой IMessageFilter не всегда работает? - PullRequest
2 голосов
/ 12 октября 2010

Я работаю над автоматизацией Word и, чтобы избавиться от сообщения «Вызов был отклонен вызываемым пользователем» / «фильтр сообщений показал, что приложение занято», я реализовал IMessageFilter.Фильтр сообщений работает как чудо, когда я автоматизирую Word напрямую, например:

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

Но когда я вызываю TOleContainer.DoVerb (ovPrimary), я все равно получаю ошибки, когда Word отображает модальное диалоговое окно.Почему MessageFilter не работает с методом TOleContainers DoVerb?

Ответы [ 2 ]

7 голосов
/ 12 октября 2010

«Вызов отклонен вызываемым пользователем» - это то, что вы всегда получаете, когда Word находится в интерактивном состоянии, то есть при отображении диалога.Это не ограничивается Word.Это также происходит с Excel, например, когда пользователь редактировал ячейку.И это не должно быть очевидным в пользовательском интерфейсе.Когда вы начинаете редактировать ячейку, перемещаете фокус в другое приложение и возвращаетесь в Excel, пользовательский интерфейс не дает подсказки, но он все еще находится в «интерактивном» режиме и будет отклонять вызовы автоматизации с помощью «Вызов отклонен вызываемым пользователем»ошибка.

Поэтому, в основном, когда вы автоматизируете Word в сочетании с взаимодействием с пользователем (а не только с Word в фоновом процессе), вы должны быть готовы к получению и обработке этих ошибок.

Edit Если вы хотите узнать, находится ли Excel или Word в интерактивном режиме, прежде чем вызывать любой другой метод COM: просто спросите 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 не имеет свойства "Готово" (кто бы ни сказал, что Microsoft была последовательной?).В этом случае вам нужно самостоятельно определить его готовность, вызвав простое (и быстрое) свойство перед фактическим вызовом и предположив, что, когда это вызывает исключение, 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.:( Вам придется экспериментировать, чтобы найти тот, который делает.

1 голос
/ 30 января 2013

IMessageFilter не обрабатывает все исключения, например, в некоторых моментах офисные приложения «приостанавливают» свою объектную модель, после чего она не может быть вызвана и выдает: 0x800AC472 (VBA_E_IGNORE)

, чтобы получитьвокруг этого вы должны поместить свой вызов в цикл и дождаться его успешного выполнения:

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

// continue after successful call

См. здесь для получения более подробной информации.

...