Смекни!
smekni.com

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

Вот поля структуры 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.