Sign in to follow this  
Followers 0
Champak

Detect CDROM Media Change

17 posts in this topic

#1 ·  Posted (edited)

1/ The dialog that pops up when you put in a CD/DVD, how can I have my app recieve that notification, without doing some type of loop looking for that window? Can I recieve the same "signal" that is causing that dialog?

2/ How can I suppress that dialog/window?

Edited by Champak

Share this post


Link to post
Share on other sites



#3 ·  Posted (edited)

Thanks, that helps to an extent. I have it basically functioning how I want, however, can you/anyone see a way I can make this into an event function, instead of putting it in the loop? And, still looking for answer to "2" in OP.

((((CODE IN MY NEXT POST))))

And if anyone is interested a lot of other CD rom functions available and ability to determine if ROM is writable or not...I've seen a few of those requests.

http://msdn.microsoft.com/en-us/library/aa394081(VS.85).aspx

Edited by Champak

Share this post


Link to post
Share on other sites

OK, so I decided instead of messing with the registry and other stuff, to just script it to hold down the shift key for a second. But two issues still remain, have this operate like an event...if that's possible, and get that first commented line working.."Not working as I think it should...investigate". Not a major issue, but I believe that it should cause less load/more efficient because it is going to a specific drive. Am I correct in thinking that?

$strDrive = "F:"
$objWMIService = ObjGet("winmgmts:\\.\root\cimv2")

 $colEvents = $objWMIService.ExecNotificationQuery _
    ("Select * From __InstanceOperationEvent Within 5 Where " _
        & "TargetInstance isa 'Win32_CDROMDrive'"); and " _ 
        ;   & "TargetInstance.Drive = " & $strDrive & "" );=========Not working as I think it should...investigate

While 1
     $objEvent = $colEvents.NextEvent
    If $objEvent.TargetInstance.MediaLoaded = True And $objEvent.TargetInstance.Drive = $strDrive Then 
        Send("{SHIFTDOWN}") 
        ;Test to see exactly what my drive is sayinggdHGDFGFDGJHG
        ConsoleWrite("!========================================================" & @CRLF)
        ConsoleWrite("1/ " & $objEvent.TargetInstance.MediaLoaded & @CRLF)
        ConsoleWrite("3/ " & $objEvent.TargetInstance.DeviceID & @CRLF)
        ConsoleWrite("4/ " & $objEvent.TargetInstance.Description & @CRLF)
        ConsoleWrite("5/ " & $objEvent.TargetInstance.VolumeName & @CRLF);Will need this in the function to be created
        ConsoleWrite("6/ " & $objEvent.TargetInstance.VolumeSerialNumber & @CRLF);Will need this in the function to be created
        ConsoleWrite("12/ " & $objEvent.TargetInstance.Caption & @CRLF)
        ConsoleWrite("!========================================================" & @CRLF)
        Sleep(1000);Set up function that sleeps based on CPU load
        Send("{SHIFTUP}") 
    EndIf

WEnd

Share this post


Link to post
Share on other sites

It seems that the first thing in the loop "$objEvent = $colEvents.NextEvent" is pausing the script and waiting to receive the event. That totally messes up my plans for this code as it renders the rest of my app useless. I know you can assign timeout values to it, but that would cause hickups and sluggish responses because it would happen every go round. So I'm definately in need of this strictly acting like a "regular" event function. Is there a way to do this?

Share this post


Link to post
Share on other sites

Maybe the WMI even notification just wont work for you. I would just use straight WMI to poll the drive every so often. You could use adlib so your script could continue.

1. Store beginning state. (MediaLoaded)

2. If MediaLoaded = -1 then store VolumeSerialNumber into a global var

3. Use adlib to call the function at a set interval, comparing the VolumeSerialNumber

$strComputer = "localhost"

$Output=""
$objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2")
$colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_CDROMDrive", "WQL", 0x10+0x20)

If IsObj($colItems) then
   For $objItem In $colItems
      $Output &= "Availability: " & $objItem.Availability & @CRLF
      $strCapabilities = $objItem.Capabilities(0)
      $Output &= "Capabilities: " & $strCapabilities & @CRLF
      $strCapabilityDescriptions = $objItem.CapabilityDescriptions(0)
      $Output &= "CapabilityDescriptions: " & $strCapabilityDescriptions & @CRLF
      $Output &= "Caption: " & $objItem.Caption & @CRLF
      $Output &= "CompressionMethod: " & $objItem.CompressionMethod & @CRLF
      $Output &= "ConfigManagerErrorCode: " & $objItem.ConfigManagerErrorCode & @CRLF
      $Output &= "ConfigManagerUserConfig: " & $objItem.ConfigManagerUserConfig & @CRLF
      $Output &= "CreationClassName: " & $objItem.CreationClassName & @CRLF
      $Output &= "DefaultBlockSize: " & $objItem.DefaultBlockSize & @CRLF
      $Output &= "Description: " & $objItem.Description & @CRLF
      $Output &= "DeviceID: " & $objItem.DeviceID & @CRLF
      $Output &= "Drive: " & $objItem.Drive & @CRLF
      $Output &= "DriveIntegrity: " & $objItem.DriveIntegrity & @CRLF
      $Output &= "ErrorCleared: " & $objItem.ErrorCleared & @CRLF
      $Output &= "ErrorDescription: " & $objItem.ErrorDescription & @CRLF
      $Output &= "ErrorMethodology: " & $objItem.ErrorMethodology & @CRLF
      $Output &= "FileSystemFlags: " & $objItem.FileSystemFlags & @CRLF
      $Output &= "FileSystemFlagsEx: " & $objItem.FileSystemFlagsEx & @CRLF
      $Output &= "Id: " & $objItem.Id & @CRLF
      $Output &= "LastErrorCode: " & $objItem.LastErrorCode & @CRLF
      $Output &= "Manufacturer: " & $objItem.Manufacturer & @CRLF
      $Output &= "MaxBlockSize: " & $objItem.MaxBlockSize & @CRLF
      $Output &= "MaximumComponentLength: " & $objItem.MaximumComponentLength & @CRLF
      $Output &= "MaxMediaSize: " & $objItem.MaxMediaSize & @CRLF
      $Output &= "MediaLoaded: " & $objItem.MediaLoaded & @CRLF ;<<<<<<<<<<<<<<<<<<<< CHECK FOR CHANGE (0 = Empty, -1 = CD Inserted)
      $Output &= "MediaType: " & $objItem.MediaType & @CRLF
      $Output &= "MfrAssignedRevisionLevel: " & $objItem.MfrAssignedRevisionLevel & @CRLF
      $Output &= "MinBlockSize: " & $objItem.MinBlockSize & @CRLF
      $Output &= "Name: " & $objItem.Name & @CRLF
      $Output &= "NeedsCleaning: " & $objItem.NeedsCleaning & @CRLF
      $Output &= "NumberOfMediaSupported: " & $objItem.NumberOfMediaSupported & @CRLF
      $Output &= "PNPDeviceID: " & $objItem.PNPDeviceID & @CRLF
      $strPowerManagementCapabilities = $objItem.PowerManagementCapabilities(0)
      $Output &= "PowerManagementCapabilities: " & $strPowerManagementCapabilities & @CRLF
      $Output &= "PowerManagementSupported: " & $objItem.PowerManagementSupported & @CRLF
      $Output &= "RevisionLevel: " & $objItem.RevisionLevel & @CRLF
      $Output &= "SCSIBus: " & $objItem.SCSIBus & @CRLF
      $Output &= "SCSILogicalUnit: " & $objItem.SCSILogicalUnit & @CRLF
      $Output &= "SCSIPort: " & $objItem.SCSIPort & @CRLF
      $Output &= "SCSITargetId: " & $objItem.SCSITargetId & @CRLF
      $Output &= "Size: " & $objItem.Size & @CRLF
      $Output &= "Status: " & $objItem.Status & @CRLF
      $Output &= "StatusInfo: " & $objItem.StatusInfo & @CRLF
      $Output &= "SystemCreationClassName: " & $objItem.SystemCreationClassName & @CRLF
      $Output &= "SystemName: " & $objItem.SystemName & @CRLF
      $Output &= "TransferRate: " & $objItem.TransferRate & @CRLF
      $Output &= "VolumeName: " & $objItem.VolumeName & @CRLF ;<<<<<<<<<<<<<<<<<<<< CHECK FOR CHANGE
      $Output &= "VolumeSerialNumber: " & $objItem.VolumeSerialNumber & @CRLF ;<<<<<<<<<<<<<<<<<<<< CHECK FOR CHANGE
      ConsoleWrite($Output)
      
      $Output=""
   Next
Else
   Msgbox(0,"WMI Output","No WMI Objects Found for class: " & "Win32_CDROMDrive" )
Endif

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

This is cool. Thanks. But when activated, if a CD/DVD is inserted, it causes the script to pause for about 5-10 seconds while the CD loads. This can be problematic with other functions that might miss an activation point.

In looking at that I found this:

http://msdn.microsoft.com/en-us/library/aa393867(VS.85).aspx

Doesn't that allow for what I'm trying to do...to react like an object event like I've been trying to do so it doesn't pause/slow up the rest of the script? I just either couldn't get the "objWbemSink" routine, or it isn't what I think. Am I on the right track, or is it not what I think/can't work with autoit?

Also, is there a way to go directly to what I want without enumerating everything/what I want by ways of a loop? Like get the "$objItem.DeviceID" without doing a loop?

Edited by Champak

Share this post


Link to post
Share on other sites

Bump

Share this post


Link to post
Share on other sites

This is something I've been playing with, it might help you do what you want. Change the Logical Disk to CD-ROM if you want, it may work better like that.

;WMI monitoring
$wbemFlagReturnImmediately = 0x10
$wbemFlagForwardOnly = 0x20

Dim $strComputer, $SINK, $objWMIService
$strComputer = "127.0.0.1"
$SINK = ObjCreate("WbemScripting.SWbemSink")
ObjEvent($SINK, "SINK_")
$objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2")
If Not @error Then
    $objWMIService.ExecNotificationQueryAsync ($SINK, "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_LogicalDisk' AND TargetInstance.DriveType = 5")
Else
    ConsoleWrite("Error encountered. Error Code was " & @error & ".")
    Exit
EndIf
ConsoleWrite("In monitoring mode. Press Ctrl+C to exit." & @CRLF)
While 1
    Sleep(10000)
WEnd
;******************************************************************************
Func SINK_OnObjectReady($objLatestEvent, $objAsyncContext)
    If $objLatestEvent.TargetInstance.VolumeName <> "" Then
        ;Trap asynchronous events.
        $output = "DeviceID: " & $objLatestEvent.TargetInstance.DeviceID & @CRLF
        $output &= "FileSystem: " & $objLatestEvent.TargetInstance.FileSystem & @CRLF
        $output &= "VolumeName: " & $objLatestEvent.TargetInstance.VolumeName & @CRLF
    
        ToolTip($output, (@DesktopWidth - 250), (@DesktopHeight - 100), "CD/DVD Inserted")
    EndIf
EndFunc   ;==>SINK_OnObjectReady

Share this post


Link to post
Share on other sites

This is what I came up with. Only problem is that WMI likes to error out while getting the serial number immediately after the window message is received...

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Opt("GUIOnEventMode", 1)  ; Change to OnEvent mode

Global Const $SourceDrive = "d:"
Global $LastSerial = GetSerial($SourceDrive)
ConsoleWrite("LastSerial: " & $LastSerial & @CRLF)

GUIRegisterMsg (0x0219, "Check") ;WM_DEVICECHANGE

;Create GUI
$mainwindow = GUICreate("CD Status", 200, 100)
GUISetOnEvent($GUI_EVENT_CLOSE, "Quit")

$okbutton = GUICtrlCreateButton("Exit", 70, 50, 60)
GUICtrlSetOnEvent($okbutton, "Quit")
GUISetState(@SW_SHOW)

While 1
  Sleep(1000)  ; Idle around
WEnd

Func Quit()
    Exit
EndFunc

;Callback for WM_DEVICECHANGE
Func Check($hWndGUI, $MsgID, $WParam, $LParam)
    Local $NewSerial
    
    ConsoleWrite("Device change detected" & @CRLF)
    
    ;ConsoleWrite("hWndGUI: " & $hWndGUI & @CRLF)
    ;ConsoleWrite("MsgID: " & $MsgID & @CRLF)
    ;ConsoleWrite("WParam: " & $WParam & @CRLF)
    ;ConsoleWrite("LParam: " & $LParam & @CRLF)
    
    Switch $WParam
        Case 0x00008004
            ConsoleWrite("A device or piece of media has been removed." & @CRLF)
        
        Case 0x00008000
            ConsoleWrite("A device or piece of media has been inserted and is now available." & @CRLF)
            $NewSerial = GetSerial($SourceDrive)
            Switch @ERROR
                Case 1
                    ConsoleWrite("Error connecting to WMI" & @CRLF)
                Case 2
                    ConsoleWrite("Error retrieving serial number from WMI" & @CRLF)
            EndSwitch
            
            CompareSerial($LastSerial,$NewSerial)
    EndSwitch
    
    ConsoleWrite(@CRLF)
EndFunc

;Compare serial number of previous CD to current
Func CompareSerial($previous, $current)
    If $previous <> $current Then
        $LastSerial = $current
        MsgBox(0,"","A CD has been inserted with serial number " & $current)
    EndIf
EndFunc

;Retrieve serial number from CD media
Func GetSerial($DriveLetter)
    Local $strComputer = "localhost"
    Local $Output=""
    Local $objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2")
    If @ERROR Then Return SetError(1,0,"")
    
    Local $colItems = $objWMIService.ExecQuery("SELECT MediaLoaded,VolumeName,VolumeSerialNumber FROM Win32_CDROMDrive WHERE Drive = '" & $DriveLetter & "'" , "WQL", 0x10+0x20)


    If IsObj($colItems) then
       For $objItem In $colItems
          $Output &= "MediaLoaded: " & $objItem.MediaLoaded & @CRLF ;<<<<<<<<<<<<<<<<<<<< CHECK FOR CHANGE (0 = Empty, -1 = CD Inserted)
          $Output &= "VolumeName: " & $objItem.VolumeName & @CRLF ;<<<<<<<<<<<<<<<<<<<< CHECK FOR CHANGE
          $Output &= "VolumeSerialNumber: " & $objItem.VolumeSerialNumber & @CRLF ;<<<<<<<<<<<<<<<<<<<< CHECK FOR CHANGE
          ConsoleWrite($Output)
          
          Return $objItem.VolumeSerialNumber
          
          $Output=""
       Next
    Else
       Return SetError(2,0,"")
    Endif
    
    ;Return NULL if an invalid drive letter is passed or cd tray is empty
    Return ""
EndFunc

Share this post


Link to post
Share on other sites

Thanks, I'll look into this.

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

@SkinnyWhiteGuy

That's great. It works as it should (generally). The problem is, it seems to be causing a problem with a program/resource, I notice in the task manager, wmiprvse.exe. I've NEVER noticed load on this program before, but since I've put this code in, I notice that the cpu load on this goes up to 33% and pretty much hovers there (This is random an I can't reproduce it EVERY time) and I can't end the task...either by closing the script or end task in the TM. Is there a way to fix this? The following is my set up, note I took out the top WMI declarations, I didn't see a purpose in them...was I wrong in that?

$SINK = ObjCreate("WbemScripting.SWbemSink")
ObjEvent($SINK, "_CDROMCheck_")
$oCDROMCheck = ObjGet("winmgmts:\\.\root\CIMV2")
$oCDROMCheck.ExecNotificationQueryAsync ($SINK, "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_CDROMDrive' AND TargetInstance.Drive = 'e:'");"d:\"
If IsObj($oCDROMCheck) Then
    Global $CD_New
Else
    ConsoleWrite("! No WMI Objects Found for class: Win32_CDROMDrive to check CDROM status" & @CRLF)
Endif

Func _CDROMCheck_OnObjectReady($objLatestEvent, $objAsyncContext)

    If GUICtrlRead($WMP_MediaSelectOption+1) = $CD_Mode Or GUICtrlRead($WMP_MediaSelectOption+1) = $DVD_Mode And IsObj($oCDROMCheck) then
        If $objLatestEvent.TargetInstance.VolumeName <> "" Then
            ;Trap asynchronous events.
            GUICtrlSetData($WMP_SongInfo, "CD/DVD has been loaded. Press play.")
            ;============================
        ;   $Output = "Caption: " & $objLatestEvent.TargetInstance.Caption & @CRLF
        ;   $Output &= "Description: " & $objLatestEvent.TargetInstance.Description & @CRLF
        ;   $Output &= "SystemName: " & $objLatestEvent.TargetInstance.SystemName & @CRLF
        ;   $Output &= "DeviceID: " & $objLatestEvent.TargetInstance.DeviceID & @CRLF
        ;   $Output &= "VolumeName: " & $objLatestEvent.TargetInstance.VolumeName & @CRLF
        ;   $Output &= "VolumeSerialNumber: " & $objLatestEvent.TargetInstance.VolumeSerialNumber & @CRLF
        ;   ConsoleWrite($Output)
        ;   $Output=""
            ;============================
            ;Prevent the window that allows you to select a program to play CD/DVD from popping up
            ;NOTE: If not in CD/DVD mode, the window will still pop up.
            Send("{shiftdown}")
            $CDROMCheckRelease = _Timer_SetTimer($GUIMain, 2500, "_CDROMCheck_Release")
        ElseIf $objLatestEvent.TargetInstance.VolumeName = "" And $CD_New = 0 Then
            GUICtrlSetData($WMP_SongInfo, "No CD/DVD in ROM to play or tray is open. Insert CD/DVD then press play.")
            $CD_New = 1
        EndIf
    EndIf

EndFunc   ;==>SINK_OnObjectReady

Func _CDROMCheck_Release($hWnd, $Msg, $iIDTimer, $dwTime);Release the shiftdown set by _CDROMCheck_OnObjectReady

    Send("{shiftup}")
    _Timer_KillTimer($GUIMain, $CDROMCheckRelease)

EndFunc

If I can't fix this, I would have to go with weaponx's version.

EDIT: It MAY be do to running the script multiple times and the accumulation of opening this sinkobject "lookout" raises the cpu load on the wmiprvse.exe. I don't know, just a theory since I don't really see this load on the first run of the app. IF that is the case, then there would have to be a way to kill the function on exit.

Edited by Champak

Share this post


Link to post
Share on other sites

OK, I've confirmed, it is definitely because of some type of accumulation. I thought Autoit is suppose to kill/release anything it uses upon exiting. I tried "resetting" the objects to a variable equaling "0" before exiting, but that didn't help.

Share this post


Link to post
Share on other sites

I've solved the problem. I put this: "$SINK.Cancel()" in an exit function, and that solved it....as far as I've tested it. I've restarted the app numerous times and I see no build up.

Share this post


Link to post
Share on other sites

The "$SINK.Cancel()" works ok, however upon an unexpected exit, where the app exits without calling the exit function, the "$SINK.Cancel()" isn't activated and thus doesn't clear the cpu load. Even when restarted and then exited properly, it isn't cleared. Any suggestion to "backup" the "$SINK.Cancel()" in case of an unexpected exit?

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  
Followers 0