Угораздило меня полезть в малознакомую мне тему — работа с COM на уровне C++/ATL/MFC. Потому как ни привычный Delphi, ни семейство .NET не пожелали работать с этим актив-иксом Delphi падает при вызове метода, нормально читая проперти, в .NET не приходят event-ы. Примеры есть на VB6 и на нем (точнее на VBA в excel) они работают.
Сделал тестовую программу на MFC с ConnectionPoints по статье
. Работает, но не могу добраться до аргументов обработчикой событий Вопрос в том, как правильно описать функцию-обработчик и использовать в ней переданный параметр... Три дня поиска по сайту и msdn ситуацию не прояснили...
в VBA это выглядело так:
Private Sub on_Iam(ByVal service As BACNETLib.IIam)
'... использую ...Debug.Print service.network
BEGIN_DISPATCH_MAP(CBacnetDlg, CDialog)
DISP_FUNCTION(CBacnetDlg, "Iam", OnBacnetIam, VT_EMPTY, VTS_DISPATCH)
END_DISPATCH_MAP()
BEGIN_INTERFACE_MAP(CBacnetDlg, CDialog)
INTERFACE_PART(CBacnetDlg, DIID__IApplicationLayerEvents, Dispatch)
END_INTERFACE_MAP()
void CBacnetDlg::OnBacnetIam(IIam *srv)
{
long x = srv->network;
// тут падаем:( ... network - "long IIam::network"
В обработчик вроде бы нормально все передается, в Watches видно IDispatch и IUnknown "содержимое" Iam. При обращении к проперти — как правило "Неверное значение ESP — возможно неправильное соглашение о вызове" или First-chance exception и выход из метода/отладчика.
VTS_DISPATCH означает передачу именно IDispatch* указателя. Поэтому перед использованием в качестве IIam нужно сделать QueryInterface. Поэтому лучше привести к нормальному (ожидаемому) виду void CBacnetDlg::OnBacnetIam(IDispatch *srv). Тогда и ошибка обращения будет очевидной.
К тому же, DIID означает dispinterface, параметры которого передаются в VARIANTах, в которых можно передать или IUnknown*, или IDispatch*. Никаких других типов указателя передать не получится. Перевод в нужный интерфейс осуществляется обработчиком Invoke при распаковке по библиотеке типов. В твоем же случае распаковка идет по указанному тобой параметру, т.е. VTS_DISPATCH. Соответственно, обработчик понятия не имеет о интерфейсе IIam.
Здравствуйте, Vi2, Вы писали:
Vi2>VTS_DISPATCH означает передачу именно IDispatch* указателя. Поэтому перед использованием в качестве IIam нужно сделать QueryInterface. Поэтому лучше привести к нормальному (ожидаемому) виду void CBacnetDlg::OnBacnetIam(IDispatch *srv). Тогда и ошибка обращения будет очевидной.
Здравствуйте, Александр Воронин, Вы писали:
АВ>В догонку — что делать с указателями — правильно ли я зоову Release() или оно само сделается для smart_ptr..?
Правильно, хотя IMHO это и лишнее: я предпочитаю использовать нормальный ход вещей, когда деструктор освободит используемый указатель. OTOH, Release() явно показывает освобождение. Так что это дело вкуса.
Vi2>Правильно, хотя IMHO это и лишнее: я предпочитаю использовать нормальный ход вещей, когда деструктор освободит используемый указатель. OTOH, Release() явно показывает освобождение. Так что это дело вкуса.
Пока пробовал — выяснил — "не правильно": получается first-chance exception по логам VS, судя по всему в недрах auto_ptr. Но есс-но ничего не падает Так что нормальный ход вещей — он как-то приятнее
Здравствуйте, Александр Воронин, Вы писали:
АВ>Пока пробовал — выяснил — "не правильно": получается first-chance exception по логам VS, судя по всему в недрах auto_ptr. Но есс-но ничего не падает Так что нормальный ход вещей — он как-то приятнее
Правильно. Ибо не надо при использовании Smart Pointers (IXXXPtr) делать явный вызов Release — указатель сам в своем деструкторе его вызовет.
Здравствуйте, Александр Воронин, Вы писали:
АВ>Пока пробовал — выяснил — "не правильно": получается first-chance exception по логам VS, судя по всему в недрах auto_ptr. Но есс-но ничего не падает Так что нормальный ход вещей — он как-то приятнее
Разница в поведении, когда переменная serv содержит NULL после его заполнения. Release() генерит прерывание E_POINTER. А ~_com_ptr_t нет. Так что, действительно, лучше его не использовать. НО! Нужно исходный код модифицировать: