DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.0, 7.1
От: morphine  
Дата: 29.11.06 19:17
Оценка:
Прошу прощения, если баян — в поиске ничего не нашел.

Есть exe-сервер, созданный в VS6.0. Тип проекта — MFC, куда добавлена поддержка ATL. Все делалось с помощью мастеров, руками была добавлена только декларация
DECLARE_CLASSFACTORY_SINGLETON. При этом работало все так: при запуске приложения автоматически создавался экземпляр объекта, в FinalConstruct которого выполнялась инициализация всяких внутренних структур данных. Приложение было работоспособно и без клиентов (в этом режиме выполняется его настройка, конфигурирование и т.п.).

Решил проапгрейдится до VS2003 и столкнулся с проблемой: при запуске программы экземпляр объекта не создается, FinalConstruct не вызывается, структуры данных пустые. Объект возникает он только при первом вызове CreateInstance на клиенте. Попробовал VS2005 — то же самое. Сделал новый проект в VS2005 — то же самое.

Как сделать так, чтобы singleton-объект создавался при запуске приложения?
Re: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.0, 7.1
От: Константин Л.  
Дата: 30.11.06 10:03
Оценка:
Здравствуйте, morphine, Вы писали:

[]

ну создай ручками в WinMain
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.0, 7.1
От: Виктор Юров Россия  
Дата: 30.11.06 14:01
Оценка:
M>Как сделать так, чтобы singleton-объект создавался при запуске приложения?
Singleton лучше всего запихать в сервис. Создай ATL сервис на атрибутах, возьми функцию Run() ипереопредели ее примерно так

    HRESULT Run(int nShowCmd = 0)
    {
        HRESULT hr = S_OK;
        CYourModule* pT = static_cast<CYourModule*>(this);

        hr = PreMessageLoop(nShowCmd);    
        if (SUCCEEDED(hr))
        {
            if (m_bService)
            {
                LogEvent(_T("Service started"));
                SetServiceStatus(SERVICE_RUNNING);
            }

            CComPtr<IYourObject> obj;
            hr = obj.CoCreateInstance( __uuidof( CYourObject ));
            if (FAILED(hr))
                return E_FAIL;
            
            pT->RunMessageLoop();
        }

        if (SUCCEEDED(hr))
            hr = pT->PostMessageLoop();
        return hr;
    }


Это будет работать и в случае, если ты зарегистрируешь модуль как приложение (/RegServer).

p.s. Проверено на людях
Каждый человек стоит столько, сколько стоит то, о чем он хлопочет.(с) Народная мудрость.
Re[2]: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.0, 7.1
От: Константин Л.  
Дата: 30.11.06 14:20
Оценка:
Здравствуйте, Виктор Юров, Вы писали:

M>>Как сделать так, чтобы singleton-объект создавался при запуске приложения?

ВЮ>Singleton лучше всего запихать в сервис. Создай ATL сервис на атрибутах, возьми функцию Run() ипереопредели ее примерно так

[]

человеку COM .exe сервер нужен, а не сервис.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.0, 7.1
От: Виктор Юров Россия  
Дата: 30.11.06 15:03
Оценка:
КЛ>человеку COM .exe сервер нужен, а не сервис.
ATL сервис с ключиком /RegServer = COM .exe сервер
Каждый человек стоит столько, сколько стоит то, о чем он хлопочет.(с) Народная мудрость.
Re[4]: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.0, 7.1
От: Константин Л.  
Дата: 30.11.06 15:24
Оценка:
Здравствуйте, Виктор Юров, Вы писали:


КЛ>>человеку COM .exe сервер нужен, а не сервис.

ВЮ>ATL сервис с ключиком /RegServer = COM .exe сервер

хорошо, никогда сервисы не делал, но там вроде всякая лишняя лабуда добавляется.
Похоже, подход к созданию синглтонов в ATL 7.1 стал отличаться от поведения в 6 студии. Я например, в WinMain сам создаю свой синглтон.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.1, 8.0
От: morphine  
Дата: 01.12.06 17:16
Оценка:
КЛ>ну создай ручками в WinMain

Вот чувствую я, что должен быть другой способ. Что удалось нарыть:

В 6.0 мой объект создается при следущем вызове (из InitInstance):
_Module.RegisterClassObjects(CLSCTX_SERVER, REGCLS_MULTIPLEUSE);

Прошелся в нем отладчиком, обнаружил вот что:
1) вызывается new для фабрики класса
2) вызывается конструктор класса CComClassFactorySingleton
3) конструктор CComClassFactory
4) конструктор CComObjectRootEx<ATL::CComMultiThreadModel>
5) конструктор CComObjectRootBase
6) в конструкторе CComObjectGlobal создается мой объект

Все замечательно.

В 8.0 (с версиями в сабже ошибся) зашел в ту же функцию:
1) вызывается new для фабрики класса
2) вызывается конструктор класса CComClassFactorySingleton
3) конструктор CComClassFactory
4) конструктор CComObjectRootEx<ATL::CComMultiThreadModel>
5) конструктор CComObjectRootBase

До CComObjectGlobal дело так и не доходит, мой объект
не создается.

Выходит надо как-то заставить вызываться конструктор CComObjectGlobal.

Буду копать дальше
Re[3]: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.1, 8.0
От: morphine  
Дата: 01.12.06 20:14
Оценка:
В 6.0 в классе CComClassFactorySingleton есть переменная CComObjectGlobal<T> m_Obj.
В 7.1 и 8.0 этой переменной нет

Очевидно, надо ее куда-то впендюрить. Куда — пока не понял
Re[3]: DECLARE_CLASSFACTORY_SINGLETON в VS6, 7.1, 8.0
От: morphine  
Дата: 01.12.06 20:32
Оценка: 1 (1)
Отсюда: http://discuss.develop.com/archives/wa.exe?A2=ind0405b&amp;L=atl&amp;D=0&amp;T=0&amp;P=1362


ATL3 — Singleton created when class factory created
ATL71 — Singleton created first time IClassFactory::CreateInstance is
called for that class.

ATL3 — Uses CComObjectGlobal to implement singleton
ATL71 — Uses CComObjectCached to implement singleton

ATL3 — Singleton's AddRef/Release directly changes module's lock count.
ATL71 — Singleton has own refcount. Module lock count updated on 1<->2
ref transition (see CComObjectCached).

ATL3 — Singleton is destroyed when class factory is destroyed
ATL71 — Singleton is destroyed when class factory is destroyed (or if
you call Release one too many times.)

In ATL71, m_spObj is a data member of the ClassFactory. It will be
released when the class factory is destroyed, which will be when the DLL
is unloaded.
Re[3]: Нашел решение
От: morphine  
Дата: 02.12.06 20:02
Оценка:
Нашел решение, которое работает так, как надо:

1. В класс приложения добавить переменную:
        IUnknown* m_pUnk;



2. В InitInstanse после
    if (FAILED(_Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE)))
        return FALSE;


вставить:

    IClassFactory* pCF = NULL;

    HRESULT hr = CoGetClassObject(CLSID_MyObject, CLSCTX_INPROC, NULL,
        IID_IClassFactory, (void**) &pCF);

    hr = pCF->CreateInstance( NULL, IID_IUnknown, (void**) &m_pUnk);
    pCF->Release() ;


3. Где-нибудь в CMainFrame::OnClose или в CMainFrame::OnDestroy освободить объект:
    CMyServerApp* pApp = (CMyServerApp*)AfxGetApp();
    pApp->m_pUnk->Release();
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.