Смекни!
smekni.com

Как сделать чтобы запущеный exe сам себя удалил 2 (стр. 1 из 3)

Укрощение строптивого… 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.