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

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

问题描述

我正在开发一个UI应用程序,在该过程中创建一个COM对象。
问题是,我想移动这个COM对象完全在不同的线程。



我所做的是:




  • 在输入此线程后,创建要将对象移入(使用CreateThread API)的新线程

  • m调用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(); //这将是COM对象方法调用
}

在对象的VB6代码中,这里是showThreadIDMessageBox的伪定义。
public SubThreadIDMessageBox()
调用MessageBox(GetCurrentThreadId())//显示1,我想显示2
End Sub
pre>

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

  void myMainUIMethod 
MessageBox(GetCurrentThreadId()); // displays 1
CoUninitialize(); // uninitialize COM on the main thread
CreateThread(& myCOMObjectThreadProc);
*** i:MessageBox(当你想在主线程上初始化COM时,确认这个);
CoInitialize();
}
void myCOMObjectThreadProc(){
MessageBox(GetCurrentThreadId()); //显示2
*** ii:MessageBox(当你想在新线程中初始化COM时,确认这个);
CoInitialize(NULL);
myObject = CoCreateInstance(myObjectsCLSID);
myObjectInterface = myObject-> QueryInterface(myObjectInterfaceCLSID);
myObjectInterface-> showThreadIDMessageBox(); //这显示2 IF *** ii在*** i之前被确认,否则
}


$ b b

非常感谢你,
Corneliu

解决方案

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



a href =http://msdn.microsoft.com/en-us/library/windows/desktop/ms682390%28v=vs.85%29.aspx> InprocServer32注册表项文档):


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



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.

What I do is this:

  • create the new thread I want to move the object into (with CreateThread API)
  • after entering this thread, I'm calling PeekMessage to setup a message queue for it
  • calling CoInitialize, CoCreateInstance to create the COM object, QueryInterface to get the interface I want
  • finally I call a method on the interface that displays a MessageBox with the value returned by GetCurrentThreadId() (I have access to the VB6 code of the COM library within which the object resides).

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.

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

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
}

Thank you very much in advance, Corneliu

解决方案

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_MACHINE\SOFTWARE\Classes\CLSID\{Your CLSID}\InprocServer32@ThreadingModel registry value and set it to Apartment.

Quote from MSDN (InprocServer32 registry key documentation):

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天全站免登陆