Newsgroup: it.comp.programmare.win32
Titolo: Come chiudere il CD-Rom

ho un piccolo problema e vorrei il vosrto aiuto.
Utilizzando l'API mciSendstring riesco ad aprire il CD-Rom ma purtroppo non
riesco poi a richiuderlo. C'è qualcuno che conosce la stringa che va
inserita per chiudere il cassetto del cd-rom??
Grazie
Giakomino

 ha scritto nel messaggio
news:8qqo3d$hl2$1...@nread2.inwind.it...

mciSendString("set cdaudio door open",NULL,0,NULL);
mciSendString("set cdaudio door closed",NULL,0,NULL);
oppure
mciSendString("set cdaudio door open wait",NULL,0,NULL);
mciSendString("set cdaudio door closed wait",NULL,0,NULL);
Ciao.
Il Michett@
--

e per sapere se e' chiuso o aperto ?
GRAZIE 1000
faf...@tiscalinet.it

Ma se ho due CDROM come faccio per distinguerli ?
GRAZIE 1000
faf...@tiscalinet.it

GRAZIE 1000
Visto  che  e' una domanda ricorrente  ti posto quanto avevo inviato a
un'altro  amico di questo newsgroup con le tue medesime problematiche.
Il progetto d'esempio, realizzato su  Visual C++, posso spedirtelo via
e-mail. Sempre che ti interessi...
[...]
Purtroppo  le  cose nel tuo  caso si complicano terribilmente!! Non e'
piu'  possibile sfruttare l'interfaccia MCI per compiere questo genere
di  azioni. Il  problema  e' che  MCI  non gestisce  i  CDR (non  sono
presenti  nell'elenco  dei  dispositivi  supportati)  e  comunque  non
supporta  piu' di  un  dispositivo  per volta.  Non  resta quindi  che
accedere  via Win32  con la funzione DeviceIoControl() o attraverso le
ASPI  (Advance SCSI  Programming Interface).  Il mio  consiglio e'  di
utilizzare quest'ultime che oltre ad essere universali (ci sono driver
di  questo tipo prodotti dall'Adaptec per praticamente tutti i sistemi
operativi)  sono decisamente piu' propedeutiche rispetto alle analoghe
API  di Windows. Queste, oltretutto, non hanno interfaccia univica per
tutte le versioni degli OS di Redmond. Mentre su WinNT/2K l'accesso al
"disk  device" avviene  per  chiamata diretta  al  logical device,  su
Win95/98/ME bisogna ricorrere indirettamente ai driver che controllano
la  periferica  (VXD,  nel  nostro caso  al  vwin32).  Tralasciando  i
problemi  di  sicurezza che nascono  da una  simile scelta (i crash si
sprecano  in quanto si accede direttamente all'hardware!) e' piuttosto
scomodo    sviluppare   un'applicazione   seguendo   due   metodologie
differenti.  Prima di  buttarti sul  codice ti  consiglio una  lettura
sulla  funzioni CreateFile(),  DeviceIoControl() e  la struttura  dati
relativa,  compresa la differenza tra l'istanza del puntatore (HANDLE)
al  virtual  device su Win95/98  e al  logical device su WinNT/2K. Nel
primo caso si tratta di operazioni a un livello piuttosto basso (quasi
in  assembler). Si  devono chiamate interrupt e gestire direttamente i
registri  della CPU  (che si aspetta che i parametri siano passati nel
modo  corretto,  altrimenti spero che il  blue sia uno dei tuoi colori
preferiti  :)! Buona  guida introduttiva a questo mondo sommerso e' la
Ralf Browns Interrupt List e la trovi al seguente indirizzo :
http://www.delorie.com/djgpp/doc/rbinter/ix/
Se   hai  avuto  modo  di  programmare  in  assembly  sotto  DOS  devi
necessariamente  conoscerla. Se  cosi' non fosse non ti spaventare; lo
so  la marea  di numeri  esadecimale  e' un  bel muro  che rischia  di
scoraggiare  chiunque ma  con calma  e analizzando  cio' che  si vuole
ottenere si riescono a fare dei bei giochetti con questi numeri!
Qui  sotto e' riportato lo schema per aprire lo sportello di un CD-ROM
attraverso le API di Win 95/98/Me (su NT/2K NON FUNZIONA!)
(...)
#define VWIN32_DIOC_DOS_IOCTL     1
typedef struct _DIOC_REGISTERS {
    DWORD reg_EBX;
    DWORD reg_EDX;
    DWORD reg_ECX;
    DWORD reg_EAX;
    DWORD reg_EDI;
    DWORD reg_ESI;
    DWORD reg_Flags;
} DIOC_REGISTERS, *PDIOC_REGISTERS;
HANDLE    hDevice;
DIOC_REGISTERS  reg;
BOOL    fResult;
DWORD    cb;
hDevice = CreateFile("\\\\.\\vwin32",
 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
reg.reg_EAX = 0x440D;       /* IOCTL for block devices           */
reg.reg_EBX = 0x0004;       /* zero-based drive A:=1 B=2 C=3 D=4 */
reg.reg_ECX = 0x0849;       /* Eject Media                       */
reg.reg_Flags = 0x0001;     /* assume error (carry flag is set)  */
fResult = DeviceIoControl(hDevice,
 VWIN32_DIOC_DOS_IOCTL, &reg, sizeof(reg), &reg, sizeof(reg), &cb, 0);
CloseHandle(hDevice);
(...)
Ti  voglio  far notare che reg_EAX,  reg_EBX e reg_ECX sono i registri
effettivi    della   CPU   e   sono   chiamati   direttamente   grazie
all'interposizione  del driver  (virtuale VWin32)  di sistema  (solo i
driver  possono  accedere  direttamente all'hardware).  Questo  chiama
l'interrupt  21h  al riferimento 440Dh corrispondente al controllo del
CDROM. Settando poi il codice per il comando di apertura del carello e
impostanto  il numero del dispositivo nel registo EBX siamo pronti per
inviare i dati al CD tramite la funzione DeviceIOControl(). VWin32 non
fara' altro che eseguire il seguente codice macchina...
(...)
mov ax, 440Dh      ; Generico IOCTL -> CD-ROM
mov bx, 04h        ; D: Drive
mov ch, 08h        ; codice per disk drives (48h Fat32 drive)
mov cl, 49h        ; Apre il carrello del CD-ROM
int 21h      ; Salta all'interrupt software 21h
jc  error_handler  ; Vai alla gestione dell'errore
(...)
...che  e'  la funzione che  Windows chiama quando selezioni "Espelli"
sui dispositivi removibili. Se vuoi chiudere lo sportello ti dico solo
AUGURI!  Serve l'interrupt 2F non supportato da VWin32 e occore creare
un  driver  apposito per accedervi.  La Microsoft platform DDK (driver
development kit) e' una bella lettura. In alternativa puoi crearti una
dll  a 16  bit (thunking) e lavorare  con il DPMS (che lavora solo con
codice a 16bit). Questa soluzione e' comunque ardua e poco elegante in
quanto  VisualC++ non genera  codice a 16bit e ricorrere  a una dll di
siffatte  dimenzioni  per chiamare gli  interrupt software via DPMS mi
fa' semplicemente rabbrividire! Stessa cosa per i TSR...:-)
Ora vediamo l'analoga versione per WinNT/2K
(...)
HANDLE    hDevice;
BOOL    fResult;
DWORD    cb;
hDevice = CreateFile("\\.\D:",
 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
fResult = DeviceIoControl(hDevice,
  IOCTL_STORAGE_EJECT_MEDIA, NULL, 0,  NULL, 0, &cb, 0);
//IOCTL_STORAGE_LOAD_MEDIA chiude lo sportello
CloseHandle(hDevice);
(...)
Come  vedi  tutto  e'  decisamente piu'  semplice  e  trasparente.  Se
entrambi  i  sistemi avessero  il supporto  dei dispositivi logici sui
dischi sarebbe un gran passo avanti...
Vediamo  ora come  muoversi con le ASPI. Prima di tutto hai bisogno di
aggiungere al tuo progetto i file include che controllano le strutture
dati  dell'ASPI32 (allegate  al messaggio). Sono delle versioni che ho
modificato  col  passare del tempo (fanne  buon uso! :). Ci sono anche
due  wrapper  che chiamano  le corrispondenti funzioni ASPI principali
GetASPI32SupportInfo()   e  SendASPI32Command()  (ho  semplificato  al
massimo  il codice per rendere la lettura il piu' semplice possibile).
Ti  consiglio  di leggerti l'aspi_w32.txt  che puoi prelevare dal sito
Adaptec.  Ci sono utili informazioni di base sull'ASPI. Se invece vuoi
approfondire  la materia  allora il  testo obbligato  e' "The  Book of
SCSI:  I/O  for the New Millennium"  di Gary Field, Peter M. Ridge. Da
poco e' usita la seconda edizione ed e' a parer mio uno dei libri piu'
completi in materia.  Parte dall'ABC e si spinge sino al piu'  piccolo
dettaglio di configurazione (BIOS o ASPI).  Davvero una pietra miliare
del settore. Ma veniamo al codice...
La prima cosa da fare ogni volta se vuoi utilizzare le ASPI e caricare
la  library WNASPI32.DLL che trovi in {WINDOWS_DIR}\system. Fatto cio'
carichi  le  due  funzioni  GetASPI32SupportInfo  e  SendASPI32Command
quindi  lanci la prima per vedere se tutto funziona a dovere. Se cosi'
fosse  deve restiturti  un  valore  dal quale  ricevi  le prime  utili
informazioni  di base  sul tuo  sistema (numero  di SCSI  Adapters ecc
ecc). Qui sotto e riportato il brano del codice sopracitato:
WinAspiHandle = LoadLibrary("WNASPI32.DLL");
if (!WinAspiHandle)
   return SS_FAILED_INIT << 8;
GetASPI32SupportInfo = (DWORD (*)(void))
 GetProcAddress(WinAspiHandle,"GetASPI32SupportInfo");
SendASPI32Command = (DWORD (*)(void *))
 GetProcAddress(WinAspiHandle,"SendASPI32Command");
if (!GetASPI32SupportInfo || !SendASPI32Command)
   {
   FreeLibrary(WinAspiHandle);
   return SS_FAILED_INIT << 8;
   }
int rval =  GetASPI32SupportInfo();
Ora  viene il  pezzo piu' importante ed interessente : il passagio del
blocco di comando al dispositivo SCSI. Tralascio i particolari tecnici
e didattici in quanto il discorso diventerebbe troppo lungo. Qui sotto
e'  riportato lo  stralcio utile per espellere/caricare il media di un
dispositivo removibile:
ScsiRequestBlock srb;
srb.io.SRB_Cmd = SC_EXEC_SCSI_CMD;
srb.io.SRB_HaId = HostAdapter; 0 = PRIMARY_IDE_ SCSI_0
srb.io.SRB_Flags = SRB_EVENT_NOTIFY;
srb.io.SRB_Target = SCSIDeviceID; -> 0=MASTER_IDE 1=SLAVE_IDE o IDSCSI
srb.io.SRB_Lun = LUN; -> Logical unit address = generalmente 0
srb.io.SRB_SenseLen = SENSE_LEN;
srb.io.SRB_CDBLen = 6; ->dimensioni del blocco di comando
srb.io.SRB_PostProc = ASPICompletionEvent;
srb.io.SRB_CDBByte[0]= 0x1B; // SCSI Cmd Data Block (load/unload)
srb.io.SRB_CDBByte[1]= 0;
srb.io.SRB_CDBByte[2]= 0;
srb.io.SRB_CDBByte[4]= CMD; // 2 Espelle; 3 Carica
srb.io.SRB_CDBByte[3]= 0;
srb.io.SRB_CDBByte[5]= 0;
aspi_SendCommand(&srb); // Invia il comando SCSI
Come  vedi l'accesso  al  dispositivo  per questo  tipo  di azioni  e'
piuttosto  semplice. In  realta' le  strutture dati  variano per  ogni
tipologia  di  comando  cosi'  come variano  le  dimensione  del  CDB.
Quest'ultimo  e' generalmente formato da bit adiacenti fra di loro con
significati  ben  precisi. Sopra ti  ho riportato la versione compatta
del CDB di load/unload media. La struttura reale e' la seguente:
typedef struct SCSI_Cdb_LoadUnload_s {
   unsigned int CommandCode    : 8;  // bit 0-7 byte 0 deve essere 0x1B
   unsigned int Immediate      : 1;  // bit 1   byte 1 =0
   unsigned int Reserved_1     : 4;  // bit 1-4 byte 1 =0
   unsigned int Lun            : 3;  // bit 5-7 byte 1 =0
   unsigned int Reserved_2     : 8;  // bit 0-7 byte 2 =0
   unsigned int Reserved_3     : 8;  // bit 0-7 byte 3 =0
   unsigned int Start          : 1;  // bit 0   byte 4 =0(unload) =1(load)
   unsigned int LoadEject      : 1;  // bit 1   byte 4 deve essere 1
   unsigned int Reserved_4     : 6;  // bit 2-7 byte 4 =0
   unsigned int Link           : 1;  // bit 0   byte 5 =0
   unsigned int Flag           : 1;  // bit 1   byte 5 =0
   unsigned int Reserved_5     : 4;  // bit 2-5 byte 5 =0
   unsigned int VendorSpecific : 2;  // bit 6-7 byte 5 =0
} SCSI_Cdb_LoadUnload_t;
Carino  vero? Ovviamente  esistono gia'  in circolazione  degli header
piu'  o  meno  completi  che semplificano  notevolmente  la  vita  del
programmatore.  Ti allego un programmino di esempio che ti aiutera' di
certo a capire il funzionamento di queste semplici funzioni.
[...]
Come  puoi ben  immaginare questa  e'  solo la  punta dell'iceberg  in
quanto  il  controllo reale di  una periferica SCSI richiede ben altri
criteri  di sicurezza  come  l'utilizzo  di semafori  e  mutex per  le
operazioni  piu'  rischiose  (critical  section)  e  la  gestione  dei
timeout. Ora sta' a te continuare...
Ciao.
Il Michett@
--
p.s.   Le  operazioni   che  ti  ho  suggerito  sono  tutte  di  medio
basso  livello, alcune  delle  quali non  ammettono repliche.  Passare
dati   casuali  puo'  rilevarsi  MOLTO  pericoloso  per  l'incolumita'
del  tuo computer e del portafoglio. Poni quindi la massima attenzione
durante la stesura del programma e debugga tutto riga per riga!

 Ma se ho due CDROM come faccio per distinguerli ?
"open D: type cdaudio alias cd1"
"open E: type cdaudio alias cd2"
E cosi` via.
---
Bye! Lorenzo Lutti
http://www.geocities.com/SiliconValley/Peaks/6258/

ehm...
scusa, se io volessi fare lo stesso (usare CreateFile e DeviceIoControl) con
il Visual Basic 6, credi che ci sarebbero problemi??
grazie.
Mario


Quello postato era parte di un discorso piu' vasto riguardante gli accessi
alle periferiche, lettura delle caratteristiche e il loro controllo a un
livello piu' basso. Mi rendo conto ora che forse non era esattamente
l'esempio migliore da portare. Nel tuo caso specifico direi che se devi
aprire o chiudere lo sportello dei lettori CD ti conviene usare la
procedura suggerita da Lorenzo in questo thread:
int mci_err;
 // Apri il CD-ROM con lettera X: e alias cdY
 if (mci_err = mciSendString("open X: type cdaudio alias cdY",NULL,0,0))
     return mci_err;
 // Apri lo sportello
 if (mci_err = mciSendString("set cdY door open",NULL,0,0))
     return mci_err;
 // Chiudi lo sportello
 if (mci_err = mciSendString("set cdY door closed",NULL,0,0))
     return mci_err;
 // Chiudi il dispositivo
 if (mci_err = mciSendString("close cdY wait",NULL,0,0))
     return mci_err;
Anche le MCI comunque hanno il loro tallone di Achille. Essendo
l'accesso al dispositivo serializzato basta che un programma
riservi l'utilizzo (aprendo il dispositivo) per renderlo inutilizzabile
a tutte le applicazioni che si appoggiano a questa interfaccia. :-\
Rispondendo invece al tuo questito, direi di si' (anche se non l'ho
mai testato) ma di lasciar perdere. La gestione dei dischi (o dei
dispositivi di memorizzazione) tramite CreateFile() lascia molto a
desiderare su Windows. Questo e' un classico esempio di mancanza di
sinergia tra i team di sviluppo degli OS MS. L' implementazione di
due tecniche diverse tra Win95/98 e WinNT/2K crea non pochi problemi
a chi desidera sfruttare questa funzione e pretende la compatibilita'
su tutte le piattaforme Microsoft. Win95/98 si affidano a un driver
virtuale (VWin32.vxd) per accedere alle periferiche tramite interrupt
software (13h 21h 25h 26h) mentre il secondo , decisamente migliore,
ha un indirizzamento fisico dei dispositivi che facilita enormemente
l'accesso ai dati.
In buona sostanza diciamo che alla MS hanno pasticciato non poco e
non hanno fatto nulla per porvi rimedio. Da qui il successo delle ASPI.
Ma questa e' un'altra storia...
Ciao.
Il Michett@
--

Non funziona.
Io uso Delphi, con questo
Primario / master       : HD , C:
Primario / slave          : HD , D:
Secondario / master : masterizzatore, E:
Secondario / slave     : CD-ROM, F:
quando faccio:
mciSendString('Set cdaudio door open wait', nil, 0, handle);
mciSendString('Set cdaudio door closed wait', nil, 0, handle);
va tutto bene, si apre e chiude il masterizzatore.
quando faccio
mciSendString('open F: type cdaudio alias cd2',nil,0,handle);
//restituisce 0
mciSendString('open F: type cdaudio alias cd1',nil,0,handle);
//restituisce 0
mciSendString('closed F: type cdaudio alias cd2',nil,0,handle);
//restituisce 261
mciSendString('set cd1 door open',nil,0,handle);
//restituisce 263
mciSendString('set cd2 door open',nil,0,handle);
//restituisce 263
mciSendString('set cd1 door closed',nil,0,handle);
//restituisce 263
mciSendString('set cd2 door closed',nil,0,handle);
//restituisce 263
mciSendString('close cd1 wait',nil,0,handle);
//restituisce 263
mciSendString('close cd2 wait',nil,0,handle);
//restituisce 263
non si apre e chiude nulla, perche' ?
Dove trovo il significato dei codici di mciSendString ?
GRAZIE 1000
faf...@tiscalinet.it

//restituisce 263
idem con patate
Prova con questo:
mciSendString('open F: type cdaudio alias cd1',nil,0,handle);
mciSendString('set cd1 door open',nil,0,handle);
qui dovrebbe aprirsi lo sportello
mciSendString('set cd1 door closed',nil,0,handle);
qui dovrebbe chiudersi lo sportello
poi non dimenticare di chiudere l'handle
mciSendString('close cd1',nil,0,handle);
ciao
SAL