现在就需要所有客户端指向同一进程内组件实例,如何做到?

解决方案 »

  1.   

    >>单实例进程外组件就可以了。
    如果是进程内组件,如何实现“单实例”?
    这个问题一直困扰我!
      

  2.   

    一楼说的单实例实际是指名为Singleton的一种设计模式,很简单,其实就是为某类型在全局位置定义一指针。而以后创建该类型的对象的规则,由原来的直接创建改为:如果全局指针无效,则创建对象,设置全局指针,并返回给调用者,如果全局指针有效,则直接将全局指针返回给调用者。如果你使用ATL创建组件,则直接在头文件里的类定义里加一句:
    class CMyObject : public......
    {
    DECLARE_CLASSFACTORY_SINGLETON(CMyObject)
    };
      

  3.   

    试过了,DECLARE_CLASSFACTORY_SINGLETON(CMyObject)只对进程外组件有效,对进程内组件无效。关键是对进程外组件,每次创建组件时旧的类厂还在。
    而对进程内组件,每次创建组件时类厂需要重新创建。
      

  4.   

    年轻人,老朽劝你讲话还是要再严谨一点才好。“试过了,DECLARE_CLASSFACTORY_SINGLETON(CMyObject)只对进程外组件有效,对进程内组件无效。”关键是那句“试过了”显出了你的浮躁,呵呵,真的试过了吗?我手里有例子程序证明DECLARE_CLASSFACTORY_SINGLETON在In-process情况下依然奏效,你有反例没有?
      

  5.   

    哦对,另外,COM的Singleton的核心部分至少不是(也不可能)实现于类厂里,Singleton的类厂只实现一种“create-or-obtain”的行为,而那个“全局指针”被ATL放哪里了我不清楚,不过猜一猜也知道大概是放在CAtlModuleT里了。
      

  6.   

    >>老朽劝你讲话还是要再严谨一点才好。绝非不谨慎!这些天就为这事发愁呢!试过无数方法了:(
      

  7.   

    >>进程内,需要自己实现类厂,并不复杂。
    试过,都不行!
    能否给个提示?如何作?
      

  8.   

    怎么回事?难道又是你没说清楚,或是我没看明白你的问题??你想要更多提示?我看还不如给你个示例代码更直接。“DECLARE_CLASSFACTORY_SINGLETON只对进程外组件有效”,若真是这样,MSDN里或者很多ATL的书籍里应该会有写。可我从来就没听说过。您是从哪里知道的?你们可以把DECLARE_CLASSFACTORY_SINGLETON和CAtlModuleT的源代码找出来自己看一看。源码面前了不秘密。
      

  9.   

    关键在于--对进程内组件,不同的客户端CoCreateInstance都会分别重建类厂,这样就无从保存组件实例。而对进程外组件,类厂被COM保存。这样不同的客户端CoCreateInstance不用分别重建类厂,只需申请类厂指针即可。这样就可以保存组件实例在类厂中,从而实现单件。也许我的理解还有问题,我在看看源代码。谢谢回复。
      

  10.   

    呃……我再确认一下:你的问题的环境是:
    <1> 一个进程(1个可执行的主程序);
    <2> 里面创建多个普通的进程内组件(用ATL创建的进程内DLL,里面创建ATL简单对象);
    <3> 然后该主程序对同一个CLSID创建多个对象实例。是这样吗?我没理解错吧?然后加上DECLARE_CLASSFACTORY_SINGLETON,却发现多个实例不是相同的实例??你是怎么测试的?有两个显然的方法可以确认是否是单实例:
    <1> 同一个CLSID创建两个对象,索取相同接口,获得两个接口指针,这两个指针的地址值应当相等;
    <2> 在接口上定义GetValue(int),SetValue(int)两个方法供测试,SetValue将得到的数存储在成员变量里,那么两个对象的GetValue应当获得相同的值(这简直是废话,不要取笑)。你怎么可能无法创建单实例对象呢?还是你说的“同一进程内组件实例”不是我理解的“单实例”??
      

  11.   

    是这样的:
    <1> 一个进程p1(1个可执行的主程序);
    <2> 里面创建1个普通的进程内组件A(用ATL创建的进程内DLL,里面创建ATL简单对象);
    <3> 然后该主程序对同这个CLSID创建1个对象实例--a1<4> 起另外一个进程p2(1个可执行的主程序);
    <5> 当其发现组件A已有一个对象实例a1时,就不再创建新的实例。就使用现有的实例a1.也就是进程p1,p2共享同一组件实例a1。不知我说清楚了没有?
      

  12.   

    在组件程序文件的头文件的最后一行,ATL向导会生成一个宏:(假设我们的组件类名称为Cobj)
    OBJECT_ENTRY_AUTO(__uuidof(obj), Cobj)展开后是这样:
    __declspec(selectany) ATL::_ATL_OBJMAP_ENTRY __objMap_Cobj = 
    {
    &__uuidof(obj), 
    Cobj::UpdateRegistry, 
    Cobj::_ClassFactoryCreatorClass::CreateInstance, 
    Cobj::_CreatorClass::CreateInstance, 
    NULL, 
    0, 
    Cobj::GetObjectDescription, 
    Cobj::GetCategoryMap, 
    Cobj::ObjectMain
    };extern "C" __declspec(allocate("ATL$__m")) __declspec(selectany) 
    ATL::_ATL_OBJMAP_ENTRY* const __pobjMap_Cobj = &__objMap_Cobj;__pragma(comment(linker, "/include:___pobjMap_Cobj"));
    说实话,里面的大部分编译选项我都看不太明白,但是或许可以大致这样解释:
    <1> ATL向导为组件Cobj定义了一个全局结构体变量__objMap_Cobj,该结构体存储了组件Cobj的很多信息,尤其是用于创建对象的静态函数CreateInstance;
    <2> ATL向导将__objMap_Cobj赋值给了一个外部的全局对象__pobjMap_Cobj,由于不懂__declspec(allocate("ATL$__m"))啥意思,所以不能确定这个extern的对象在哪里;然后,我们再看看供Client获得类厂的方法“DllGetClassObject”。其中具体为Client获取类厂的调用是向导生成的这句:
    _AtlModule.DllGetClassObject(rclsid, riid, ppv);展开后看到如下代码:
    ATLINLINE ATLAPI AtlComModuleGetClassObject(_ATL_COM_MODULE* pComModule, REFCLSID rclsid, REFIID riid, LPVOID* ppv)
    {
    ATLASSERT(pComModule != NULL);
    if (pComModule == NULL)
    return E_INVALIDARG;
    if (pComModule->cbSize == 0)  // Module hasn't been initialized
    return E_UNEXPECTED; if (ppv == NULL)
    return E_POINTER;
    *ppv = NULL; HRESULT hr = S_OK; for (_ATL_OBJMAP_ENTRY** ppEntry = pComModule->m_ppAutoObjMapFirst; ppEntry < pComModule->m_ppAutoObjMapLast; ppEntry++)
    {
    if (*ppEntry != NULL)
    {
    _ATL_OBJMAP_ENTRY* pEntry = *ppEntry;
    if ((pEntry->pfnGetClassObject != NULL) && InlineIsEqualGUID(rclsid, *pEntry->pclsid))
    {
    if (pEntry->pCF == NULL)
    {
    CComCritSecLock<CComCriticalSection> lock(pComModule->m_csObjMap, false);
    hr = lock.Lock();
    if (FAILED(hr))
    {
    ATLTRACE(atlTraceCOM, 0, _T("ERROR : Unable to lock critical section in AtlComModuleGetClassObject\n"));
    ATLASSERT(0);
    break;
    }
    if (pEntry->pCF == NULL)
    hr = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, __uuidof(IUnknown), (LPVOID*)&pEntry->pCF);
    }
    if (pEntry->pCF != NULL)
    hr = pEntry->pCF->QueryInterface(riid, ppv);
    break;
    }
    }
    } if (*ppv == NULL && hr == S_OK)
    hr = CLASS_E_CLASSNOTAVAILABLE;
    return hr;
    }关键就在这个变量pComModule->m_ppAutoObjMapFirst里,感觉它肯定与先前的全局变量__objMap_Cobj有关联……………………所以你说进程内类厂会“重建”我不知道怎么重建法?