Jump to content

[SOLVED] _WinAPI_CloseHandle


Recommended Posts

Hi. _WinAPI_CloseHandle() gives me an invalid handle message.

I am working on a script that registers a USB device to receive removal notifications. So, if you plug in a USB device and then you do a soft eject then your script will receive a notification that the user wants to remove the device. This allows your script to either refuse the removal if it isn't finished with some files that are on the USB device or to allow the user to remove the device.

Everything works fine except for in the 'MY_WM_DEVICECHANGE' function. In the switch case '$DBT_DEVICEQUERYREMOVE' is the option to allow or disallow device removal. If removal is allowed then the script attempts to close the open file and then unregister the device from receiving notifications. The problem is with the _WinAPI_CloseHandle() function.

If you have a few minutes to look into my code I would appreciate that. I tried to explain as much as I could so far. Any questions? Just ask...

[updated working code]

#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=n
#AutoIt3Wrapper_Res_Description=Registers a device to receive device change notifications.
#AutoIt3Wrapper_Res_Language=1033
#AutoIt3Wrapper_Add_Constants=n
#AutoIt3Wrapper_AU3Check_Stop_OnWarning=y
#AutoIt3Wrapper_AU3Check_Parameters=-w 1 -w 2 -w 3 -w 4 -w 6 -q -d
#AutoIt3Wrapper_Run_Tidy=y
#Tidy_Parameters=/tc 0 /kv 0
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include <Misc.au3>
_Singleton(@ScriptName)

#include <WinAPI.au3>
#include <AutoItObject.au3>
#include <FileConstants.au3>
#include <GUIConstantsEX.au3>

Opt("TrayIconHide", 1)

#region GUI
Opt("GUIOnEventMode", 1)

Global Const $hRecipient = GUICreate("USB Handler", 400, 400, -1, -1)
GUISetOnEvent($GUI_EVENT_CLOSE, "term", $hRecipient)

Global Const $WM_DEVICECHANGE = 0x0219
Global Const $RegisterStatus = GUIRegisterMsg($WM_DEVICECHANGE, "MY_WM_DEVICECHANGE")

Global Const $EditBox = GUICtrlCreateEdit('', 5, 5, 350, 350)

Global Const $AllowDenyCheckBox = GUICtrlCreateCheckbox("Check to deny device removal.", 5, 355, 162, 40)
GUICtrlSetState($AllowDenyCheckBox, $GUI_CHECKED)

GUISetState()
#endregion GUI

_AutoItObject_Startup()

HotKeySet("{ESC}", "term")
OnAutoItExitRegister("_exit")

Global $hDevNotify
Global $DriveLetter
Global $FileHandle
Global $LastError

While 1
    Sleep(100)
WEnd

Func MY_WM_DEVICECHANGE($hWnd, $msg, $wParam, $lParam)
    Local Const $DBT_CONFIGCHANGECANCELED   = 0x0019 ; A request to change the current configuration(dock or undock) has been canceled.
    Local Const $DBT_CONFIGCHANGED  = 0x0018 ; The current configuration has changed due to a dock or undock.
    Local Const $DBT_CUStoMEVENT    = 0x8006 ; A custom event has occurred.
    Local Const $DBT_DEVICEARRIVAL  = 0x8000 ; A device or piece of media has been inserted and is now available.
    Local Const $DBT_DEVICEQUERYREMOVE  = 0x8001 ; Permission is requested to remove a device or piece of media. Any application can deny this request and cancel the removal.
    Local Const $DBT_DEVICEQUERYREMOVEFAILED = 0x8002 ; A request to remove a device or piece of media has been canceled.
    Local Const $DBT_DEVICEREMOVECOMPLETE   = 0x8004 ; A device or piece of media has been removed.
    Local Const $DBT_DEVICEREMOVEPENDING    = 0x8003 ; A device or piece of media is about to be removed. Cannot be denied.
    Local Const $DBT_DEVICETYPESPECIFIC     = 0x8005 ; A device-specific event has occurred.
    Local Const $DBT_DEVNODES_CHANGED   = 0x0007 ; A device has been added to or removed from the system.
    Local Const $DBT_QUERYCHANGECONFIG  = 0x0017 ; Permission is requested to change the current configuration(dock or undock).
    Local Const $DBT_USERDEFINED    = 0xFFFF ; The meaning of this message is user-defined.
    Local Const $DBT_DEVTYP_VOLUME = 2
    Local Const $DBT_DEVTYP_HANDLE = 6

    If $msg = $WM_DEVICECHANGE Then
        Switch $wParam
            Case $DBT_DEVICEARRIVAL
                Local $DeviceType = GetDeviceType($lParam)

                If $DeviceType = $DBT_DEVTYP_VOLUME Then
                    Write("DEVICEARRIVAL")

                    Local Const $UnitMask = GetUnitMask($lParam)
                    Local Const $tmpDriveLetter = GetDriveLetterFromUnitMask($UnitMask)

                    Switch @error
                        Case 0
                            $DriveLetter = $tmpDriveLetter

                            ; open a file on the USB device
                            Local Const $FileHandleTMP = OpenFile("F:\Text.txt", True)

                            Switch @error
                                Case 0
                                    $FileHandle = $FileHandleTMP
                                Case 1
                                    Write("OpenDirectory failed.")
                                    Return 0
                            EndSwitch

                            Local Const $hDevNotifyTMP = DoRegisterDeviceInterfaceToHwnd($FileHandle, True)

                            Switch @error
                                Case 0
                                    $hDevNotify = $hDevNotifyTMP
                                Case 1
                                    Return 0
                            EndSwitch
                        Case 1
                            Return 0
                    EndSwitch
                EndIf
            Case $DBT_DEVICEQUERYREMOVE
                Write("DEVICEQUERYREMOVE")

                $DeviceType = GetDeviceType($lParam)

                If $DeviceType = $DBT_DEVTYP_HANDLE Then
                    ; Check/Uncheck to allow/disallow device removal
                    Switch GUICtrlRead($AllowDenyCheckBox)
                        Case $GUI_UNCHECKED ; allow
                            Local Const $FileClosed = CloseFile($FileHandle, True)

                            If $FileClosed Then
                                UnregisterDeviceNotification(True)
                            EndIf

                        Case $GUI_CHECKED ; deny
                            Local Const $BROADCAST_QUERY_DENY = 0x424D5144
                            Return $BROADCAST_QUERY_DENY
                    EndSwitch
                EndIf

            Case $DBT_DEVICEREMOVECOMPLETE
                Write("DEVICEREMOVECOMPLETE")

            Case $DBT_CONFIGCHANGECANCELED
                Write("CONFIGCHANGECANCELED")

            Case $DBT_CONFIGCHANGED
                Write("CONFIGCHANGED")

            Case $DBT_CUStoMEVENT
                Write("CUSTOMEVENT")

            Case $DBT_DEVICEQUERYREMOVEFAILED
                Write("DEVICEQUERYREMOVEFAILED")

            Case $DBT_DEVICEREMOVEPENDING
                Write("DEVICEREMOVEPENDING")

            Case $DBT_DEVICETYPESPECIFIC
                Write("DEVICETYPESPECIFIC")

            Case $DBT_DEVNODES_CHANGED
;~              Write("DEVNODES_CHANGED")

            Case $DBT_QUERYCHANGECONFIG
                Write("QUERYCHANGECONFIG")

            Case $DBT_USERDEFINED
                Write("USERDEFINED")

        EndSwitch
    EndIf
EndFunc ;==>MY_WM_DEVICECHANGE

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func CLoseFile($Handle, $CheckError = False)
    Local Const $Result = _WinAPI_CloseHandle($Handle)
    $LastError = _WinAPI_GetLastErrorMessage()

    If $Result Then
        Return $Result
    Else
        If $CheckError Then ShowError("CLoseFile")
        Return SetError(1, 0, 1)
    EndIf
EndFunc ;==>CLoseFile

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func DEV_BROADCAST_HANDLE($Handle, $CheckError = False)
    Local Const $tagDEV_BROADCAST_HANDLE = "dword dbch_size; dword dbch_devicetype; dword dbch_reserved; handle dbch_handle; ptr dbch_hdevnotify; char dbch_eventguid[128]; long dbch_nameoffset; byte dbch_data[1]"

    Local Const $DEV_BROADCAST_HANDLE = DllStructCreate($tagDEV_BROADCAST_HANDLE)
    $LastError = _WinAPI_GetLastErrorMessage()

    If IsDllStruct($DEV_BROADCAST_HANDLE) Then
        Local Const $Size = DllStructGetSize($DEV_BROADCAST_HANDLE)

        Local Const $EventGUID = "{0x25dbce51-0x6c8f-0x4a72-0x8a6d-0xb54c2b4fc835}"

        Local Const $DBT_DEVTYP_HANDLE = 6

        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_size", $Size)
        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_devicetype", $DBT_DEVTYP_HANDLE)
        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_reserved", 0)
        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_handle", $Handle)
        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_hdevnotify", 0)
        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_eventguid", $EventGUID)
        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_nameoffset", 0)
        DllStructSetData($DEV_BROADCAST_HANDLE, "dbch_data", 0)

        Return $DEV_BROADCAST_HANDLE
    Else
        If $CheckError Then ShowError("DEV_BROADCAST_HANDLE")
        Return SetError(1, 0, 1)
    EndIf
EndFunc ;==>DEV_BROADCAST_HANDLE

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func DEV_BROADCAST_VOLUME($lParam, $CheckError = False)
    ; Create a struct from $lParam which contains a pointer to a Windows-created struct.
    Local Const $tagDEV_BROADCAST_VOLUME = "dword dbcv_size; dword dbcv_devicetype; dword dbcv_reserved; dword dbcv_unitmask; word dbcv_flags"
    Local Const $DEV_BROADCAST_VOLUME = _AutoitObject_DllStructCreate($tagDEV_BROADCAST_VOLUME, Number($lParam))

    If IsObj($DEV_BROADCAST_VOLUME) Then
        Return $DEV_BROADCAST_VOLUME
    Else
        If $CheckError Then Write("DEV_BROADCAST_VOLUME: Fail")
        Return SetError(1, 0, 1)
    EndIf
EndFunc ;==>DEV_BROADCAST_VOLUME

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func DoRegisterDeviceInterfaceToHwnd($Handle, $CheckError = False)
    ; create the DEV_BROADCAST_HANDLE struct
    Local Const $DEV_BROADCAST_HANDLE   = DEV_BROADCAST_HANDLE($Handle)
    Local Const $ptrDEV_BROADCAST_HANDLE = DllStructGetPtr($DEV_BROADCAST_HANDLE)

    ; tells RegisterDeviceNotifications to send notices to a window
    Local Const $DEVICE_NOTIFY_WINDOW_HANDLE = 0
    Local Const $Flags = $DEVICE_NOTIFY_WINDOW_HANDLE

    Local Const $RDNhandle = DllCall("User32.dll", "handle", "RegisterDeviceNotification", "hwnd", $hRecipient, "ptr", $ptrDEV_BROADCAST_HANDLE, "uint", $Flags)
    $LastError = _WinAPI_GetLastErrorMessage()

    If $RDNhandle[0] Then
        Return $RDNhandle[0]
    Else
        If $CheckError Then ShowError("RegisterDeviceNotification")
        Return SetError(1, 0, 1)
    EndIf
EndFunc ;==>DoRegisterDeviceInterfaceToHwnd

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func GetDeviceType($lParam, $CheckError = False)
    Local Const $DEV_BROADCAST_VOLUME = DEV_BROADCAST_VOLUME($lParam)

    If IsObj($DEV_BROADCAST_VOLUME) Then
        Return $DEV_BROADCAST_VOLUME.dbcv_devicetype
    Else
        If $CheckError Then Write("GetDeviceType: Fail")
        Return SetError(1, 0, 1)
    EndIf
EndFunc ;==>GetDeviceType

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func GetDriveLetterFromUnitMask($UnitMask, $CheckError = False)
    Local Const $Drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    Local $Count = 1
    Local $Pom = $UnitMask / 2

    While $Pom <> 0
        $Pom = BitShift($Pom, 1)
        $Count += 1
    WEnd

    If $Count >= 1 And $Count <= 26 Then
        If $CheckError Then Write("DriveLetter: " & StringMid($Drives, $Count, 1))
        Return StringMid($Drives, $Count, 1)
    Else
        If $CheckError Then Write("Error in getting drive letter.")
        Return SetError(1, 0, '?')
    EndIf
EndFunc ;==>GetDriveLetterFromUnitMask

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func GetUnitMask($lParam, $CheckError = False)
    Local Const $DEV_BROADCAST_VOLUME = DEV_BROADCAST_VOLUME($lParam)

    If IsObj($DEV_BROADCAST_VOLUME) Then
        Return $DEV_BROADCAST_VOLUME.dbcv_unitmask
    Else
        If $CheckError Then Write("GetUnitMask: Fail")
        Return SetError(1, 0, 1)
    EndIf
EndFunc ;==>GetUnitMask

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func OpenFileDialog($CreationDisposition = $OPEN_EXISTING, $DesiredAccess = 4, $ShareMode = 0, $FlagsAndAttributes = 0, $SecurityAttributes = 0, $CheckError = False)
    If $CreationDisposition = $OPEN_EXISTING Then
        Local $File = FileOpenDialog("Open file...", '', "All (*.*)", 1 + 2)
    Else
        $File = FileOpenDialog("Open file...", '', "All (*.*)")
    EndIf

    Local Const $FileHandleTMP = _WinAPI_CreateFile("\\?\" & $File, $CreationDisposition, $DesiredAccess, $ShareMode, $FlagsAndAttributes, $SecurityAttributes)
    $LastError = _WinAPI_GetLastErrorMessage()

    If IsPtr($FileHandleTMP) Then
        Return $FileHandleTMP
    Else
        If $CheckError Then ShowError("OpenFileDialog")
        Return SetError(1, $LastError, 1)
    EndIf
EndFunc ;==>OpenFileDialog

; ------------------------------------------------------------------------------------------------------------------------------------------------------

Func OpenFile($File, $CreationDisposition = $OPEN_EXISTING, $DesiredAccess = 4, $ShareMode = 0, $FlagsAndAttributes = 0, $SecurityAttributes = 0, $CheckError = False)
    Local Const $FileHandleTMP = _WinAPI_CreateFile($File, $CreationDisposition, $DesiredAccess, $ShareMode, $FlagsAndAttributes, $SecurityAttributes)
    $LastError = _WinAPI_GetLastErrorMessage()

    If IsPtr($FileHandleTMP) Then
        Return $FileHandleTMP
    Else
        If $CheckError Then ShowError("OpenFile")
        Return SetError(1, 0, 1)
    EndIf
EndFunc ;==>OpenFile

; ------------------------------------------------------------------------------------------------------------------------------------------------------

Func ShowError($Function)
    Write($Function & ": " & $LastError, True)
;~  DllCall("Kernel32.dll", "none", "SetLastError", "dword", 0)
    $LastError = 0
EndFunc ;==>ShowError

; ------------------------------------------------------------------------------------------------------------------------------------------------------

Func UnregisterDeviceNotification($CheckError = False)
    _WinAPI_CloseHandle($FileHandle)

    Local Const $Check = DllCall("User32.dll", "bool", "UnregisterDeviceNotification", "ptr", $hDevNotify)
    $LastError = _WinAPI_GetLastErrorMessage()

    If Not $Check[0] Then
        If $CheckError Then
            If $CheckError Then ShowError("UnregisterDeviceNotification")
            Return SetError(1, 0, 1)
        EndIf
    EndIf
EndFunc ;==>UnregisterDeviceNotification

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

Func Write($message, $CRLF = True)
    If $CRLF Then
        GUICtrlSetData($EditBox, $message & @CRLF, ' ')
    Else
        GUICtrlSetData($EditBox, $message, ' ')
    EndIf
EndFunc ;==>Write

; ------------------------------------------------------------------------------------------------------------------------------------------------------------------

#region Clean_up
Func _exit()
    If $RegisterStatus Then GUIRegisterMsg($WM_DEVICECHANGE, "")
    If IsHWnd($hRecipient) Then GUIDelete()
    _AutoItObject_Shutdown()
    OnAutoItExitUnregister("_exit")
EndFunc ;==>_exit

; -----------------------------------------------------------------------------

Func term()
    Exit
EndFunc ;==>term
#endregion Clean_up
Edited by jaberwocky6669
Link to comment
Share on other sites

  • Moderators

http://msdn.microsoft.com/en-us/library/ms724211%28VS.85%29.aspx

Read the remarks section, you'll probably have a hand to forehead moment.

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

Yup, a double hand to forehead moment. Well, I guess I'll go back and read that MSDN entry then because I did kinda rush through it. Thanks for taking the time. I appreciate it!

Link to comment
Share on other sites

Read carefully MSDN. Why did you decide to use CloseHandle()?

Device notification handles returned by RegisterDeviceNotification must be closed by calling the UnregisterDeviceNotification function when they are no longer needed.

Link to comment
Share on other sites

Read carefully MSDN. Why did you decide to use CloseHandle()?

Umm, I just got ahead of myself I guess. I was thinking in my mind "handle" and so I thought of CloseHandle! Yeah, you have to unregister device notifications and close the device or file too, at least from what I can tell.

Link to comment
Share on other sites

Awesome! Yall rock! Thanks to Smoke_N and Yashied, it occurred to me how much more I had complicated things than what is necessary. So now it works and the working code is in the OP (far from bug free). Thanks.

Edited by jaberwocky6669
Link to comment
Share on other sites

When the Checkbox "Check to deny device removal"

is checked I should not be able to remove it,

but I can! Posted Image ( i'm on xp )

So you don't see a MessageBox at all? Also, when you try to eject a USB do you get a QUERYDEVICEREMOVE message in the GUI?

Edited by jaberwocky6669
Link to comment
Share on other sites

So you don't see a MessageBox at all? Also, when you try to eject a USB do you get a QUERYDEVICEREMOVE message in the GUI?

Checked or unchecked gui gives this :

DEVICEREMOVECOMPLETE

DEVICEARRIVAL

DriveLetter: G

CreateFile: pass

_WinAPI_CreateFileEx: pass.

RegisterDeviceNotification: Le service a renvoyé un code d'erreur qui lui est spécifique.

RegisterDeviceNotification: fail -- @error: 0

DEVICEREMOVECOMPLETE

DEVICEARRIVAL

DriveLetter: G

CreateFile: pass

_WinAPI_CreateFileEx: pass.

RegisterDeviceNotification: Le service a renvoyé un code d'erreur qui lui est spécifique.

RegisterDeviceNotification: fail -- @error: 0

DEVICEREMOVECOMPLETE

DEVICEARRIVAL

DriveLetter: G

CreateFile: pass

_WinAPI_CreateFileEx: pass.

RegisterDeviceNotification: Le service a renvoyé un code d'erreur qui lui est spécifique.

RegisterDeviceNotification: fail -- @error: 0

DEVICEREMOVECOMPLETE

I get only msgbox when i insert key...

AutoIt 3.3.14.2 X86 - SciTE 3.6.0WIN 8.1 X64 - Other Example Scripts

Link to comment
Share on other sites

OK, is G the correct drive letter for your specific device?

Oh great, now I'm getting the same error. What did you do Wakillon (kidding)?

Edited by jaberwocky6669
Link to comment
Share on other sites

OHHHH, create a file in the root directory of your device named Test.txt

Yes, I've been using the green arrow too but I bet other methods would work too

Edited by jaberwocky6669
Link to comment
Share on other sites

No problems, glad it works for you!

Will you automate the txt file creation ?

I'll set myself to the task this instant Completed

Edited by jaberwocky6669
Link to comment
Share on other sites

At this point the script is a proof of concept, definately not ready for primetime at all. Consider yourself a beta tester nay an alpha tester if you will.

I don't use an existing file because what if the device is blank?

ALso, I want to make this script into a UDF so I need to be as broad in scope as possible.

Edited by jaberwocky6669
Link to comment
Share on other sites

At this point the script is a proof of concept, definately not ready for primetime at all. Consider yourself a beta tester nay an alpha tester if you will.

I don't use an existing file because what if the device is blank?

ALso, I want to make this script into a UDF so I need to be as broad in scope as possible.

yes but the "_WinAPI_CreateFileEx" function was supposed create the txt file, no ?

AutoIt 3.3.14.2 X86 - SciTE 3.6.0WIN 8.1 X64 - Other Example Scripts

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...