Скачиваний:
100
Добавлен:
01.05.2014
Размер:
1.56 Mб
Скачать

Реализация методов объекта автоматизации

Итак, описаны свойства и методы нашего объекта и теперь нужно приступить к их реализации. Для этой цели следует нажать кнопку 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.