عند تثبيت think-cell، تفشل بعض عمليات استدعاء التنفيذ التلقائي لوظيفة Excel COM.
المشكلة
عند تثبيت think-cell، تفشل بعض عمليات استدعاء التنفيذ التلقائي لوظيفة Excel COM. رمز خطأ HRESULT هو 0x8001010A RPC_E_SERVERCALL_RETRYLATER
. في LotusScript، الخطأ هو Automation object error
.
تستطيع خوادم COM تصفية طلبات الاستدعاء الواردة باستخدام الآلية IMessageFilter
. بصورة أكثر تحديدًا، إذا أدت عملية التنفيذ IMessageFilter::HandleInComingCall
إلى إرجاع SERVERCALL_RETRYLATER
، يعود طلب الاستدعاء الخاص بالعميل بالنتيجة RPC_E_SERVERCALL_RETRYLATER
.
يستخدم Excel هذه الآلية بصرف النظر عن think-cell. يؤدي وجود think-cell إلى زيادة معدل تكرار RPC_E_SERVERCALL_RETRYLATER
، مما يؤدي إلى انتشار المشكلة بصورة أكبر.
يعد محرك البرمجة النصية LotusScript لبرنامج IBM Lotus Notes، أحد المنتجات المعروفة بإظهار أخطاء في هذه الحالات.
الحل
استجابةً لـ RPC_E_SERVERCALL_RETRYLATER
، يجب أن ينتظر صاحب طلب الاستدعاء لفترة وجيزة ثم يقوم بتكرار طلب الاستدعاء. يحتوي Visual Basic for Applications على هذه المعالجة مضمّنة، لذلك يمكن القول إنه خطأ إذا كانت بيئات البرمجة النصية الأخرى الخاصة بنظام Windows والتي تدعم COM، مثل LotusScript، لا تقوم بتنفيذه.
خارج بيئات البرمجة النصية، أو إذا لم تقم بيئة البرمجة النصية بتنفيذه، يجب على أصحاب طلبات الاستدعاء معالجة RPC_E_SERVERCALL_RETRYLATER
بأنفسهم. تتيح COM هذا الأمر بسهولة إلى حد ما نظرًا لأنها تستطيع تكرار طلبات الاستدعاء تلقائيًا بالنيابة عن العميل في حالة مطالبتها بذلك. قد تظل التعليمات البرمجية الفعلية لطلبات الاستدعاء كما هي دون تغيير.
لتمكين هذا السلوك، يجب أن يقوم العميل بتنفيذ IMessageFilter
الخاص به، على أن يقوم الأسلوب IMessageFilter::RetryRejectedCall
بإرجاع شيء آخر بخلاف -1 إذا كانت المعلمة dwRejectType
هي SERVERCALL_RETRYLATER
. بعد ذلك، في كل مؤشر ترابط STA يقوم بتنفيذ طلبات استدعاء COM، يجب أن يستخدم العميل CoRegisterMessageFilter
لتسجيل مثيل لهذا التنفيذ باستخدام COM.
على سبيل المثال، فيما يلي تنفيذ C++ ATL RAII:
class CHandleRetryLaterBase :
public CComObjectRootEx<CComSingleThreadModel>,
public IMessageFilter
{
protected:
CComPtr<IMessageFilter> m_imessagefilterOld;
BEGIN_COM_MAP(CHandleRetryLaterBase)
COM_INTERFACE_ENTRY(IMessageFilter)
END_COM_MAP()
public:
// IMessageFilter implementation
DWORD STDMETHODCALLTYPE HandleInComingCall(
/* [in] */ DWORD dwCallType,
/* [in] */ HTASK htaskCaller,
/* [in] */ DWORD dwTickCount,
/* [in] */ LPINTERFACEINFO lpInterfaceInfo
) {
if( m_imessagefilterOld ) {
// pass on to old handler, no change in behavior
return m_imessagefilterOld->HandleInComingCall(
dwCallType,
htaskCaller,
dwTickCount,
lpInterfaceInfo
);
} else {
// default behavior
return SERVERCALL_ISHANDLED;
}
}
DWORD STDMETHODCALLTYPE RetryRejectedCall(
/* [in] */ HTASK htaskCallee,
/* [in] */ DWORD dwTickCount,
/* [in] */ DWORD dwRejectType
) {
if( SERVERCALL_RETRYLATER==dwRejectType ) {
// in a script, wait indefinitely for robustness under load
return 100;
} else if( m_imessagefilterOld ) {
// pass on to old handler, no change in behavior
return m_imessagefilterOld->RetryRejectedCall(
htaskCallee,
dwTickCount,
dwRejectType );
} else {
// default behavior
return (DWORD)-1;
}
}
DWORD STDMETHODCALLTYPE MessagePending(
/* [in] */ HTASK htaskCallee,
/* [in] */ DWORD dwTickCount,
/* [in] */ DWORD dwPendingType
) {
if( m_imessagefilterOld ) {
// pass on to old handler, no change in behavior
return m_imessagefilterOld->MessagePending(
htaskCallee,
dwTickCount,
dwPendingType );
} else {
// default behavior
return PENDINGMSG_WAITDEFPROCESS;
}
}
};
// Instantiate this class in your scope to enable robust IMessageFilter.
class CHandleRetryLater :
public CHandleRetryLaterBase
{
public:
CHandleRetryLater() {
// set new handler and save old one
_ASSERT( m_dwRef==0 );
HRESULT hr=CoRegisterMessageFilter( this, &m_imessagefilterOld );
// m_imessagefilterOld may be nullptr if there is no
// filter previously installed
_ASSERT( SUCCEEDED(hr) );
}
~CHandleRetryLater() {
// reset old handler
{
CComPtr<IMessageFilter> iMessageFilter;
HRESULT hr=CoRegisterMessageFilter(
m_imessagefilterOld,
&iMessageFilter );
_ASSERT( SUCCEEDED(hr) );
// make sure noone replaced our IMessageFilter
_ASSERT( iMessageFilter==static_cast<IMessageFilter*>(this) );
} // iMessageFilter is last release
_ASSERT( m_dwRef==0 );
}
STDMETHOD_(ULONG, AddRef)() throw() {
#ifdef _DEBUG
return InternalAddRef();
#else
return 0;
#endif
}
STDMETHOD_(ULONG, Release)() throw() {
#ifdef _DEBUG
return InternalRelease();
#else
return 0;
#endif
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw() {
return _InternalQueryInterface(iid, ppvObject);
}
};