我正在进行Word自动化并且摆脱“被叫被拒绝的呼叫”/“消息过滤器指示应用程序正忙”错误我实现了IMessageFilter。当我直接自动化Word时,messagefilter就像魅力一样:
Word.Documents.Open(...)
Document.SaveAs(...)
但是当我调用TOleContainer.DoVerb(ovPrimary)时,Word在显示模式对话框时仍然会出错。为什么MessageFilter不能与TOleContainers DoVerb方法一起使用?
答案 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。