COM IMessageFilter接口使用示例

在COM编程中,单线程公寓(STA)对象可能会遇到重入问题,即当一个线程正在执行某个操作时,另一个线程尝试调用该对象的方法,可能会导致不可预知的行为。为了解决这个问题,可以使用IMessageFilter接口。IMessageFilter接口允许COM对象决定是否接受或拒绝传入的调用。本文将介绍如何实现IMessageFilter接口,并提供一个具体的示例。

IMessageFilter接口简介

IMessageFilter接口定义了三个方法:HandleInComingCall、RetryRejectedCall和MessagePending。这些方法用于处理传入的调用请求。当一个调用请求到达时,COM会调用HandleInComingCall方法,该方法可以返回以下几种值:

  • SERVERCALL_ISHANDLED:表示调用已被处理。
  • SERVERCALL_REJECTED:表示调用被拒绝。
  • SERVERCALL_RETRYLATER:表示调用可以稍后重试。

如果HandleInComingCall返回SERVERCALL_REJECTED,COM会调用RetryRejectedCall方法,以确定是否应该取消调用或稍后重试。如果HandleInComingCall返回SERVERCALL_RETRYLATER,COM会调用MessagePending方法,以确定是否应该处理挂起的消息。

实现IMessageFilter接口

要实现IMessageFilter接口,首先需要创建一个继承自IMessageFilter的类。以下是一个简单的实现示例:

class ATL_NO_VTABLE CFoo : public CComObjectRootEx, public IMessageFilterImpl, ... { public: CFoo() {} BEGIN_COM_MAP(CFoo) COM_INTERFACE_ENTRY(IFoo) COM_INTERFACE_ENTRY(IMessageFilter) ... END_COM_MAP() HRESULT FinalConstruct() { return RegisterFilter(); } DWORD ProcessInComingCall(DWORD dwCallType, HTASK threadIDCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo) { if (dwCallType == CALLTYPE_TOPLEVEL) { return SERVERCALL_ISHANDLED; } else { return SERVERCALL_REJECTED; } } };

在这个示例中,CFoo类继承自IMessageFilterImpl,这是一个模板类,用于实现IMessageFilter接口。CFoo类还实现了ProcessInComingCall方法,该方法用于处理传入的调用请求。

注册IMessageFilter

在CFoo类的构造函数中,调用RegisterFilter方法将当前对象注册为消息过滤器。这可以通过调用CoRegisterMessageFilter函数实现。

HRESULT RegisterFilter() { return ::CoRegisterMessageFilter(static_cast(this), NULL); }

注册消息过滤器后,当有新的调用请求到达时,COM会调用CFoo对象的HandleInComingCall方法。

处理传入的调用请求

HandleInComingCall方法用于处理传入的调用请求。在这个方法中,可以根据调用类型和其他参数决定是否接受或拒绝调用。以下是一个简单的示例:

STDMETHODIMP_(DWORD) HandleInComingCall(DWORD dwCallType, HTASK threadIDCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo) { if (dwCallType == CALLTYPE_ASYNC_CALLPENDING || dwCallType == CALLTYPE_ASYNC) { return SERVERCALL_ISHANDLED; } T* pT = static_cast(this); return pT->ProcessInComingCall(dwCallType, threadIDCaller, dwTickCount, lpInterfaceInfo); }

在这个示例中,如果调用类型是CALLTYPE_ASYNC_CALLPENDING或CALLTYPE_ASYNC,表示调用可以被接受。否则,调用将被拒绝。

处理被拒绝的调用

如果HandleInComingCall方法返回SERVERCALL_REJECTED,COM会调用RetryRejectedCall方法。在这个方法中,可以根据需要决定是否取消调用或稍后重试。以下是一个简单的示例:

STDMETHODIMP_(DWORD) RetryRejectedCall(HTASK threadIDCallee, DWORD dwTickCount, DWORD dwRejectType) { if (dwRejectType == SERVERCALL_REJECTED) { return -1; // 表示取消调用 } return dwTimeOut; // 表示稍后重试 }

在这个示例中,如果调用被拒绝,RetryRejectedCall方法返回-1,表示取消调用。否则,返回超时时间,表示稍后重试。

处理挂起的消息

如果HandleInComingCall方法返回SERVERCALL_RETRYLATER,COM会调用MessagePending方法。在这个方法中,可以根据需要决定是否处理挂起的消息。以下是一个简单的示例:

STDMETHODIMP_(DWORD) MessagePending(HTASK threadIDCallee, DWORD dwTickCount, DWORD dwPendingType) { return PENDINGMSG_WAITNOPROCESS; // 表示不处理挂起的消息 }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485