Windows COM异步调用不';不尊重信息过滤器

Windows COM异步调用不';不尊重信息过滤器,windows,winapi,asynchronous,com,Windows,Winapi,Asynchronous,Com,我有一个实现自定义接口的STA COM对象。我的自定义接口有一个自定义代理存根,它是根据MIDL编译器生成的代码构建的。我希望能够从其他单元异步调用接口。我发现同步接口调用尊重调用线程上的OLE消息过滤器,但异步接口调用不尊重。这意味着,如果调用单元具有建议稍后重试调用的消息筛选器,则COM异步调用不能以“即发即弃”的方式使用 这是预期的吗?除了不使用消息过滤器、不使用fire-and-forget操作或使用一个单独的国产组件来管理fire-and-forget操作之外,还有其他方法可以解决这个

我有一个实现自定义接口的STA COM对象。我的自定义接口有一个自定义代理存根,它是根据MIDL编译器生成的代码构建的。我希望能够从其他单元异步调用接口。我发现同步接口调用尊重调用线程上的OLE消息过滤器,但异步接口调用不尊重。这意味着,如果调用单元具有建议稍后重试调用的消息筛选器,则COM异步调用不能以“即发即弃”的方式使用

这是预期的吗?除了不使用消息过滤器、不使用fire-and-forget操作或使用一个单独的国产组件来管理fire-and-forget操作之外,还有其他方法可以解决这个问题吗

对于下面的代码,MessageFilter是IMessageFilter的一个简单的模块内实现,它将调用路由到lambdas。如果我不使用消息过滤器,那么同步和异步调用都可以正常工作。如果我使用下面显示的消息过滤器,同步调用会工作(在主STA消息过滤器停止返回SERVERCALL_RETRYLATER之后),但异步调用会立即失败,并且不会重试

主STA具有延迟一段时间的消息过滤器

// establish deferral time
chrono::time_point<chrono::system_clock> defer_until = ...;

// create message filter
auto message_filter = new MessageFilter;
message_filter->AddRef();
message_filter->handle_incoming_call
    = [defer_until](DWORD, HTASK, DWORD, LPINTERFACEINFO)
      {
          return chrono::high_resolution_clock::now() >= defer_until
              ? SERVERCALL_ISHANDLED
              : SERVERCALL_RETRYLATER;
      };

// register message filter
CoRegisterMessageFilter(message_filter, nullptr);
在该次要STA中,我从主STA获取对象接口的代理

// get global interface table
IGlobalInterfaceTablePtr global_interface_table;
global_interface_table.CreateInstance(CLSID_StdGlobalInterfaceTable);

// get interface reference
IMyInterfacePtr object_interface;
global_interface_table->GetInterfaceFromGlobal(cookie, __uuidof(IMyInterface), reinterpret_cast<LPVOID*>(&object_interface)));
这不起作用:

// get call factory
ICallFactoryPtr call_factory;
object_interface->QueryInterface(&call_factory);

// create async call
AsyncIMyInterfacePtr async_call;
call_factory->CreateCall(__uuidof(AsyncIMyInterface), nullptr, __uuidof(AsyncIMyInterface), reinterpret_cast<LPUNKNOWN*>(&async_call)));

// begin executing asynchronously
async_call->Begin_SomeMethod();

// end executing asynchronously
HRESULT hr = async_call->Finish_SomeMethod();

/* final result, immediate: hr == RPC_E_SERVERCALL_RETRYLATER */
//获取调用工厂
ICallFactoryPtr呼叫工厂;
对象接口->查询接口(&调用工厂);
//创建异步调用
AsyncIMyInterfacePtr异步调用;
call_factory->CreateCall(uuu uuidof(AsyncIMyInterface)、nullptr、uu uuidof(AsyncIMyInterface)、reinterpret_cast(&async_call));
//开始异步执行
异步调用->开始方法();
//结束异步执行
HRESULT hr=async_call->Finish_SomeMethod();
/*最终结果,立即:hr==RPC\u E\u SERVERCALL\u RETRYLATER*/

当您执行同步调用时,COM在等待响应的同时运行内部消息循环,并从此循环和IMessageFilter的调用方法中运行。如果您执行异步调用-COM立即返回-没有任何内部循环,因此没有人(除了您自己)不会调用IMessageFilter。从异步模型的另一面看,您始终保持控制权。您自己运行自己的消息循环,可以做任何您想做的事情。在这种情况下,您不需要IMessageFilter。这正是我看到的行为。经过进一步思考,这是有道理的。因为这是一个STA,所以在这里运行重试逻辑的唯一机会是在消息泵(或CoWait)中,如果您开始多个异步操作,则在消息循环允许他们再次重试之前,他们只会尝试一次,这可能不及时。运行我自己的循环属于“自制组件”类别。有什么现成的东西可以促进这一点吗?我想不出一个好的方法来制作一个框架级组件来管理它,因为重试循环将使用Begin_XXX调用进行硬编码。可以说,消息过滤器“接入呼叫链”。如果不构建自己的代理类,还有其他方法可以做到这一点吗?在本文中,COM在等待回复时进入一个模式循环,并且只从调用的这个模式循环IMessageFilter开始。异步确实必须是基于事件的-您确实开始调用,而不是等待它结束-在调用完成时继续执行自己的任务(或者简单地运行自己的消息循环)-您得到了(来自循环)关于这一点的消息。并处理结果。所以你们只需要像往常一样处理来自循环的消息。若我只是运行一个普通的消息循环,它就不起作用了。在Begin_XXX调用之后,我正好收到一条消息—一个用于管理对STA的调用/回调的COM窗口的0x407。我假设这是包含或引用完成(失败)信息的消息。如果我发送消息,COM STA窗口进程不会为我重试。如果我想创建一个通用的消息泵(或一些基于事件的解决方案),可以进入消息过滤器,我需要能够解码这个完成信息,如果可能的话,通过编程找到足够的信息来重试调用。是时候找到规格了。。。
// execute synchronously
HRESULT hr = object_interface->SomeMethod();

/* final result, after the deferral period: hr == S_OK */
// get call factory
ICallFactoryPtr call_factory;
object_interface->QueryInterface(&call_factory);

// create async call
AsyncIMyInterfacePtr async_call;
call_factory->CreateCall(__uuidof(AsyncIMyInterface), nullptr, __uuidof(AsyncIMyInterface), reinterpret_cast<LPUNKNOWN*>(&async_call)));

// begin executing asynchronously
async_call->Begin_SomeMethod();

// end executing asynchronously
HRESULT hr = async_call->Finish_SomeMethod();

/* final result, immediate: hr == RPC_E_SERVERCALL_RETRYLATER */