FAQ VCL
Железо\Диски\CDRom

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика

Как програмно открыть/закрыть CDRom?

// Способ первый
uses
  {...,} MMSystem;

// Открыть CDRom
mciSendString( 'Set cdaudio door open wait', nil, 0, handle );
// Закрыть CDRom
mciSendString( 'Set cdaudio door closed wait', nil, 0, handle );

// Способ второй
uses
  {...,} MMSystem;

function OpenCD(Drive: Char; Open: Boolean): Boolean;
var
  Res: MciError;
  OpenParm: TMCI_Open_Parms;
  Flags: DWord;
  S: string;
  DeviceID: Word;
begin
   Result := false;
   S := Drive + ':';
   Flags := mci_Open_Type or mci_Open_Element;
   with OpenParm do
   begin
      dwCallback := 0;
      lpstrDeviceType := 'CDAudio';
      lpstrElementName := PChar(S);
   end;
   Res := mciSendCommand(0, mci_Open, Flags, Longint(@OpenParm));
   if Res <> 0 then
      Exit;
   DeviceID := OpenParm.wDeviceID;
   try
      if Open then
         Res := mciSendCommand(DeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0)
      else
         Res := mciSendCommand(DeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, 0);
      if Res = 0 then
         Exit;
      Result := true;
   finally
      mciSendCommand( DeviceID, mci_Close, Flags, Longint( @OpenParm ) );
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   OpenCD('E', True);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   OpenCD('E', False);
end;

// Способ третий
function OpenCD(Drive: Char; Open: Boolean): Boolean;
//const
//  IOCTL_DISK_EJECT_MEDIA =
//    (IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or ($0202 shl 2) or (METHOD_BUFFERED);
//  IOCTL_DISK_LOAD_MEDIA =
//    (IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or ($0203 shl 2) or (METHOD_BUFFERED);
var
  hCDDevice: THandle;
  lpBytesReturned: DWORD;
begin
   hCDDevice := CreateFile(PChar('\\.\' + Drive + ':'), GENERIC_READ {or GENERIC_WRITE},
      FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
   if hCDDevice <> INVALID_HANDLE_VALUE then
   begin
      if Open then
         DeviceIoControl(hCDDevice, IOCTL_DISK_EJECT_MEDIA, nil, 0, nil, 0, lpBytesReturned, nil)
      else
         DeviceIoControl(hCDDevice, IOCTL_DISK_LOAD_MEDIA, nil, 0, nil, 0, lpBytesReturned, nil);
      CloseHandle(hCDDevice);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   OpenCD('E', True);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   OpenCD('E', False);
end;


Как получить имена всех CDRom'ов, установленных в системе?

uses
  {...,} Registry;

procedure GetAviableCDROM( ListCD: TStrings );
var
  Reg: TRegistry;
  Key, SubKey, TempKey: string;
  i, KolCD: byte;
begin
   Reg := TRegistry.Create;
   with Reg do
   try
      RootKey := HKEY_LOCAL_MACHINE;
      // Здесь лежат имена ключей с информацией об установленных CDROM'ах
      Key := 'SYSTEM\CurrentControlSet\Services\Cdrom\Enum';
      SubKey := 'SYSTEM\CurrentControlSet\Enum\';
      Access := KEY_READ; // На общих правах NT не пускает к этим ключам
      if not OpenKey( Key, false ) then Exit;
      // Узнаем количество CDROM'ов
      KolCD := ReadInteger( 'Count' );
      if KolCD = 0 then Exit;
      for i:=0 to KolCD-1 do
      begin
         OpenKey( Key, False );
         TempKey := ReadString( IntToStr( i ) );
         CloseKey;
         if not OpenKey( SubKey + Tempkey, false ) then Break;
         ListCD.Add( ReadString( 'FriendlyName' ) );
         CloseKey;
      end;
   finally
      Reg.Free;
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   GetAviableCDROM( Memo1.Lines );
end;


Как проверить, включен ли автозапуск CDRom'а?

// Проверено в Windows 2000
uses
  {...,} Registry;

function IsCDAutoRun: boolean;
var
  Reg: TRegistry;
  i: integer;
begin
   Reg := TRegistry.Create;
   try
      Reg.RootKey := HKEY_LOCAL_MACHINE;
      if Reg.KeyExists( 'System\CurrentControlSet\Services\CDROM' ) then
      begin
         if Reg.OpenKey( 'System\CurrentControlSet\Services\CDROM', false ) then
            i := Reg.ReadInteger( 'AutoRun' );
      end;
      case i of
         0: Result := false;
         else
            Result := true;
      end;
   finally
      Reg.Free;
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   if IsCDAutoRun then
      ShowMessage( 'Автозапуск включен' )
   else
      ShowMessage( 'Автозапуск выключен' );
end;


Как включить/отключить автозапуск CDRom'а?

// Проверено в Windows 2000
uses
  {...,} Registry;

procedure SetCDAutoRun( ARun: boolean );
var
  Reg: TRegistry;
  i: integer;
begin
   Reg := TRegistry.Create;
   try
      Reg.RootKey := HKEY_LOCAL_MACHINE;
      if Reg.KeyExists( 'System\CurrentControlSet\Services\CDROM' ) then
      begin
         if Reg.OpenKey( 'System\CurrentControlSet\Services\CDROM', false ) then
            Reg.WriteInteger( 'AutoRun', Abs( Integer( ARun ) ) );
      end;
   finally
      Reg.Free;
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   // Включить
   SetCDAutoRun( true );
   // Отключить
   SetCDAutoRun( false );
end;


Как заблокировать/разблокировать CDRom?

const
  {$EXTERNALSYM IOCTL_CDROM_MEDIA_REMOVAL}
  IOCTL_CDROM_MEDIA_REMOVAL =
    (FILE_DEVICE_CD_ROM shl 16) or (FILE_READ_ACCESS shl 14) or ($201 shl 2) or (METHOD_BUFFERED);
  // Вместо IOCTL_CDROM_MEDIA_REMOVAL можно использовать значения:
  //   • IOCTL_DISK_MEDIA_REMOVAL
  //   • IOCTL_STORAGE_MEDIA_REMOVAL
  //   • $00024804;
  //   • $002D4804;

type
  PREVENT_MEDIA_REMOVAL = packed record
    PreventMediaRemoval: ByteBool;
  end;

function LockCD(Drive: Char; Lock: Boolean): Boolean;
var
  hCDDevice: THandle;
  lpBytesReturned: DWORD;
  pmrLockCDROM: PREVENT_MEDIA_REMOVAL;
begin
   hCDDevice := CreateFile(PChar('\\.\' + Drive + ':'), GENERIC_READ or GENERIC_WRITE,
      FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
   if hCDDevice <> INVALID_HANDLE_VALUE then
   begin
      pmrLockCDROM.PreventMediaRemoval := Lock;
      DeviceIoControl(hCDDevice, IOCTL_CDROM_MEDIA_REMOVAL, @pmrLockCDROM,
         SizeOf(PREVENT_MEDIA_REMOVAL), nil, 0, lpBytesReturned, nil);
      CloseHandle(hCDDevice);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   LockCD('E', True);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   LockCD('E', False);
end;

// Вариация "на тему" 1
procedure CD_Lock( DriveLetter: string; Locked: Boolean );
const
  IOCTL_STORAGE_MEDIA_REMOVAL = $002D4804;
var
  hDrive: THandle;
  Returned: DWORD;
begin
   hDrive := CreateFile( PChar( '\\.\' + DriveLetter ), GENERIC_READ,
                         FILE_SHARE_READ, nil, OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL, 0 );
   if GetLastError <> 0 then
      MessageDlg( 'Ошибка: ' + IntToStr( GetLastError ), mtError, [mbOK], 0 );
   try
      if not DeviceIoControl( hDrive, IOCTL_STORAGE_MEDIA_REMOVAL,
         @Locked, SizeOf( Locked ), nil, 0, Returned, nil ) then
         MessageDlg( 'Ошибка: ' + IntToStr( GetLastError ), mtError, [mbOK], 0 );
   finally
      CloseHandle( hDrive );
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   // Заблокировать
   CD_Lock( 'f:', True );
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   // Разблокировать
   CD_Lock( 'f:', False );
end;

// Вариация "на тему" 2
function LockMedia(ADriveLetter: Char; ALock: Boolean): Boolean; 
var
  LWStatus: Hwnd;
  LTemp: Cardinal;
  LPMR32: Boolean;
const
  IOCTL_STORAGE_MEDIA_REMOVAL = $2D4804;
begin
   LwStatus := CreateFile( PChar( '\\.\' + ADriveLetter + ':' ), GENERIC_READ or
                           GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0 );
   LPMR32 := ALock;
   if LwStatus <> INVALID_HANDLE_VALUE then
   begin
      Result := DeviceIoControl( LwStatus, IOCTL_STORAGE_MEDIA_REMOVAL, @LPMR32, sizeof( LPMR32 ), nil, 0, LTemp, nil );
      CloseHandle( LwStatus );
   end
   else
      Result := false;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   // Заблокировать
   LockMedia( 'm', True );
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   // Разблокировать
   LockMedia( 'm', False );
end;


Как проверить состояние лотка CDRom (открыт/закрыт)?

// Способ первый
type
  USHORT = Word;

  PSCSI_PASS_THROUGH_DIRECT = ^SCSI_PASS_THROUGH_DIRECT;
  _SCSI_PASS_THROUGH_DIRECT = {packed} record
    Length: USHORT;
    ScsiStatus: UCHAR;
    PathId: UCHAR;
    TargetId: UCHAR;
    Lun: UCHAR;
    CdbLength: UCHAR;
    SenseInfoLength: UCHAR;
    DataIn: UCHAR;
    DataTransferLength: ULONG;
    TimeOutValue: ULONG;
    DataBuffer: ULONG;
    SenseInfoOffset: ULONG;
    Cdb: array [0..15] of UCHAR;
  end;
  SCSI_PASS_THROUGH_DIRECT = _SCSI_PASS_THROUGH_DIRECT;

  TSCSIPassThroughDirectBuffer = record
    Header: SCSI_PASS_THROUGH_DIRECT;
    SenseBuffer: array [0..31] of UCHAR;
    DataBuffer: array [0..191] of UCHAR;
  end;

const
  IOCTL_SCSI_BASE = FILE_DEVICE_CONTROLLER;

  IOCTL_SCSI_PASS_THROUGH = (IOCTL_SCSI_BASE shl 16) or
    ((FILE_WRITE_ACCESS or FILE_READ_ACCESS) shl 14) or
    ($0401 shl 2) or METHOD_BUFFERED;

  SCSI_IOCTL_DATA_IN = 1;
  SCSIOP_MECHANISM_STATUS = $BD;

function IsCDOpen(const Drive: Char): Boolean;
var
  PassTrought: TSCSIPassThroughDirectBuffer;
  dwQueryLen, dwBytesReturned: DWORD;
  hCDHandle: THandle;
begin
   Result := False;

   // Подготавливаем буффер запроса
   ZeroMemory(@PassTrought, SizeOf(TSCSIPassThroughDirectBuffer));
   PassTrought.Header.Length := SizeOf(SCSI_PASS_THROUGH_DIRECT);
   // Размер команды в байтах
   PassTrought.Header.CdbLength := 12;
   // Тип обмена данных
   PassTrought.Header.DataIn := SCSI_IOCTL_DATA_IN;
   // Размер буффера данных
   PassTrought.Header.DataTransferLength := SizeOf(PassTrought.DataBuffer);
   // Время ожидания ответа
   PassTrought.Header.TimeOutValue := 10;
   // Оффсет на начало буффера данных
   PassTrought.Header.DataBuffer :=
     DWORD(@PassTrought.DataBuffer) - DWORD(@PassTrought);
   // Заполняем Command Descriptor Block
   // Код команды
   PassTrought.Header.Cdb[0] := SCSIOP_MECHANISM_STATUS;
   // Размер ожидаемого ответа
   PassTrought.Header.Cdb[8] := 8;
   // общий размер запроса
   dwQueryLen := PassTrought.Header.DataBuffer + PassTrought.Header.DataTransferLength;
   // Открываем устройство
   hCDHandle := CreateFile(PChar(Format('\\.\%s:', [Drive])),
      GENERIC_WRITE or GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE,
      nil, OPEN_EXISTING, 0, 0);
   if hCDHandle <> INVALID_HANDLE_VALUE then
   try
      // Отправка запроса
      if DeviceIoControl(hCDHandle, IOCTL_SCSI_PASS_THROUGH, @PassTrought,
         dwQueryLen, @PassTrought, dwQueryLen, dwBytesReturned, nil) then
         // при успешном ответе 12-ый бит буффера будет содержать состояние лотка
         // 1 - открыт, 0 - закрыт
         Result := PassTrought.DataBuffer[1] and $10 = $10;
   finally
      CloseHandle(hCDHandle);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  Drive: Char = 'E';
begin
   if IsCDOpen(Drive) then
      ShowMessage('Лоток устройства ' + Drive + ': открыт.')
   else
      ShowMessage('Лоток устройства ' + Drive + ': закрыт.');
end;

// Способ второй
function IsCDDoorOpen(CDDrive:string):boolean;
type
  _SCSI_PASS_THROUGH_DIRECT = record
    Length:             USHORT;
    ScsiStatus:         UCHAR;
    PathId:             UCHAR;
    TargetId:           UCHAR;
    Lun:                UCHAR;
    CdbLength:          UCHAR;
    SenseInfoLength:    UCHAR;
    DataIn:             UCHAR;
    DataTransferLength: ULONG;
    TimeOutValue:       ULONG;
    DataBuffer:         PUCHAR;
    SenseInfoOffset:    ULONG;
    Cdb:                array[0..15] of UCHAR;
  end;
  SCSI_PASS_THROUGH_DIRECT  = _SCSI_PASS_THROUGH_DIRECT;
  PSCSI_PASS_THROUGH_DIRECT = ^SCSI_PASS_THROUGH_DIRECT;

const
  FILE_DEVICE_CONTROLLER   =  $00000004;
  IOCTL_SCSI_BASE          =  FILE_DEVICE_CONTROLLER;
  FILE_READ_ACCESS         =  $0001;
  FILE_WRITE_ACCESS        =  $0002;
  METHOD_BUFFERED          =  0;
  IOCTL_SCSI_PASS_THROUGH_DIRECT = (IOCTL_SCSI_BASE shl 16) or
                                   ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14)
                                   or ($0405 shl 2) or METHOD_BUFFERED;

var
  hCD: THandle;
  srb: SCSI_PASS_THROUGH_DIRECT;
  returned:DWORD;
  buf:   PUCHAR;
  state: UCHAR;
  c: string;
begin
   Result := False;
   if Length(CDDrive) = 1 then
      c := ':'
   else
      c := '';
   // открываем устройство
   hCD := CreateFile(PChar('\\.\' + CDDrive + c), GENERIC_WRITE or GENERIC_READ,
                     FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
   if hCD = INVALID_HANDLE_VALUE then
      Exit;
   try
      buf := HeapAlloc(GetProcessHeap,HEAP_ZERO_MEMORY,8); // выделяем буфер
      if buf <> nil then
      try
         ZeroMemory(@srb, SizeOf(srb));
         srb.Length := SizeOf(srb);
         srb.CdbLength := 12;         // длина CDB пакета
         srb.DataTransferLength := 8; // сколько мы будем читать
         srb.TimeOutValue := 200;     // время выхода по TimeOut
         srb.DataBuffer := buf;       // указатель на буфер
         srb.DataIn := 1;

         srb.Cdb[0] := $0bd; // mechanism status
         srb.Cdb[8] := 0;    // старш байт длины буфера
         srb.Cdb[9] := 8;    // мл. байт длины буфера

         // запрашиваем информацию
         if not DeviceIoControl(hCD, IOCTL_SCSI_PASS_THROUGH_DIRECT,
            @srb, sizeof(srb), nil, 0, returned, nil) then
            Exit;

         // При некоторых обстоятельствах привод может возвращать больше данных
         // чем их было запрошено

         // В полученном буфере информацию несут только первые 2 байта:
         // Первый байт: биты 5 и 6 - состояние механизма,где  0 - механ готов...
         // Второй байт: бит 4 - сост дверцы, чего нам и надо.
         // Биты 5, 6, 7 - состояние устройства. где  0 - сост ожидания
         // хотя они документированы, но в некоторых приводах почему-то не используются

         Inc(buf);  // смотрим  второй байт
         state := buf^;
         Dec(buf); // для корректного освобождения буфера
      finally
         HeapFree(GetProcessHeap, 0, buf)  // освобождаем буфер
      end;
   finally
      CloseHandle(hCD); // и закрываем дескриптор
   end;

   // тут анализируем нужный нам бит
   case (state and $10) of
      $10: Result := True; // дисковод открыт
      $0: begin
         SetLastError(ERROR_SUCCESS); // говорим вызывающей проге, что
                                      // False возвратилось не в результате ошибки
         Result := False; // дисковод закрыт. эту строчку можно убрать,
                          // тут она просто для наглядности
      end;
   end;
end;

При использовании материала - ссылка на сайт обязательна