Jump to content

Registering callback using HandlerEx


Recommended Posts

I'm coding a Windows service using where I'm not supposed to create a GUI & thereby can't trap events using WTSRegisterSessionNotification. Instead, this page says that

To receive session change notifications from a service, use the HandlerEx function.

Below is my code snippet

#include "Log.au3"
#include "Misc.au3"
#include "Network.au3"
#include "Services.au3"
#include "Credentials.au3"

#NoTrayIcon
#AutoIt3Wrapper_Change2CUI=y

Global $sServiceName = "BlockInternet"
Global $sServiceDisplayName = "BlockInternet"


If Not @Compiled Then Exit
If Not _Singleton(@ScriptName) Then
    If $cmdline[0] > 0 Then
        If $cmdline[1] = "stop" or StringRight($cmdline[1],1) = "s" Then
        Else
            MsgBox(0, "BlockInternet", "Process is running.")
            WriteLog("Process is running.")
            Exit
        EndIf
    Else
        MsgBox(0, "BlockInternet", "Process is running.")
        WriteLog("Process is running.")
        Exit
    EndIf
EndIf


If $cmdline[0] > 0 Then
    Switch $cmdline[1]
        Case "install", "-i", "/i"
            InstallService()
        Case "remove", "-u", "/u", "uninstall"
            RemoveService()
        Case "run", "-r", "/r", "start"
            _SelfRun($sServiceName,"start")
        Case "stop", "-s", "/s"
            _SelfRun($sServiceName,"stop")
        Case Else
            WriteLog(" - - - Help - - - " & @crlf)
            WriteLog(" Service Example Params : " & @crlf)
            WriteLog("  -i : Installs service" & @crlf)
            WriteLog("  -u : Removes service" & @crlf)
            WriteLog("  -r : Runs the service" & @crlf)
            WriteLog("  -s : Stops the service" & @crlf)
            WriteLog(" - - - - - - - - " & @crlf)
            Exit
    EndSwitch
    _Service_Init($sServiceName)
Else
    If Not _Service_Exists($sServiceName) Then
        If Msgbox(20,"Service Not Installed.","Would you like to install this service?" & @CRLF & $sServiceName) = 6 Then
            If InstallService() Then
                If Msgbox(4,$sServiceName,"Service Installed Successfully." & @CRLF & "Do you wish to start the process?") = 6 Then _SelfRun($sServiceName,"start")
                EndIf
            Else
                Exit
        EndIf
    Else
        _Service_Init($sServiceName)
    EndIf
EndIf

Func _Svc_Main()
    WriteLog("Starting service: BlockInternet")
    While $gServiceStateRunning
        $CurrentUser = GetProcessOwner("Explorer.exe")
        WriteLog("Logged in username: " & $CurrentUser)
        WriteLog("IsUserAdmin: " & isAdministrator($CurrentUser))
        If isAdministrator($CurrentUser) Then
            $GatewayInfo = RegRead("HKEY_LOCAL_MACHINE\Software\BlockInternet", "GatewayInfo")
            WriteLog("Gateway:Admin: " & $GatewayInfo)
            SetGatewayInfo($GatewayInfo, 1)
        Else
            SetGatewayInfo($GatewayInfo, 0)
            WriteLog("Gateway:Limited: " & $GatewayInfo)
        EndIf
        WriteLog("Stopping service: BlockInternet")
        Sleep(2000)
        _SelfRun($sServiceName,"stop")
    WEnd
    _Service_Cleanup()
    WriteLog("Service stopped: BlockInternet")
EndFunc

Func InstallService()
    If Not(IsAdmin()) Then
        MsgBox(0, "BlockInternet", "Admin rights needed, will exit now")
        Exit
    EndIf
    WriteLog("Installing Service, Please Wait" & @CRLF)
    _Service_Create($sServiceName,$sServiceDisplayName,$SERVICE_WIN32_OWN_PROCESS,$SERVICE_DEMAND_START, $SERVICE_ERROR_SEVERE, '"' & @SystemDir & '\' & @ScriptName & '"')
    If @error Then
        WriteLog("Problem Installing Service, Error number is " & @error & @CRLF & " message  : " & _WinAPI_GetLastErrorMessage())
        Return 0
    Else
        WriteLog("Installation of Service Successful")
    EndIf
    
    $NetworkDetails = _IPDetails()
    $MACAddress = $NetworkDetails[2]
    $Gateway = $NetworkDetails[3]
    MsgBox(0, "DEBUG", $MACAddress)
    RegWrite("HKEY_LOCAL_MACHINE\Software\BlockInternet", "GatewayInfo", "REG_SZ", $MACAddress & '|' & $Gateway)
    FileCopy(@ScriptName, @SystemDir & '\' & @ScriptName, 1)
    RegWrite("HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run", "BlockInternet", "REG_SZ", '"' & @SystemDir & '\' & @ScriptName & '" -r')

    Return 1
    Exit
EndFunc

Func RemoveService()
    _Service_Stop($sServiceName)
    _Service_Delete($sServiceName)
    if not @error then WriteLog("Service Removed Successfully" & @crlf)
    Exit
EndFunc

Func _SelfRun($servicename,$action)
    $sCmdFile = 'sc ' & $action & ' "' & $servicename & '"'
    Run($sCmdFile, @TempDir, @SW_HIDE)
    Exit
EndFunc

Can anybody please help me in implementing HandlerEx as I'm lacking in knowledge to play with Windows API ?

Edited by HolmesShelock

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

I did following modifications to arcker's Services.au3

Func _Service_Ctrl($dwControl, $dwEventType, $lpEventData, $lpContext)
    #forceref $dwEventType, $lpEventData, $lpContext
    Local $return = $NO_ERROR
    Switch $dwControl
        Case $SERVICE_CONTROL_STOP
            ;stop the service.
            ;
            ;SERVICE_STOP_PENDING should be reported before
            ;setting the Stop Event - hServerStopEvent - in
            ;service_stop().  This avoids a race condition
            ;which may result in a 1053 - The Service did not respond...
            ;error.
            _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 3000)
            _Service_SetStopEvent()
            ; call _Service_Cleanup() from _Svc_Main() immediately before exiting
            Return $NO_ERROR
        Case $SERVICE_CONTROL_PAUSE
            DllStructSetData($tService_Status, "dwCurrentState", $SERVICE_PAUSED)
        Case $SERVICE_CONTROL_CONTINUE
            DllStructSetData($tService_Status, "dwCurrentState", $SERVICE_RUNNING)
        Case $SERVICE_CONTROL_INTERROGATE
            ; report the service status
        Case $SERVICE_CONTROL_SESSIONCHANGE
            WriteLog("Session Changed")
        Case 128 To 255 ; custom messages
            ; handle custom messages
        Case Else
            ; invalid or unhandled control code
            $return = $ERROR_CALL_NOT_IMPLEMENTED
    EndSwitch
    _Service_ReportStatus(DllStructGetData($tService_Status, "dwCurrentState"), $NO_ERROR, 0)
    Return $return
EndFunc   ;==>_Service_Ctrl
I've added Case $SERVICE_CONTROL_SESSIonchange to the switch in the function _Service_Ctrl(), But, still it's not trapping "Switch User" events. Any ideas, why? Edited by HolmesShelock

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

To my understanding, in _Service_ServiceMain($iArg, $pArgs) serves the purpose of ServiceMain() & _Service_Ctrl($dwControl, $dwEventType, $lpEventData, $lpContext) is equivalent to HandlerEx as documented in MSDN. MS Documentation says,

The service will need to register for its service control handler function to receive SCM notifications during the SetServiceStatus call. The SERVICE_STATUS structure (http://go.microsoft.com/fwlink/?LinkID=70746) will need to include SERVICE_ACCEPT_SESSIonchange 0x00000080 as an accepted control.

My intention is to capture SERVICE_CONTROL_SESSIonchange event from within my service. If anybody can shed some light on how to register the service control handler to receive the mentioned event, it'll be a great help. I'm trying hard since last few days, but with no success.

Edited by HolmesShelock

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

Based on the sample service, I modified the code in Services.au3 (_Service_ReportStatus())

from

DllStructSetData($tService_Status, "dwControlsAccepted", $SERVICE_ACCEPT_STOP)
To

DllStructSetData($tService_Status, "dwControlsAccepted", Bitor($SERVICE_ACCEPT_STOP, $SERVICE_ACCEPT_SESSIONCHANGE))
It worked for me!!!!!!!! Edited by HolmesShelock

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

Hi,

So do you need help anymore ?That sounds logical, but great seek of the problem.

Btw it's a good thing to know and can serve many things.

Will try to add it so the UDFs.

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

Hi,

So do you need help anymore ?

No arcker, help is no more required. But, I'd thank you a lot for paying attention to my problem. Your contribution to our community is great.

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

Now, I'm facing a new problem.

As said in my second last post, the BitOr-ing of $SERVICE_ACCEPT_SESSIonchange made it possible to capture session change event. However, as HandlerEx domentation says,

If dwControl is SERVICE_CONTROL_SESSIonchange, this parameter can be one of the values specified in the wParam parameter of the WM_WTSSESSION_CHANGE message.

So, I modified my code to obtain SessionID. This is how my changed code looks like

[/size]

[size="2"]Func _Service_Ctrl($dwControl, $dwEventType, $lpEventData, $lpContext)[/size]
[size="2"]  #forceref $dwEventType, $lpEventData, $lpContext[/size]
[size="2"]  Local $return = $NO_ERROR[/size]
[size="2"];~    WriteLog("_Service_Ctrl entered")[/size]
[size="2"]  Switch $dwControl[/size]
[size="2"]      Case $SERVICE_CONTROL_STOP[/size]
[size="2"]          ;stop the service.[/size]
[size="2"]          ;[/size]
[size="2"]          ;SERVICE_STOP_PENDING should be reported before[/size]
[size="2"]          ;setting the Stop Event - hServerStopEvent - in[/size]
[size="2"]          ;service_stop().  This avoids a race condition[/size]
[size="2"]          ;which may result in a 1053 - The Service did not respond...[/size]
[size="2"]          ;error.[/size]
[size="2"]          _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 3000)[/size]
[size="2"]          _Service_SetStopEvent()[/size]
[size="2"]          ; call _Service_Cleanup() from _Svc_Main() immediately before exiting[/size]
[size="2"]          Return $NO_ERROR[/size]
[size="2"]      Case $SERVICE_CONTROL_PAUSE[/size]
[size="2"]          DllStructSetData($tService_Status, "dwCurrentState", $SERVICE_PAUSED)[/size]
[size="2"]      Case $SERVICE_CONTROL_CONTINUE[/size]
[size="2"]          DllStructSetData($tService_Status, "dwCurrentState", $SERVICE_RUNNING)[/size]
[size="2"]      Case $SERVICE_CONTROL_INTERROGATE[/size]
[size="2"]          ; report the service status[/size]
[size="2"]      Case $SERVICE_CONTROL_SESSIONCHANGE[/size]
[size="2"]          WriteLog("$SERVICE_CONTROL_SESSIONCHANGE trapped: " & $dwControl & " : " & $dwEventType & " : " & $lpEventData & " : " & $lpContext)[/size]
[size="2"]          $PWTSSESSION_NOTIFICATION = $lpEventData[/size]
[size="2"]          WriteLog("$PWTSSESSION_NOTIFICATION = " & $PWTSSESSION_NOTIFICATION)[/size]
[size="2"]          Local $cbSize = DllStructGetData($WTSSESSION_NOTIFICATION, "cbSize")[/size]
[size="2"]          Local $dwSessionId = DllStructGetData($WTSSESSION_NOTIFICATION, "dwSessionId")[/size]
[size="2"]          WriteLog("cbSize = " & $cbSize & " ,dwSessionId = " & $dwSessionId)[/size]
[size="2"]          [/size]
[size="2"]          Switch $dwEventType[/size]
[size="2"]              Case $WTS_CONSOLE_CONNECT[/size]
[size="2"]                  WriteLog("Console session connected")[/size]
[size="2"]                  SetResetGatewaySettings($lpEventData)[/size]
[size="2"]              Case $WTS_REMOTE_CONNECT[/size]
[size="2"]                  WriteLog("Remote session connected")[/size]
[size="2"]                  SetResetGatewaySettings($lpEventData)[/size]
[size="2"]          EndSwitch       [/size]
[size="2"]      Case 128 To 255 ; custom messages[/size]
[size="2"]          ; handle custom messages[/size]
[size="2"]      Case Else[/size]
[size="2"]          ; invalid or unhandled control code[/size]
[size="2"]          $return = $ERROR_CALL_NOT_IMPLEMENTED[/size]
[size="2"]  EndSwitch[/size]
[size="2"]  _Service_ReportStatus(DllStructGetData($tService_Status, "dwCurrentState"), $NO_ERROR, 0)[/size]
[size="2"]  Return $return[/size]
[size="2"]EndFunc   ;==>_Service_Ctrl[/size]
[size="2"]
In the above code, I altered $SERVICE_CONTROL_SESSIonchange case. But, every time I'm getting BOTH $cbSize & $dwSessionId as zero(0). I've tried running the code from both from first & second login switching to second user by "Fast user Switching, thus creating a non-zero sessionID. Moreover, how can $cbSize be zero also? the Am I making a mistake? Edited by HolmesShelock

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

Following the discussion & page__view__findpost__p__97598, I managed to resolve the problem. The correct code will be

Local $WTSSESSION_NOTIFICATION  = DllStructCreate("dword cbsize; dword dwSessionId", $lpEventData)
Local $cbSize = DllStructGetData($WTSSESSION_NOTIFICATION, "cbSize")
Local $dwSessionId = DllStructGetData($WTSSESSION_NOTIFICATION, "dwSessionId")
WriteLog("cbSize = " & $cbSize & " ,dwSessionId = " & $dwSessionId)

The struct is to be created from the data pointed to by $lpEventData

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

Just a suggestion, but why not add your code, or a link to it, to the _Services UDF thread? Otherwise, your nice addition may get lost here in the Help section. :unsure:

Link to comment
Share on other sites

Just a suggestion, but why not add your code, or a link to it, to the _Services UDF thread? Otherwise, your nice addition may get lost here in the Help section. :unsure:

Means ?

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

Don't worry ascendant, this thread is more for his researches, but i'll put all of this in the _Service_ UDF since it could be really usefull, so I will not forget this job !

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

Don't worry ascendant, this thread is more for his researches, but i'll put all of this in the _Service_ UDF since it could be really usefull, so I will not forget this job

Thanks a lot, arcker

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

Link to comment
Share on other sites

What I forgot to mention is, one needs to add the following constant definitions to ServicesConstants.au3

; Service event types
Global Const $WTS_CONSOLE_CONNECT = 0x00000001
Global Const $WTS_CONSOLE_DISCONNECT = 0x00000002
Global Const $WTS_REMOTE_CONNECT = 0x00000003
Global Const $WTS_REMOTE_DISCONNECT = 0x00000004
Global Const $WTS_SESSION_LOGON = 0x00000005
Global Const $WTS_SESSION_LOGOFF = 0x00000006
Global Const $WTS_SESSION_LOCK = 0x00000007
Global Const $WTS_SESSION_UNLOCK = 0x00000008
Global Const $WTS_SESSION_REMOTE_CONTROL = 0x00000009

[size="2"][font="arial, verdana, tahoma, sans-serif"]ProtectData - A Data Protection software for floppies[/font][/size] [size="2"][hr][/size][size="2"]Sessionchange - A Windows service capable of tracking session change events[/size][size="2"][b][/b][/size]

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...