COM对象方法不在CoInitialize-d和创建对象的线程上执行

Cor*_*uzu 4 com methods multithreading invoke

我正在开发一个UI应用程序,在此过程中创建一个COM对象.问题是,我想完全在另一个线程上"移动"这个COM对象.

我这样做是:

  • 创建我要将对象移动到的新线程(使用CreateThread API)
  • 进入这个线程后,我正在调用PeekMessage为它设置一个消息队列
  • 调用CoInitialize,CoCreateInstance来创建COM对象,QueryInterface来获取我想要的接口
  • 最后,我在接口上调用一个方法,该方法显示一个MessageBox,其中包含GetCurrentThreadId()返回的值(我可以访问对象所在的COM库的VB6代码).

问题是,正如此消息框所示,对象方法仍然在原始UI线程上执行,而不是在我创建的线程上执行并完成所有这些步骤.还有一点需要注意,在调用接口方法之后,我还在其中设置了一个经典的消息循环.

我怎样才能改变这种行为并实现我想要的目标?(也就是说,我希望源自我新创建的线程的COM对象调用在IT上执行,而不是在原始应用程序线程上执行)

这里有一些伪代码使它更清晰:

void myMainUIMethod(){
  MessageBox(GetCurrentThreadId()); // displays 1
  CreateThread(&myCOMObjectThreadProc);
}
void myCOMObjectThreadProc(){
  MessageBox(GetCurrentThreadId()); // displays 2
  CoInitialize(NULL);
  myObject = CoCreateInstance(myObjectsCLSID);
  myObjectInterface = myObject->QueryInterface(myObjectInterfaceCLSID);
  myObjectInterface->showThreadIDMessageBox(); // this would be the COM object method call
}

And, in the VB6 code of the object, here's the pseudo-definition of showThreadIDMessageBox.
Public Sub showThreadIDMessageBox()
  Call MessageBox(GetCurrentThreadId()) //displays 1, I want it to display 2
End Sub
Run Code Online (Sandbox Code Playgroud)

在创建新线程之前,我已经通过CoUninitalizing在主线程上实现了我想要的.但为什么会这样呢?如果COM在我创建新线程之前在主线上初始化,可能由于某种原因它必须是..我不希望应用程序稍后崩溃,因为我必须在创建新线程之前调用CoUninitialize.这里有一些伪代码,它说明了首先调用CoInitialize的线程将是STA对象选择的线程.

void myMainUIMethod(){
  MessageBox(GetCurrentThreadId()); // displays 1
  CoUninitialize(); // uninitialize COM on the main thread
  CreateThread(&myCOMObjectThreadProc);
  ***i: MessageBox("When you want to initialize COM on main thread, confirm this");
  CoInitialize();
}
void myCOMObjectThreadProc(){
  MessageBox(GetCurrentThreadId()); // displays 2
  ***ii: MessageBox("When you want to initialize COM on the new thread, confirm this");
  CoInitialize(NULL);
  myObject = CoCreateInstance(myObjectsCLSID);
  myObjectInterface = myObject->QueryInterface(myObjectInterfaceCLSID);
  myObjectInterface->showThreadIDMessageBox(); // this shows 2 IF ***ii is confirmed before ***i, 1 otherwise
}
Run Code Online (Sandbox Code Playgroud)

非常感谢,Corneliu

Ros*_*ost 6

看起来您的问题是您的COM组件线程模型在注册表项InprocServer32中指定.这意味着该对象被视为STA(单线程单元),但将被加载到主(或主机)STA,而不是创建它的STA.这是第一个调用的线程CoInitialize.要在调用CoCreateInstance您的同一STA中创建,必须创建HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{Your CLSID}\InprocServer32@ThreadingModel注册表值并将其设置为Apartment.

从MSDN引用(InprocServer32注册表项文档):

如果ThreadingModel不存在或未设置为值,则服务器将加载到在该过程中初始化的第一个公寓中.这间公寓有时被称为主要的单线程公寓(STA).如果进程中的第一个STA由COM初始化,而不是通过显式调用CoInitialize或CoInitializeEx,则称为主机STA.例如,如果要加载的进程内服务器需要STA但是进程中当前没有STA,则COM会创建主机STA.