Jump to content
Sign in to follow this  
solzorb

CD Tray and Locking

Recommended Posts

solzorb

Fair warning: I'm new to AutoIt.

I've pull code from a couple different places in the forum and put together a piece that HotKey's the Scroll Lock to opening / closing the CD Tray (and automatically closes the tray if left open after five minutes). Thanks to bhoar, bobsobol, and grayfox.

Some programs lock the CD for exclusive access (specifically ImgBurn). Unfortunately my code doesn't allow that and I have to close the script to allow the disc to be burned.

My question, how can I write the code to relinquish control to a program that wants to lock and then resume the HotKey script once the program is done (I run it as a background exe)?

#cs ----------------------------------------------------------------------------

AutoIt Version: 3.2.12.1
Author: solzorb (thanks to bhoar, bobsobol, and grayfox)

Script Function: Use Scroll Lock key for CD Open/Close with 5-min for auto-close


#ce ----------------------------------------------------------------------------

;-----------------------------------------------------
; Setup Globals Environment
;-----------------------------------------------------

Global Const $INVALID_HANDLE_VALUE = 0xFFFFFFFF
Global Const $GENERIC_READ = 0x80000000
Global Const $GENERIC_WRITE = 0x40000000
Global Const $FILE_SHARE_READ = 0x00000001
Global Const $FILE_SHARE_WRITE = 0x00000002
Global Const $OPEN_EXISTING = 3

Global Const $IOCTL_SCSI_PASS_THROUGH = 0x0004D004; (0x00000004 << 16) | ((0x0001 | 0x0002) << 14) | (0x0401 << 2) | 0 [from CTL_CODE macro]
Global Const $SCSI_IOCTL_DATA_IN = 0x01
Global Const $SCSI_IOCTL_DATA_OUT = 0x00
Global Const $SCSI_IOCTL_DATA_UNSPECIFIED = 0x02
Global Const $SPTCDBSIZE = 0x10; always sixteen, IOCTL_SCSI_PASS_THROUGH requires this - windows checks to make sure the size of $spt = 44.
Global Const $REALCDBSIZE = 0x0c; twelve - more compatible than sixteen for ATAPI drives
Global Const $SENSEBUFFERSIZE = 0xF0;240 is max (was 32, then 255, which was stupid for byte alignment reasons)
Global Const $DATABUFFERSIZE = 0x0400;1024 should handle most calls, except IO, firmware and large changers. Increase? (was 512)

Global Const $VK_NUMLOCK = 0x90
Global Const $VK_SCROLL = 0x91
Global Const $VK_CAPITAL = 0x14

Global $cdb
Global $spt
Global $sptwb


;-----------------------------------------------------
; Main Routine
;-----------------------------------------------------


$timer = 0 
HotKeySet("{SCROLLLOCK}","ToggleTray")





While 1
    Sleep(1000)

    If _GetScrollLock() Then
        HotKeySet("{SCROLLLOCK}")
        Send("{SCROLLLOCK off}")
        HotKeySet("{SCROLLLOCK}","ToggleTray")
    EndIf

    If $timer > 0 Then
        If TimerDiff($timer) >= 300000 Then
            CreateDLLStructures()
            $hVolume = OpenVolume(0)
            If $hVolume <> $INVALID_HANDLE_VALUE Then
                If IsTrayOpen($hVolume) Then
                    LoadDisc($hVolume)
                    $timer = 0
                EndIf
            EndIf           
        EndIf
    EndIf

WEnd

Func ToggleTray()
;Call routine to create structures to use with DLLs
CreateDLLStructures()
$hVolume = OpenVolume(0)

Sleep(95)

HotKeySet("{SCROLLLOCK}")
Send("{SCROLLLOCK off}")
HotKeySet("{SCROLLLOCK}","ToggleTray")

If $hVolume <> $INVALID_HANDLE_VALUE Then
If IsTrayOpen($hVolume) Then
LoadDisc($hVolume)
$timer = 0
Else
UnLoadDisc($hVolume)
$timer = TimerInit()
EndIf
EndIf
EndFunc


;-----------------------------------------------------
; Top-level Function Definitions
;-----------------------------------------------------


Func CreateDLLStructures()
$CDB_STRUCT = ("ubyte[" & String($SPTCDBSIZE) & "]")
$SCSI_PASS_THROUGH = "ushort;ubyte;ubyte;ubyte;ubyte;ubyte;ubyte;ubyte;ubyte[3];uint;uint;uint;uint;ubyte[" & String($SPTCDBSIZE) & "]"
$SCSI_PASS_THROUGH_WITH_BUFFERS = $SCSI_PASS_THROUGH & ";ubyte[" & String($SENSEBUFFERSIZE) & "];ubyte[" & String($DATABUFFERSIZE) & "]"

$cdb = DllStructCreate($CDB_STRUCT)
$spt = DllStructCreate($SCSI_PASS_THROUGH);used only for length calculations
$sptwb = DllStructCreate($SCSI_PASS_THROUGH_WITH_BUFFERS)
EndFunc ;==>CreateDLLStructures

Func PopulateCDB_TrayStatus()
$CDBCOMMAND = 0xBD;Mechanism Status in hex

DllStructSetData($cdb, 1, $CDBCOMMAND, 1)
DllStructSetData($cdb, 1, 0x00, 2)
DllStructSetData($cdb, 1, 0x00, 3)
DllStructSetData($cdb, 1, 0x00, 4)
DllStructSetData($cdb, 1, 0x00, 5)
DllStructSetData($cdb, 1, 0x00, 6)
DllStructSetData($cdb, 1, 0x00, 7)
DllStructSetData($cdb, 1, 0x00, 8)
DllStructSetData($cdb, 1, 0x00, 9)
DllStructSetData($cdb, 1, 0x08, 10);Request that the device returns only 08 bytes into our databuffer, which is the defined size of the header We could do more if we had a changer, which would want to give more info, but by setting 8 here, we tell the device not to send more than the header anyway.
DllStructSetData($cdb, 1, 0x00, 11)
DllStructSetData($cdb, 1, 0x00, 12)
;The next four are not used for ATAPI compatibility, but should be set to zero anyway.
DllStructSetData($cdb, 1, 0x00, 13)
DllStructSetData($cdb, 1, 0x00, 14)
DllStructSetData($cdb, 1, 0x00, 15)
DllStructSetData($cdb, 1, 0x00, 16)
EndFunc ;==>PopulateCDB_TrayStatus

Func PopulateCDB_LoadDisc()
$CDBCOMMAND = 0x1B;Start/Stop Unit

DllStructSetData($cdb, 1, $CDBCOMMAND, 1)
DllStructSetData($cdb, 1, 0x00, 2)
DllStructSetData($cdb, 1, 0x00, 3)
DllStructSetData($cdb, 1, 0x00, 4)
DllStructSetData($cdb, 1, 0x03, 5);00000011 = Load Disc and Spin Up
DllStructSetData($cdb, 1, 0x00, 6)
DllStructSetData($cdb, 1, 0x00, 7)
DllStructSetData($cdb, 1, 0x00, 8)
DllStructSetData($cdb, 1, 0x00, 9)
DllStructSetData($cdb, 1, 0x00, 10)
DllStructSetData($cdb, 1, 0x00, 11)
DllStructSetData($cdb, 1, 0x00, 12)
;The next four are not used for ATAPI compatibility, but should be set to zero anyway.
DllStructSetData($cdb, 1, 0x00, 13)
DllStructSetData($cdb, 1, 0x00, 14)
DllStructSetData($cdb, 1, 0x00, 15)
DllStructSetData($cdb, 1, 0x00, 16)
EndFunc ;==>PopulateCDB_LoadDisc

Func PopulateCDB_UnLoadDisc()
$CDBCOMMAND = 0x1B;Start/Stop Unit

DllStructSetData($cdb, 1, $CDBCOMMAND, 1)
DllStructSetData($cdb, 1, 0x00, 2)
DllStructSetData($cdb, 1, 0x00, 3)
DllStructSetData($cdb, 1, 0x00, 4)
DllStructSetData($cdb, 1, 0x02, 5);00000010 = Spin Down and UnLoad Disc
DllStructSetData($cdb, 1, 0x00, 6)
DllStructSetData($cdb, 1, 0x00, 7)
DllStructSetData($cdb, 1, 0x00, 8)
DllStructSetData($cdb, 1, 0x00, 9)
DllStructSetData($cdb, 1, 0x00, 10)
DllStructSetData($cdb, 1, 0x00, 11)
DllStructSetData($cdb, 1, 0x00, 12)
;The next four are not used for ATAPI compatibility, but should be set to zero anyway.
DllStructSetData($cdb, 1, 0x00, 13)
DllStructSetData($cdb, 1, 0x00, 14)
DllStructSetData($cdb, 1, 0x00, 15)
DllStructSetData($cdb, 1, 0x00, 16)
EndFunc ;==>PopulateCDB_UnLoadDisc

Func PopulateSPTWB()
$Len_spt = DllStructGetSize($spt)
;Are these necessary if the optical drive is at a drive letter and we pass the handle, right?
;docs seem to suggest that the port driver fills these in if we are using an enumerated device
$Bus = 0x00
$ID = 0x00
$Lun = 0x00

DllStructSetData($sptwb, 1, $Len_spt);Length of pre-filler to be set before making call
DllStructSetData($sptwb, 2, 0x00);Checked on return from call
DllStructSetData($sptwb, 3, $Bus);SCSI bus # - I believe the port driver fills this in
DllStructSetData($sptwb, 4, $ID);SCSI ID # - I believe the port driver fills this in
DllStructSetData($sptwb, 5, $Lun);SCSI Lun # -I believe the port driver fills this in
DllStructSetData($sptwb, 6, $REALCDBSIZE);Length of CDB to be set before making call (12 for ATAPI compatibility)?
DllStructSetData($sptwb, 7, $SENSEBUFFERSIZE);Length of Sense buffer to be set before making call - or always 32?
DllStructSetData($sptwb, 8, $SCSI_IOCTL_DATA_IN);Flag for Data Transfer direction to be set before making call
;item #9 is simple a placehold for byte alignment, so ignore it
DllStructSetData($sptwb, 10, $DATABUFFERSIZE);Length of Data buffer to be set before making call - or always 512
DllStructSetData($sptwb, 11, 0x05);Timeout for call - to be set before making call
DllStructSetData($sptwb, 12, $Len_spt + $SENSEBUFFERSIZE);Offset from first byte to beginning of data buffer
DllStructSetData($sptwb, 13, $Len_spt);Offset from first byte to beginning of sense buffer
For $i = 1 To $SPTCDBSIZE
DllStructSetData($sptwb, 14, DllStructGetData($cdb, 1, $i), $i);12 bytes of data representing the CDB
Next
DllStructSetData($sptwb, 15, 0x00, 1);Sense Buffer - leave alone before call
DllStructSetData($sptwb, 16, 0x00, 1);Data Buffer - leave alone before call

EndFunc ;==>PopulateSPTWB

;-----------------------------------------------------
; Lower-level Function Definitions
;-----------------------------------------------------

Func DoSCSICall(ByRef $hVolume)
$returnvalue = DllStructCreate("ptr")

;!!! DeviceIOControl expects ptr;long;ptr;long;ptr;long;ptr;ptr !!!
$ret = DllCall( _
"kernel32.dll", "int", _
"DeviceIoControl", _
"hwnd", $hVolume, _
"int", $IOCTL_SCSI_PASS_THROUGH, _
"ptr", DllStructGetPtr($sptwb), _
"int", DllStructGetSize($spt), _
"ptr", DllStructGetPtr($sptwb), _
"int", DllStructGetSize($sptwb), _
"int*", $returnvalue, _
"ptr", 0 _
)

If @error Then
MsgBox(1, "EXITING...", "DeviceIoControl DLLCall failed with error level: " & String(@error) & "!")
exit (1)
EndIf

If $ret[0] = 0 Then
_GetLastErrorMessage("Error in DeviceIoControl call to IOCTL_SCSI_PASS_THROUGH:")
exit (1)
EndIf

EndFunc ;==>DoSCSICall

Func IsTrayOpen(ByRef $hVolume)
;Call routine to set up the CDB of this SCSI transaction's SRB, in this case 0xBD
PopulateCDB_TrayStatus()
;Call routine to set up the SCSI_PASS_THROUGH_WITH_BUFFERS stucture - this is general purpose, though the sizes of globals might need to be adjusted for certain SRBs
PopulateSPTWB(); to ensure a fresh SRB each time
;Perform the SCSI transaction
DoSCSICall($hVolume)

$second_byte = DllStructGetData($sptwb, 16, 2);should be the second byte
;now we need the bit here 00010000

$traystatus = BitAND($second_byte, 0x10)
If $traystatus = 0x10 Then
Return (True)
Else
Return (False)
EndIf

EndFunc ;==>IsTrayOpen

Func LoadDisc(ByRef $hVolume)
;Call routine to set up the CDB of this SCSI transaction's SRB
PopulateCDB_LoadDisc()
;Call routine to set up the SCSI_PASS_THROUGH_WITH_BUFFERS stucture - this is general purpose, though the sizes of globals might need to be adjusted for certain SRBs
PopulateSPTWB(); to ensure a fresh SRB each time
;Perform the SCSI transaction
DoSCSICall($hVolume)

;TODO: Analyze sense data to verify this worked.

EndFunc ;==>LoadDisc

Func UnLoadDisc(ByRef $hVolume)
;Call routine to set up the CDB of this SCSI transaction's SRB
PopulateCDB_UnLoadDisc()
;Call routine to set up the SCSI_PASS_THROUGH_WITH_BUFFERS stucture - this is general purpose, though the sizes of globals might need to be adjusted for certain SRBs
PopulateSPTWB(); to ensure a fresh SRB each time
;Perform the SCSI transaction
DoSCSICall($hVolume)

;TODO: Analyze sense data to verify this worked.

EndFunc ;==>UnLoadDisc

Func OpenVolume($cDriveNumber)
; From AUTOIT forums
Local $hVolume, $uDriveType, $szVolumeName, $dwAccessFlags
Local $szRootName = "\\.\CdRom" & $cDriveNumber
;We need write access in order to send scsi commands.
$dwAccessFlags = BitOR($GENERIC_READ, $GENERIC_WRITE)
$szVolumeName = "\\.\CdRom0"

;in addition to getting the handle, the following also verifies write access, which is required to use the scsi pass through
$hVolume = DllCall( _
"kernel32.dll", "hwnd", _
"CreateFile", _
"str", $szVolumeName, _
"long", $dwAccessFlags, _
"long", BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), _
"ptr", 0, _
"long", $OPEN_EXISTING, _
"long", 0, _
"long", 0 _
)

If @error Then
MsgBox(1, "EXITING...", "CreateFile DLLCall failed!")
exit (1)
EndIf

Return $hVolume[0]
EndFunc ;==>OpenVolume

;===============================================
; _GetLastErrorMessage($DisplayMsgBox="")
; Format the last windows error as a string and return it
; if $DisplayMsgBox <> "" Then it will display a message box w/ the error
; Return Window's error as a string
;===============================================
Func _GetLastErrorMessage($DisplayMsgBox = "")
Local $ret, $s
Local $p = DllStructCreate("char[4096]")
Local Const $FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000

If @error Then Return ""

$ret = DllCall("Kernel32.dll", "int", "GetLastError")
$LastError = $ret[0]
$ret = DllCall("kernel32.dll", "int", "FormatMessage", _
"int", $FORMAT_MESSAGE_FROM_SYSTEM, _
"ptr", 0, _
"int", $LastError, _
"int", 0, _
"ptr", DllStructGetPtr($p), _
"int", 4096, _
"ptr", 0)
$s = DllStructGetData($p, 1)
If $DisplayMsgBox <> "" Then MsgBox(0, "_GetLastErrorMessage", $DisplayMsgBox & @CRLF & String($LastError) & "-" & $s)
Return $s
EndFunc ;==>_GetLastErrorMessage

Func _GetNumLock()
Local $ret
$ret = DllCall("user32.dll","long","GetKeyState","long",$VK_NUMLOCK)
Return $ret[0]
EndFunc

Func _GetScrollLock()
Local $ret
$ret = DllCall("user32.dll","long","GetKeyState","long",$VK_SCROLL)
Return $ret[0]
EndFunc

Share this post


Link to post
Share on other sites
solzorb

In case anyone runs across this, I got it figured out a while ago with some more digging. Simple enough but wasn't sure how to do it at the time.

CloseVolume($hVolume)

Func CloseVolume($hVolume)

Local $rVal = DllCall("kernel32.dll", "hwnd", "CloseHandle", "hwnd", $hVolume)

If @error Then Return SetError(-1, -1, 0)

Return $rVal[0]

EndFunc ;==>_CloseVolume

Below is the final set of code I'm using, it got tweaked a little more to fix an issue (Scroll Lock didn't always turn off as planned).

#cs ----------------------------------------------------------------------------

AutoIt Version: 3.2.12.1
Author: solzorb (thanks to bhoar, bobsobol, garyfrost, and grayfox)

Script Function: Use Scroll Lock key for CD Open/Close with 5-min for auto-close


#ce ----------------------------------------------------------------------------

;-----------------------------------------------------
; Setup Globals Environment
;-----------------------------------------------------

Global Const $INVALID_HANDLE_VALUE = 0xFFFFFFFF
Global Const $GENERIC_READ = 0x80000000
Global Const $GENERIC_WRITE = 0x40000000
Global Const $FILE_SHARE_READ = 0x00000001
Global Const $FILE_SHARE_WRITE = 0x00000002
Global Const $OPEN_EXISTING = 3

Global Const $IOCTL_SCSI_PASS_THROUGH = 0x0004D004; (0x00000004 << 16) | ((0x0001 | 0x0002) << 14) | (0x0401 << 2) | 0 [from CTL_CODE macro]
Global Const $SCSI_IOCTL_DATA_IN = 0x01
Global Const $SCSI_IOCTL_DATA_OUT = 0x00
Global Const $SCSI_IOCTL_DATA_UNSPECIFIED = 0x02
Global Const $SPTCDBSIZE = 0x10; always sixteen, IOCTL_SCSI_PASS_THROUGH requires this - windows checks to make sure the size of $spt = 44.
Global Const $REALCDBSIZE = 0x0c; twelve - more compatible than sixteen for ATAPI drives
Global Const $SENSEBUFFERSIZE = 0xF0;240 is max (was 32, then 255, which was stupid for byte alignment reasons)
Global Const $DATABUFFERSIZE = 0x0400;1024 should handle most calls, except IO, firmware and large changers. Increase? (was 512)

Global Const $VK_NUMLOCK = 0x90
Global Const $VK_SCROLL = 0x91
Global Const $VK_CAPITAL = 0x14

Global $cdb
Global $spt
Global $sptwb


;-----------------------------------------------------
; Main Routine
;-----------------------------------------------------


$timer = 0 
HotKeySet("{SCROLLLOCK}","ToggleTray")




While 1
    
    If _GetScrollLock() Then
        HotKeySet("{SCROLLLOCK}")
        Send("{SCROLLLOCK off}")
        HotKeySet("{SCROLLLOCK}","ToggleTray")
    EndIf

    If $timer > 0 Then
        If TimerDiff($timer) >= 300000 Then
            CreateDLLStructures()
            $hVolume = OpenVolume(0)
            If $hVolume <> $INVALID_HANDLE_VALUE Then
                If IsTrayOpen($hVolume) Then
                    LoadDisc($hVolume)
                    $timer = 0
                EndIf
            EndIf
            CloseVolume($hVolume)
        EndIf
    EndIf

    Sleep(1000)
    
WEnd

Func ToggleTray()
;Call routine to create structures to use with DLLs
CreateDLLStructures()
$hVolume = OpenVolume(0)

Sleep(95)

If $hVolume <> $INVALID_HANDLE_VALUE Then
    If IsTrayOpen($hVolume) Then
        LoadDisc($hVolume)
        $timer = 0
    Else
        UnLoadDisc($hVolume)
        $timer = TimerInit()
    EndIf
EndIf

CloseVolume($hVolume)

EndFunc


;-----------------------------------------------------
; Top-level Function Definitions
;-----------------------------------------------------


Func CreateDLLStructures()
$CDB_STRUCT = ("ubyte[" & String($SPTCDBSIZE) & "]")
$SCSI_PASS_THROUGH = "ushort;ubyte;ubyte;ubyte;ubyte;ubyte;ubyte;ubyte;ubyte[3];uint;uint;uint;uint;ubyte[" & String($SPTCDBSIZE) & "]"
$SCSI_PASS_THROUGH_WITH_BUFFERS = $SCSI_PASS_THROUGH & ";ubyte[" & String($SENSEBUFFERSIZE) & "];ubyte[" & String($DATABUFFERSIZE) & "]"

$cdb = DllStructCreate($CDB_STRUCT)
$spt = DllStructCreate($SCSI_PASS_THROUGH);used only for length calculations
$sptwb = DllStructCreate($SCSI_PASS_THROUGH_WITH_BUFFERS)
EndFunc ;==>CreateDLLStructures

Func PopulateCDB_TrayStatus()
$CDBCOMMAND = 0xBD;Mechanism Status in hex

DllStructSetData($cdb, 1, $CDBCOMMAND, 1)
DllStructSetData($cdb, 1, 0x00, 2)
DllStructSetData($cdb, 1, 0x00, 3)
DllStructSetData($cdb, 1, 0x00, 4)
DllStructSetData($cdb, 1, 0x00, 5)
DllStructSetData($cdb, 1, 0x00, 6)
DllStructSetData($cdb, 1, 0x00, 7)
DllStructSetData($cdb, 1, 0x00, 8)
DllStructSetData($cdb, 1, 0x00, 9)
DllStructSetData($cdb, 1, 0x08, 10);Request that the device returns only 08 bytes into our databuffer, which is the defined size of the header We could do more if we had a changer, which would want to give more info, but by setting 8 here, we tell the device not to send more than the header anyway.
DllStructSetData($cdb, 1, 0x00, 11)
DllStructSetData($cdb, 1, 0x00, 12)
;The next four are not used for ATAPI compatibility, but should be set to zero anyway.
DllStructSetData($cdb, 1, 0x00, 13)
DllStructSetData($cdb, 1, 0x00, 14)
DllStructSetData($cdb, 1, 0x00, 15)
DllStructSetData($cdb, 1, 0x00, 16)
EndFunc ;==>PopulateCDB_TrayStatus

Func PopulateCDB_LoadDisc()
$CDBCOMMAND = 0x1B;Start/Stop Unit

DllStructSetData($cdb, 1, $CDBCOMMAND, 1)
DllStructSetData($cdb, 1, 0x00, 2)
DllStructSetData($cdb, 1, 0x00, 3)
DllStructSetData($cdb, 1, 0x00, 4)
DllStructSetData($cdb, 1, 0x03, 5);00000011 = Load Disc and Spin Up
DllStructSetData($cdb, 1, 0x00, 6)
DllStructSetData($cdb, 1, 0x00, 7)
DllStructSetData($cdb, 1, 0x00, 8)
DllStructSetData($cdb, 1, 0x00, 9)
DllStructSetData($cdb, 1, 0x00, 10)
DllStructSetData($cdb, 1, 0x00, 11)
DllStructSetData($cdb, 1, 0x00, 12)
;The next four are not used for ATAPI compatibility, but should be set to zero anyway.
DllStructSetData($cdb, 1, 0x00, 13)
DllStructSetData($cdb, 1, 0x00, 14)
DllStructSetData($cdb, 1, 0x00, 15)
DllStructSetData($cdb, 1, 0x00, 16)
EndFunc ;==>PopulateCDB_LoadDisc

Func PopulateCDB_UnLoadDisc()
$CDBCOMMAND = 0x1B;Start/Stop Unit

DllStructSetData($cdb, 1, $CDBCOMMAND, 1)
DllStructSetData($cdb, 1, 0x00, 2)
DllStructSetData($cdb, 1, 0x00, 3)
DllStructSetData($cdb, 1, 0x00, 4)
DllStructSetData($cdb, 1, 0x02, 5);00000010 = Spin Down and UnLoad Disc
DllStructSetData($cdb, 1, 0x00, 6)
DllStructSetData($cdb, 1, 0x00, 7)
DllStructSetData($cdb, 1, 0x00, 8)
DllStructSetData($cdb, 1, 0x00, 9)
DllStructSetData($cdb, 1, 0x00, 10)
DllStructSetData($cdb, 1, 0x00, 11)
DllStructSetData($cdb, 1, 0x00, 12)
;The next four are not used for ATAPI compatibility, but should be set to zero anyway.
DllStructSetData($cdb, 1, 0x00, 13)
DllStructSetData($cdb, 1, 0x00, 14)
DllStructSetData($cdb, 1, 0x00, 15)
DllStructSetData($cdb, 1, 0x00, 16)
EndFunc ;==>PopulateCDB_UnLoadDisc

Func PopulateSPTWB()
$Len_spt = DllStructGetSize($spt)
;Are these necessary if the optical drive is at a drive letter and we pass the handle, right?
;docs seem to suggest that the port driver fills these in if we are using an enumerated device
$Bus = 0x00
$ID = 0x00
$Lun = 0x00

DllStructSetData($sptwb, 1, $Len_spt);Length of pre-filler to be set before making call
DllStructSetData($sptwb, 2, 0x00);Checked on return from call
DllStructSetData($sptwb, 3, $Bus);SCSI bus # - I believe the port driver fills this in
DllStructSetData($sptwb, 4, $ID);SCSI ID # - I believe the port driver fills this in
DllStructSetData($sptwb, 5, $Lun);SCSI Lun # -I believe the port driver fills this in
DllStructSetData($sptwb, 6, $REALCDBSIZE);Length of CDB to be set before making call (12 for ATAPI compatibility)?
DllStructSetData($sptwb, 7, $SENSEBUFFERSIZE);Length of Sense buffer to be set before making call - or always 32?
DllStructSetData($sptwb, 8, $SCSI_IOCTL_DATA_IN);Flag for Data Transfer direction to be set before making call
;item #9 is simple a placehold for byte alignment, so ignore it
DllStructSetData($sptwb, 10, $DATABUFFERSIZE);Length of Data buffer to be set before making call - or always 512
DllStructSetData($sptwb, 11, 0x05);Timeout for call - to be set before making call
DllStructSetData($sptwb, 12, $Len_spt + $SENSEBUFFERSIZE);Offset from first byte to beginning of data buffer
DllStructSetData($sptwb, 13, $Len_spt);Offset from first byte to beginning of sense buffer
For $i = 1 To $SPTCDBSIZE
DllStructSetData($sptwb, 14, DllStructGetData($cdb, 1, $i), $i);12 bytes of data representing the CDB
Next
DllStructSetData($sptwb, 15, 0x00, 1);Sense Buffer - leave alone before call
DllStructSetData($sptwb, 16, 0x00, 1);Data Buffer - leave alone before call

EndFunc ;==>PopulateSPTWB



;-----------------------------------------------------
; Lower-level Function Definitions
;-----------------------------------------------------

Func DoSCSICall(ByRef $hVolume)
$returnvalue = DllStructCreate("ptr")

;!!! DeviceIOControl expects ptr;long;ptr;long;ptr;long;ptr;ptr !!!
$ret = DllCall( _
"kernel32.dll", "int", _
"DeviceIoControl", _
"hwnd", $hVolume, _
"int", $IOCTL_SCSI_PASS_THROUGH, _
"ptr", DllStructGetPtr($sptwb), _
"int", DllStructGetSize($spt), _
"ptr", DllStructGetPtr($sptwb), _
"int", DllStructGetSize($sptwb), _
"int*", $returnvalue, _
"ptr", 0 _
)

If @error Then
MsgBox(1, "EXITING...", "DeviceIoControl DLLCall failed with error level: " & String(@error) & "!")
exit (1)
EndIf

If $ret[0] = 0 Then
_GetLastErrorMessage("Error in DeviceIoControl call to IOCTL_SCSI_PASS_THROUGH:")
exit (1)
EndIf

EndFunc ;==>DoSCSICall

Func IsTrayOpen(ByRef $hVolume)
;Call routine to set up the CDB of this SCSI transaction's SRB, in this case 0xBD
PopulateCDB_TrayStatus()
;Call routine to set up the SCSI_PASS_THROUGH_WITH_BUFFERS stucture - this is general purpose, though the sizes of globals might need to be adjusted for certain SRBs
PopulateSPTWB(); to ensure a fresh SRB each time
;Perform the SCSI transaction
DoSCSICall($hVolume)

$second_byte = DllStructGetData($sptwb, 16, 2);should be the second byte
;now we need the bit here 00010000

$traystatus = BitAND($second_byte, 0x10)
If $traystatus = 0x10 Then
Return (True)
Else
Return (False)
EndIf

EndFunc ;==>IsTrayOpen

Func LoadDisc(ByRef $hVolume)
;Call routine to set up the CDB of this SCSI transaction's SRB
PopulateCDB_LoadDisc()
;Call routine to set up the SCSI_PASS_THROUGH_WITH_BUFFERS stucture - this is general purpose, though the sizes of globals might need to be adjusted for certain SRBs
PopulateSPTWB(); to ensure a fresh SRB each time
;Perform the SCSI transaction
DoSCSICall($hVolume)

;TODO: Analyze sense data to verify this worked.

EndFunc ;==>LoadDisc

Func UnLoadDisc(ByRef $hVolume)
;Call routine to set up the CDB of this SCSI transaction's SRB
PopulateCDB_UnLoadDisc()
;Call routine to set up the SCSI_PASS_THROUGH_WITH_BUFFERS stucture - this is general purpose, though the sizes of globals might need to be adjusted for certain SRBs
PopulateSPTWB(); to ensure a fresh SRB each time
;Perform the SCSI transaction
DoSCSICall($hVolume)

;TODO: Analyze sense data to verify this worked.

EndFunc ;==>UnLoadDisc

Func OpenVolume($cDriveNumber)
; From AUTOIT forums
Local $hVolume, $uDriveType, $szVolumeName, $dwAccessFlags
Local $szRootName = "\\.\CdRom" & $cDriveNumber
;We need write access in order to send scsi commands.
$dwAccessFlags = BitOR($GENERIC_READ, $GENERIC_WRITE)
$szVolumeName = "\\.\CdRom0"

;in addition to getting the handle, the following also verifies write access, which is required to use the scsi pass through
$hVolume = DllCall( _
"kernel32.dll", "hwnd", _
"CreateFile", _
"str", $szVolumeName, _
"long", $dwAccessFlags, _
"long", BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), _
"ptr", 0, _
"long", $OPEN_EXISTING, _
"long", 0, _
"long", 0 _
)

If @error Then
MsgBox(1, "EXITING...", "CreateFile DLLCall failed!")
exit (1)
EndIf

Return $hVolume[0]
EndFunc ;==>OpenVolume

Func CloseVolume($hVolume)
    Local $rVal = DllCall("kernel32.dll", "hwnd", "CloseHandle", "hwnd", $hVolume)
    If @error Then Return SetError(-1, -1, 0)
    Return $rVal[0]
EndFunc   ;==>_CloseVolume


;===============================================
; _GetLastErrorMessage($DisplayMsgBox="")
; Format the last windows error as a string and return it
; if $DisplayMsgBox <> "" Then it will display a message box w/ the error
; Return Window's error as a string
;===============================================
Func _GetLastErrorMessage($DisplayMsgBox = "")
Local $ret, $s
Local $p = DllStructCreate("char[4096]")
Local Const $FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000

If @error Then Return ""

$ret = DllCall("Kernel32.dll", "int", "GetLastError")
$LastError = $ret[0]
$ret = DllCall("kernel32.dll", "int", "FormatMessage", _
"int", $FORMAT_MESSAGE_FROM_SYSTEM, _
"ptr", 0, _
"int", $LastError, _
"int", 0, _
"ptr", DllStructGetPtr($p), _
"int", 4096, _
"ptr", 0)
$s = DllStructGetData($p, 1)
If $DisplayMsgBox <> "" Then MsgBox(0, "_GetLastErrorMessage", $DisplayMsgBox & @CRLF & String($LastError) & "-" & $s)
Return $s
EndFunc ;==>_GetLastErrorMessage

Func _GetNumLock()
Local $ret
$ret = DllCall("user32.dll","long","GetKeyState","long",$VK_NUMLOCK)
Return $ret[0]
EndFunc

Func _GetScrollLock()
Local $ret
$ret = DllCall("user32.dll","long","GetKeyState","long",$VK_SCROLL)
Return $ret[0]
EndFunc

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.