tags:

views:

713

answers:

2

I have tried this code:

uses MMSystem;

mciSendString('Set cdaudio door open wait', nil, 0, handle);

mciSendString('Set cdaudio door closed wait', nil, 0, handle);

but there was no effect. I have heard that this does not work on all systems.

I'm trying to get a drive to eject on Windows XP, and would like to specify the drive via the drive letter (necessary for systems with multiple drives).

+8  A: 

This is code using the DeviceIOControl() API function, works for me on Windows XP (compiled and tested using Delphi 5):

function DeviceIOControlHelper(ADeviceHandle: THandle;
  ADeviceIOControlCode: DWORD): boolean;
var
  BytesReturned: Cardinal;
begin
  Result := DeviceIOControl(ADeviceHandle, ADeviceIOControlCode,
    nil, 0, nil, 0, BytesReturned, nil);
end;

function SetDriveDoorOpen(ADriveLetter: char; AValue: boolean): boolean;
const
  FILE_DEVICE_FILE_SYSTEM = 9;
  FILE_ANY_ACCESS = 0;
  FILE_READ_ACCESS = 1;
  METHOD_BUFFERED = 0;
  IOCTL_STORAGE_BASE = $2D;

(*
#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
*)

//  FSCTL_LOCK_VOLUME = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6,
//                               METHOD_BUFFERED, FILE_ANY_ACCESS);
  FSCTL_LOCK_VOLUME = (FILE_DEVICE_FILE_SYSTEM shl 16)
                      or (FILE_ANY_ACCESS shl 14)
                      or (6 shl 2) or METHOD_BUFFERED;
//  FSCTL_DISMOUNT_VOLUME = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8,
//                                   METHOD_BUFFERED, FILE_ANY_ACCESS);
  FSCTL_DISMOUNT_VOLUME = (FILE_DEVICE_FILE_SYSTEM shl 16)
                          or (FILE_ANY_ACCESS shl 14)
                          or (8 shl 2) or METHOD_BUFFERED;
//  IOCTL_STORAGE_EJECT_MEDIA = CTL_CODE(IOCTL_STORAGE_BASE, 0x0202,
//                                       METHOD_BUFFERED, FILE_READ_ACCESS);
  IOCTL_STORAGE_EJECT_MEDIA = (IOCTL_STORAGE_BASE shl 16)
                              or (FILE_READ_ACCESS shl 14)
                              or ($0202 shl 2) or METHOD_BUFFERED;
//  IOCTL_STORAGE_LOAD_MEDIA = CTL_CODE(IOCTL_STORAGE_BASE, 0x0203,
//                                      METHOD_BUFFERED, FILE_READ_ACCESS);
  IOCTL_STORAGE_LOAD_MEDIA = (IOCTL_STORAGE_BASE shl 16)
                              or (FILE_READ_ACCESS shl 14)
                              or ($0203 shl 2) or METHOD_BUFFERED;
var
  DriveCmdStr: string;
  DriveHandle: THandle;
begin
  Result := FALSE;
  DriveCmdStr := Format('\\.\%s:', [ADriveLetter]);
  DriveHandle := CreateFile(PChar(DriveCmdStr), GENERIC_READ, FILE_SHARE_WRITE,
    nil, OPEN_EXISTING, 0, 0);
  if DriveHandle <> INVALID_HANDLE_VALUE then begin
    if AValue then begin
      Result := DeviceIOControlHelper(DriveHandle, FSCTL_LOCK_VOLUME)
        and DeviceIOControlHelper(DriveHandle, FSCTL_DISMOUNT_VOLUME)
        and DeviceIOControlHelper(DriveHandle, IOCTL_STORAGE_EJECT_MEDIA);
    end else
      Result := DeviceIOControlHelper(DriveHandle, IOCTL_STORAGE_LOAD_MEDIA);
    CloseHandle(DriveHandle);
  end;
end;

Error handling omitted.

mghie
A: 

uses ComObj;

function EjectDrive(const ADriveLetter: string): Boolean;
var
  WMP: Variant;
  CDROMs: Variant;
  Drive: Variant;
begin
  WMP := CreateOleObject('WMPlayer.OCX.7');
  CDROMs := WMP.CDROMCollection;
  Drive := CDROMs.GetByDriveSpecifier(ADriveLetter + ':');
  Drive.Eject;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  EjectDrive('Q');
end;

Edit: As you can see, I didn't write any error handling code. This code will raise an exception if an invalid drive is specified.

Jozz