#include-once

; #INDEX# =======================================================================================================================
; Title .........: AutoIt SEH
; AutoIt Version : 3.3.12.0 / 3.3.9.4 / 3.3.8.X / 3.3.6.X
; Description ...: Enables compiled script to gain control when events that normally terminate program execution occur.
; Version .......: 0.2.2
; Author(s) .....: Firex
; Dll(s) ........: Kernel32.dll
; ===============================================================================================================================

; #CURRENT# =====================================================================================================================
; OnAutoItErrorRegister
; OnAutoItErrorUnRegister
; ===============================================================================================================================

; #VARIABLES# ===================================================================================================================
Global $__Au3ErrCallback[2]
; ===============================================================================================================================

; #EXAMPLE# =====================================================================================================================
;~#Include <SEH.au3>
;~OnAutoItErrorRegister( '__Example_OnError' ) ; >>> __TRY {
;~	For $Idx = 1 To 5 Step 1
;~		$PlsError[1] = $PlsError[2]
;~	Next
;~OnAutoItErrorUnRegister() ; >>> }
;~
;~Func __Example_OnError( $pErrMsg ) ; >>> __EXCEPT {
;~	Local $tError, $aRes
;~	; *
;~	$aRes = DllCall("kernel32.dll", "int", "lstrlenW", "ptr", $pErrMsg)
;~	If @Error Or Not $aRes[0] Then _
;~		Return 0 ;Terminate script
;~
;~	$tError = DllStructCreate( "wchar Msg[" & $aRes[0] & "]", $pErrMsg )
;~	If @Error Then _
;~		Return 0 ;Terminate script
;~	; ---
;~
;~	If MsgBox( 5, 'Custom Au3Error callback', DllStructGetData( $tError, 'Msg' ) ) = 2 Then _
;~		Return 0 ;Terminate script
;~
;~	; ---
;~	Return 1 ;Continue execute
;~EndFunc ; >>> }
; ===============================================================================================================================


; #FUNCTION# ====================================================================================================================
; Author ........: Firex
; Modified.......:
; ===============================================================================================================================
Func OnAutoItErrorRegister( $sFunc = '' )
	If Not IsDeclared( "__Au3ErrCallback" ) Then _
		Global $__Au3ErrCallback[2]

    If Not @Compiled Or ( ( Not $__Au3ErrCallback[0] ) <> ( $sFunc <> '' ) ) Then _
        Return SetError( -1 )
	; *
	Local $aRes, $hModule, $pOffset, $tPatch, $iOpCode_sz, $pvCallback, _
		$aLib[5][5] = [ [4], _ ;* - ptr32
			[ '3.3.12.0', 0x0004CE15, 0x000572F2, _
				'0xFF75ECB8*FFD085C07550909090909090909090', _
				'0x488B4C2430B8*FFD085C0755D909090909090909090909090' _
			], _
			[ '3.3.9.4', 0x00065FAC, 0x000708B0, _
				'0x50B8*FFD085C0754F9090', _
				'0x488B4C2430B8*FFD085C07554909090909090909090909090' _
			], _
			[ '3.3.8.', 0x0005E8ED, 0x0006CE50, _
				2, _ ;Ref to 3.3.9.4
				2 _  ;Ref to 3.3.9.4
			], _
			[ '3.3.6.', 0x0005E7EA, 0x000702B3, _
				'0x50B8*FFD085C0755B9090', _
				'0x488B4C2430B8*FFD085C0756890909090909090909090909090909090' _
			] _
		], _
		$Ver, $Arch = 3 + @AutoItX64
	; ---
	$aRes = DllCall( "kernel32.dll", "handle", "GetModuleHandle", "ptr", 0 )
    If @Error Or Not $aRes[0] Then Return SetError( -2 )
		$hModule = $aRes[0]

	For $Ver = 1 To $aLib[0][0] Step 1
		If Not StringInStr( @AutoItVersion, $aLib[$Ver][0] ) Then _
			ContinueLoop
		; *
		$pOffset = $hModule + Ptr( $aLib[$Ver][$Arch-2] ) ; !!! Ptr() 3.3.8.1 <= !!!
		If IsInt( $aLib[$Ver][$Arch] ) Then _
			$Ver = $aLib[$Ver][$Arch] ;Ref to equal path

		$iOpCode_sz = BinaryLen( StringReplace( $aLib[$Ver][$Arch], '*', '00000000' ) )
		; < Replace opcode region
		$aRes = DllCall( "kernel32.dll", "bool", "VirtualProtect", "ptr", $pOffset, "ulong", $iOpCode_sz, "dword", 0x40, "dword*", "" )
		If @Error Or Not $aRes[0] Then _
			ExitLoop

		$tPatch = DllStructCreate( "byte Code[" & $iOpCode_sz & "]", $pOffset )
		If Not $__Au3ErrCallback[0] Then
			If Not $__Au3ErrCallback[1] Then _
				$__Au3ErrCallback[1] = DllStructGetData( $tPatch, 'Code' ) ;Save opcode

			$__Au3ErrCallback[0] = DllCallbackRegister( $sFunc, 'int', 'ptr' )
			$pvCallback = Hex( Binary( DllCallbackGetPtr( $__Au3ErrCallback[0] ) ) )
			$pvCallback = StringLeft( $pvCallback, 8 ) ;Fix for x64 (dword=4 byte)
			; -
			DllStructSetData( $tPatch, 'Code', StringReplace( $aLib[$Ver][$Arch], '*', $pvCallback ) )
		Else
			DllStructSetData( $tPatch, 'Code', $__Au3ErrCallback[1] ) ;Restore opcode
			; -
			DllCallbackFree( $__Au3ErrCallback[0] )
			$__Au3ErrCallback[0] = 0
		EndIf
		DllCall( "kernel32.dll", "bool", "VirtualProtect", "ptr", $pOffset, "ulong", $iOpCode_sz, "dword", $aRes[4], "ptr", 0 )
		; < Replace opcode endregion
		; -
		Return True
	Next
	; ---
	Return False
EndFunc

; #FUNCTION# ====================================================================================================================
; Author ........: Firex
; Modified.......:
; ===============================================================================================================================
Func OnAutoItErrorUnRegister()
	Return OnAutoItErrorRegister()
EndFunc