Jump to content
Sign in to follow this  
sb1920alk

Can't Open the CD tray in WinPE

Recommended Posts

sb1920alk

I'm using WinPE 3.0. This script opens the CD tray fine in XP, but not in WinPE. Any ideas or can someone confirm that it is not possible?

Thanks

$var = DriveGetDrive( "CDROM" )
If NOT @error Then
    For $i = 1 to $var[0]
        CDTray($var[$i],"open")
    Next
EndIf

Share this post


Link to post
Share on other sites
James

I'm using WinPE 3.0. This script opens the CD tray fine in XP, but not in WinPE. Any ideas or can someone confirm that it is not possible?

How about you find out what the error is, then see why.

$var = DriveGetDrive( "CDROM" )
If NOT @error Then
    For $i = 1 to $var[0]
        CDTray($var[$i],"open")
    Next
Else
    ConsoleWrite(@error & @CRLF)
EndIf

Share this post


Link to post
Share on other sites
sb1920alk

How about you find out what the error is, then see why.

No error. Running your code did nothing visible. I tried msgbox too, but still no. Then I ran the code below to make sure it was doing something. I get a message box with d: in it. So...it can list the drives, but the CDTray function isn't doing anything. Also, if I open Notepad, File->Open, right-click on the cd drive, and select "Eject", it really does eject, so WinPE has the ability to open the tray. I added @error to my message box and it's returning 0, so CDTray thinks it's locked or it's not a CD drive or something, even though DriveGetDrive says it is a CD drive. I'm not sure.

$var = DriveGetDrive( "CDROM" )
If NOT @error Then
    For $i = 1 to $var[0]
        CDTray($var[$i],"open")
        MsgBox(0,"",$var[$i] & @CRLF & @error)
    Next
Else
    MsgBox(0,"",@error)
    ConsoleWrite(@error & @CRLF)
EndIf

Share this post


Link to post
Share on other sites
sb1920alk

I have a solution now, so it's not a big deal. I tried adding the scripting package and running this vbscript:

Const CDROM = 4
For Each d in CreateObject("Scripting.FileSystemObject").Drives
    If d.DriveType = CDROM Then
        Eject d.DriveLetter & ":\"
    End If
Next

Sub Eject(CDROM)
    Dim ssfDrives
    ssfDrives = 17
    CreateObject("Shell.Application")_
    .Namespace(ssfDrives).ParseName(CDROM).InvokeVerb("E&ject")
End Sub

...but it did nothing.

Since I already added the scripting package, I tried this:

Set oWMP = CreateObject("WMPlayer.OCX.7" )
Set colCDROMs = oWMP.cdromCollection

For i = 0 to colCDROMs.Count - 1
    colCDROMs.Item(i).Eject
Next

...but it errored (no WMP in WinPE).

Running the original au3 as adminitrator didn't help. I stumbled across this link: http://memberwebs.com/stef/software/eject/eject.exe, and it worked fine.

Obviously, I would prefer not to have to rely on a helper file, but it's good enough for now. If anyone can get the tray to open in WinPE 3.0 with pure AutoIT code, or vbscript, please let me know.

Thanks,

Share this post


Link to post
Share on other sites
trancexx

Try this:

Global $sDriveLetter = "D:" ;<- correct letter of your CD drive

Global $hFile = _OpenDrive($sDriveLetter) ; as getting handle
_Eject($hFile) ; ejecting
; ...Close handle


Func _OpenDrive($sDriveLetter)

    Local $aCall = DllCall("kernel32.dll", "ptr", "CreateFileW", _
            "wstr", "\\.\" & $sDriveLetter, _
            "dword", 0x80000000, _ ; GENERIC_READ
            "dword", 3, _ ; FILE_SHARE_READ|FILE_SHARE_WRITE
            "ptr", 0, _
            "dword", 3, _ ; OPEN_EXISTING
            "dword", 0, _ ; SECURITY_ANONYMOUS
            "ptr", 0)

    If @error Or $aCall[0] = -1 Then
        Return SetError(1, 0, 0)
    EndIf

    Return $aCall[0]

EndFunc   ;==>_OpenDrive

Func _Eject($hDriveHandle)

    Local $aCall = DllCall("kernel32.dll", "int", "DeviceIoControl", _
            "ptr", $hDriveHandle, _
            "dword", 0x2D4808, _ ; IOCTL_STORAGE_EJECT_MEDIA
            "ptr", 0, _
            "dword", 0, _
            "ptr", 0, _
            "dword", 0, _
            "dword*", 0, _
            "ptr", 0)

    If @error Or Not $aCall[0] Then
        Return SetError(1, 0, 0)
    EndIf

    Return 1

EndFunc   ;==>_Eject

Just set proper $sDriveLetter before. If that doesn't work then something is terribly wrong with your hardware.

edit:

Thing is AutoIt is using winmm.dll (mciSendStringW function) to do the job. Apparently on your system that's not working properly.

AutoIt's way of oppening that door, translated to AutoIt for drive D, seems to be (speculation):

DllCall("winmm.dll", "int", "mciSendStringW", "wstr", "open D: type cdaudio alias cd wait", "ptr", 0, "dword", 0, "ptr", 0)
DllCall("winmm.dll", "int", "mciSendStringW", "wstr", "set cd door open wait", "ptr", 0, "dword", 0, "ptr", 0)
DllCall("winmm.dll", "int", "mciSendStringW", "wstr", "close cd wait", "ptr", 0, "dword", 0, "ptr", 0)
;...
Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
sb1920alk

Try this:

Global $sDriveLetter = "D:" ;<- correct letter of your CD drive

Global $hFile = _OpenDrive($sDriveLetter) ; as getting handle
_Eject($hFile) ; ejecting
; ...Close handle


Func _OpenDrive($sDriveLetter)

    Local $aCall = DllCall("kernel32.dll", "ptr", "CreateFileW", _
            "wstr", "\\.\" & $sDriveLetter, _
            "dword", 0x80000000, _ ; GENERIC_READ
            "dword", 3, _ ; FILE_SHARE_READ|FILE_SHARE_WRITE
            "ptr", 0, _
            "dword", 3, _ ; OPEN_EXISTING
            "dword", 0, _ ; SECURITY_ANONYMOUS
            "ptr", 0)

    If @error Or $aCall[0] = -1 Then
        Return SetError(1, 0, 0)
    EndIf

    Return $aCall[0]

EndFunc   ;==>_OpenDrive

Func _Eject($hDriveHandle)

    Local $aCall = DllCall("kernel32.dll", "int", "DeviceIoControl", _
            "ptr", $hDriveHandle, _
            "dword", 0x2D4808, _ ; IOCTL_STORAGE_EJECT_MEDIA
            "ptr", 0, _
            "dword", 0, _
            "ptr", 0, _
            "dword", 0, _
            "dword*", 0, _
            "ptr", 0)

    If @error Or Not $aCall[0] Then
        Return SetError(1, 0, 0)
    EndIf

    Return 1

EndFunc   ;==>_Eject

Just set proper $sDriveLetter before....

That worked. Very nice...thank you. I much prefer code I can see rather than a black box like the exe above I found. I still wonder why CDTray didn't work.

Share this post


Link to post
Share on other sites
trancexx

That worked. Very nice...thank you. I much prefer code I can see rather than a black box like the exe above I found. I still wonder why CDTray didn't work.

Just debug that other code. Check errors and return values of the DllCalls and you will know why it doesn't work.

You know how to do that?


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
sb1920alk

Just debug that other code. Check errors and return values of the DllCalls and you will know why it doesn't work.

You know how to do that?

I have not worked with DllCalls much before.

Share this post


Link to post
Share on other sites
trancexx

I have not worked with DllCalls much before.

Something like this:

#include <WinAPI.au3>

; FIRST CALL
$aCall = DllCall("winmm.dll", "dword", "mciSendStringW", "wstr", "open D: type cdaudio alias cd wait", "ptr", 0, "dword", 0, "ptr", 0)
If @error Then
    MsgBox(48, "Error", "DllCall function to mciSendStringW failed with first call" & @CRLF & "Error code number = " & @error)
Else
    If $aCall[0] Then
        MsgBox(48, "Error", "mciSendStringW failed with first call" & @CRLF & "Error code number = " & _WinAPI_LoWord($aCall[0]))
    Else
        MsgBox(64, "First call ok", "mciSendStringW was successful with first call")
    EndIf
EndIf

; SECOND CALL
$aCall = DllCall("winmm.dll", "dword", "mciSendStringW", "wstr", "set cd door open wait", "ptr", 0, "dword", 0, "ptr", 0)
If @error Then
    MsgBox(48, "Error", "DllCall function to mciSendStringW failed with second call" & @CRLF & "Error code number = " & @error)
Else
    If $aCall[0] Then
        MsgBox(48, "Error", "mciSendStringW failed with second call" & @CRLF & "Error code number = " & _WinAPI_LoWord($aCall[0]))
    Else
        MsgBox(64, "Second call ok", "mciSendStringW was successful with second call")
    EndIf
EndIf

; THIRD CALL
$aCall = DllCall("winmm.dll", "dword", "mciSendStringW", "wstr", "close cd wait", "ptr", 0, "dword", 0, "ptr", 0)
If @error Then
    MsgBox(48, "Error", "DllCall function to mciSendStringW failed with third call" & @CRLF & "Error code number = " & @error)
Else
    If $aCall[0] Then
        MsgBox(48, "Error", "mciSendStringW failed with third call" & @CRLF & "Error code number = " & _WinAPI_LoWord($aCall[0]))
    Else
        MsgBox(64, "Third call ok", "mciSendStringW was successful with third call")
    EndIf
EndIf


; Possible errors: http://msdn.microsoft.com/en-us/library/dd757162(VS.85).aspx

edit: int -> dword

Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
sb1920alk

mciSendStringW failed with first call

Error code number = 306

...click OK...

mciSendStringW failed with second call

Error code number = 306

...click OK...

mciSendStringW failed with third call

Error code number = 263

Share this post


Link to post
Share on other sites
trancexx

That's MCIERR_DEVICE_NOT_INSTALLED twice and the last one is MCIERR_INVALID_DEVICE_NAME.

Derive conclusions yourself.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
duncanchaos

I need to find a way to do this under WinPE 64bit. There is no 32bit subsystem loading on one of my customer's WinPE 64bit boot CD and I need to find a way to eject the CD. The code in the earlier post makes a call to kernel32.dll. Is there a 64bit equivalent?

Share this post


Link to post
Share on other sites
trancexx

I need to find a way to do this under WinPE 64bit. There is no 32bit subsystem loading on one of my customer's WinPE 64bit boot CD and I need to find a way to eject the CD. The code in the earlier post makes a call to kernel32.dll. Is there a 64bit equivalent?

Did you try it?

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
AdmiralAlkex

I need to find a way to do this under WinPE 64bit. There is no 32bit subsystem loading on one of my customer's WinPE 64bit boot CD and I need to find a way to eject the CD. The code in the earlier post makes a call to kernel32.dll. Is there a 64bit equivalent?

What do you mean? If your exe is x64 then you're using the x64 kernel32.dll.

On x64 windows x64 apps use \System32\, and x86 apps use \SysWOW64\ and to provide backwards compatibility it's all "automagically" redirected by WoW64.

Here's some more reading:

Programming Guide for 64-bit Windows

File System Redirector

Edit: trancexx cheated! :idea:

Edited by AdmiralAlkex

Share this post


Link to post
Share on other sites
zorphnog

I've ran into this exact issue running my scripts in WinPE a while back. This is what I use, works great for me.

Func _EjectDisc()
    Local $sDrive, $aDrives, $oShell, $iTimer
    $aDrives = DriveGetDrive("all")
    If Not @error Then
        For $i = 1 To $aDrives[0]
            If DriveGetType($aDrives[$i] & "\") = "CDROM" Then
                $sDrive = StringUpper($aDrives[$i]) & "\"
                ExitLoop
            EndIf
        Next
    EndIf

    $oShell = ObjCreate("Shell.Application")
    $oShell.Namespace(17).ParseName($sDrive).InvokeVerb("Eject")

    $iTimer = TimerInit()
    While DriveStatus($sDrive) <> "NOTREADY" And TimerDiff($iTimer) < 10000
        Sleep(10)
    WEnd
EndFunc  ;==>_EjectDisc

The timeout loop at the end was added because the eject command does not block, so the timeout loop waits for the NOTREADY status on the drive or 10 seconds.

Share this post


Link to post
Share on other sites
Homes32

Try this:

Global $sDriveLetter = "D:" ;<- correct letter of your CD drive

Global $hFile = _OpenDrive($sDriveLetter) ; as getting handle
_Eject($hFile) ; ejecting
; ...Close handle

......

noticed your comment in the code above. is it necessary to use CloseHandle() to free $hFile afterwords or not? Edited by Homes32

Share this post


Link to post
Share on other sites
trancexx

noticed your comment in the code above. is it necessary to use CloseHandle() to free $hFile afterwords or not?

I's good practice to close opened handle when no longer needed. If you ejected the drive then close the handle.

Not closing handles can lead to different problems with your application and even the whole system. Of course I don't mean one handle here, more like thousands.

But luckily, when (leaking) app terminates, system clears the whole memory space of that process, releasing everything that process had locked.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
Homes32

I's good practice to close opened handle when no longer needed. If you ejected the drive then close the handle.

Not closing handles can lead to different problems with your application and even the whole system. Of course I don't mean one handle here, more like thousands.

But luckily, when (leaking) app terminates, system clears the whole memory space of that process, releasing everything that process had locked.

ok thanks.

for those interested here is the sister func to close the cdrom drive. use in conjunction with trancexx's post here

Func _CloseTray($hDriveLetter)

        Local $aCall = DllCall("kernel32.dll", "int", "DeviceIoControl", _
            "ptr", $hDriveLetter, _
            "dword", 0x002D480C, _ ; IOCTL_STORAGE_LOAD_MEDIA
            "ptr", 0, _
            "dword", 0, _
            "ptr", 0, _
            "dword", 0, _
            "dword*", 0, _
            "ptr", 0)

    If @error Or Not $aCall[0] Then
        Return SetError(1, 0, 0)
    EndIf

    Return 1
EndFunc

Share this post


Link to post
Share on other sites
ModemJunki

I realize this is an old topic but this really saved me a lot of time with a Windows PE task, so I will post both the Eject Tray and Close Tray functions using the same variables here. For sure the built-in CDTray function wasn't working in my PE environment, where it seems to work fine in my Windows environment.

#include <WinAPI.au3>
;~ MJ ~; You should pass a drive letter to these functions, or you can fix it.
;~ MJ ~; I set mine as an EnvSet in my custom PE launcher because my internal client
;~ MJ ~; insisted on keeping ancient MS-DOS based batch files alive
;~ MJ ~; $drive = EnvGet("YourDriveHere") lots of ways to get a driveletter,
$drive = "F:"
Global $sDriveLetter = $drive

Global $hFile = _OpenDrive($sDriveLetter) ; as getting handle

_Eject($hFile) ; ejecting
Sleep(5000); really you only need one function or the other, not both like this
_CloseTray($hFile); closing

_WinAPI_CloseHandle($hFile); ...Close handle

;~ MJ ~; Functions below ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Func _OpenDrive($sDriveLetter)
Local $aCall = DllCall("kernel32.dll", "ptr", "CreateFileW", _
   "wstr", "." & $sDriveLetter, _
   "dword", 0x80000000, _ ; GENERIC_READ
   "dword", 3, _ ; FILE_SHARE_READ|FILE_SHARE_WRITE
   "ptr", 0, _
   "dword", 3, _ ; OPEN_EXISTING
   "dword", 0, _ ; SECURITY_ANONYMOUS
   "ptr", 0)
If @error Or $aCall[0] = -1 Then
  Return SetError(1, 0, 0)
EndIf
Return $aCall[0]
EndFunc   ;==>_OpenDrive
Func _Eject($hDriveHandle)
Local $aCall = DllCall("kernel32.dll", "int", "DeviceIoControl", _
   "ptr", $hDriveHandle, _
   "dword", 0x2D4808, _ ; IOCTL_STORAGE_EJECT_MEDIA
   "ptr", 0, _
   "dword", 0, _
   "ptr", 0, _
   "dword", 0, _
   "dword*", 0, _
   "ptr", 0)
If @error Or Not $aCall[0] Then
  Return SetError(1, 0, 0)
EndIf
Return 1
EndFunc   ;==>_Eject
Func _CloseTray($hDriveHandle)
Local $aCall = DllCall("kernel32.dll", "int", "DeviceIoControl", _
   "ptr", $hDriveHandle, _
   "dword", 0x002D480C, _ ; IOCTL_STORAGE_LOAD_MEDIA
   "ptr", 0, _
   "dword", 0, _
   "ptr", 0, _
   "dword", 0, _
   "dword*", 0, _
   "ptr", 0)
If @error Or Not $aCall[0] Then
  Return SetError(1, 0, 0)
EndIf
Return 1
EndFunc   ;==>_CloseTray

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  

×