рефераты
Главная

Рефераты по рекламе

Рефераты по физике

Рефераты по философии

Рефераты по финансам

Рефераты по химии

Рефераты по хозяйственному праву

Рефераты по цифровым устройствам

Рефераты по экологическому праву

Рефераты по экономико-математическому моделированию

Рефераты по экономической географии

Рефераты по экономической теории

Рефераты по этике

Рефераты по юриспруденции

Рефераты по языковедению

Рефераты по юридическим наукам

Рефераты по истории

Рефераты по компьютерным наукам

Рефераты по медицинским наукам

Рефераты по финансовым наукам

Рефераты по управленческим наукам

Психология и педагогика

Промышленность производство

Биология и химия

Языкознание филология

Издательское дело и полиграфия

Рефераты по краеведению и этнографии

Рефераты по религии и мифологии

Рефераты по медицине

Рефераты по сексологии

Рефераты по информатике программированию

Краткое содержание произведений

Реферат: Как сделать чтобы запущеный exe сам себя удалил?

Реферат: Как сделать чтобы запущеный exe сам себя удалил?

Укрощение строптивого… CD-ROM

Алексей Фоминов

Кто не мечтает о быстром CD-ROM? Быстрый CD-ROM – это хорошо… с одной стороны. А если на компакт-диске появилась трещина? Быстрый CD-ROM – это уже нехорошо. На скорости 52х такой компакт-диск читать просто опасно. А если на этом диске жизненно важные данные? Выход есть. Просто снизить скорость привода. Если вы знакомы с языком программирования Object Pascal, тогда читайте далее.

Использование интерфейса SCSI

SCSI (Small Computer System Interface - интерфейс малой компьютерной системы) – шина ввода/вывода, которая разрабатывалась как метод соединения нескольких классов периферийных устройств в главную систему, не требующий внесения модификации в общие аппаратные средства и программное обеспечение.

Поскольку цель данной статьи рассказать читателю о том, как программно управлять устройствами, которые подключаются к SCSI-шине, а не о том, как написать драйвер SCSI-устройства, описывать технические особенности шины SCSI и её отличие от IDE я не буду.

Каким же образом операционная система Windows общается со SCSI-устройствами?

Это зависит от версии операционной системы. В системах семейства Windows 9х (95, 98, 98SE, Me) применяется ASPI (Advanced SCSI Programmer Interface – улучшенный интерфейс программирования SCSI). В стандартную поставку этих операционных систем входят ASPI-драйвер и библиотека для работы с ним, разработанные фирмой Adaptec. В системах семейства Windows NT (NT 4.0, 2000, XP, Server) используется SPTI (SCSI Pass Through Interface – интерфейс передачи через SCSI). То есть, в NT-системах компания Майкрософт полностью отказалась от продукта фирмы Adaptec и создала свой интерфейс общения со SCSI-устройствами. Принесло ли это пользу пользователям? Вряд ли. На мой субъективный взгляд, рядовому пользователю всё равно, как происходит доступ к SCSI, ему важно, чтобы всё работало правильно. Принесло ли это пользу разработчикам программного обеспечения? Однозначно нет. Теперь, разрабатывая приложения для управления SCSI-устройствами, разработчик должен либо создавать две версии своего продукта (одну для Win9x, другую для WinNT), либо включать поддержку двух интерфейсов в свой продукт, что вряд ли является целесообразным с точки зрения размера программы.

Какой из двух интерфейсов лучше, мне сказать трудно. Отмечу лишь то, что программа Nero использует ASPI-драйвер, специально разработанный для неё фирмой Adaptec.

Рассмотрим сначала программирование с помощью интерфейса ASPI, на примере управления приводом CD-ROM/R/RW.

Предполагается, что читатель умеет работать с динамически компонуемыми библиотеками (dll). Как вы будете подключать библиотеку для работы с ASPI-драйвером wnaspi32.dll (статически или динамически) – дело ваше, главное, чтобы ваше приложение правильно импортировало из этой библиотеки необходимые нам функции.

Я подключал эту библиотеку статически и импорт нужных нам функций у меня выглядел так:

function GetASPI32SupportInfo:DWORD; external 'wnaspi32.dll'

 name 'GetASPI32SupportInfo';

function SendASPI32Command(LPSRB:Pointer):DWORD; external 'wnaspi32.dll'

 name 'SendASPI32Command'.

Функция GetASPI32SupportInfo инициализирует ASPI и возвращает информацию об основной конфигурации. При успешном выполнении она возвращает двойное слово (DWORD), в котором старший байт младшего слова содержит статус ASPI, а младший байт – количество устройств (адаптеров), поддерживающих ASPI. Байт статуса может содержать следующие значения:

$01 – выполнено без ошибок;

$E8 – нет адаптеров;

$E2 – не может быть выполнено под управлением Windows 3.1;

$E3 – неправильная установка ASPI, или имеются конфликты ресурсов;

$E7 – установка ASPI нарушена (требуется повторная установка);

$E9 – недостаточно системных ресурсов для инициализации ASPI;

$E4 – общий внутренний сбой ASPI.

Количество возвращенных адаптеров представляет собой количество логических шин, а не физических адаптеров. Для адаптеров с единственной шиной количество адаптеров и количество логических шин идентичны.

Функция SendASPI32Command оперирует со всеми SCSI-запросами ввода/вывода. Каждый SCSI-запрос использует SCSI Request Block (SRB – Блока Запроса SCSI), определяющий операцию ASPI, которую нужно выполнить.

Параметр, передаваемый функции SendASPI32Command – указатель на определённую структуру. Описание этих структур приведено ниже.

type

 SRB_HAInquiry = packed record

 SRB_Cmd: Byte; // код команды ASPI (константа SC_HA_INQUIRY = $00)

 SRB_Status, // байт статуса ASPI команды

 SRB_HaId, // номер адаптера ASPI

 SRB_Flags: Byte; // зарезервировано, должно быть 0

 SRB_Hdr_Rsvd: Dword; // зарезервировано, должно быть 0

 HA_Count: Byte; // количество адаптеров

 HA_SCSI_ID: Byte; // ID SCSI-адаптера

 HA_ManagerId: array [0..15] of Byte; // строка, описывающая менеджер

 HA_Identifier: array [0..15] of Byte; // строка, описывающая адаптер

 HA_Unique: array [0..15] of Byte; // уникальные параметры адаптера

 HA_Rsvd1: Word; // зарезервировано, должно быть 0

 end;

 PSRB_HAInquiry = ^SRB_HAInquiry;

 TSRB_HAInquiry = SRB_HAInquiry;

Структура TSRB_HAInquiry используется для получения информации о физических SCSI-адаптерах.

type

 SRB_GDEVBlock = packed record

 SRB_Cmd, // код команды ASPI (константа SC_GET_DEV_TYPE = $01);

 SRB_Status, // байт статуса ASPI команды;

 SRB_HaId, // номер адаптера ASPI;

 SRB_Flags: Byte; // зарезервировано, должно быть 0;

 SRB_Hdr_Rsvd: Dword; // зарезервировано, должно быть 0;

 SRB_Target, // ID объекта SCSI;

 SRB_Lun, // Logical Unit Number (LUN - логический номер устройства);

 SRB_DeviceType, // тип периферийного устройства;

 SRB_Rsvd1: Byte; // зарезервировано, должно быть 0;

 end;

 TSRB_GDEVBlock = SRB_GDEVBlock;

 PSRB_GDEVBlock = ^SRB_GDEVBlock;

Структура TSRB_GDEVBlock используется для идентификации устройств на шине SCSI.

type

 SRB_ExecSCSICmd = packed record

 SRB_Cmd, // код команды ASPI (константа SC_EXEC_SCSI_CMD = $02)

 SRB_Status, // байт статуса ASPI команды

 SRB_HaId, // номер адаптера ASPI

 SRB_Flags: Byte; // флаги запроса ASPI

 SRB_Hdr_Rsvd: Dword; // зарезервировано, должно быть 0

 SRB_Target, // ID объекта SCSI

 SRB_Lun: Byte; // Logical Unit Number (LUN - логический номер устройства)

 SRB_Rsvd1: Word; // зарезервировано для выравнивания

 SRB_BufLen: Dword; // длина буфера

 SRB_BufPointer: Pointer; // указатель на буфер данных

 SRB_SenseLen, // длина значения;

 SRB_CDBLen, // длина Command Descriptor Block – блока дескриптора команды

 SRB_HaStat, // статус адаптера

 SRB_TargStat: Byte; // статус объекта

 SRB_PostProc, // указатель на функцию постинга (см.ниже)

 SRB_Rsvd2: Pointer; // зарезервировано, должно быть 0;

 SRB_Rsvd3, // зарезервировано для выравнивания

 CDBByte: array [0..15] of byte; // SCSI Command Descriptor Block

 // буфер значения для SCSI-запроса

 SenseArea: array [0..SENSE_LEN + 1] of byte;

 end;

 TSRB_ExecSCSICmd = SRB_ExecSCSICmd;

 PSRB_ExecSCSICmd = ^SRB_ExecSCSICmd;

Структура TSRB_ExecSCSICmd используется для выполнения команд ввода/вывода. Константа SENSE_LEN (длина буфера значения) по умолчанию равна 14.

На мой взгляд, теории пока достаточно. Перейду к практике.

Для начала инициализируем ASPI.

function GetASPI: Integer;

var

 dwSupportInfo: DWORD;

 byASPIStatus,byHACount: Byte;

begin

 Result := 0;

 dwSupportInfo := GetASPI32SupportInfo;

 byASPIStatus := HIBYTE(LOWORD(dwSupportInfo)); // статус ASPI

 byHACount := LOBYTE(LOWORD(dwSupportInfo)); // количество адаптеров

 

 case byASPIStatus of

 SS_COMP: Result := Integer(byHACount);

 SS_NO_ADAPTERS: ShowMessage('ASPI-контроллеры не обнаружены!');

 SS_ILLEGAL_MODE: ShowMessage(

 'ASPI не может быть выполнен под управлением Windows 3.1!');

 SS_NO_ASPI: ShowMessage(

 'Неправильная установка ASPI, или имеются конфликты ресурсов!');

 SS_MISMATCHED_COMPONENTS: ShowMessage(

 'Установка ASPI нарушена! Установите повторно, пожалуйста!');

 SS_INSUFFICIENT_RESOURCES: ShowMessage(

 'Недостаточно системных ресурсов для инициализации ASPI!');

 SS_FAILED_INIT: ShowMessage('Общий внутренний сбой ASPI!');

 end;

end;

Итак, мы получили информацию об имеющихся SCSI-адаптерах. Теперь выделим из их числа (если их несколько) устройства CD-ROM/R/RW. Для этого создадим вспомогательные структуры: TCDROM и TCDROMs.

type

 TCDROM=record

 HaID, // номер адаптера ASPI

 Target, // ID объекта SCSI

 Lun: Byte; // логический номер устройства

 DriveLetter: string; // буквенное обозначение диска

 VendorID, // идентификатор производителя

 ProductID, // идентификатор продукта

 Revision, // изменение

 VendorSpec, // спецификация производителя

 Description: string; // описание

 end;

Тип TCDROM будет хранить необходимые нам данные об устройствах CD-ROM.

type

 TCDROMs=record

 CdromCount: Byte;

 Cdroms: array [Byte] of TCDROM;

end;

Поскольку у некоторых пользователей может быть подключено несколько CD-ROM, мы объявили тип TCDROMs, содержащий в себе информацию о количестве CD-ROM и массив элементов TCDROM. А теперь давайте напишем функцию для определения всех имеющихся в системе устройств CD-ROM, объявив перед этим глобальную переменную Cdroms: TCDROMs.

// в качестве параметра передаётся количество всех SCSI-адаптеров,

// имеющихся в системе. Результат работы функции – количество CD-ROM.

function GetCDROMs(var Adapters:Byte): Integer;

var

 sh: TSRB_HAInquiry;

 sd: TSRB_GDEVBlock;

 maxTgt: Byte;

 H, T, L: byte;

Begin

 Result := 0;

 if Adapters = 0 then

 exit; // если количество адаптеров 0 – выходим

 // начинаем перебирать все адаптеры

 for H := 0 to Adapters - 1 do

 begin

 FillChar(sh,sizeof(sh),0); // инициализируем структуру TSRB_HAInquiry

 // (константа SC_HA_INQUIRY = $00) запрос ASPI для получения информации

 // об адаптерах.

 sh.SRB_Cmd := SC_HA_INQUIRY;

 sh.SRB_HaID := H;

 SendASPI32Command(@sh); // посылаем ASPI команду

 if sh.SRB_Status=SS_COMP then // если выполнено без ошибок, тогда:

 begin

 // четвёртый байт уникальных параметров определяет максимальное

 // количество объектов SCSI

 maxTgt := sh.HA_Unique[3];

 // если этот байт равен 0, тогда присваиваем переменной максимально

 // возможное значение (константа MAXTARG=7)

 if maxTgt=0 then maxTgt := MAXTARG;

 for T := 0 to maxTgt-1 do // начинаем перебирать все объекты SCSI

 begin

 for L := 0 to MAXLUN-1 do // и все логические номера устройств

 begin

 // инициализируем структуру TSRB_GDEVBlock

 FillChar(sd,sizeof(sd),0);

 

 // команда запрашивает тип устройства для объекта SCSI (константа

 // SC_GET_DEV_TYPE = $01)

 sd.SRB_Cmd := SC_GET_DEV_TYPE;

 sd.SRB_HaID := H;

 sd.SRB_Target := T;

 sd.SRB_Lun := L;

 SendASPI32Command(@sd); // посылаем ASPI-команду

 

 // если выполнено без ошибок, и устройство является CD-ROM,

 // заполняем переменную Cdroms.

 if (sd.SRB_Status=SS_COMP) and (sd.SRB_DeviceType=DTYPE_CDROM) then

 begin

 Cdroms.Cdroms[Cdroms.CdromCount].HaID := H;

 Cdroms.Cdroms[Cdroms.CdromCount].Target := T;

 Cdroms.Cdroms[Cdroms.CdromCount].Lun := L;

 // получаем информацию об этом CD-ROM

 CdromInfo(Cdroms.CdromCount);

 // увеличиваем счётчик количества устройств CD-ROM

 inc(Cdroms.CdromCount);

 end;

 end;

 end;

 end;

 end;

 

 Result := Cdroms.CdromCount; // присваиваем результату функции количество CD-ROM

end;

Вы, наверное, обратили внимание на то, что в коде используется процедура CdromInfo. Это процедура, с помощью которой, мы получаем информацию о нашем CD-ROM. Перед тем, как привести её описание, я хочу рассказать вам о том, как происходит управление SCSI-устройствами посредством специальных команд, и как при этом используется структура TSRB_ExecSCSICmd.

Вот поля структуры TSRB_ExecSCSICmd, на которые нужно, прежде всего, обратить внимание: SRB_Cmd, SRB_Flags, SRB_CDBLen, CDBByte. Поле SRB_Cmd всегда должно содержать значение SC_EXEC_SCSI_CMD. Поле SRB_Flags должно определять направление передачи данных. Если данные передаются из SCSI-устройства в приложение, используется шестнадцатиричное значение $08 (определим это значение как константу SRB_DIR_IN). Если происходит обратная передача данных (от приложения к SCSI-устройству), используется шестнадцатиричное значение $10 (определим это значение как константу SRB_DIR_OUT). В зависимости от посылаемой команды, поле SRB_CDBLen может содержать значения: 6, 10 или 12. Массив байт CDBByte подробно описывает параметры выполняемой команды. Значение массива различно для всех команд. Замечу лишь, то, что нулевой байт этого массива всегда определяет код команды. Какие команды я имею в виду? Например: команда установки скорости CD-привода, команда записи CD-R или CD-RW-диска, команды управления аудио-CD (Play, Pause, Stop и так далее).

Существуют SCSI-команды, которые поддерживают все устройства, и есть команды, которые специфичны для определённого типа устройств. Первая команда, которую мы рассмотрим, команда INQUIRY, является обязательной для всех устройств. Она запрашивает информацию о SCSI-устройстве. А теперь собственно перейдём к коду процедуры:

// параметр, передаваемый процедуре – номер CD-ROM.

procedure CdromInfo(const Number: Byte);

var

 // буфер будет содержать информацию о приводе

 buffer: array [1..100] of Char;

begin

 // инициализируем буфер (просто обнуляем его)

 Fillchar(buffer, sizeof(buffer), 0);

 // инициализируем структуру TSRB_ExecSCSICmd (глобальная переменная Srb)

 Fillchar(Srb, sizeof(TSRB_ExecSCSICmd), 0);

 hEvent := CreateEvent(nil, true, false, nil); // создаём событие

 ResetEvent(hEvent); // переключаем на наше событие

 with Srb do

 begin

 SRB_Cmd := SC_EXEC_SCSI_CMD;

 SRB_HaId := Cdroms.Cdroms[Number].HaID;

 SRB_Target := Cdroms.Cdroms[Number].Target;

 SRB_Lun := Cdroms.Cdroms[Number].Lun;

 // здесь добавляется ещё один флаг SRB_EVENT_NOTIFY ($40), уведомляющий

 // систему о событии

 SRB_Flags := SRB_DIR_IN or SRB_EVENT_NOTIFY;

 SRB_BufLen := sizeof(buffer); // указываем размер буфера

 SRB_BufPointer := @buffer; // определяем указатель на наш буфер

 SRB_SenseLen := SENSE_LEN; // определяем длину буфера значения

 SRB_CDBLen := 6; // эта команда – шестибайтная

 SRB_PostProc := Pointer(hEvent); // процедура постинга – созданное событие

 CDBByte[0] := $12; // код команды INQUIRY

 // сюда помещаем старший байт длины буфера

 CDBByte[3] := HIBYTE(sizeof(buffer));

 // а сюда помещаем младший байт длины буфера

 CDBByte[4] := LOBYTE(sizeof(buffer));

 end;

 

 // после того как заполнили структуру TSRB_ExecSCSICmd, посылаем

 // ASPI-команду

 dwASPIStatus := SendASPI32Command(@Srb);

 if dwASPIStatus=SS_PENDING then

 WaitForSingleObject(hEvent, INFINITE); // ждём окончания обработки команды

 

 CloseHandle(hEvent); // закрываем хэндл события

 

 // если команда выполнена без ошибок, заполняем данные об устройстве:

 if Srb.SRB_Status=SS_COMP then

 begin

 with Cdroms.Cdroms[Number] do

 begin

 // восемь байт буфера, начиная с девятого, содержат

 // идентификатор производителя

 VendorID := PChar(Copy(buffer, 9, 8));

 // шестнадцать байт, начиная с семнадцатого, содержат

 // идентификатор продукта

 ProductID := PChar(Copy(buffer, 17, 16));

 // четыре байта, начиная с тридцать третьего, содержат номер

 // изменения продукта

 Revision := PChar(Copy(buffer, 33, 4));

 // двадцать байт, начиная с тридцать седьмого, содержат

 // спецификацию производителя

 VendorSpec := PChar(Copy(buffer, 37, 20));

 end;

 end;

end;

Я понимаю, что многим эта процедура покажется неинтересной – я её привёл лишь для того, чтобы показать основы работы со SCSI-устройствами.

Следующие две процедуры, на мой взгляд, заинтересуют большее число пользователей. Уверен, многие из вас постоянно пользуются, или пользовались ранее, программами, управляющими скоростью привода CD-ROM (например, программой CDSlow). Хотите написать подобную программу сами? Позвольте помочь вам кодом, состоящим из двух процедур, одна из которых определяет текущую и максимально поддерживаемую скорость привода, а другая устанавливает необходимую пользователю скорость.

Для этого я воспользовался SCSI-командой MODE SENSE(10). Цифра десять означает, что команда десятибайтная. Это важно, потому что существует такая же шестибайтная команда. В принципе, можно было бы воспользоваться и шестибайтной командой, но поскольку команда MODE SENSE(10) более совершенна, я остановил свой выбор на ней. Итак, для чего же нужна данная команда? Всё просто, она читает значения режимов (Mode Sense), установленных для SCSI-устройства. Существуют так называемые страницы режима (Mode Page), в которых хранится некоторая информация (например, параметры скорости привода, параметры для записи CD-R/RW-дисков и многое другое). Доступ к этим страницам осуществляется по их коду с использованием команды MODE SENSE.

Опишем вспомогательный тип TCDSpeeds.

type

 TCDSpeeds=record

 MaxSpeed, // максимальная скорость чтения

 CurrentSpeed, // текущая скорость чтения

 MaxWriteSpeed, // максимальная скорость записи

 CurrentWriteSpeed:integer; // текущая скорость записи

 end;

Теперь, я думаю, понятно для чего эта структура нужна.

// какие параметры передавать функции, объяснять, по моему, не надо

function GetCDSpeeds(Host,Target,Lun:Byte):TCDSpeeds;

var

 buffer: array [0..29] of Byte; // буфер для принимаемых данных

Здесь я сделаю небольшое пояснение относительно размера буфера. Данные, возвращаемые при использовании страницы режима CD Capabilities and Mechanical Status Page, имеют размер 20 байт. Но, как вы заметили, я использовал буфер размером 30 байт, и вот почему. Перед самой страницей режима, идут заголовок режима параметров, код страницы и её размер. Размер заголовка при использовании шестибайтной команды MODE SENSE составляет 4 байта, а при использовании команды MODE SENSE(10) – 8 байт.

Продолжим. Код, который уже встречался ранее, приведен без комментариев:

begin

 hEvent := CreateEvent(nil, true, false, nil);

 FillChar(buffer,sizeof(buffer),0);

 FillChar(Srb,sizeof(TSRB_ExecSCSICmd),0);

 Srb.SRB_Cmd := SC_EXEC_SCSI_CMD;

 Srb.SRB_Flags := SRB_DIR_IN or SRB_EVENT_NOTIFY;

 Srb.SRB_Target := Target;

 Srb.SRB_HaId := Host;

 Srb.SRB_Lun := Lun;

 Srb.SRB_BufLen := sizeof(buffer);

 Srb.SRB_BufPointer := @buffer;

 Srb.SRB_SenseLen := SENSE_LEN;

 Srb.SRB_CDBLen := $0A; // это десятибайтная команда

 Srb.SRB_PostProc := Pointer(hEvent);

 Srb.CDBByte[0] := $5A; // код команды MODE SENSE(10)

 // код страницы CD Capabilities and Mechanical Status Page

 Srb.CDBByte[2] := $2A;

 Srb.CDBByte[7] := HIBYTE(sizeof(buffer));

 Srb.CDBByte[8] := LOBYTE(sizeof(buffer));

 ResetEvent(hEvent);

 dwASPIStatus := SendASPI32Command(@Srb);

 if dwASPIStatus=SS_PENDING then

 WaitForSingleObject(hEvent,INFINITE);

 

 if Srb.SRB_Status<>SS_COMP then

 // если ошибка, обнуляем структуру TCDSpeeds

 FillChar(Result,sizeof(TCDSpeeds),0);

 else begin

 // почему сумма байт делится на 176? 176 – это скорость передачи

 // данных, равная одному килобайту в секунду.

 Result.MaxSpeed := ((buffer[16] shl 8) + buffer[17]) div 176;

 Result.CurrentSpeed := ((buffer[22] shl 8) + buffer[23]) div 176;

 Result.MaxWriteSpeed := ((buffer[26] shl 8) + buffer[27]) div 176;

 Result.CurrentWriteSpeed := ((buffer[28] shl 8) + buffer[29]) div 176;

 end;

 

 CloseHandle(hEvent);

end;

Итак, скорости мы определили, теперь нужно научиться ими управлять.

Для этого воспользуемся SCSI-командой SetCDSpeed.

// параметры ReadSpeed и WriteSpeed – скорость чтения и записи соответственно

function SetSpeed(

 Host, Target, Lun : Byte;

 ReadSpeed, WriteSpeed : integer) : boolean;

begin

 if ReadSpeed=0 then

 result := false

 else

 begin

 hEvent := CreateEvent(nil, true, false, nil);

 FillChar(Srb,sizeof(TSRB_ExecSCSICmd), 0);

 Srb.SRB_Cmd := SC_EXEC_SCSI_CMD;

 // обратите внимание здесь данные передаются из приложения в

 // устройство (флаг SRB_DIR_OUT)

 Srb.SRB_Flags := SRB_DIR_OUT or SRB_EVENT_NOTIFY;

 Srb.SRB_Target := Target;

 Srb.SRB_HaId := Host;

 Srb.SRB_Lun := Lun;

 Srb.SRB_SenseLen := SENSE_LEN;

 Srb.SRB_CDBLen := $0C; // эта команда двенадцатибайтная

 Srb.SRB_PostProc := Pointer(hEvent);

 Srb.CDBByte[0] := $BB; // код команды Set CD Speed

 // устанавливаем скорость чтения

 Srb.CDBByte[2] := Byte((ReadSpeed * 176) shr 8);

 Srb.CDBByte[3] := Byte(ReadSpeed * 176);

 

 if WriteSpeed<>0 then // если привод пишущий

 begin

 // ...устанавливаем скорость записи

 Srb.CDBByte[4] := Byte((WriteSpeed * 176) shr 8);

 Srb.CDBByte[5] := Byte(WriteSpeed * 176);

 end;

 

 ResetEvent(hEvent);

 dwASPIStatus := SendASPI32Command(@Srb);

 

 if dwASPIStatus=SS_PENDING then

 WaitForSingleObject(hEvent,INFINITE);

 

 if Srb.SRB_Status<>SS_COMP then

 result := false

 else

 result := true;

 end;

end;

Напоследок хочу рассказать о том, как узнать все скорости, которые поддерживает привод. Разместите на форме компоненты TComboBox и TButton. В обработчике события OnClick компонента TButton поместите следующий код:

var

 i : integer;

begin

 ComboBox1.Items.Clear; // очищаем элементы выпадающего списка

 

 with Cdroms.Cdroms[0] do // используем первый CD-ROM

 begin

 // открываем цикл от 1 до максимальной скорости привода

 for i := 1 to GetCDSpeeds(HaID, Target, Lun).MaxSpeed do

 begin

 SetSpeed(HaID, Target, Lun, i, 0); // устанавливаем скорость, равную i

 

 if i = GetCDSpeeds(HaID, Target, Lun).CurrentSpeed then

 // сравниваем, если текущая скорость равна i, заносим это

 // значение в выпадающий список

 ComboBox1.Items.Add(IntToStr(i));

 end;

 end;

end;

Вот и всё. Следующая часть статьи посвящена работе с SPTI-интерфейсом.

Использование интерфейса SPTI

Итак, в предыдущей статье было рассказано, как управлять приводом CD-ROM, используя интерфейс ASPI.

Однако интерфейс ASPI поддерживается в операционных системах семейства Win9x, которые сейчас используются крайне редко. Здесь я расскажу о том, как осуществлять управление CD-ROM посредством SPTI-интерфейса, который поддерживается в операционных системах WinNT, 2000, XP, 2003 Server. Начну с описания основных структур, которые при этом понадобятся:

type

 TScsiPassThrough = record

 Length : Word; // Размер структуры TScsiPassThrough

 ScsiStatus : Byte; // Статус SCSI-запроса

 PathId : Byte; // Идентификатор SCSI-адаптера

 TargetId : Byte; // Идентификатор объекта SCSI

 Lun : Byte; // Logical Unit Number (LUN - логический номер устройства)

 // Длина CDB (Command Descriptor Block – блока дескриптора команды)

 CDBLength : Byte;

 SenseInfoLength : Byte; // Длина буфера значения

 DataIn : Byte; // Байт, определяющий тип запроса (ввод или вывод)

 DataTransferLength : DWORD; // Размер передаваемых данных

 TimeOutValue : DWORD; // Время ожидания запроса в секундах

 DataBufferOffset : DWORD; // Смещение буфера данных

 SenseInfoOffset : DWORD; // Смещение буфера значения

 // SCSI Command Descriptor Block (Блок дескриптора команды)

 CDB: array [0..15] of Byte;

 end;

Следующая структура:

TScsiPassThroughWithBuffers = record

 spt : TScsiPassThrough;

 bSenseBuf : array [0..31] of Byte; // Буфер значения

 bDataBuf : array [0..191] of Byte; // Буфер данных

end;

 

ScsiPassThroughWithBuffers=TScsiPassThroughWithBuffers;

PScsiPassThroughWithBuffers=^TScsiPassThroughWithBuffers;

Как видите, эта структура содержит тип TScsiPassThrough и два буфера. Для удобства мы будем использовать структуру TScsiPassThroughWithBuffers.

Теперь постараюсь объяснить принцип использования интерфейса SPTI.

Сначала, с помощью функции CreateFile, создаём хэндл для доступа к устройству. Затем заполняем данными структуру TScsiPassThroughWithBuffers. И, наконец, с помощью функции DeviceIoControl, посылаем устройству управляющий код.

Выглядит это примерно так:

procedure GetSPTIDrives; // Процедура получает информацию о CD-ROM

var

 j : integer;

 s : string;

 len, returned : DWORD;

 sptwb : TScsiPassThroughWithBuffers;

 Cdroms : TCdroms; // Структура Tcdroms описана в предыдущей статье

 const

 SCSI_IOCTL_DATA_IN = 1;

 IOCTL_SCSI_PASS_THROUGH = ($00000004 shl 16)

 or (($0001 or $0002) shl 14) or ($0401 shl 2) or (0);

begin

 // Кроме строки '.E : ', можно использовать, 'cdrom0', 'cdrom1' и т.д.

 // в зависимости от количества устройств

 hDevice := CreateFile('.E : ', GENERIC_READ or GENERIC_WRITE,

 FILE_SHARE_READ or FILE_SHARE_WRITE,

 nil, OPEN_EXISTING, 0, 0);

 

 if hDevice=INVALID_HANDLE_VALUE then

 ShowMessage('INVALID_HANDLE_VALUE');

 

 sptwb.Spt.Length := sizeof(TSCSIPASSTHROUGH);

 sptwb.Spt.CdbLength := 6; // Шестибайтная команда

 sptwb.Spt.SenseInfoLength := 24;

 // Команда будет получать данные от устройства (ввод)

 sptwb.Spt.DataIn := SCSI_IOCTL_DATA_IN;

 // Устанавливаем размер передаваемых данных

 sptwb.Spt.DataTransferLength := sizeof(sptwb.bDataBuf);

 sptwb.Spt.TimeOutValue := 10; // Время ожидания – 10 секунд

 sptwb.Spt.DataBufferOffset := DWORD(@sptwb.bDataBuf)-DWORD(@sptwb);

 sptwb.Spt.SenseInfoOffset := DWORD(@sptwb.bSenseBuf)-DWORD(@sptwb);

 len := sptwb.Spt.DataBufferOffset+sptwb.spt.DataTransferLength;

 // Команда INQUIRY вам уже известна по предыдущей статье

 sptwb.Spt.CDB[0] := SCSI_INQUIRY;

 sptwb.Spt.CDB[3] := HiByte(sizeof(sptwb.bDataBuf));

 sptwb.Spt.CDB[4] := LoByte(sizeof(sptwb.bDataBuf));

 if DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, @sptwb,

 len, @sptwb, len, Returned, nil) and (sptwb.Spt.ScsiStatus = $00) then

 begin

 // Нижеследующие циклы предназначены для разделения информации о

 // производителе, спецификации и т.д. Если вашей программе это не нужно,

 // можно сделать так : ShowMessage(PChar(@sptwb.bDataBuf[8]));

 s := '';

 

 for j := 8 to 15 do

 s := s + Chr(sptwb.bDataBuf[j]);

 // Идентификатор производителя

 Cdroms.Cdroms[Cdroms.ActiveCdrom].VendorID := s;

 s := '';

 

 for j := 16 to 31 do

 s := s + Chr(sptwb.bDataBuf[j]);

 

 Cdroms.Cdroms[Cdroms.ActiveCdrom].ProductID := s; // Идентификатор продукта

 s := '';

 for j := 32 to 35 do

 s := s+chr(sptwb.bDataBuf[j]);

 

 Cdroms.Cdroms[Cdroms.ActiveCdrom].Revision := s; // Номер изменения

 s := '';

 

 for j := 36 to 55 do

 s := s+chr(sptwb.bDataBuf[j]);

 

 // Спецификация производителя

 Cdroms.Cdroms[Cdroms.ActiveCdrom].VendorSpec := s;

 end;

end;

Если вы заметили, использование параметров PathId, TargetId и Lun для интерфейса SPTI не является обязательным (в отличие от ASPI). Поэтому, если вы всё же хотите, чтобы ваша программа определяла идентификатор SCSI-адаптера, идентификатор объекта SCSI и логический номер устройства, могу посоветовать воспользоваться таким кодом:

procedure Get_PathId_TargetId_Lun;

var

 buf : array [0..1023] of Byte;

 pscsiAddr:PSCSI_ADDRESS;

const

 IOCTL_SCSI_GET_ADDRESS = $41018;

begin

 ZeroMemory(@buf, sizeof(buf));

 pscsiAddr := PSCSI_ADDRESS(@buf);

 pscsiAddr^.Length := sizeof(TSCSI_ADDRESS);

 

 if (DeviceIoControl(hDevice, IOCTL_SCSI_GET_ADDRESS, nil, 0,

 pscsiAddr, sizeof(TSCSI_ADDRESS), returned, nil)) then

 begin

 Cdroms.Cdroms[Cdroms.ActiveCdrom].HaID := pscsiAddr^.PortNumber;

 Cdroms.Cdroms[Cdroms.ActiveCdrom].Target := pscsiAddr^.TargetId;

 Cdroms.Cdroms[Cdroms.ActiveCdrom].Lun := pscsiAddr^.Lun;

 end else

 ShowMessage(SysErrorMessage(GetLastError));

end;

В этом куске кода используется структура PSCSI_ADDRESS, которая выглядит следующим образом:

type

 TSCSI_ADDRESS = record

 Length : LongInt; // Размер структуры TSCSI_ADDRESS

 PortNumber : Byte; // Номер адаптера SCSI

 PathId : Byte; // Идентификатор адаптера SCSI

 TargetId : Byte; // Идентификатор объекта SCSI

 Lun : Byte; // Логический номер устройства

 end;

SCSI_ADDRESS = TSCSI_ADDRESS;

PSCSI_ADDRESS = ^TSCSI_ADDRESS;

Как вы уже успели заметить, SCSI-команды для интерфейсов ASPI и SPTI одинаковы, поэтому необходимо знать лишь сами команды и заполнять соответствующим образом CDB (Command Descriptor Block). Для наглядности приведу пример использования интерфейса SPTI для установки скорости CD-ROM. Сравните этот код с таким же, но использующим интерфейс ASPI, и вы сами увидите все отличия.

function SPTISetSpeed(ReadSpeed, WriteSpeed:integer):Boolean;

var

 spti:TScsiPassThroughWithBuffers;

const

 SCSI_IOCTL_DATA_OUT = 0;

 Rate = 176;

begin

 spti.Spt.Length := sizeof(TSCSIPASSTHROUGH);

 spti.Spt.CdbLength := 10;

 spti.Spt.SenseInfoLength := 24;

 spti.Spt.DataIn := SCSI_IOCTL_DATA_OUT;

 spti.Spt.TimeOutValue := 10;

 spti.spt.DataBufferOffset := DWORD(@spti.bDataBuf)-DWORD(@spti);

 spti.spt.SenseInfoOffset := DWORD(@spti.bSenseBuf)-DWORD(@spti);

 spti.Spt.DataTransferLength := sizeof(spti.bDataBuf);

 spti.spt.CDB[0] := $BB;

 spti.spt.CDB[2] := BYTE(ReadSpeed*Rate shr 8);

 spti.spt.CDB[3] := BYTE(ReadSpeed*Rate);

 

 if WriteSpeed<>0 then

 begin

 spti.spt.CDB[4] := BYTE(WriteSpeed*Rate shr 8);

 spti.spt.CDB[5] := BYTE(WriteSpeed*Rate);

 end else

 spti.spt.CDB[4] := $FF;

 spti.spt.CDB[5] := $FF;

 if DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, @spti, len, @spti, len, returned, nil) and

 (spti.spt.ScsiStatus=$00) then result := true

 else

 result := false;

end;

Думаю, данный код не нуждается в пояснениях.

Кстати, всё вышесказанное (в том числе и в предыдущей статье) относится не только к устройствам CD-ROM, но и к другим SCSI-устройствам. Отличия лишь в командах. Есть команды, которые обязательны для всех устройств (MODE SELECT, MODE SENSE, INQUIRY и т.д.), и есть команды, которые специфичны для разных типов устройств (BLANK – для устройств CD-RW, PRINT – для принтеров, SCAN – для сканеров, и т.д.).

Теперь вы знаете, как осуществляется управление устройствами, подключёнными к шине SCSI. Какой использовать интерфейс, ASPI или SPTI, или оба вместе – дело ваше. Могу сказать лишь, что для использования двух интерфейсов рациональнее будет либо создать два приложения для двух семейств операционных систем Windows, либо создать две отдельные библиотеки и подгружать их в зависимости от операционной системы, поскольку поддержка двух интерфейсов в одном приложении может отрицательно сказаться на его размере и объеме используемой оперативной памяти.


© 2012 Рефераты, курсовые и дипломные работы.