Меню сайта
Наш опрос
Оцените мой сайт
Всего ответов: 6
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Главная » 2014 » Июль » 22 » Работа с COM и ActiveX в Visual C++
15:23
Работа с COM и ActiveX в Visual C++
Сразу внесу некоторую поправку к обозначенной в заголовке теме. В этой статье речь пойдёт не просто о COM и ActiveX, а о COM и ActiveX библиотеках, содержащих библиотеки типов (type library).Поводом для написания этой статьи стали мои собственные изыскания в этой теме. На просторах Интернета тут и там разбросана информация о работе с COM и ActiveX из Visual C++ (отмечу, что речь идёт не о .NET, а обычном C++), но хорошего, структурированного материала мне найти не удалось. В этой статье я разберу два примера: в первом я расскажу о работе с простыми COM библиотеками; во втором будет показана работа с объектом ActiveX. Я не буду рассказывать, что такое COM, ActiveX и Type libraries, об этом Вы сможете прочитать, например, здесь.Директива препроцессора #importКомпилятор Microsoft Visual C++ определяет директиву препроцессора #import. Основное её предназначение – загружать информацию из библиотеки типов и представлять её в виде кода C++. В простейшем случае директива #import используется так#import "libname.tlb"После компиляции в каталоге сборки программы появляются два файла: libname.tlh и libname.tli. Первый из них является заголовочным и автоматически подключается к программе, а второй содержит код реализации и компилируется вместе с проектом. У директивы #import есть множество дополнительных опций, о которых Вы сможете прочитать тут или в справочной системе к Visual Studio.Где взять библиотеку типовХотя директива #import и позволяет подключать сами COM серверы, я советую подключать именно библиотеки типов, это бинарные файлы, обычно имеющие расширение TLB. Это позволит добавить такой файл в Ваш проект и не заботиться о расположении библиотеки при сборке проекта на другом компьютере.Но вот тут, на первый взгляд, может возникнуть проблема. Где взять библиотеку типов, если большинство серверов распространяется одним файлом (чаще всего это либо *.dll, либо *.ocx)? Ответ прост; библиотека типов является ни чем иным как ресурсом это сервера. А если это ресурс, то получить его можно, к примеру, с помощью Resource Hacker. На скриншоте ниже показано, как это сделать.Простой COM серверНачнём работу с простого примера, в котором не нужно отображать визуальные компоненты. Для демонстрации я взял библиотеку msxml6.dll. Стоит отметить, что в Windows API существует привязка к этой библиотеке. Ниже приведён листинг программы, которая создаёт файл "test.xml" с одним лишь тегом "Example".#include <iostream>#import "msxml6.tlb"class CComInitializer{public: CComInitializer() { ::CoInitialize(NULL); } ~CComInitializer() { ::CoUninitialize(); }} gComInitializer;int main(){ MSXML2::IXMLDOMDocument3Ptr xml_document; HRESULT hr = ::CoCreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER, __uuidof(MSXML2::IXMLDOMDocument3), reinterpret_cast<void **>(&xml_document)); if(SUCCEEDED(hr)) { MSXML2::IXMLDOMElementPtr xml_elem = xml_document->createElement(L"Example"); xml_document->appendChild(xml_elem); xml_document->save(L"test.xml"); } else std::cerr << "Error creating instance\n"; return 0;}Назначение класса CComInitializer только в том, чтобы инициализировать модель COM в приложении. Для этого создаётся один глобальный объект этого класса. При разрушении объекта модель COM деинициализируется.Директива #import помещает весь сгенерированный код в пространство имён, соответствующее имени библиотеки, в нашем случае – это MSXML2. В сгенерированный код, по умолчанию, добавляются объявления "умных" указателей для каждого интерфейса. Имена их соответствуют именам интерфейсов с постфиксом Ptr. Объявив такой объект, мы можем передать указатель на него в функцию CoCreateInstance.Каждый интерфейс и класс объявляется в файлах *.tlh специальным образом, напримерstruct __declspec(uuid("2933bf96-7b36-11d2-b20e-00c04f983e60"))IXMLDOMDocument3 : IXMLDOMDocument2Это позволяет получить их GUID’ы с помощью оператора (специфичного для Visual C++) __uuidof. Для того, чтобы быть точно уверенными, что Вы получаете GUID именно того класса, который Вам нужен, следует обратиться к текстам библиотеки типов. Чтобы получить текст библиотеки из двоичного файла можно воспользоваться утилитой OleView или просмоторщиком из Total Commander. В IDL коде библиотеки мы должны найти coclass, который реализует нужный интерфейс.[ uuid(88D96A05-F192-11D4-A65F-0040963251E5), helpstring("W3C-DOM XML Document 6.0 (Apartment)")]coclass DOMDocument60 { [default] interface IXMLDOMDocument3; [default, source] dispinterface XMLDOMDocumentEvents;};Все последующие действия специфичны для библиотеки MSXML и приведены только для примера.Обратите внимание, я не вызываю метод Release из xml_document и xml_elem, это за меня делают "умные" указатели.Элемент управления ActiveXДля того, чтобы разместить элемент ActiveX в окне требуется выполнить очень много скучной рутинной работы, поэтому мы воспользуемся готовым решением, предоставляемым библиотекой MFC. Те, кому интересно узнать всю подноготную, могут пройти по этой и этой ссылкам.Для демонстрации встраивания элемента управления, я воспользовался библиотекой MSFLXGRD.OCX, которая входит в стандартную поставку Visual Basic 6.0. То, что у меня получилось, видно на следующем скриншоте.По традиции, сразу приведу листинг программы, а затем прокомментирую.#define WINVER 0x0501#include <afxwin.h>#include <afxole.h>#import "msflexgrid.tlb"template<typename InterfaceT>class CComWindow : public CWnd{public: CComWindow(const IID & iid, const CLSID & class_id) : mp_ax(NULL), m_class_id(class_id), m_iid(iid) { } virtual ~CComWindow() WS_VISIBLE, CRect(CPoint(10, 10), CSize(520, 450)), this, m_grid_id); if(created) const long cols = 8; const long rows = 25; mp_flex_grid->mp_ax->Cols = cols; mp_flex_grid->mp_ax->Rows = rows; mp_flex_grid->mp_ax->ColWidth[0] = 350; mp_flex_grid->mp_ax->Col = 0; wchar_t text[8]; for(long i = 1; i < rows; ++i) _itow(i, text, 10); mp_flex_grid->mp_ax->Row = i; mp_flex_grid->mp_ax->Text = text; mp_flex_grid->mp_ax->Row = 0; mp_flex_grid->mp_ax->Col = 1; mp_flex_grid->mp_ax->Text = L"Пн"; mp_flex_grid->mp_ax->Col = 2; mp_flex_grid->mp_ax->Text = L"Вт"; mp_flex_grid->mp_ax->Col = 3; mp_flex_grid->mp_ax->Text = L"Ср"; mp_flex_grid->mp_ax->Col = 4; mp_flex_grid->mp_ax->Text = L"Чт"; mp_flex_grid->mp_ax->Col = 5; mp_flex_grid->mp_ax->Text = L"Пт"; mp_flex_grid->mp_ax->Col = 6; mp_flex_grid->mp_ax->Text = L"Сб"; mp_flex_grid->mp_ax->Col = 7; mp_flex_grid->mp_ax->Text = L"Вс";> } else result = 1; } return result;}HRESULT CMainWindow::OnFlexGridClick(){ static int i_text = 1; wchar_t text[16]; ::_itow(i_text++, text, 10); mp_flex_grid->mp_ax->Text = text; return S_OK;}class CApplication : public CWinApp{public: CApplication(); virtual ~CApplication(); virtual BOOL InitInstance();} gApplication; // class CApplicationCApplication::CApplication(){}CApplication::~CApplication(){ delete m_pMainWnd;}BOOL CApplication::InitInstance(){ BOOL result = CWinApp::InitInstance(); if(FALSE Flex Grid Example", WS_OVERLAPPEDWINDOW, 200, 100, 800, 600, GetDesktopWindow(), NULL); m_pMainWnd = main_window; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); } return result;}Теперь, нам требуется не просто получить указатель на интерфейс, но и связать его с окном, поэтому создаём класс CComWindow, сделав его шаблонным для универсальности. В качестве параметра шаблона требуется указывать тип интерфейса COM объекта.Операции Create замещают операции класса CWnd и создают контрол из переданных в конструктор GUID’ов интерфейса и кокласса. Член mp_ax инициализируется указателем из элемента управления и является мостом между клиентом и интерфейсом COM.Далее окно ActiveX создаётся как и все другие окна, с той лишь разницей, что в конструктор мы передаём GUID’ы класса и интерфейса.Следует заметить, что директива #import генерирует специальные поля классов – свойства. Такие поля также являются расширением Visual C++ и выглядят следующим Microsoft FlexGrid Control 6.0"), helpcontext(0x00059621), licensed, control]coclass MSFlexGrid { [default] interface IMSFlexGrid; [default, source] dispinterface DMSFlexGridEvents;};В нашем случае – это DMSFlexGridEvents. В *.tlh файле Вы найдёте объявление этого класса со всеми доступными событиями, а в файле *.tli – все реализации.Чтобы добавить обработку события в карту EVENTSINK_MAP следует добавить в класс окна функцию с идентичной сигнатурой (за исключением имени). После этого в карту событий помещается элемент с именем ON_EVENT. Первым параметром этого макроса является класс, принимающей событие, второй - идентификатор ActiveX объекта, третий - это номер события, его можно подсмотреть в реализации методов-событий в файле *.tli. Стандартные события имеют макроимена. Следующим параметром мы передаём метод, который будет вызван в ответ на событие. Последний параметр - это набор аргументов, которым соответствуют макроопределения. Набор этих параметров пишется без разделения запятой. Все возможные значения объявлены в файле afxdisp.h.Для примера, я определил обработчик события Click. Теперь, при щелчке мышью, в соответствующую ячейку будут устанавливаться числовые значения, каждый раз на единицу больше предыдущего. Следует отметить, что для работы с COM в приложении MFC необходимо вызвать функцию AfxOleInit, а для работы с компонентами ActiveX - функцию AfxEnableControlContainer. Все они находятся в файле afxdisp.h, но подключать их следует из afxole.h.
Просмотров: 333 | Добавил: admin | Рейтинг: 0.0/0
Всего комментариев: 0
avatar
Форма входа
Календарь
«  Июль 2014  »
ПнВтСрЧтПтСбВс
 123456
78910111213
14151617181920
21222324252627
28293031
Архив записей