Dispid 1;
property I tern[Index: Integer]: OleVariant dispid 2; procedure Remove(Index: Integer); dispid 3; procedure Clear; dispid 4; function Add(Item: OleVariant): Integer; dispid 5; function _NewEnum: lUnknown; dispid -4; End; В отличие от обычного интерфейсного класса класс Автоматизации не может иметь родительского класса, и поэтому за словом dispinterface нельзя указать список родителей. Идентификаторы методов (свойств) должны быть уникальными в пределах объявления класса. Все возвращаемые функциями и свойствами результаты, а также все параметры обращения к методам должны иметь один из Следующих типов: Byte, Currency, Real, Double, Longint, Integer, Single, Smallint, AnsiString, WideString, TDateTime, Variant, OleVariant, WordBool или любой интерфейсный тип. За исключением директивы default, которую можно указать для свойства-массива, никакие другие директивы доступа в объявлении методов и свойств не допускаются. Для доступа к объектам Автоматизации используются переменные типа вариант (см. следующую лекцию). Инициация такой переменной осуществляется вызовом функции CreateOleObject, определенной в модуле comobj. Эта функция возвращает ссылку на интерфейс IDispatch, с помощью которой можно обращаться к методам и свойствам класса Автоматизации так, как если бы они были методами и свойствами варианта. Например, следующая программа вызывает текстовый процессор MS Word, вставляет в пустую страницу две строки и сохраняет полученный документ на диске: Uses ComObj; Var Word: Variant; Begin Word := CreateoieObject('Word.Basic'); Word.FileNew('Normal'); Word.Insert('Первая строка'#13); Word.Insert('Вторая строка'#13); Word.FileSaveAs('с:\temp\test.txf, 3); End; Параметром обращения к CreateoieObject является имя сервера Автоматизации, которое должно быть предварительно зарегистрировано в реестре Windows 32. Характерно, что методы сервера не известны на этапе компиляции программы, поэтому компилятор никак не контролирует правильность их вызовов. Названия методов не подчиняются правилам построения идентификаторов Delphi, и в них могут использоваться символы национальных алфавитов.
ОСНОВНЫЕ СВОЙСТВА ВАРИАНТА Вариант (в Delphi 1 он отсутствует) - это тип variant, разработанный специально для тех случаев, когда на этапе компиляции программист не может сказать, какого типа данные будут использоваться в выражении или как параметры вызова подпрограмм. Переменная-вариант занимает в памяти дополнительные 2 байта, в которые помещается информация о действительном типе переменной. Эта информация позволяет компилятору создать код, который будет осуществлять необходимое преобразование типов на этапе прогона программы. В переменную-вариант можно поместить:
Варианты могут участвовать в целочисленных, вещественных, логических и время-дата выражениях при условии корректности соответствующих преобразований. Например, если варианту v присвоена строка '1.0', то выражение 1+v будет правильным вещественным значением 2,0. Однако если v:= 'текст', выражение 1+v вызовет исключение EVariantError. В Delphi определены такие константы, указывающие тип помещенных в вариант данных: Таблица 10.1. Типы возможных значений варианта
Структура вариантного типа описывается следующим образом: TVarData = packed record VType: Word; Reservedly Reserved2, ReservedS: Word; case Integer of varSmallInt: (VSmallInt: Smallint); varlnteger: (VInteger: Integer); varSingle: (VSingle: Single); varDouble: (VDouble: Double); varCurrency: (VCurrency: Currency); varDate:(VDate: Double); varOleStr: (VOleStr: PWideChar); varDispatch: (VDispatch: Pointer); varError: (VError: WordBool); varString: (VString: Pointer); varArray: (VArray: PVarArray); varByRef: (VPointer: Pointer); End; Как нетрудно убедиться, любая переменная вариантного типа представляет собой 16-байтную запись, содержащую 8-байтную вариантную часть, которая хранит либо собственно данные, либо их адрес (т. е. указатель на динамически размещ
енные данные). В поле VType в момент создания варианта компилятор помещает, признак отсутствия данных varEmpty. В работающей программе значение этого поля меняется в соответствии с текущим типом данных, размещенных в вариантной части. Замечу, что программа не может получить прямого доступа к полям вариантной записи. Получить тип вариантных данных можно с помощью функции varType (см. ниже), а изменить тип - путем присваивания варианту нового значения. ПРЕОБРАЗОВАНИЕ ВАРИАНТОВ К ДАННЫМ ДРУГИХ ТИПОВ При участии вариантов в выражениях, а также при присваивании их значений переменным других типов тип размещенных в варианте данных преобразуется по следующим правилам: Таблица 10.2. Преобразование типов для вариантов Здесь К целым Отнесены varByte, varSmallInt, varlnteger/ varError; К вещественным — varSingle, varDouble/ varCurrency; К строковым -var String, varOleStr. ПОДПРОГРАММЫ ДЛЯ РАБОТЫ С ВАРИАНТАМИ Для работы с вариантами можно использовать такие подпрограммы: Таблица 10.3. Подпрограммы для работы с вариантами
ВАРИАНТНЫЕ МАССИВЫ Значением варианта может быть массив данных, такие варианты называются вариантными массивами. (Не путайте с обычным или динамическим массивом, элементами которого являются варианты!) Значениями элементов вариантного массива могут быть любые допустимые для варианта значения, кроме строк varstring. Значениями элементов вариантного массива могут быть и варианты, а это значит, что в таком массиве могут одновременно храниться данные разных типов (и в том числе строки). Например: Var V: Variant; Begin // Создаем одномерный вариантный массив с 5 элементами: V:= VarArrayCreate([0, 4], varVariant); // Наполняем его: V[0]:= 1; //Тип целый V[1]:= 1234.5678; //Тип вещественный V[2]:= 'Hello world'; //Строковый тип V[3]:= True; //Логический тип //Пятым элементом исходного массива сделаем еще один массив: V[4]:= VarArrayOf([l, 10, 100, 1000]); Caption:= V[2]; //Hello world IbOutput.Caption:= IntToStr(V[4][2]); //200 End; Все действия с вариантными массивами осуществляются с помощью следующих процедур и функций: Таблица 10.4. Подпрограммы для работы с вариантными массивами
ПОЛЬЗОВАТЕЛЬСКИЕ ВАРИАНТЫ Стандартный вариант может хранить только одно из значений, указанных в табл. 10.2. В версии Delphi 6 появились так называемые пользовательские варианты, которые фактически снимают ограничения на характер значений варианта. Чтобы познакомиться со свойствами новых вариантов, воспользуемся одним из них - вариантом, способным хранить комплексные числа, преобразовывать их в другие типы и осуществлять над ними нужные действия. Как мы увидим дальше, создание пользовательского варианта может быть весьма трудоемким делом - все зависит от сложности хранимых в нем данных. Мы воспользуемся вариантом, созданным разработчиками Delphi и включенным в модуль VarCmplx. Создайте такой обработчик bbRunClick: uses VarCmplx; // Эта ссылка обязательна! procedure TfmExample.bbRunClick(Sender: TObject); Var VI, V2: Variants- begin // Создаем два случайных комплексных числа: VI:= VarComplexCreate(Trunc(Random*1000)/100, Trunc(Random*1000)/100); V2:= VarComplexCreate(Trunc(Random*1000)/100, Trunc(Random*1000)/100); with mmOutput.Lines do Begin // Пустая строка-разделитель Add (' '); Add('1-e число: '# 9+V1); Add('2-е число: '#9+V2); Add('Сложение'#9+(V1+V2)); Add('Вычитание'#9+(V1-V2)); Add('Умножение'# 9+(VI*V2)); Add('Деление'#9#9+(V1/V2)) End End; Небольшой комментарий: сложная конструкция Trunc (Random*1000) /100 понадобилась только для того, чтобы реальные и мнимые части комплексных чисел содержали по три значащих цифры. Вид экрана работающей программы показан на рис. 10.1. Как видим, новый вариант легко справляется с поддержкой комплексных чисел: функция VarComplexCreate создает вариант, содержащий комплексное число, а дальнейшее поведение варианта -стандартное (он поддерживает математические операции и преобразование к строковому типу). Однако эта легкость обманчива: исходный текст модуля VarCmplx, который, собственно, и придал варианту дополнительные свойства (по умолчанию располагается в файле Source\Rtl\Common\VarCmplx.pas), содержит более 30000 байт.. На с. 229 показана структура записи TVarData. Два первых байта в этой записи (поле VType) хранят признак значения варианта, остальные 14 могут использоваться для размещения данных. Рис. 10.1. Демонстрация комплексных вариантов Создание пользовательского варианта проходит в три этапа.
В результате перечисленных шагов вы получаете полноценный вариант, обогащенный новыми свойствами: он может хранить не только те значения, которые перечислены в табл. 10.2, но и любые другие, в том числе свойства и методы! (В этом последнем случае наследником для исполняемого класса нового варианта вместо TCustomVariantType является TInvokeableVariantType или TPublishableVariantType.) 10.5.1. Размещение в варианте новых значений Для размещения в варианте нового (не предусмотренного стандартным вариантом) значения нужно создать соответствующий класс и поместить в подходящее поле rvarData объект этого класса. Вот как, например, размещаются комплексные данные в модуле VarCmplx: TComplexVarData = packed record VType: TVarType; Reserved1, Reserved2, Reserved3: Word; VComplex: TComplexData; Reserved4: Longint; End; Такая запись лишь сохраняет 16-байтную структуру TVarData, помещая в поле VComplex ссылку на объект класса TComplexData. Собственно комплексные числа хранятся в полях достаточно сложного класса: Type TComplexData = class(TPersistent) private FReal, FImaginary: Double; End; В этом классе предусмотрены многочисленные методы, управляющие новыми данными. Так, простой вызов VarComplexCreate приводит к срабатыванию нескольких методов, создающих объект VComplex и наполняющих его поля: procedure VarComplexCreateInto (var ADest: Variant; const AComplex: TComplexData); Begin VarClear(ADest); TComplexVarData(ADest).VType:= VarComplex; TComplexVarData(ADest).VComplex:= AComplex; end; function VarComplexCreate(const AReal, AImaginary: Double): Variant; Begin VarComplexCreateInto(Result, TComplexData.Create(AReal, AImaginary)); End; (CM. файл Source\Rtl\Common\VarCmplx.pas). Запись в которой размещаются новые данные или ссылка на поддерживающий их обьект, должно обьявляться как packed record. 10.5.2. Создание наследника TCustomVariantType Тип TCustomVariantType или его ближайшие Наследники TPublishableVariantType и TInvokeableVariantType Содержат методы и свойства, которые в нужный момент вызывают методы и свойства объекта VComplex для осуществления тех или иных преобразований. В модуле varcmpix объявляется такой класс: Type TComplexVariantType = class(TPublishableVariantType, IVarStreamable) protected function LeftPromotion(const V: TVarData; const Operator: TVarOp; out RequiredVarType: TVarType): Boolean; override; function RightPromotion(const V: TVarData; const Operator: TVarOp; out RequiredVarType: TVarType): Boolean; override; function Getlnstance(const V: TVarData): TObject; override; Public procedure Clear(var V: TVarData);
|