Графика DirectX в Delphi
Шрифт:
IDirectDraw = interface; DirectDraw7 = interface;
Ключевое слово interface здесь, конечно, является не началом секции модуля, а типом, соответствующим интерфейсам СОМ-объектов.
Обработчик создания окна приведите к следующему виду:
procedure TForml.FormCreate(Sender: TObject); var
hRet : HRESULT; // Вспомогательная переменная
begin
// Создание главного объекта DirectDraw hRet := DirectDrawCreate (nil, FDD, nil);
if Failed (hRet) // Проверка успешности предыдущего действия
then ShowMessage ('Ошибка при выполнении DirectDrawCreate')
// Поддерживается ли интерфейс 7-й
else hRet := FDD.Querylnterface (IID_IDirectDraw7, FDD7);
if Failed (hRet) // Или один из двух,
// или оба интерфейса не получены
then ShowMessage ('DirectX 7-й версии не доступен')
else ShowMessage ('DirectX 7-й версии доступен');
// Освобождение памяти, занятой объектами if Assigned (FDD7) then FDD7 := nil;
if Assigned (FDD) then FDD := nil;
end;
Уже при подготовке этого, простейшего, примера я прибегнул к некоторым упрощениям, но все равно у каждого новичка здесь появится масса вопросов. Попробую предвосхитить и разрешить их.
Итак, первая строка кода - создание главного объекта, через интерфейсы которого выполняются действия по созданию остальных объектов. Как я говорил, для СОМ-объектов нельзя использовать обычный конструктор.
Переменная DirectDrawCreate описывается в заголовочном файле Direct Draw, pas так:
DirectDrawCreate : function (IpGUID: PGUID;
out IplpDD: IDirectDraw;
pUnkOuter: lUnknown) : HResult; stdcall;
При инициализации модуля происходит связывание переменной и получение адреса точки входа:
DirectDrawCreate := GetProcAddress(DDrawDLL,'DirectDrawCreate');
Это нам немного знакомо по первому примеру. Здесь происходит динамическая загрузка функции из библиотеки. Ссылка на библиотеку описывается так:
var
DDrawDLL : HMODULE = 0;
Первое действие при инициализации модуля - загрузка библиотеки:
DDrawDLL := LoadLibrary('DDraw.dll');
С помощью утилиты быстрого просмотра можем убедиться, что действительно в списке экспортируемых функций данной библиотеки (обратите внимание, что этот список сравнительно невелик) присутствует имя функции DirectDrawCreate. Напоминаю, что сам файл библиотеки содержится в системном каталоге, как правило, это C:\Windows\System\. Оттуда загружается функция. Но каков смысл ее аргументов и возвращаемой ею величины? Начнем с возвращаемой величины. Из описания ясно, что тип ее - HRESULT, который имеет результат всех функций, связанных с OLE. Обрабатывается результат таких функций для проверки успешности каких-либо действий, как в данном случае, для того, чтобы выяснить, успешно ли выполнена операция получения интерфейса.
Это 32-битное целое значение, описание типа которого вы можете найти
В модуле system. раз: HRESULT = type Longint;
HRESOLT - общий для OLE тип, соответствующий коду ошибки. Каждый сервер по-своему распределяет возможные ошибки и возвращаемый код. Общим является то, что нулевое значение эквивалентно отсутствию ошибки.
Коды ошибок, возвращаемых функциями, связанными с DirectDraw, можно интерпретировать в осмысленную фразу с помощью функции
function DDErrorString (Value: HResult) : string;
Эта функция описана в модуле DirectDraw. pas. Аргументом ее является код ошибки, результатом - строка, раскрывающая смысл произошедшей неудачи. Равенство нулю кода выступает признаком успешно выполненной операции. Анализ успешности операции часто выполняется просто сравнением возвращаемой величины с константой DD_OK, равной нулю.
Константа S_OK,
равная нулю, также может применяться во всех модулях, использующих OLE, но обычно каждый из них определяет собственную нулевую константу.В примере для оценки успешности операции я пользуюсь системной функцией, описанной в модуле windows. раз:
function Failed (Status: HRESULT): BOOL;
Функция возвращает значение True, если аргумент отличен от нуля. Есть и обратная ей функция, возвращающая значение True при отсутствии ошибок:
function Succeeded (Status: HRESULT): BOOL;
Теперь вернемся к аргументам функции DirectDrawCreate. Первый из них задает параметры работы приложения, если задавать значение его в nil, то при работе будет применяться текущий видеодрайвер. Если же необходимо строго оговорить, чтобы приложение не использовало все преимущества аппаратного ускорения, то это значение нужно установить так:
PGUID ( DDCREATE_EMULATIONONLY )
Если же требуется оговорить, что создаваемый объект DirectDraw не будет эмулировать особенности, не поддерживаемые аппаратно, надо использовать в качестве этого параметра константу DDCREATE_HARDWAREONLY. Функция DirectDrawCreate тогда "проглотит" аргумент в любом случае, но, в будущем, попытка вызвать методы, требующие неподдерживаемые особенности, приведет к генерации ошибки с кодом DDERRJJNSUPPORTED.
Второй параметр функции - собственно наш объект, который примет данные.
Последним аргументом всегда надо указывать nil. Этот параметр зарезервирован для будущих нужд, чтобы старые приложения смогли в перспективе работать при измененной СОМ-модели.
Так, все, связанное с первым действием, - созданием главного объекта, - разобрали. Функция DirectorawCreate вряд ли когда-либо возвратит ненулевое значение. Это будет соответствовать ситуации серьезного сбоя в работе системы. Однако после каждого действия необходимо проверять успешность его выполнения. Приходится сразу же привыкнуть к тому, что код будет испещрен подобной проверкой, анализом возвращаемого функцией значения. Некоторые действия вполне безболезненно можно выполнять без проверки на неудачу, поскольку ошибки при их выполнении если и возможны, то крайне редки. Ключевые же операции следует обязательно снабжать подобным кодом, поскольку ошибки при их выполнении вполне возможны и даже ожидаемы. Появляются эти исключения не по причине неустойчивого поведения системы или приложения, а закономерно в ответ на изменения окружения работы приложения. Например, пользователь может временно переключиться на другое приложение или поменять параметры рабочего стола по ходу работы вашего приложения. Анализ исключений позволяет вашему приложению отслеживать такие моменты и реагировать на изменившиеся условия работы.
Дальше в коде примера идет следующая строка:
FDD.Querylnterface (IID_IDirectDraw7, FDD7);
Вызываем метод Queryinterface главного объекта для получения нужного нам интерфейса, соответствующего седьмой версии DirectX. Заглянем в описание этого интерфейса. Начало выглядит так:
IDirectDraw7 = interface (lUnknown)
['{15e65ec0-3b9c-lld2-b92f-00609797ea5b}']
Все интерфейсы строятся на базе интерфейса lUnknown, в следующей строке указывается идентификатор конкретного интерфейса, за которой приведено перечисление его методов. Идентификаторы интерфейсов используются при взаимодействии клиента с сервером. Следы наиболее важных идентификаторов мы можем обнаружить в реестре. Например, в заголовочном файле вы можете найти такие строки описания идентификаторов: