问了无数遍了,还是没理解透彻:(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是进程内组件或进程外组件有关系吗?

解决方案 »

  1.   

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

  2.   

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

  3.   

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

  4.   

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

  5.   


    也就是说下面这个函数的实现本身就是有问题的,对吗?
    ulong CA::threadProc(LPVOID pVoid)
    {
        CA *pCA = (CA *)pVoid;
        return pCA->realProc();
    }
    如果这个实现有问题,那么调用IA_Method2还可以通过将IA接口指针放到GIT中来解决,但如何才能调用到CA_Method1呢?
      

  6.   

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

  7.   

    那么像我的这种需求的话,必须将IA接口和IB接口同时列集到工作线程中去,即使工作线程也是CA(IA实现类)的一个静态函数。对吧?这里我想不明白了。如果用来调用IA_Method1的指针是一个通过列集散集得到的指针而不是原始指针,那么将IA的指针转为CA的指针不会出问题吗?
      

  8.   

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

  9.   

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

  10.   

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

  11.   

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

  12.   

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

  13.   

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

  14.   

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

  15.   

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

  16.   

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

  17.   

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

  18.   

    那么,按浆糊老师的指导我整理一下思路,但是不知道是否能形成结论
    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。请教浆糊老师,上述结论中有没有哪个步骤是可以省略的(或者说有没有哪句话是不必要的)?严重感谢,加分以示谢意!
      

  19.   

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

  20.   

    只要你把调用过程都放在组件线程中完成,上述一切问题都不存在,所以最简单最通用的方法还是向一个隐藏窗口发消息,由窗口过程来完成实际的调用。如果组件是自由线程模型并且处于MTA中,相当于COM库把所有同步控制交给程序员来完成,这跟一个普通的多线程程序没什么两样。所以我上面的说的通常都是指STA组件。
    跨线程使用自由线程组件,可以直接传递接口指针,没有列集过程,也可以CAST,唯一需要关注的就是CA内部数据的同步。
      

  21.   

    谢谢浆糊不厌其烦耐心解答。
    以下是我的实际测试结果,很是头痛:(以下结论适用于进程内组件也适用于进程外组件,适用于套间线程模型也适用于自由线程模型。进程内组件的载体为CA.DLL,进程外组件的载体为CA.EXE。
    CA组件实现了IA接口
    IA接口有一个方法名为m1
    CA类有一个成员变量int m_nX,在CA的构造中m_nX值被设置为12345
    CA类有一个辅助函数m2(不属于IA接口的方法),m2的实现是弹出一个MessageBoxm1的实现如下:
    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指针传入到工作线程,在工作线程中可以调用到接口实现类的辅助函数和成员变量等,没有问题。
    没有经过权威的论证,只测试过进程内组件和进程外组件,没有测试过远程组件(因为不知道怎么测)
      

  22.   

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

  23.   

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

  24.   

    31楼的结论不知道是否能成立?如果在组件的工作线程中不涉及到其它的接口(或者说涉及到其它的接口的地方都进行了列集与散集)的话。我的测试工程可以在这里下载:test.rar
      

  25.   

    --------------这是最正宗的解决办法------------在其中只增加你需要在外部线程中调用的方法,这些方法只是简单调用主接口的方法。另外的间接,效率低一点的就是发送windows消息来解决。
    --
    不管你的测试工程是否正确,我可以肯定的告诉你,你在test中的理解是错的。
      

  26.   

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

  27.   

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

  28.   


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

  29.   

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