ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Як зробити щоб запущений 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, або створити два окремі бібліотеки і довантажувати їх в залежності від операційної системи, оскільки підтримка двох інтерфейсів в одному додатку може негативно позначитися на його розмірі і обсязі використовуваної оперативної пам'яті.

    Список літератури

    Для підготовки даної роботи були використані матеріали з сайту http://www.rsdn.ru/

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status