COM 对象方法不在 CoInitialize-d 并创建对象的线程上执行 [英] COM object methods are not executed on the thread that CoInitialize-d and created the object

查看:15
本文介绍了COM 对象方法不在 CoInitialize-d 并创建对象的线程上执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am developing an UI application that creates a COM object along the way. The problem is, I want to "move" this COM object entirely on a different thread.

我的做法是这样的:

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

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

The problem is, as this message box shows, the object methods are still executed on the original UI thread, not on the thread I created and done all those steps into. One more thing to mention, after calling the interface method, I'm also setting up a classic message loop in it.

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

How can I change this behaviour and achieve what I want? (that is, I want the COM object calls that originate from my newly created thread to be executed ON IT, not on the original application thread)

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

Here's some pseudocode to make it even more clearer:

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

在创建新线程之前,我通过在主线程上进行 CoUninitalizing 实现了我想要的.但是为什么会这样呢?如果在我创建新线程之前在主线程上初始化了 COM,也许出于某种原因它必须是......我不希望应用程序稍后崩溃,因为我必须在创建新线程之前调用 CoUninitialize.下面是一些伪代码,说明无论哪个线程首先调用 CoInitialize 都会被 STA 对象选中.

I have achieved what I wanted by CoUninitalizing on the main thread, before creating the new thread. But why does this happen? If COM was initialized ON THE MAIN THREAD before I'm creating the new thread, maybe for some reason it had to be..I would't want the application to crash later because I had to call CoUninitialize before creating my new thread. Here's some pseudocode that illustrates that whichever thread calls CoInitialize first will be the one picked by the STA objects.

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
}

非常感谢您,科尼留

推荐答案

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

Looks like your problem is that your COM component threading model is not specified in registry key InprocServer32. This means that object is considered as STA (single-threaded apartment) but will be loaded to main (or host) STA, not the STA that created it. This is the first thread that called CoInitialize. To be created in same STA that called CoCreateInstance you must create HKEY_LOCAL_MACHINESOFTWAREClassesCLSID{Your CLSID}InprocServer32@ThreadingModel registry value and set it to Apartment.

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

Quote from MSDN (InprocServer32 registry key documentation):

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

If ThreadingModel is not present or is not set to a value, the server is loaded into the first apartment that was initialized in the process. This apartment is sometimes referred to as the main single-threaded apartment (STA). If the first STA in a process is initialized by COM, rather than by an explicit call to CoInitialize or CoInitializeEx, it is called the host STA. For example, COM creates a host STA if an in-process server to be loaded requires an STA but there is currently no STA in the process.

这篇关于COM 对象方法不在 CoInitialize-d 并创建对象的线程上执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆