C++ 从另一个线程触发COM事件

C++ 从另一个线程触发COM事件,c++,com,atl,C++,Com,Atl,我已经使用ATL创建了一个进程内COM对象(DLL)。请注意,这是一个对象而不是控件(因此没有窗口或用户界面)。我的问题是,我正试图从第二个线程触发事件,并且我得到了一个“灾难性故障”(0x8000FFFF)。如果我从主线程触发事件,那么我不会得到错误。第二个线程正在调用CoInitializeEx,但这没有什么区别。我使用的是单元线程模型,但切换到自由线程没有帮助 事实上,我正试图从第二个线程做这件事,这显然是至关重要的。有没有一种简单的方法可以做到这一点,或者我必须实现某种隐藏窗口形式的消息

我已经使用ATL创建了一个进程内COM对象(DLL)。请注意,这是一个对象而不是控件(因此没有窗口或用户界面)。我的问题是,我正试图从第二个线程触发事件,并且我得到了一个“灾难性故障”(0x8000FFFF)。如果我从主线程触发事件,那么我不会得到错误。第二个线程正在调用
CoInitializeEx
,但这没有什么区别。我使用的是单元线程模型,但切换到自由线程没有帮助

事实上,我正试图从第二个线程做这件事,这显然是至关重要的。有没有一种简单的方法可以做到这一点,或者我必须实现某种隐藏窗口形式的消息传递

例如,在我的主对象的源文件中:

STDMETHODIMP MyObject::SomeMethod(...)
{
  CreateThread(NULL, 0, ThreadProc, this, 0, NULL);
  // Succeeds with S_OK
  FireEvent(L"Hello, world!");
  return S_OK;
}

DWORD WINAPI ThreadProc(LPVOID param)
{
  CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  MyObject* comObject = reinterpret_cast<MyObject*>(param);
  // Fails with 0x8000FFFF
  comObject->FireEvent(L"Hello, world!");
}

void MyObject::FireEvent(BSTR str)
{
  ...
  // Returns 0x8000FFFF if called from ThreadProc
  // Returns S_OK if called from SomeMethod
  pConnection->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
}
STDMETHODIMP MyObject::SomeMethod(…)
{
CreateThread(NULL,0,ThreadProc,this,0,NULL);
//成功使用S_OK
FireEvent(L“你好,世界!”);
返回S_OK;
}
DWORD WINAPI ThreadProc(LPVOID参数)
{
CoInitializeX(空,Conit_APARTMENTTHREADED);
MyObject*comObject=重新解释投影(参数);
//使用0x8000FFFF失败
comObject->FireEvent(L“你好,世界!”);
}
void MyObject::FireEvent(BSTR str)
{
...
//如果从ThreadProc调用,则返回0x8000FFFF
//如果从SomeMethod调用,则返回S_OK
pConnection->Invoke(dispid、IID\u NULL、LOCALE\u USER\u DEFAULT、DISPATCH\u METHOD和params、NULL、NULL、NULL);
}

我认为真正的问题不是您的组件配置了什么,而是主机进程。许多主机(如Office对象模型主机)都位于单线程单元中,在这种情况下,除了主线程之外,不允许从任何位置调用它们。
如果是这种情况,您可以使用单线程单元模型,将实际调用移动到CoClass中的函数,并从您的线程中调用该函数,从而让COM完成这项工作。
这也只能在幕后传递窗口消息,但不会让您自己实现它

(维基百科):
单线程单元(STA)模型是一种非常常用的模型。在这里,COM对象的位置类似于桌面应用程序的用户界面。在STA模型中,单个线程专用于驱动对象的方法,即始终使用单个线程来执行对象的方法。在这种安排中,来自单元外部线程的方法调用被系统编组并自动排队(通过标准Windows消息队列)。因此,不必担心竞争条件或缺乏同步性,因为对象的每个方法调用总是在调用另一个方法之前执行到完成


另请参见同一篇文章。

COM基础知识

在STA中,对象位于单个线程(线程)上。这个线程是它创建的线程,它的方法被执行,它的事件被触发。STA确保不会同时执行对象的两个方法(因为它们必须在线程上执行,所以这是一个很好的结果)

这并不意味着不能从其他线程访问对象。这是通过为除线程之外的每个线程创建对象的代理来实现的。在您打包IUnknown的线程上,以及在您解包的另一个线程上,实际在另一个线程上创建代理。此代理使用消息泵同步对对象的调用,这些调用仍在线程上执行,因此线程必须空闲(不忙)才能执行来自另一个线程的调用

在本例中,您希望对象能够在一个线程上执行方法,并在另一个线程上引发事件。所以这必须发生在MTA中,所以你的对象必须存在于MTA中,所以你的类必须是自由线程的。线程只属于一个单元,因此线程不能同时位于MTA和STA中。如果每当STA对象尝试使用它时,您的对象都位于MTA中,那么它必须创建一个代理。所以你的头顶有点高

我猜您正在考虑一些非常聪明的“技术”来卸载您的主线程,并执行一些“异步”事件,这些事件最终不会飞行:-)如果您考虑一下,那么第二个[工作线程]上必须有一个侦听器

顺便说一句,这条线

MyObject* comObject = reinterpret_cast<MyObject*>(param);
MyObject*comObject=重新解释投射(参数);

只能在MTA中完成。

您是否有更多关于此处所涉及内容的详细信息?用更多信息更新了答案-现在没有时间爬过msdn以获取简明的详细信息:)简而言之:如果您的主机应用程序是STA,您需要从拥有的线程调用,或者通过COM调用正确的线程(编组)。否则,您必须自己处理从正确线程进行的调用。