问题描述:
最近帮朋友弄个程序,处理Office Excel表.该Excel表加了密码,并且含有很多数据,当在VC中通过 Excel Ole对象打开该Excel文档时候,出现 "服务器正在运行中“对话框,其中有”切换到。。。“按钮。
根本原因:
发现问题我知道这是Ole Server Busy对话框,但是我不知为什么会弹出该对话框,是系统默认的还是可以进行定制。经过搜索发现微软对该问题进行了解释。
地址如下:
http://support.microsoft.com/kb/248019/en-us
大意是说:如果你使用了MFC 应用程序并且初始化COM环境使用的是AfxOleInit,当执行一个需要很长时间返回的COM调用时就会遇到这个问题。
一般使用MFC客户应用程序,包含消息循环,推荐使用AfxOleInit来初始化COM. 而AfxOleInit默认会设置Ole消息过滤器.请看AfxOleInit的源码:
BOOL AFXAPI AfxOleInit()
{
...
// allocate and initialize default message filter
if (pThread->m_pMessageFilter == NULL)
{
pThread->m_pMessageFilter = new COleMessageFilter;
ASSERT(AfxOleGetMessageFilter() != NULL);
AfxOleGetMessageFilter()->Register();
}
return TRUE;
InitFailed:
AfxOleTerm();
return FALSE;
}
COleMessageFilter就是用来使被调用的Ole Server 应用处于一个Busy的状态,以便于其他调用这个Ole Server 应用能够进行相应的处理或者Retry.
通过查看COleMessageFilter()构造函数,会发现默认是会显示Ole Server Busy对话框和Not Responding 对话框的。所以当一个COM调用需要花很长时间才返回,出现这个对话框是正常的。
COleMessageFilter::COleMessageFilter()
{
// begin in not-busy state
m_nBusyCount = 0;
// dialogs are enabled by default
m_bEnableBusy = TRUE;
m_bEnableNotResponding = TRUE;
m_nBusyReply = SERVERCALL_RETRYLATER;
// effective only when m_nBusyCount != 0
m_nRetryReply = 10000; // default is 10 sec
m_nTimeout = 8000; // default is 8 sec
m_bUnblocking = FALSE;
// TRUE to avoid re-entrancy when busy dialog is up
m_bRegistered = FALSE;
ASSERT_VALID(this);
}
解决方案:
微软官方给出的解决方案是:
1, 使用 AfxOleGetMessageFilter()->SetMessagePendingDelay(nTimeout); 来延长超时时间,也就是延长出现对话框的时间,但如果到这个时间还没有返回,这个对话框还是会出来。
2,使用
AfxOleGetMessageFilter()->EnableNotRespondingDialog(FALSE);
AfxOleGetMessageFilter()->EnableBusyDialog(FALSE);
来阻止弹出Server Busy 对话框和Not Responding 对话框。
即使用了第2种方案,对话框是不弹了,但是客户应用还是处于一个freeze的状态,所以为了客户应用友好,最好创建一个线程函数,在线程函数中显示一个进度条对话框,等待一个Event,并且定时处理消息循环,当COM调用返回后,设置Event为有信号状态,关闭进度条。