Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Скрестить Callback и Events в COM объекте, Скрестить Callback и Events в COM объект 
:(
    Опции темы
ivs4
Дата 1.7.2009, 11:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Добрый день. Столкнулся  с нетривиальной задачей. Делаю обертку над сторонней библиотекой в виде COM объекта в ATL. В этой библиотеке есть некай ф-ция результат выполнения которой можно узнать через асинхронно через Callback ф-цию. Задача же стоит в вызове метода объекта при наступлении Callback. Сначала подготовил свой объект для вызова ивентов. 
Делаю первый метод ком объекта без параметров, он отвечает за назначение callback.  Сначала хотел сделать CallBack методом класса ком объекта через такой изврат:
Код

typedef void (__stdcall *Func2)(long);
typedef void (__stdcall CMyClass::*Func)(long);

class ATL_NO_VTABLE CMyClass: 
...
{
public:
    CMyClass()
    {
    }

    void __stdcall Callback (long var);

}
...
STDMETHODIMP CMyClass::SetCallback(VARIANT *var){
...
pFunc2=CMyClass::Callback;
long addr=*(long*)&pFunc;
pFunc2=*(Func2*)&addr;
setcallbackfunction(addr);
...
}


void __stdcall CMyClass::Callback (long var){
    VARIANTARG var;
    var.lVal=var;
    var.vt=VT_I4;
    Fire_CallBackDone(var);
}


При таком раскладе вызов Callback происходит но проблема в вызове метода определенного мною метода события, а конкретно ошибка происходит в этом месте функции события:

Код

int nConnections = m_vec.GetSize();
 

те как я понял при таком назначениию callback я не могу корректно обращаться к членам родителя моего класса к таким как IConnectionPointImpl.

Хорошо, решил уйти от callback как члена класса и сделал его статическим методом класса. Кроме того ввел в класс статический указатель на самого себя:
Код

class ATL_NO_VTABLE CMyClass: 
...
{
public:
    CMyClass()
    {
    }
        static CMyClass *p;
    static void __stdcall Callback (long var);

}


вроде все хорошо, callback вызывается но опять проблема с вызовом событийного события Fire_CallBackDone. Выполнение в этом методе ф-ции
Код

HRESULT hres=pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);


Возвращало ошибку 

-2147221008 (800401F0) Не был произведен вызов CoInitialize. 

переработал Callback
Код

void __stdcall CMyClass::Callback (long var){
CoInitialize(NULL);
    VARIANTARG var;
    var.lVal=var;
    var.vt=VT_I4;
    p->Fire_CallBackDone(var);
CoUninitialize();
}


после этого тот же вызов Invoke выдал ошибку 

-2147417842 (8001010E) Приложение обратилось к интерфейсу, относящемуся к
другому потоку. 

На сегодняшний момент прихожу к выводу, что невозможно вызвать из callback событие в рамках COM идеология. Думаю уходить от этого решения. Последняя надежда на ваше мнение. Если вдохнете надежду, то буду продолжать.

Это сообщение отредактировал(а) ivs4 - 1.7.2009, 16:23
PM MAIL ICQ   Вверх
xvr
Дата 1.7.2009, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Вызвать можно, но не просто  smile 
Для начала мелкое замечание - CoInitialize нужно вызывать в начале каждого потока, где планируется работать с COM объектами.
Для вызова объектов между разными потоками (т.е. создан он был в одном, а вызывать его надо в другом) нужно передать сам объект в тот поток, где его планируется вызывать (см CoMarshalInterThreadInterfaceInStream и CoGetInterfaceAndReleaseStream). И еще очень желательно, что бы этот поток крутил цикл обработки очереди сообщений Windows (иначе половина COM инфраструктуры отвалится)

PM MAIL   Вверх
ivs4
Дата 1.7.2009, 16:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Здравствуйте, xvr.
Как я думаю, Callback вызывается в своем потоке, не в том, где создается COM объект и соответственно в своем апартменте. Тогда понятно природа ошибок и нужность маршалинга. Но возникают вопросы:

Каким образом осуществить маршалинг. Т.е. вы предлагаете в статической переменной вместо того как у меня хранения указателя на класс CMyClass. хранить IStream из которого потом в CallBack доставать указатель на интерфейс CMyClass.

Код

void __stdcall CMyClass::Callback (long var){
CoInitialize(NULL);
    CoGetInterfaceAndReleaseStream(str, ..... &p)
    VARIANTARG var;
    var.lVal=var;
    var.vt=VT_I4;
    ....
CoUninitialize();
}


Правильно я Вас понял?
PM MAIL ICQ   Вверх
xvr
Дата 1.7.2009, 23:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



В общем так. Вся прелесть в деталях  smile 
В том потоке, где был созданн COM объект надо записать его в стрим (с помощью CoMarshalInterThreadInterfaceInStream) и записать полученный стрим в глобал. Затем, в потоке, где надо объект вызывать, делается CoGetInterfaceAndReleaseStream. Полученный объект сохраняется опять же в глобале. Затем все вызовы делаются с этим объектом. 
Вызывать пару CoInitialize + CoUninitialize на каждый callback не стоит - это довольно дорогая операция
Главный вопрос как синхронизировать создание стрима с объектом и его использование, т.к. это должно делаться в разных потоках. Для этого можно воспользоваться сообщениями Windows (т.к. поток-приемник все равно должен крутить цикл обработки сообщений, что бы COM нормально работал). Можно применить и другие методы.
Callback'и вызываются в заранее известном потоке, или о их месте вызова ничего неизвестно?


PM MAIL   Вверх
jonie
Дата 2.7.2009, 07:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 5
Всего: 118



можно без сериализации в IStream жить между потоками используя GIT. http://forum.sources.ru/index.php?showtopic=40322 вот примерное объяснение. Это несколько (?) быстрее чем марашалить как делаете вы, но имеет ряд ограничений.


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
ivs4
Дата 2.7.2009, 18:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Давайте еще раз обрисуем ситуацию. Вот описание CMyCllass:
Код

class ATL_NO_VTABLE CMyClass : 
    public CComObjectRootEx<CComSingleThreadModel>,
    //public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CMyClass &CLSID_MyClass>,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl<CMyClass>,
    public IDispatchImpl<IMyClass, &IID_IMyClass, &LIBID_TRANSACLib>,
    public CProxy_IMyClassEvents< CMyClass >
{
public:
    CMyClass()
    {
    }

    IStream *str;

    static CMyClass *p;
    
    static void __stdcall Callback (long var);

    DECLARE_REGISTRY_RESOURCEID(IDR_MYCLASS)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CMyClass)
    COM_INTERFACE_ENTRY(IMyClass
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
    COM_INTERFACE_ENTRY(IConnectionPointContainer)
    COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
END_COM_MAP()

// ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

public:
    STDMETHOD(SetCallback)(...);
....
public :

BEGIN_CONNECTION_POINT_MAP(CMyClass)
    CONNECTION_POINT_ENTRY(DIID__IMyClassEvents)
END_CONNECTION_POINT_MAP()

};


обратите внимание на наследование от CProxy_IMyClassEvents. Он отвечает за вызова рукотворных событий. Вот он

Код

template <class T>
class CProxy_IMyClassEvents : public IConnectionPointImpl<T, &DIID__IMyClassEvents, CComDynamicUnkArray>
{
    //Warning this class may be recreated by the wizard.
public:
    HRESULT Fire_CallbackDone(VARIANT var)
    {
        CComVariant varResult;
        T* pT = static_cast<T*>(this);
        int nConnectionIndex;
        CComVariant* pvars = new CComVariant[1];
        int nConnections = m_vec.GetSize();
        
        for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
        {
            pT->Lock();
            CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
            pT->Unlock();
            IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
            if (pDispatch != NULL)
            {
                VariantClear(&varResult);
                pvars[0] = var;
                DISPPARAMS disp = { pvars, NULL, 1, 0 };
                HRESULT hres=pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
            }
        }
        delete[] pvars;
        return varResult.scode;
    
    }
};



Так вот как раз это метод я и вызываю из моего CallBack

Код

void __stdcall CMyClass::Callback (long var){
CoInitialize(NULL);
    VARIANTARG var;
    var.lVal=var;
    var.vt=VT_I4;
    p->Fire_CallBackDone(var);
CoUninitialize();
}


1. Как я уже говорил, сначала была проблема с назначением calback в методе SetCallback. Извратился и сделал с помощью typedef и приведения адреса ф-ции к типу long.
2. Потом встала проблема с вызовом из callback метода отвечающего за событие. Это я сделал через статический указатель на сам класс CMyClass. Считаю это не совсем корректным для многопользовательского режима, но там посмотрим. Пока оставил так.
3. Далее спотыкания пошли на Fire_CallbackDone. Не хотелась выполняться строка 
Код

HRESULT hres=pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);

Если посмотреть на ф-цию Fire_CallbackDone, то виден цикл, который пробегает по всем членам  динамического массива IUnknown интерфейсов m_vec. Там хранятся точки в которых от нас ждут вызова события, например в VBA 

Код

Dim withevents MyObj as test.MyClass

pub MyObj_CallbackDone()

end pub



По вашему совету, добавил CMyClass укfзатель на IStream.

в методе SetCallBack добавил маршпалинг диспатч интерфейса, который используется в Fire_CallbackDone

Код

HRESULT hres;
CComPtr<IUnknown> sp = m_vec.GetAt(0);
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
hres=CoMarshalInterThreadInterfaceInStream(IID_IDispatch,pDispatch,&str);



в сам CallBack добавил

Код

HRESULT hres;
hres=CoGetInterfaceAndReleaseStream(obj->str,IID_IDispatch,(void**)&(ptr));


где ptr - глобальный указатель на IDispatch.  И уже через него вызываю Invoke

Код

HRESULT Fire_CallbackDone(VARIANT var)
    {
        CComVariant varResult;
        T* pT = static_cast<T*>(this);
        int nConnectionIndex;
        CComVariant* pvars = new CComVariant[1];
        int nConnections = m_vec.GetSize();
        
        for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
        {
            pT->Lock();
            CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
            pT->Unlock();
            IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
            if (pDispatch != NULL)
            {
                VariantClear(&varResult);
                pvars[0] = var;
                DISPPARAMS disp = { pvars, NULL, 1, 0 };
                HRESULT hres=ptr->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
            }
        }
        delete[] pvars;
        return varResult.scode;
    
    }


все проходит, hres=0

НО! ивент в бейсике все равно не отрабатывает. В общем я грешу на хранение указателя класса в статической переменной. Иначе в статическом методе до него не добраться. А как ваше мнение?
PM MAIL ICQ   Вверх
xvr
Дата 2.7.2009, 19:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Сработает только один раз. Функция CoGetInterfaceAndReleaseStream извлекает из стрима объект и УНИЧТОЖАЕТ стрим. Т.е. при ее втором вызове (а он будет, т.к. она вставленна в CallBack и значит будет вызываться при каждом его срабатывании) ей уже будет неоткуда извлекать объект.

При таком раскладе вариант jonie с GIT выглядит предпочтительней

PM MAIL   Вверх
ivs4
Дата 4.7.2009, 21:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Попробовал маршилинг через GIT - то же самое. Вот эта строка в Fire_CallBackDone

Код

HRESULT hres=pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);


выполняется без ошибок и hres=0, НО ивент из VBA не срабатывает.
Подумал про цикл сообщений для доступа к маршалируемому интерфейсу, но где в ATL проекте его прицепить не пойму.
PM MAIL ICQ   Вверх
xvr
Дата 5.7.2009, 00:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цитата(ivs4 @ 4.7.2009,  21:36)
Подумал про цикл сообщений для доступа к маршалируемому интерфейсу, 

Без него работать не будет.
Цитата

но где в ATL проекте его прицепить не пойму.

Прицеплять надо там, где VBA создает твой объект.
Хотя он там и так есть. Надо обеспечить, что бы он работал, т.е. не мешать ему своими объектами (например создать для их жизнедеятельности отдельную нить)

PM MAIL   Вверх
ivs4
Дата 5.7.2009, 14:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Цитата

Надо обеспечить, что бы он работал, т.е. не мешать ему своими объектами (например создать для их жизнедеятельности отдельную нить)


Уважаемый, xvr,
Можно об этом чуть по-подробнее. 
PM MAIL ICQ   Вверх
xvr
Дата 5.7.2009, 15:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Поподробнее можно.
Это означает, что если методы вашего класса вызываются из VBA (да и не только) вы не имеете права запускать внутри них длительную обработку. Вся работа подсистемы COM по доставке сообщений и вызовов будет заморожена, пока вы не вернетесь из вызванного метода обратно в VBA
Так же это означает, что если вы создаете свои нити (или кто то создает их от вашего имени - с точки зрения VBA), то в этих нитях должен работать цикл обработки сообщений, если вы собираетесь пользоваться в них инфраструктурой COM

PM MAIL   Вверх
ivs4
Дата 5.7.2009, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Т.е. в потоке сторонней библиотеки к которой принадлежит CallBack я должен сделать цикл? Но этот поток мне доступен только когда библиотека библиотека активирует CallBack. Т.е. в этом CallBack я должен сделать цикл?

А он должен быть до или после вызова Fire_CallBackDone?

Добавлено через 8 минут и 59 секунд
И еще вопрос. Вечный цикл не поставишь, тогда на какое сообщение надо выходить?
PM MAIL ICQ   Вверх
xvr
Дата 5.7.2009, 22:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цикл должен крутится со стороны VBS, там где клиенты подсоединяются к вашему CProxy_IMyClassEvents
(кстати, у вас маршалинг CProxy_IMyClassEvents расчитан только на 1 подсоединение) Кроме того, event может вообще не сработать, т.к. вы маршалите к себе чужой интерфейс, а он может быть на это не рассчитан
Я бы вообще отказался от прямого вызова Fire_CallbackDone из CMyClass::Callback, а сделал бы вызов через передачу Виндового оконного сообщения

PM MAIL   Вверх
ivs4
Дата 6.7.2009, 08:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Спасибо за ответы, xvr.
Вы писали
Цитата

Я бы вообще отказался от прямого вызова Fire_CallbackDone из CMyClass::Callback, а сделал бы вызов через передачу Виндового оконного сообщения


Попробую расписать ваше предложение. Вы говорите о передаче оконного сообщения. Хорошо, я регистрирую новое сообщение. Затем в конструкторе класса CProxy_IMyClassEvents запускаю некий новый поток с циклом приема оконных сообщений и в случае получения им моего сообщения вызываю Fire_CallbackDone. В самом Fire_CallbackDone производить демаршалинг интерфейсов и вызывать через них ивенты в VBA.

1. Правильно ли я развернул Вашу мысль?
2. Какому HWND посылать мое сообщение о вызове CallBack?
PM MAIL ICQ   Вверх
ivs4
Дата 6.7.2009, 13:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Может быть сделать не ожидание сообщения, а ожидания виндового события с таймером, а между тиками проверки события обрабатывать сообщения?
PM MAIL ICQ   Вверх
xvr
Дата 6.7.2009, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Вам нужно в каждом thread'е, в котором вызывается Advise из вашего IConnectionPointContainer'а создавать невидимое окно с оконной функцией, которая в ответ на некоторое сообщение (например WM_USER) будет вызывать FireEvent. Handle'ы на все созданные окна нужно сложить в массив и рассылать по ним этот самый WN_USER на каждый callback. При shutdown'е всей этой бодяги надо не забыть разрушить все окна.

PM MAIL   Вверх
ivs4
Дата 7.7.2009, 08:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Как я понимаю, Advise вызывается клиентской стороной для передачи своего интерфейса, через который в свою очередь вызывается ивент.
Вот, что я подумал. Что если в ф-ции SetCallBack производить следующтие действия.
Создать новый поток, передать в этот поток указатель на CMyClass, в потоке работает цикл сообщений, при срабатывании CallBack посылаем в порожденный поток сообщение и в этом потоке вызывается  Fire_CallbackDone. В данной схеме возможно еще понадобиться маршалинг. 
А вообще как можно отследить когда вызван advise, чтобы тогда создать этот поток не в SetCallBack а там.


Это сообщение отредактировал(а) ivs4 - 7.7.2009, 09:16
PM MAIL ICQ   Вверх
xvr
Дата 7.7.2009, 09:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Вызывается Advise клиентской стороной но из вашего объекта (это метод интерфейса IConnectionPointContainer, который имплементирован в вашем классе)
Создание отдельного потока в CallBack у себя не поможет - при этом придется маршалить к себе в поток VBA'ный объект - приемник событий, а именно он после именно такого маршалинга и не работал  smile 

И вообще, все методы всех интерфейсов вашего объекта будут вызываться в одном потоке (это обеспечивает COM, если вы конечно не поставили у вашего класса Neutral Threaded модель)
Из этого же потока должны вызываться все event'ы (FireEvent). В этом же потоке VBS будет САМ крутить цикл обработки сообщений


Это сообщение отредактировал(а) xvr - 7.7.2009, 09:24
PM MAIL   Вверх
ivs4
Дата 7.7.2009, 09:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Тогда не понятно куда ставить цикл сообщений, если его надо ставить в поток где вызываются все методы интерфейсов. В методы его не поставишь. Где вызывается Advise, честно говоря, я не вижу.

Добавлено через 6 минут и 22 секунды
Или же все тщетно - не вызвать из Callback событие?
PM MAIL ICQ   Вверх
xvr
Дата 7.7.2009, 12:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цитата(ivs4 @ 7.7.2009,  09:37)
Тогда не понятно куда ставить цикл сообщений, если его надо ставить в поток где вызываются все методы интерфейсов.

Никуда его не надо ставить - он уже есть в VBA
Цитата

Где вызывается Advise, честно говоря, я не вижу.
В Advise надо ставить создание окна (при условии, что на текущем thread'е оно еще не создавалась) Сам Advise вызывается из VBA, так что его вызова вы не увидите. Создание окна нужно вставить в САМ Advise, а не в место его вызова. 
Как то так:

Код

class YourCOMClass :
    public IDispatchImpl<...>,
    public CComObjectRoot,
    public CProxyYourCOM_Event< YourCOMClass >,
    public IConnectionPointContainerImpl<YourCOMClass>
{
...

  STDMETHOD(Advise) (IUnknown* pUnkSink,    DWORD* pdwCookie)
   {
     // Create WINDOWS here (if need to)
     return CProxyYourCOM_Event< YourCOMClass >::Advise(pUnkSink,pdwCookie);
   }

}



Цитата

Добавлено @ 09:43
Или же все тщетно - не вызвать из Callback событие?
Надежда умирает последней  smile 
PM MAIL   Вверх
ivs4
Дата 10.7.2009, 17:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Попробовал вызов события через передачу сообщения в ф-цию окна. Теперь работает все без маршалинга и hres=0, но все равно, к сожалени ИВЕНТ не вызывется. Может что не то сделал?

Код


UINT WM_EVNT=RegisterWindowMessage("MYEVENTMESSAGE");

class ATL_NO_VTABLE CMyClass : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CMyClass, &CLSID_MyClass>,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl<CMyClass>,
    public IDispatchImpl<IMyClass, &IID_IMyClass, &LIBID_TRANSACLib>,
    public CProxy_IMyClassEvents< CMyClass >
{
public:
    CMyClass()
    {
    }

    
    STDMETHOD(Advise)(IUnknown* pUnkSink, DWORD* pdwCookie){
        WNDCLASSEX  wndc;
        wndc.cbSize=sizeof(WNDCLASSEX);
        wndc.style=CS_HREDRAW|CS_VREDRAW;
        wndc.lpfnWndProc=(WNDPROC)WindowProc;
        wndc.cbWndExtra=0;
        wndc.cbClsExtra=0;
        wndc.hInstance=hInst;
        wndc.hIcon=NULL;
        wndc.hCursor=NULL;
        wndc.hbrBackground=NULL;
        wndc.lpszMenuName=NULL;
        wndc.lpszClassName="TEST";
        wndc.hIconSm=NULL;
        if(RegisterClassEx(&wndc)==0)
            return CONNECT_E_CANNOTCONNECT;
        hWnd=CreateWindow("TEST",NULL,WS_OVERLAPPED,0,0,100,100,0,0,hInst,NULL);
        DWORD dwErr=GetLastError();
        if (hWnd==NULL){
            return CONNECT_E_CANNOTCONNECT;
        }
        //ShowWindow(hWnd, SW_SHOW);
                //UpdateWindow(hWnd);
        return CProxy_IMyClassEvents< CMyClass >::Advise(pUnkSink,pdwCookie);
    }

    static LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg, WPARAM wParam,LPARAM lParam){

        if (uMsg==WM_EVNT){
            CMyClass *ptr;
            ptr=(CMyClass*)wParam;
            VARIANTARG var;
            var.lVal=777;
            var.vt=VT_I4;
            obj->Fire_CallBackDone(var);
            return 0;
        }
        else{
        switch( uMsg ){
                case WM_DESTROY:
                PostQuitMessage( 0 );
                return 0;
        }
        }

        return CallWindowProc( (WNDPROC)DefWindowProc, hWnd, uMsg, wParam, lParam );
    }


    static CMyClass *obj;

static void __stdcall SASCallback (long nTransactionResult, long nTransactionExtendedErrorCode, long nTransactionReplyCode, DWORD dwTransId, double dOrderNum, LPCSTR lpcstrTransactionReplyMessage);


Код

void __stdcall CMyClass::Callback (long var){
    LRESULT lres=PostMessage(hWnd,WM_EVNT,0,0);    
}

PM MAIL ICQ   Вверх
xvr
Дата 10.7.2009, 22:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Вроде все правильно (не считая того, что класс окна нужно регистрировать только 1 раз).
А obj->Fire_CallBackDone вызывается?
Может какие то несостыковки в Appartament моделях объекта и приложения?


PM MAIL   Вверх
ivs4
Дата 11.7.2009, 23:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Попробовал еще вот этот вариант:
http://support.microsoft.com/kb/196026/
все равно не вызывается событие.

В Fire_CallBackDone

Код

HRESULT hres=pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);

возвращает 0, но без события.

Может что-то не то с этой библиотекой откуда вызывается CallBack? Хотя не должно. Сообщение доходит до обработчика и файр вызывается.
PM MAIL ICQ   Вверх
ivs4
Дата 12.7.2009, 17:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Удалось заставить заработать событие в VBA. Для этого в VBA в то место где ожидается срабатывание события добавил следующую ф-ция засыпания на 1 секунду.

Код


SleepVB(1)
...

Sub SleepVB(Seconds)
  Dim Start
  Start = Timer 
  Do While Timer < Start + Seconds
    DoEvents
  Loop
End Sub


Скорее всего сообщение ассоциированное с Invoke не обрабатвается. Конечно нет желания ставить такую обработку в клиентском коде. Как бы это обойти?
PM MAIL ICQ   Вверх
xvr
Дата 12.7.2009, 19:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



А как VBA'ная прога ждет события? Если она в это время что то считает, то событие не будет принято - события принимаются только в Idle состоянии. 
Можно заставить их принимать и не в Idle, но тогда надо явно крутить цикл обработки событий, что SleepVB и делает (в DoEvents)

PM MAIL   Вверх
ivs4
Дата 13.7.2009, 21:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



А другого варианта как цикл DoEvents в VBA нет?
Я знаю, что майкрософтовский  winsock сервер работает не так. Там событие прихода вызывается асинхронно.

Это сообщение отредактировал(а) ivs4 - 13.7.2009, 21:51
PM MAIL ICQ   Вверх
xvr
Дата 14.7.2009, 09:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цитата(ivs4 @ 13.7.2009,  21:47)
А другого варианта как цикл DoEvents в VBA нет?

Есть. Написать нормальный обработчик в VBA. Точнее - обеспечить что бы VBA приложение работало так, как рассчитана модель событий VBA. Все события в VBA сериализуются, т.е. пока не закончится обработка одного, обработка другого не начнется (если не звать принудительно DoEvents)
Цитата

Я знаю, что майкрософтовский  winsock сервер работает не так. Там событие прихода вызывается асинхронно.
winsock (=Win32 API) != VBA. Но и там события приходят либо в функции ожидания (WaitFor*Object) либо через сообщения к окнам, что опять же требует наличия работающего цикла обработки сообщений  smile 
PM MAIL   Вверх
ivs4
Дата 16.7.2009, 07:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 19.7.2004

Репутация: нет
Всего: нет



Спасибо за ответы. Полагаю тема исчерпана.
PM MAIL ICQ   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: COM/DCOM/ActiveX/ATL/CORBA | Следующая тема »


 




[ Время генерации скрипта: 0.1358 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.