- •Проблемы, решаемые с помощью технологии com
- •Основные понятия
- •Именование com-объектов
- •Понятие интерфейса
- •Основы компиляции
- •Простейший пример использования
- •Интерфейс iUnknown
- •Еще один пример
- •Com-сервер
- •Создание "настоящего" com-сервера.
- •Фабрика классов
- •Использование фабрики классов
- •ITypeLib, iTypeInfo, библиотеки типов и информация о методах сервера. Язык idl.
- •IDispatch и вызов методов "по имени"
- •IMarshall, маршалинг и взаимодействие клиента с внутренними, локальными и удаленными серверами
- •Технология ole Внедрение и связывание объектов - ole
- •Использование ole-документов в приложениях
- •Использование свойств OleContainer
- •Создание элементов управления ActiveX Понятие ActiveX.
- •Создание элементов управления ActiveX на основе vcl-компонентов
- •Создание страниц свойств
- •Создание активных форм
- •Создание локальных серверов и контроллеров автоматизации Понятие автоматизации.
- •Создание приложения, подлежащего автоматизации
- •Превращение приложения в сервер автоматизации
- •Библиотека типов
- •Реализация методов объекта автоматизации
- •Создание контроллера
- •Раннее и позднее связывание
- •Создание контроллеров для произвольных серверов автоматизации
- •Коллекции объектов внутри серверов автоматизации
- •Использование информации из библиотек типов
- •Создание коллекций объектов
- •Экспонируемые свойства и методы
- •Процессы в Windows Процессы
- •Планирование потоков
- •Приоритеты
- •Привязка к процессорам
- •Создание процессов
- •Завершение процессов
- •Создание потоков
- •Завершение потоков
- •Wait функции
- •Синхронизация потоков
- •Синхронизация процессов
- •Взаимодействие между процессами
- •Создание внутренних серверов и контроллеров автоматизации Внутренний сервер автоматизации и передача данных
- •Создание внутреннего сервера автоматизации
- •Манипуляция ресурсами в dll
- •Работа внутреннего сервера автоматизации в многопоточном режиме Понятие многопоточности. Синхронизация
- •Класс tThread
- •Пример многопоточного приложения
- •Применение com-объектов, входящих в состав Windows Создание ярлыков
- •Использование Internet Explorer в приложениях
Реализация методов объекта автоматизации
Итак, описаны свойства и методы нашего объекта и теперь нужно приступить к их реализации. Для этой цели следует нажать кнопку Refresh на инструментальной панели редактора библиотеки типов. В модуль реализации (сгенерированный ранее экспертом, использованным при создании сервера) будут добавлены заготовки методов. В эти заготовки следует вписать соответствующий код:
STDMETHODIMP TMyAutoImpl::AddLine(BSTR lpwString)
{
Form1->Memo1->Lines->Add (lpwString);
}
STDMETHODIMP TMyAutoImpl::get_Visible(VARIANT_BOOL* Value)
{
try
{
*Value = Form1->Visible;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto);
}
return S_OK;
};
STDMETHODIMP TMyAutoImpl::get_Width(long* Value)
{
try
{
*Value = Form1->Width;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto);
}
return S_OK;
};
STDMETHODIMP TMyAutoImpl::NewFile()
{
Form1->Memo1->Lines->Clear ();
}
STDMETHODIMP TMyAutoImpl::OpenFile(BSTR lpwFileName)
{
Form1->Memo1->Lines->LoadFromFile (lpwFileName);
}
STDMETHODIMP TMyAutoImpl::SaveFile(BSTR lpwFileName)
{
Form1->Memo1->Lines->SaveToFile (lpwFileName);
}
STDMETHODIMP TMyAutoImpl::set_Visible(VARIANT_BOOL Value)
{
try
{
Form1->Visible = Value;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto);
}
return S_OK;
};
STDMETHODIMP TMyAutoImpl::set_Width(long Value)
{
try
{
Form1->Width = Value;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto);
}
return S_OK;
};
Скомпилируем и запустим сервер на выполнение. При этом он зарегистрируется в реестре. В разделах реестра HKEY_CLASSES_ROOTиHKEY_LOCAL_MASHINE\SOFTWAREпоявятся несколько записей, связанных с данным сервером и его интерфейсами, в том числе и информация о местоположении сервера.
Если в дальнейшем отпадет необходимость в использовании созданного сервера автоматизации, рекомендуется запустить его с ключом /unregserver. В этом случае соответствующие записи будут удалены из реестра. Если же возникнет необходимость перенести сервер автоматизации в другой каталог, можно после этого просто запустить его снова, и при этом записи в реестре обновятся.
Итак, создано настольное приложение, являющееся сервером автоматизации. Теперь, основываясь на информации о методах класса его объекта автоматизации, содержащейся в библиотеке типов, можно создавать приложения, управляющие этим сервером, с помощью довольно широкого спектра средств разработки (включающего Delphi, C++Builder, Visual Basic, Visual C++ и др.).Итак, мы рассмотрели создание настольного приложения, являющегося сервером автоматизации. Теперь, основываясь на информации о методах класса его объекта автоматизации, содержащейся в библиотеке типов, создадим приложение, управляющее этим сервером. Такие приложения называются контроллерами автоматизации.
Создание контроллера
На главной форме будущего приложения-контроллера разместим 10 кнопок, а также компоненты TEdit,TCheckBox,TOpenDialog,TSaveDialog. Создадим обработчики событий, связанные с нажатием на кнопки (при этом следует подключить модуль ComObj.hpp):
void __fastcall TForm1::Button1Click(TObject *Sender)
{
serv = CreateOleObject ("Project1.MyAuto");
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
serv = Unassigned;
}
void __fastcall TForm1::Button3Click(TObject *Sender)
{
Edit1->Text = serv.OlePropertyGet ("Width");
}
void __fastcall TForm1::Button4Click(TObject *Sender)
{
serv.OlePropertySet ("Width", atoi (Edit1->Text.c_str ()));
}
void __fastcall TForm1::Button8Click(TObject *Sender)
{
CheckBox1->Checked = serv.OlePropertyGet ("Visible");
}
void __fastcall TForm1::Button9Click(TObject *Sender)
{
serv.OlePropertySet("Visible", CheckBox1->Checked);
}
void __fastcall TForm1::Button5Click(TObject *Sender)
{
if (OpenDialog1->Execute ())
{
serv.OleProcedure ("OpenFile",
WideString((OpenDialog1->FileName).c_str ()).c_bstr ());
}
}
void __fastcall TForm1::Button6Click(TObject *Sender)
{
if (SaveDialog1->Execute ())
{
serv.OleProcedure ("SaveFile",
WideString((SaveDialog1->FileName).c_str ()).c_bstr ());
}
}
void __fastcall TForm1::Button7Click(TObject *Sender)
{
serv.OleProcedure ("NewFile");
}
void __fastcall TForm1::Button10Click(TObject *Sender)
{
serv.OleProcedure ("AddLine",
WideString((Edit1->Text).c_str ()).c_bstr ());
}
Теперь настало время пояснить, что именно делает приведенный выше код.
Для управления сервером автоматизации мы создали переменную типа Variant(в C++Builder для этой цели имеется соответствующий класс) и вызвали функциюCreateOleObject, содержащуюся в модуле ComObj.hpp библиотеки VCL.
При выполнении функции CreateOleObjectпроизойдет следующее. Эта функция, вызвав несколько функций COM API, создаст экземпляр СОМ-объектаIDispatchи вернет его внутри вариантной переменной. Этот объект, в свою очередь, содержит интерфейс объекта (в данном случае нашего сервера автоматизации), методы которого мы хотим вызывать из приложения. Если исследовать реализацию функцииCreateOleObjectв исходном тексте модуля ComObj.hpp, можно обнаружить, что она, в свою очередь, вызывает функцию COM APICoCreateInstance, назначение которой - создать объект из исполняемого файла или DLL. Переменная типаVariantможет содержать разнообразные данные (строку, число и др., в том числе и интерфейс СОМ-объекта).
Отметим, что в отличие от Visual Basic или Delphi C++Builder не позволяет обращаться к методам и свойствам вариантных переменных, существование которых заранее неизвестно. Поэтому допустимый в Delphi код вида
if VarType(Serv) = varDispatch then
Serv.Width := StrToInt(Edit1.Text);
не имеет аналога в C++Builder. Дело в том, что при создании контроллеров с помощью Delphi в случае объектов типа Variant в отличие от объектов другого типа, например TForm, компилятор не проверяет, имеется ли в действительности такое свойство (в данном случаеWidth) у данного объекта. На этапе выполнения такого кода происходит вызов функций Win32 API, в результате работы которых меняется свойствоWidthобъекта, содержащегося не в адресном пространстве созданного контроллера, а в адресном пространстве сервера.
В С++Builder достичь такого же результата можно с помощью следующего кода:
if (VarType(Serv) == varDispatch)
{
Serv.OlePropertySet ("Width", StrToInt(Edit1->Text));
}
В этом случае на этапе выполнения производится вызов тех же самых функций Win32 API, что и в предыдущем случае. OlePropertySetпредставляет собой оболочку для метода вариантной переменнойExec ()(наряду сOlePropertyGet,OleProcedureиOleFunction, позволяющими получать значения свойств объектов автоматизации и выполнять их методы). Отметим, что в Delphi также можно использовать вызовыOlePropertySet,OlePropertyGet,OleProcedure,OleFunction.
После запуска контроллера при нажатии кнопки "Connect" запускается сервер. При нажатии кнопки "Disconnect" он выгружается. При нажатии кнопок "New File", Öpen File" и "Save File" происходит очистка окна редактирования, загрузка текста в окно редактирования сервера из файла, сохранение текста в файле. Кнопка "Get Visible" показывает и скрывает окно сервера в зависимости от наличия отметки возле надписи "Visible", при этом в невидимом состоянии сервер продолжает выполнять свои функции. Нажатие кнопки "Set Visible" приводит отметку возле надписи "Visible" в соответствие значению свойства "Visible" сервера. Нажатие кнопки "Get Width" приводит к тому, что в строке редактирования в верхней части окна контроллера отображается ширина окна сервера в пикселах. Если ввести в строку редактирования другое число и нажать кнопку "Set Width", ширина окна сервера станет равной введенному числу пикселов. Нажатие кнопки Ädd String" приводит к тому, что в редактируемый текст добавляется строка, находящаяся в этот момент в компоненте TEdit.