关于COM组件内部工作线程的问题

时间:2021-08-20 05:34:10
问了无数遍了,还是没理解透彻:(

coclass CA实现了IA接口,IA接口有两个方法IA_Method1,IA_Method2。同时CA有一个辅助函数CA_Method1。
在CA类中有一成员IB *pIB,是在CA的FinalConstruct中创建的,创建成功后将其放置到了GIT中,cookie保存在m_dwCookie中。GIT指针保存在一个 全局智能指针变量中:CComPtr<IGlobalInterfaceTable> g_spGIT = NULL;

CA::IA_Method1实现如下:
CreateThread(NULL,0,threadProc,this,0,NULL);
即将CA的this指针传递到了工作线程中。

工作线程中使用的是强制转换:
ulong CA::threadProc(LPVOID pVoid)
{
CA *pCA = (CA *)pVoid;
return pCA->realProc();
}


然后在realProc中通过m_dwCookie获取到IB指针并调用IB接口的方法。同时在readProc中还需要调用IA_Method2和CA_Method1。

不知道这样实现会不会有问题(也不知道表达清楚了没有)?还有,这跟CA是进程内组件或进程外组件有关系吗?

47 个解决方案

#1


有点绕,自己的线程调用自己对象的接口函数?
保险的方法还是列集、散集吧。

#2


方法本身没有问题,问题在于你的GIT保存了几个接口?可以把IA也保存到GIT中,跟IB一样通过cookie获取。或者如果IA和IB能互相查询到,也可以从IB查询出IA接口。

#3


在GIT中只保存了IB接口的指针。
我想确认的就是我在realProc(工作线程中执行)函数中通过强制转换得到的CA指针来调用CA的辅助函数和IA接口函数会不会有问题?而这个IA接口不是通过列集散集得到的。

#4


帮顶一下

#5


不能在其它线程中把接口强制转换成实现类后再调用类方法,这违反了接口的套间线程约定。

#6


引用 3 楼 hailongxl 的回复:
在GIT中只保存了IB接口的指针。
我想确认的就是我在realProc(工作线程中执行)函数中通过强制转换得到的CA指针来调用CA的辅助函数和IA接口函数会不会有问题?而这个IA接口不是通过列集散集得到的。

对于Com接口等,最好通过列集散集等方式得到接口,而不是指针强制转换等

#7


顶一下,这个是COM里最烦的问题

#8


回调接口?

#9


引用 5 楼 jameshooo 的回复:
不能在其它线程中把接口强制转换成实现类后再调用类方法,这违反了接口的套间线程约定。


也就是说下面这个函数的实现本身就是有问题的,对吗?
ulong CA::threadProc(LPVOID pVoid)
{
    CA *pCA = (CA *)pVoid;
    return pCA->realProc();
}

如果这个实现有问题,那么调用IA_Method2还可以通过将IA接口指针放到GIT中来解决,但如何才能调用到CA_Method1呢?

#10


线程函数中能传递CA*进来,但是不能直接或间接调用跟组件有关的变量或方法,可以调用CA_Method1,但是CA_Method1里面不能直接调用IA_Method2

#11


那么像我的这种需求的话,必须将IA接口和IB接口同时列集到工作线程中去,即使工作线程也是CA(IA实现类)的一个静态函数。对吧?

引用 10 楼 jameshooo 的回复:
线程函数中能传递CA*进来,但是不能直接或间接调用跟组件有关的变量或方法,可以调用CA_Method1,但是CA_Method1里面不能直接调用IA_Method2

这里我想不明白了。如果用来调用IA_Method1的指针是一个通过列集散集得到的指针而不是原始指针,那么将IA的指针转为CA的指针不会出问题吗?

#12


列集散集后的接口指针可以调用。不能在工作线程中把IA*强制转换成CA*,除非这个组件是一个*线程组件,否则工作线程中通过列集获得的IA*已经不是原始的IA*了,而是一个代理指针。代理是系统自动创建的对象,强制CAST是错误的。

#13


mk

#14


引用 12 楼 jameshooo 的回复:
列集散集后的接口指针可以调用。不能在工作线程中把IA*强制转换成CA*,除非这个组件是一个*线程组件,否则工作线程中通过列集获得的IA*已经不是原始的IA*了,而是一个代理指针。代理是系统自动创建的对象,强制CAST是错误的。

谢谢。
CA_Method1函数里还要用到一些CA的类变量,也不想把这个函数从IA接口中导出,那么在工作线程中应该如何才能调用到CA::CA_Method1呢?

#15


mark

#16


在主线程中创建一个隐藏窗口,工作线程向这个窗口Send消息,窗口过程中来调用CA_Method1

#17


Oh,my god!
有没有简单一点点的方法呢?因为不光是CA_Method1,还有CA_Method2,CA_Method3...

#18



学习

#19


还有一种方式是再添加一个接口IC,CA同样实现了IC,但是不要在接口映射表中添加IC的查询映射(即从IA无法查询出IC)。然后把IC列集,工作线程获得IC代理指针后调用它的方法,IC的每个方法对应CA的一个内部函数。这样做虽然外部能看到有IC这个接口,但是外部永远得不到这个接口,只有组件内部才能使用IC接口。

#20


CA的类变量必须通过IC接口的方法来访问,对吧?

#21


CA的类变量和类方法只能通过IC接口来访问,对吧?CA_Method1里面要调用到IA_Method2又该怎么办?
另外,既然不要在接口映射表中添加IC的查询映射,那么是否是直接通过static_cast转换一个IC的指针存在GIT中?
还有一个问题,GIT的指针是一个全局指针,没有通过列集散集在工作线程中直接使用,会不会存在问题?


谢谢!谢谢!谢谢!谢谢!谢谢!谢谢!谢谢!谢谢!

#22


有些接口指针无需列集,比如GIT、IStream、IStorage,这些指针可以在其它线程中随意使用。
如果CA的类方法是被IC来调用的,那么CA_Method1里面可以随意调用IA_Method2或者其它方法,因为它们的线程上下文是相同的。

#23


能不能请大侠解释一下“它们的线程上下文是相同的”的意思?我不太理解。

#24


当你在工作线程中调用IC的方法时,真实的调用将在组件线程中完成,在组件线程中可以任意调用CA的任意方法,也能做CAST操作。

#25


在工作线程中调用IC的方法时,直实的调用将在组件线程中完成。这是针对套间线程模型的组件而言的吧?

对于*线程套间中的组件呢?

#26


在工作线程中调用IC的方法时,直实的调用将在组件线程中完成。这是针对套间线程模型的组件而言的吧? 
对于*线程套间中的组件呢?


对于一个必须使用工作线程来实现某些功能,而且在工作线程中要访问到接口实现类的一些成员变量的组件来说,要绕的弯弯实在太多了:(

#27


没办法,总要有取舍,即使不使用COM的方式来实现,多线程访问共享数据也需要同步,*线程组件的同步也必须由程序员来完成,自己判断一下到底哪个工作量大。

#28


那么,按 浆糊老师的指导我整理一下思路,但是 不知道是否能形成结论


coclass CA实现了IA接口,IA接口有两个方法IA_Method1,IA_Method2。
CA有一个辅助函数CA_Method1以及一个成员变量int m_nX;
同时,CA类中还有一成员变量IB *pIB,是在CA的FinalConstruct中进行CreateInstance。IB有一个接口函数IB_Method1。

CA::IA_Method1是一个耗时非常长的操作,所以使用了工作线程。在工作线程的线程函数体中需要调用到IA_Method2、IB_Method1、CA_Method1以及CA::m_nX。

我的结论是:
不管CA是套间线程模型还是*线程模型,都必须对IA和IB接口进行列集散集或保存到GIT以供工作线程使用。对于CA::m_nX来说,必须使用一个不公开的接口例如IC,CA实现IC,同时将IC接口也进行列集散集或通过GIT传递到工作线程,在工作线程中再通过IC::get_X和IC::put_X来访问CA::m_nX。

请教 浆糊老师,上述结论中有没有哪个步骤是可以省略的(或者说有没有哪句话是不必要的)?严重感谢,加分以示谢意!

#29


引用 24 楼 jameshooo 的回复:
当你在工作线程中调用IC的方法时,真实的调用将在组件线程中完成,在组件线程中可以任意调用CA的任意方法,也能做CAST操作。

如果组件线程和工作线程都在MTA中,同时组件是*线程模型的,这句话还能适用吗?同时,请您再看一下第28楼!感觉越来越晕了。

#30


只要你把调用过程都放在组件线程中完成,上述一切问题都不存在,所以最简单最通用的方法还是向一个隐藏窗口发消息,由窗口过程来完成实际的调用。

如果组件是*线程模型并且处于MTA中,相当于COM库把所有同步控制交给程序员来完成,这跟一个普通的多线程程序没什么两样。所以我上面的说的通常都是指STA组件。
跨线程使用*线程组件,可以直接传递接口指针,没有列集过程,也可以CAST,唯一需要关注的就是CA内部数据的同步。

#31


谢谢浆糊不厌其烦耐心解答。
以下是我的实际测试结果,很是头痛:(

以下结论适用于进程内组件也适用于进程外组件,适用于套间线程模型也适用于*线程模型。

进程内组件的载体为CA.DLL,进程外组件的载体为CA.EXE。
CA组件实现了IA接口
IA接口有一个方法名为m1
CA类有一个成员变量int m_nX,在CA的构造中m_nX值被设置为12345
CA类有一个辅助函数m2(不属于IA接口的方法),m2的实现是弹出一个MessageBox

m1的实现如下:
STDMETHODIMP CA::m1()
{
CreateThread(NULL,0,threadProc,this,0,NULL);
return S_OK;
}

threadProc的实现如下:
unsigned long CA::threadProc(LPVOID pVoid)
{
CA *pC = (CA*)pVoid;

wchar_t buffer[100];
swprintf(buffer,L"[%d] : %d",GetCurrentThreadId(),pC->m_nX);
MessageBoxW(NULL,buffer,L"Info",MB_ICONINFORMATION);

pC->m2();
return 0;
}


测试流程如下:

在测试程序的主对话框类中添加一个成员变量IA *pA。
在OnInitDialog函数中对pA进行CreateInstance,并调用其m1函数。可以看到输出的m_nX值是正确的12345,m2函数的调用也是正确的。而且弹出的MessageBox所在的线程是位于CA.DLL(CA.EXE)中的。

在测试程序的OnOK中添加如下代码:
void CTestfdsDlg::OnOK() 
{
IStream *pStream;
CoMarshalInterThreadInterfaceInStream(IID_IA,static_cast<IUnknown*>(pA),&pStream);
CreateThread(NULL,0,threadProc,pStream,0,NULL);
}

threadProc实现如下:
unsigned long CTestfdsDlg::threadProc(LPVOID pVoid)
{
CoInitialize(NULL); //此处换作CoInitializeEx(NULL,COINIT_MULTITHREADED);测试结果亦一样。

IStream *pStream = static_cast<IStream*>(pVoid);
IA *iht;
if(FAILED(CoGetInterfaceAndReleaseStream(pStream,IID_IA,(void**)&iht)))
{
::MessageBox(NULL,"CoGetInterfaceAndReleaseStream failed","Error",MB_ICONINFORMATION);

CoUninitialize();
return -1;
}

iht->m1();
iht->Release();

CoUninitialize();
return 0;
}
可以看到输出的m_nX值是正确的12345,m2函数的调用也是正确的。而且弹出的MessageBox所在的线程是位于CA.DLL(CA.EXE)中的。


结论是:在实现某个接口方法时,可以将接口实现类的this指针传入到工作线程,在工作线程中可以调用到接口实现类的辅助函数和成员变量等,没有问题。
没有经过权威的论证,只测试过进程内组件和进程外组件,没有测试过远程组件(因为不知道怎么测)

#32


没看到你是怎么测试调用CA::M2的代码的。另外你的测试用例过于简单,m2访问一个成员变量或者弹出一个对话框不能说明问题,应该添加测试COM接口的用例,比如在组件内部创建一个其他接口,外部接口或者聚合的接口都可以,然后把这个接口保存在类成员变量 IB* m_pB; 里面,在m2()里尝试访问 m_pB->SomeMethod(); 然后在工作线程中尝试调用CA::m2(),再看看结果如何。

#33


呵呵,您说的这种情况我测试过,这个m_pB->SomeMethod(); 是不能调用成功的。但是如果不访问其它的接口,好像都可以。哎,搞晕了!

#34


如果我有空,我会找时间共享一段原创代码,整个功能就是“线程的创建和委托”,用了这段代码,不熟多线程编程的人也能轻松使用多线程,并且能委托其它线程调用一个函数或者调用一个对象的成员函数,就像本线程调用函数一样方便。只是现在手头工作太紧,没空整理代码和文章。

#35


;-)

#36


31楼的结论不知道是否能成立?如果在组件的工作线程中不涉及到其它的接口(或者说涉及到其它的接口的地方都进行了列集与散集)的话。

#37


31楼的结论不知道是否能成立?如果在组件的工作线程中不涉及到其它的接口(或者说涉及到其它的接口的地方都进行了列集与散集)的话。

我的测试工程可以在这里下载: test.rar

#38


引用 19 楼 jameshooo 的回复:
还有一种方式是再添加一个接口IC,CA同样实现了IC,但是不要在接口映射表中添加IC的查询映射(即从IA无法查询出IC)。然后把IC列集,工作线程获得IC代理指针后调用它的方法,IC的每个方法对应CA的一个内部函数。这样做虽然外部能看到有IC这个接口,但是外部永远得不到这个接口,只有组件内部才能使用IC接口。

--------------这是最正宗的解决办法------------



在其中只增加你需要在外部线程中调用的方法,这些方法只是简单调用主接口的方法。

另外的间接,效率低一点的就是发送windows消息来解决。


--
不管你的测试工程是否正确,我可以肯定的告诉你,你在test中的理解是错的。

#39


我这里没有装vb,其实,你可以用vb来做测试,用vc来做测试其实是很不准确的。

#40


我个人认为,造成,你测试情况是正确的原因,只能算是一种偶然,是实现的问题造成的。在很多情况下,在单继承的情况下,为了效率问题,在实现中应该是同一个对象(接口和类对象实例应该是一个东西),你的实现应该是对的。因为没有代理出现。

而在多个接口,复杂的情况下,就象浆糊说的,一旦出现代理,比如权限限制的时候,分布式的调用时,不同机器上执行肯定需要代理,你的代码肯定不能正常工作。因为在不同的机器上,你强制转换,不光是结果不对,可以说,可能是想不到的结果。

#41


谢谢楼上大虾。
事实上我是在实际使用过程中使用了test中的结论,而且这个实际应用远比test复杂,如下图所示:
经过vc,vb,javascript调用都没有出现问题,所以才又做了一个test工程来进行专门的测试。

[img=http://202.102.234.68:5679/test.jpg]实际应用[/img]

#42


我不知道你的工程有多复杂,但我还是坚持我的观点。

你的情况就是因为没有出现代理的时候。

可能和你的代码应该用环境有关,你还没有碰到。举个最通俗的例子就是多继承的时候,强制转换都会出错。



#43


帮顶

#44


引用 42 楼 wwwllg 的回复:
我不知道你的工程有多复杂,但我还是坚持我的观点。 

你的情况就是因为没有出现代理的时候。 

可能和你的代码应该用环境有关,你还没有碰到。举个最通俗的例子就是多继承的时候,强制转换都会出错。 


是啊,我也在想将经过代理得到的IA指针转换为CA会出错,所以才做了个test工程来测试。
但是结果出乎我的意料啊,就算CA是进程外组件,然后我在主线程中创建然后列集到工作线程中再散集得到IA指针,然后再调用此指针的IA_Method1,也没有出现问题。
这个IA接口肯定是经过代理的。

#45


从你测试的情况来看,应该实体没有发生变化,对象没有重新复制,所以转换还是成功的。
从理论上来说,你就算不用列集,直接传递给线程 ,从理论上来说,应该是指向的同一个对象,但是
套件是不充许的,显然是有什么地方是限制了的,只是我们不知道。

如果你想证明的话,你应该做一个远程 调用看看,就是DCOM,那肯定就是代理,你再试试看。

#46


呵呵,我就是不知道怎么样做远程调用。要不然麻烦您帮测试一下:-)

#47


另外,跨进程应该也是用了代理的呀。

#1


有点绕,自己的线程调用自己对象的接口函数?
保险的方法还是列集、散集吧。

#2


方法本身没有问题,问题在于你的GIT保存了几个接口?可以把IA也保存到GIT中,跟IB一样通过cookie获取。或者如果IA和IB能互相查询到,也可以从IB查询出IA接口。

#3


在GIT中只保存了IB接口的指针。
我想确认的就是我在realProc(工作线程中执行)函数中通过强制转换得到的CA指针来调用CA的辅助函数和IA接口函数会不会有问题?而这个IA接口不是通过列集散集得到的。

#4


帮顶一下

#5


不能在其它线程中把接口强制转换成实现类后再调用类方法,这违反了接口的套间线程约定。

#6


引用 3 楼 hailongxl 的回复:
在GIT中只保存了IB接口的指针。
我想确认的就是我在realProc(工作线程中执行)函数中通过强制转换得到的CA指针来调用CA的辅助函数和IA接口函数会不会有问题?而这个IA接口不是通过列集散集得到的。

对于Com接口等,最好通过列集散集等方式得到接口,而不是指针强制转换等

#7


顶一下,这个是COM里最烦的问题

#8


回调接口?

#9


引用 5 楼 jameshooo 的回复:
不能在其它线程中把接口强制转换成实现类后再调用类方法,这违反了接口的套间线程约定。


也就是说下面这个函数的实现本身就是有问题的,对吗?
ulong CA::threadProc(LPVOID pVoid)
{
    CA *pCA = (CA *)pVoid;
    return pCA->realProc();
}

如果这个实现有问题,那么调用IA_Method2还可以通过将IA接口指针放到GIT中来解决,但如何才能调用到CA_Method1呢?

#10


线程函数中能传递CA*进来,但是不能直接或间接调用跟组件有关的变量或方法,可以调用CA_Method1,但是CA_Method1里面不能直接调用IA_Method2

#11


那么像我的这种需求的话,必须将IA接口和IB接口同时列集到工作线程中去,即使工作线程也是CA(IA实现类)的一个静态函数。对吧?

引用 10 楼 jameshooo 的回复:
线程函数中能传递CA*进来,但是不能直接或间接调用跟组件有关的变量或方法,可以调用CA_Method1,但是CA_Method1里面不能直接调用IA_Method2

这里我想不明白了。如果用来调用IA_Method1的指针是一个通过列集散集得到的指针而不是原始指针,那么将IA的指针转为CA的指针不会出问题吗?

#12


列集散集后的接口指针可以调用。不能在工作线程中把IA*强制转换成CA*,除非这个组件是一个*线程组件,否则工作线程中通过列集获得的IA*已经不是原始的IA*了,而是一个代理指针。代理是系统自动创建的对象,强制CAST是错误的。

#13


mk

#14


引用 12 楼 jameshooo 的回复:
列集散集后的接口指针可以调用。不能在工作线程中把IA*强制转换成CA*,除非这个组件是一个*线程组件,否则工作线程中通过列集获得的IA*已经不是原始的IA*了,而是一个代理指针。代理是系统自动创建的对象,强制CAST是错误的。

谢谢。
CA_Method1函数里还要用到一些CA的类变量,也不想把这个函数从IA接口中导出,那么在工作线程中应该如何才能调用到CA::CA_Method1呢?

#15


mark

#16


在主线程中创建一个隐藏窗口,工作线程向这个窗口Send消息,窗口过程中来调用CA_Method1

#17


Oh,my god!
有没有简单一点点的方法呢?因为不光是CA_Method1,还有CA_Method2,CA_Method3...

#18



学习

#19


还有一种方式是再添加一个接口IC,CA同样实现了IC,但是不要在接口映射表中添加IC的查询映射(即从IA无法查询出IC)。然后把IC列集,工作线程获得IC代理指针后调用它的方法,IC的每个方法对应CA的一个内部函数。这样做虽然外部能看到有IC这个接口,但是外部永远得不到这个接口,只有组件内部才能使用IC接口。

#20


CA的类变量必须通过IC接口的方法来访问,对吧?

#21


CA的类变量和类方法只能通过IC接口来访问,对吧?CA_Method1里面要调用到IA_Method2又该怎么办?
另外,既然不要在接口映射表中添加IC的查询映射,那么是否是直接通过static_cast转换一个IC的指针存在GIT中?
还有一个问题,GIT的指针是一个全局指针,没有通过列集散集在工作线程中直接使用,会不会存在问题?


谢谢!谢谢!谢谢!谢谢!谢谢!谢谢!谢谢!谢谢!

#22


有些接口指针无需列集,比如GIT、IStream、IStorage,这些指针可以在其它线程中随意使用。
如果CA的类方法是被IC来调用的,那么CA_Method1里面可以随意调用IA_Method2或者其它方法,因为它们的线程上下文是相同的。

#23


能不能请大侠解释一下“它们的线程上下文是相同的”的意思?我不太理解。

#24


当你在工作线程中调用IC的方法时,真实的调用将在组件线程中完成,在组件线程中可以任意调用CA的任意方法,也能做CAST操作。

#25


在工作线程中调用IC的方法时,直实的调用将在组件线程中完成。这是针对套间线程模型的组件而言的吧?

对于*线程套间中的组件呢?

#26


在工作线程中调用IC的方法时,直实的调用将在组件线程中完成。这是针对套间线程模型的组件而言的吧? 
对于*线程套间中的组件呢?


对于一个必须使用工作线程来实现某些功能,而且在工作线程中要访问到接口实现类的一些成员变量的组件来说,要绕的弯弯实在太多了:(

#27


没办法,总要有取舍,即使不使用COM的方式来实现,多线程访问共享数据也需要同步,*线程组件的同步也必须由程序员来完成,自己判断一下到底哪个工作量大。

#28


那么,按 浆糊老师的指导我整理一下思路,但是 不知道是否能形成结论


coclass CA实现了IA接口,IA接口有两个方法IA_Method1,IA_Method2。
CA有一个辅助函数CA_Method1以及一个成员变量int m_nX;
同时,CA类中还有一成员变量IB *pIB,是在CA的FinalConstruct中进行CreateInstance。IB有一个接口函数IB_Method1。

CA::IA_Method1是一个耗时非常长的操作,所以使用了工作线程。在工作线程的线程函数体中需要调用到IA_Method2、IB_Method1、CA_Method1以及CA::m_nX。

我的结论是:
不管CA是套间线程模型还是*线程模型,都必须对IA和IB接口进行列集散集或保存到GIT以供工作线程使用。对于CA::m_nX来说,必须使用一个不公开的接口例如IC,CA实现IC,同时将IC接口也进行列集散集或通过GIT传递到工作线程,在工作线程中再通过IC::get_X和IC::put_X来访问CA::m_nX。

请教 浆糊老师,上述结论中有没有哪个步骤是可以省略的(或者说有没有哪句话是不必要的)?严重感谢,加分以示谢意!

#29


引用 24 楼 jameshooo 的回复:
当你在工作线程中调用IC的方法时,真实的调用将在组件线程中完成,在组件线程中可以任意调用CA的任意方法,也能做CAST操作。

如果组件线程和工作线程都在MTA中,同时组件是*线程模型的,这句话还能适用吗?同时,请您再看一下第28楼!感觉越来越晕了。

#30


只要你把调用过程都放在组件线程中完成,上述一切问题都不存在,所以最简单最通用的方法还是向一个隐藏窗口发消息,由窗口过程来完成实际的调用。

如果组件是*线程模型并且处于MTA中,相当于COM库把所有同步控制交给程序员来完成,这跟一个普通的多线程程序没什么两样。所以我上面的说的通常都是指STA组件。
跨线程使用*线程组件,可以直接传递接口指针,没有列集过程,也可以CAST,唯一需要关注的就是CA内部数据的同步。

#31


谢谢浆糊不厌其烦耐心解答。
以下是我的实际测试结果,很是头痛:(

以下结论适用于进程内组件也适用于进程外组件,适用于套间线程模型也适用于*线程模型。

进程内组件的载体为CA.DLL,进程外组件的载体为CA.EXE。
CA组件实现了IA接口
IA接口有一个方法名为m1
CA类有一个成员变量int m_nX,在CA的构造中m_nX值被设置为12345
CA类有一个辅助函数m2(不属于IA接口的方法),m2的实现是弹出一个MessageBox

m1的实现如下:
STDMETHODIMP CA::m1()
{
CreateThread(NULL,0,threadProc,this,0,NULL);
return S_OK;
}

threadProc的实现如下:
unsigned long CA::threadProc(LPVOID pVoid)
{
CA *pC = (CA*)pVoid;

wchar_t buffer[100];
swprintf(buffer,L"[%d] : %d",GetCurrentThreadId(),pC->m_nX);
MessageBoxW(NULL,buffer,L"Info",MB_ICONINFORMATION);

pC->m2();
return 0;
}


测试流程如下:

在测试程序的主对话框类中添加一个成员变量IA *pA。
在OnInitDialog函数中对pA进行CreateInstance,并调用其m1函数。可以看到输出的m_nX值是正确的12345,m2函数的调用也是正确的。而且弹出的MessageBox所在的线程是位于CA.DLL(CA.EXE)中的。

在测试程序的OnOK中添加如下代码:
void CTestfdsDlg::OnOK() 
{
IStream *pStream;
CoMarshalInterThreadInterfaceInStream(IID_IA,static_cast<IUnknown*>(pA),&pStream);
CreateThread(NULL,0,threadProc,pStream,0,NULL);
}

threadProc实现如下:
unsigned long CTestfdsDlg::threadProc(LPVOID pVoid)
{
CoInitialize(NULL); //此处换作CoInitializeEx(NULL,COINIT_MULTITHREADED);测试结果亦一样。

IStream *pStream = static_cast<IStream*>(pVoid);
IA *iht;
if(FAILED(CoGetInterfaceAndReleaseStream(pStream,IID_IA,(void**)&iht)))
{
::MessageBox(NULL,"CoGetInterfaceAndReleaseStream failed","Error",MB_ICONINFORMATION);

CoUninitialize();
return -1;
}

iht->m1();
iht->Release();

CoUninitialize();
return 0;
}
可以看到输出的m_nX值是正确的12345,m2函数的调用也是正确的。而且弹出的MessageBox所在的线程是位于CA.DLL(CA.EXE)中的。


结论是:在实现某个接口方法时,可以将接口实现类的this指针传入到工作线程,在工作线程中可以调用到接口实现类的辅助函数和成员变量等,没有问题。
没有经过权威的论证,只测试过进程内组件和进程外组件,没有测试过远程组件(因为不知道怎么测)

#32


没看到你是怎么测试调用CA::M2的代码的。另外你的测试用例过于简单,m2访问一个成员变量或者弹出一个对话框不能说明问题,应该添加测试COM接口的用例,比如在组件内部创建一个其他接口,外部接口或者聚合的接口都可以,然后把这个接口保存在类成员变量 IB* m_pB; 里面,在m2()里尝试访问 m_pB->SomeMethod(); 然后在工作线程中尝试调用CA::m2(),再看看结果如何。

#33


呵呵,您说的这种情况我测试过,这个m_pB->SomeMethod(); 是不能调用成功的。但是如果不访问其它的接口,好像都可以。哎,搞晕了!

#34


如果我有空,我会找时间共享一段原创代码,整个功能就是“线程的创建和委托”,用了这段代码,不熟多线程编程的人也能轻松使用多线程,并且能委托其它线程调用一个函数或者调用一个对象的成员函数,就像本线程调用函数一样方便。只是现在手头工作太紧,没空整理代码和文章。

#35


;-)

#36


31楼的结论不知道是否能成立?如果在组件的工作线程中不涉及到其它的接口(或者说涉及到其它的接口的地方都进行了列集与散集)的话。

#37


31楼的结论不知道是否能成立?如果在组件的工作线程中不涉及到其它的接口(或者说涉及到其它的接口的地方都进行了列集与散集)的话。

我的测试工程可以在这里下载: test.rar

#38


引用 19 楼 jameshooo 的回复:
还有一种方式是再添加一个接口IC,CA同样实现了IC,但是不要在接口映射表中添加IC的查询映射(即从IA无法查询出IC)。然后把IC列集,工作线程获得IC代理指针后调用它的方法,IC的每个方法对应CA的一个内部函数。这样做虽然外部能看到有IC这个接口,但是外部永远得不到这个接口,只有组件内部才能使用IC接口。

--------------这是最正宗的解决办法------------



在其中只增加你需要在外部线程中调用的方法,这些方法只是简单调用主接口的方法。

另外的间接,效率低一点的就是发送windows消息来解决。


--
不管你的测试工程是否正确,我可以肯定的告诉你,你在test中的理解是错的。

#39


我这里没有装vb,其实,你可以用vb来做测试,用vc来做测试其实是很不准确的。

#40


我个人认为,造成,你测试情况是正确的原因,只能算是一种偶然,是实现的问题造成的。在很多情况下,在单继承的情况下,为了效率问题,在实现中应该是同一个对象(接口和类对象实例应该是一个东西),你的实现应该是对的。因为没有代理出现。

而在多个接口,复杂的情况下,就象浆糊说的,一旦出现代理,比如权限限制的时候,分布式的调用时,不同机器上执行肯定需要代理,你的代码肯定不能正常工作。因为在不同的机器上,你强制转换,不光是结果不对,可以说,可能是想不到的结果。

#41


谢谢楼上大虾。
事实上我是在实际使用过程中使用了test中的结论,而且这个实际应用远比test复杂,如下图所示:
经过vc,vb,javascript调用都没有出现问题,所以才又做了一个test工程来进行专门的测试。

[img=http://202.102.234.68:5679/test.jpg]实际应用[/img]

#42


我不知道你的工程有多复杂,但我还是坚持我的观点。

你的情况就是因为没有出现代理的时候。

可能和你的代码应该用环境有关,你还没有碰到。举个最通俗的例子就是多继承的时候,强制转换都会出错。



#43


帮顶

#44


引用 42 楼 wwwllg 的回复:
我不知道你的工程有多复杂,但我还是坚持我的观点。 

你的情况就是因为没有出现代理的时候。 

可能和你的代码应该用环境有关,你还没有碰到。举个最通俗的例子就是多继承的时候,强制转换都会出错。 


是啊,我也在想将经过代理得到的IA指针转换为CA会出错,所以才做了个test工程来测试。
但是结果出乎我的意料啊,就算CA是进程外组件,然后我在主线程中创建然后列集到工作线程中再散集得到IA指针,然后再调用此指针的IA_Method1,也没有出现问题。
这个IA接口肯定是经过代理的。

#45


从你测试的情况来看,应该实体没有发生变化,对象没有重新复制,所以转换还是成功的。
从理论上来说,你就算不用列集,直接传递给线程 ,从理论上来说,应该是指向的同一个对象,但是
套件是不充许的,显然是有什么地方是限制了的,只是我们不知道。

如果你想证明的话,你应该做一个远程 调用看看,就是DCOM,那肯定就是代理,你再试试看。

#46


呵呵,我就是不知道怎么样做远程调用。要不然麻烦您帮测试一下:-)

#47


另外,跨进程应该也是用了代理的呀。