Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Скрестить Callback ф-цию и Event в ATL  (Прочитано 9063 раз)
0 Пользователей и 1 Гость смотрят эту тему.
ivs4
Гость
« : 01-07-2009 08:23 » 

Добрый день. Столкнулся  с нетривиальной задачей. Делаю обертку над сторонней библиотекой в виде 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;
Fire_CallBackDone(var);
CoUninitialize();
}

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

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

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

Записан
sss
Специалист

ru
Offline Offline

« Ответ #1 : 02-07-2009 02:20 » 

ivs4, не понятно всё это.  Явно вызов CALLBACK происходит в другом потоке (отделении). Для того что бы можно было вызывать методы COM класса, указателя на интерфейс явно недостаточно. Необходим маршаллинг вызовов. Из приведенных тобой примеров не понятно, откуда взялся pDispatch? И как реализована обертка то же непонятно. Вот например функция setcallbackfunction(addr); чего делает? Как создается "сторонний COM объект"? Функцией CoCreateInstance ? в общем тема не простая и так просто советами помочь сложно.
Записан

while (8==8)
ivs4
Гость
« Ответ #2 : 02-07-2009 15:11 » 

Давайте еще раз обрисуем ситуацию. Вот описание 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. Не хотелась выполняться строка
Код: (C++)
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
Записан
sss
Специалист

ru
Offline Offline

« Ответ #3 : 03-07-2009 02:31 » 

ivs4, так не пойдет.... Я не вижу в твоих кодах кто и как вызывает функцию static void __stdcall Callback (long var); ? Где setcallbackfunction(addr); ? Одни реализации точек подключения... Еще раз - откуда берется вызов CALLBACK функции? Я так понимаю, у тебя есть некая библиотека периодически вызывающая этот метод из своих рабочих потоков. При этом ты хочешь добраться до библиотеки из, например, VBA for Excel. Для того, что бы у тебя заработало, тебе надо проводить маршаллинг в поток, создавший экземпляр объекта  CMyClass.

Вообще я могу заблуждаться конечно... Может  я  вовсе не понял, что происходит в твоем коде.

P.S.: А вот это место очень напрягает
Код:
 IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
« Последнее редактирование: 03-07-2009 02:37 от sss » Записан

while (8==8)
ivs4
Гость
« Ответ #4 : 04-07-2009 18:35 » 

Да, вы правы. Есть библиотека у которой есть CallBack ф-ция на выполнение некоторых действий. Мне надо этот CallBack перевести в COM event.

Установка CallBack выполняется через метод CMyClass вот таким образом

Код:

typedef void (__stdcall *Func2)(long);

....
STDMETHODIMP CMyClass::SetCallback()
{
// TODO: Add your implementation code here
.....
Func2 pFunc2;
obj=this;
pFunc2=CCMyClass::Callback;
HRESULT hres;
ires=SET_CALLBACK(pFunc2);
}

Пробовал маршалинг, все вроде выполняется без ошибок но ивент в VBA не вызывается.


P.S.
Код:
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);

Эта строка создана ATL визардом.



Записан
sss
Специалист

ru
Offline Offline

« Ответ #5 : 06-07-2009 01:51 » 

ivs4, а ты не пробовал вызывать события COM из потока, создающего объект CMyClass ? Ну например, вызывать из VBA какой либо метод этого объекта, а из этого метода вызвать событие, которое по идее должно уйти в клиента? Вначале проверь - вообще будет работать? Затем можно и усложнить, применив некую сериализацию...

Кстати, VBA 6.0 умеет работать только с STA COM компонентами. Дальше что там по версиям не знаю... Проверь для начала что это из-за потоков.
Записан

while (8==8)
ivs4
Гость
« Ответ #6 : 08-07-2009 08:43 » 

Спасибо за ответы. Уважаемый, sss, то что вы описали у меня работает.
Записан
sss
Специалист

ru
Offline Offline

« Ответ #7 : 08-07-2009 09:13 » 

ivs4, если работает, создай очередь, наполняющуюся в CALLBACK потоках, а в потоке, создающем твой объект ожидай появления. Если этот поток есть основной поток VBA (невозможно остановить), попробуй вызывать для него APC. Или разбирайся вокруг функции CoCreateFreeThreadedMarshaler.

P.S.: Настораживает что-то...

...
 Уважаемый, sss,
...

Эх. Надеюсь искренне
Записан

while (8==8)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines