com线程模型实验之三

  之前我们实验的是在多个线程中创建不同的套间,并且在每个线程中都分别创建单独的com对象。这次实验的是在一个线程中创建对象,然后将对象传递到另外一个线程,再进行调用。我们知道com中是不允许直接把接口指针从一个线程从直接传递到另外一个线程的,当需要传递的时候需要先从当前线程对接口对象进行列集,并且在另外一个接受的线程进行散集,使用散集后的接口指针调用对象提供的方法。下面直接看代码:

先实验APARTMENTTHREADED线程套间,com线程模型为apartment:


1
2 UINT __stdcall _ThreadShowMessage( LPVOID pParam)
3 {
4
5 CString str;
6 str.Format(L"线程二 threadid:%d ,msg:%d", GetCurrentThreadId(), GetCurrentThreadId());
7 MessageBox(NULL, str, str, MB_OK);
8
9
10 CoInitializeEx( NULL, COINIT_APARTMENTTHREADED);
11
12 ITestShowMsg* pShowMsg = NULL;
13 HRESULT hr = ::CoGetInterfaceAndReleaseStream((IStream*)pParam, __uuidof(ITestShowMsg), (void**)&pShowMsg);
14 pShowMsg->ShowMsg( _bstr_t(L"My Message"));
15
16 return 0;
17 }
18
19 UINT __stdcall _ThreadShowMessage2( LPVOID pParam)
20 {
21 CoInitializeEx( NULL, COINIT_APARTMENTTHREADED);
22 {
23 CString str;
24 str.Format(L"线程三 threadid:%d ,msg:%d", GetCurrentThreadId(), GetCurrentThreadId());
25 MessageBox( NULL, str, str, MB_OK);
26
27 ITestShowMsgPtr ptrMsg;
28 HRESULT hr = ptrMsg.CreateInstance( __uuidof(TestShowMsg));
29 ptrMsg->ShowMsg( _bstr_t(L"My Message"));
30
31 //列集对象
32   IStream* pStream = NULL;
33 hr = ::CoMarshalInterThreadInterfaceInStream( __uuidof(ITestShowMsg),ptrMsg,&pStream);
34 //传递IStream到其他线程
35   _beginthreadex( NULL, 0, _ThreadShowMessage, pStream, 0, NULL);
36 MessageBox( NULL, L"将代码停在这里", L"将代码停在这里", MB_OK);
37 //相当于建立消息泵,必须的,不可以用Sleep
38   }
39 return 0;
40 }
41
42 BOOL CrunserviceDlg::OnInitDialog()
43 {
44 CDialog::OnInitDialog();
45 // TODO: 在此添加额外的初始化代码
46
47 //CoInitializeEx( NULL, COINIT_APARTMENTTHREADED);
48   CoInitializeEx( NULL, COINIT_APARTMENTTHREADED);
49 {
50 CString str;
51 str.Format(L"线程一 threadid:%d ,msg:%d", GetCurrentThreadId(), GetCurrentThreadId());
52 MessageBox( str, str, MB_OK);
53
54 ITestShowMsgPtr ptrMsg;
55 HRESULT hr = ptrMsg.CreateInstance( __uuidof(TestShowMsg));
56 ptrMsg->ShowMsg( _bstr_t(L"My Message"));
57
58 //列集对象
59   IStream* pStream = NULL;
60 hr = ::CoMarshalInterThreadInterfaceInStream( __uuidof(ITestShowMsg),ptrMsg,&pStream);
61 //传递IStream到其他线程
62   _beginthreadex( NULL, 0, _ThreadShowMessage, pStream, 0, NULL);
63 MessageBox( L"将代码停在这里1");
64 _beginthreadex( NULL, 0, _ThreadShowMessage2, pStream, 0, NULL);
65 MessageBox( L"将代码停在这里2");
66 }
67
68
69 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
70  }


解释:

以上代码主线程ID为1,在主线程调用COM对象方法,COM执行线程为1,然后启动线程2,并列集COM接口指针传递到线程2,线程2ID为2,com接口散集后执行线程为1.

然后主线程再启动线程3线程,线程3ID为3,调用创建新的COM对象并调用,执行线程为3,然后启动线程4,并列集COM接口指针传递到线程4,线程4ID为4,com接口散集后执行线程为3.


情况2

初始化MULTITHREADED线程套间,com线程模型为free:

主线程ID为1,在主线程调用COM对象方法,COM执行线程为1,然后启动线程2,并列集COM接口指针传递到线程2,线程2ID为2,com接口散集后执行线程为2.

然后主线程再启动线程3线程,线程3ID为3,调用创建新的COM对象并调用,执行线程为3,然后启动线程4,并列集COM接口指针传递到线程4,线程4ID为4,com接口散集后执行线程为4.

情况3

初始化APARTMENTTHREADED线程套间,com线程模型为single:

主线程ID为1,在主线程调用COM对象方法,COM执行线程为1,然后启动线程2,并列集COM接口指针传递到线程2,线程2ID为2,com接口散集后执行线程为1.

然后主线程再启动线程3线程,线程3ID为3,调用创建新的COM对象并调用,执行线程为1,然后启动线程4,并列集COM接口指针传递到线程4,线程4ID为4,com接口散集后执行线程为1


总结:
以上解释的比较简单,其实画图说明一下比较好,但我还是比较懒,没有画图,下面再来总体解释一下吧,COM套间其实就是为了将COM调用串行化的一种方式,对于Apartment套间来说,COM库会为我们自动串行化COM的调用,底层机制是在创建套间的时候COM库为我们创建一个隐藏窗口,所有对COM对象的调用都通过向这个窗口SENDMESSAGE发送消息来进行串行化调用。这种机制是首先要建立在对指针的传递先要进行散集列集的基础上,很多同志不理解COM套间就是卡在这个问题上,不知道跨线程传递接口要进行散集和列集,列集和散集后的指针可能和以前的指针所指向的对象一样,也有可能不一样(可能指针指向接口代理)。而对于single线程模型的com组件对象,和apartment线程模型的组件对象的区别是,所有对single对象的调用都会send到主线程套间,也就是第一个创建single 对象的套间,情况三可以说明这一点。而对apartment 对象的调用会分别在创建他们的每个套间执行,apartment对象是在哪个线程创建的,所有针对其传递到其他线程列集散集后指针所指对象的调用都会汇集到创建线程,简单来说single就是多个调用汇集到一个线程套间(只有一个),apartment是多个调用汇集到各自创建者的线程套间(可以多个),而free则是,多个调用分别在各自线程的套间执行。
先写到这吧,这些都挺抽象,算是给自己看的,做个笔记了。
原文地址:https://www.cnblogs.com/sosopop/p/1770118.html