Wie "Tasten Sperren"

  • Hallo AutoIt-Com,
    mal wieder ich ( :rofl: ), diesmal mit einer Frage der anderen Art.
    Wie kann ich am PC Tasten sperren; meinetwegen so etwas wie "ALT+TAB", "Windows-Taste" ?
    Um hier noch aufkommenden Verdacht nicht zu bestätigen: Ich schreibe grade ein "Sperrprogramm", das nur auf Eingabe eines PW's den Nutzer wieder auf Windows zugreifen lässt - eine simple Windows-Sperre.
    MfG, TripleT Matthias :)

    Es gibt sehr viele Leute, die glauben. Aber aus Aberglauben.
    - Blaise Pascal

  • schau Dir mal _WinAPI_SetWindowsHookEx an

    MfG Schnuffel

    "Sarkasmus ist die niedrigste Form des Witzes, aber die höchste Form der Intelligenz."
    Val McDermid

    ein paar Infos ...

    Wer mehr als "nur" Hilfe benötigt, kann sich gern im Forum "Programmieranfragen" an uns wenden. Wir helfen in allen Fällen, die die Forenregeln zulassen.

    Für schnelle Hilfe benötigen wir ein ! lauffähiges ! Script, dass wir als Demonstration des Problems testen können. Wer von uns erwartet ein Teilscript erstmal lauffähig zu bekommen, der hat
    1. keine wirkliche Not
    2. keinen Respekt vor Menschen die ihm in ihrer Freizeit Ihre Hilfe anbieten
    3. oder ist einfach nur faul und meint wir coden das für ihn

    In solchen Fällen erlaube ich mir, die Anfrage einfach zu ignorieren. ;)

  • Blockinputex macht dich wunschlos glücklich!

    Spoiler anzeigen
    [autoit]

    #include-once
    #include <WindowsConstants.au3>
    #include <WinAPI.au3>
    ;

    [/autoit] [autoit][/autoit] [autoit]

    #Region ======================== BlockInputEx UDF Info ========================
    ; Extended (advanced) function to block mouse & keyboard inputs.
    ;
    ; This UDF supports few features that built-in BlockInput() function does not.
    ; Here is a quick "features list":
    ;----------------------------------------------------------------------
    ;* Block seperately mouse or keyboard input.
    ;* Block specific keyboard/mouse keys/clicks.
    ; [+] Not only hex keys are supported, string keys (such as {ENTER}) is also valid.
    ;* Block all keyboard/mouse keys *except* specific keys/events.
    ;* Block keys by CLASS Name (see UDF documentation).
    ;* Block inputs only for specific window.
    ;----------------------------------------------------------------------
    ;
    ; AutoIt Version: 3.2.12.1+
    ; Author: G.Sandler (a.k.a MrCreatoR). Initial idea and hooks example by rasim.
    ;
    ;==================
    ; History version:
    ;==================
    ;
    ; [v1.2 - 16.01.2009, 21:00]
    ; + Added key strings support.
    ; Now users can set simple hotkey strings for Exclude/Include parameters + Group chars, i.e: "[Group]|{UP}|{DOWN}"
    ; (See UDF documentation)
    ; + Added mouse events blocking support to the $sExclude/$sInclude parameters.
    ; + Added example for mouse events blocking.
    ;
    ; [v1.1 - 16.01.2009]
    ; + Added CLASSes support (see UDF documentation), thanks to FireFox for the idea and the keys lists.
    ; + Added example for CLASS usage.
    ; * Changed behaviour of $iBlockMode parameter as following:
    ;
    ; 1 - Block All
    ; <= 0 - UnBlock all
    ; 2 - Block only mouse
    ; 3 - Block only keyboard.
    ;
    ; * Fixed hard-crash that related to incorrect BlockInput releasing process (only callbacks was released, not the window hooks).
    ;
    ; [v1.0 - 15.01.2009]
    ; First release.
    #EndRegion ======================== BlockInputEx UDF Info ========================
    ;

    [/autoit] [autoit][/autoit] [autoit]

    Global $ah_MouseKeyboard_WinHooks[7]

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name...........: _BlockInputEx
    ; Description ...: Disable/enable the mouse and/or keyboard. Supporting blocking (by include/exclude) of seperate keys.
    ; Syntax.........: _BlockInputEx( [iBlockMode [, sExclude [, sInclude [, hWindows]]]] )
    ; Parameters ....: $iBlockMode - Set the block mode.
    ; 1 - Block All
    ; <= 0 - UnBlock all
    ; 2 - Block only mouse
    ; 3 - Block only keyboard.
    ; $sExclude - Keys hex/string-list (| delimited) to *exclude* when blocking.
    ; [*] All keys will be blocked, except the keys in $sExclude list.
    ; [!] This list supports keys CLASSes,
    ; key strings as supported in HotKeySet() function,
    ; and seperate mouse events classes, see "Remarks" for more details.
    ;
    ;
    ;
    ; $sInclude - Keys hex/string-list (| delimited) to *include* when blocking.
    ; [*] Only these keys will be blocked, the $sExclude ignored in this case.
    ; [!] This list supports keys CLASSes,
    ; key strings as supported in HotKeySet() function,
    ; and seperate mouse events classes, see "Remarks" for more details.
    ;
    ; $hWindows - Window handles list (| delimited) to limit the blocking process (Only for keyboard blocking).
    ;
    ; Return values .: Success - 1.
    ; Failure - 0 and set @error to 1. This can happend only when passing wrong parameters.
    ;
    ; Author ........: G.Sandler (a.k.a MrCreatoR), Rasim -> Initial idea and hooks example.
    ; Thanks to FireFox for other block methods, it gave me a starting point for adding the "keys classes" support.
    ;
    ;
    ; Modified.......: 16.01.2009, 21:03
    ; Remarks .......: * This UDF includes OnAutoItExit function to release the callback/hooks resources.
    ;
    ; * $sExclude and $sInclude parameters supporting keys/mouse classes as hex or as string,
    ; [!] The hex keys list can be found in _IsPressed documentation section.
    ; Here is a full list of supported classess/events:
    ; ======= KeyBoard =======
    ; [:FUNC:]
    ; [:ALPHA:]
    ; [:NUMBER:]
    ; [:ARROWS:]
    ; [:SPECIAL:]
    ; [GROUP_abcd] -> Use raw chars/key strings inside squere brackets.
    ; {KEY} -> Standard keys support ({F1}, {ENTER} etc.).
    ;
    ; ========= Mouse =========
    ; {MMOVE} -> Mouse move event
    ; {MPDOWN} -> Mouse Primary Down event
    ; {MPUP} -> Mouse Primary Up event
    ; {MSDOWN} -> Mouse Secondary Down event
    ; {MSUP} -> Mouse Secondary Up event
    ; {MWDOWN} -> Mouse Whell Button Down event
    ; {MWUP} -> Mouse Whell Button Up event
    ; {MWSCROLL} -> Mouse Whell Scroll event
    ; {MSPDOWN} -> Mouse Special Button Down event
    ; {MSPUP} -> Mouse Special Button Up event
    ;
    ;
    ; * See also built-in BlockInput() documentation.
    ;
    ; Related .......: BlockInput()?
    ; Link ..........; http://www.autoitscript.com/forum/index.php?s=&showtopic=87735
    ; Example .......; Yes
    ; ===============================================================================================================================
    Func _BlockInputEx($iBlockMode = -1, $sExclude = "", $sInclude = "", $hWindows = "")
    If $iBlockMode < -1 Or $iBlockMode > 3 Then Return SetError(1, 0, 0) ;Only -1 to 3 modes are supported.

    [/autoit] [autoit][/autoit] [autoit]

    If $iBlockMode <= 0 Then Return _BlockInput_UnhookWinHooks_Proc()

    [/autoit] [autoit][/autoit] [autoit]

    Local $pStub_KeyProc = 0, $pStub_MouseProc = 0, $hHook_Keyboard = 0, $hHook_Mouse = 0
    Local $hHook_Module = _WinAPI_GetModuleHandle(0)

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To 3
    If $ah_MouseKeyboard_WinHooks[$i] > 0 Then
    _BlockInput_UnhookWinHooks_Proc()
    ExitLoop
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If $iBlockMode = 1 Or $iBlockMode = 2 Then
    $pStub_MouseProc = DllCallbackRegister("_MouseHook_Proc", "int", "int;ptr;ptr")
    $hHook_Mouse = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($pStub_MouseProc), $hHook_Module, 0)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    If $iBlockMode = 1 Or $iBlockMode = 3 Then
    $pStub_KeyProc = DllCallbackRegister("_KeyBoardHook_Proc", "int", "int;ptr;ptr")
    $hHook_Keyboard = _WinAPI_SetWindowsHookEx($WH_KEYBOARD_LL, DllCallbackGetPtr($pStub_KeyProc), $hHook_Module, 0)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $ah_MouseKeyboard_WinHooks[0] = $pStub_KeyProc
    $ah_MouseKeyboard_WinHooks[1] = $pStub_MouseProc
    $ah_MouseKeyboard_WinHooks[2] = $hHook_Keyboard
    $ah_MouseKeyboard_WinHooks[3] = $hHook_Mouse
    $ah_MouseKeyboard_WinHooks[4] = "|" & _Parse_vmCodesList_CLASSes(_Parse_vkCodesList_CLASSes($sInclude)) & "|"
    $ah_MouseKeyboard_WinHooks[5] = "|" & _Parse_vmCodesList_CLASSes(_Parse_vkCodesList_CLASSes($sExclude)) & "|"
    $ah_MouseKeyboard_WinHooks[6] = "|" & $hWindows & "|"

    [/autoit] [autoit][/autoit] [autoit]

    Return 1
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ;KeyBoard hook processing function
    Func _KeyBoardHook_Proc($nCode, $wParam, $lParam)
    If $nCode < 0 Then Return _WinAPI_CallNextHookEx($ah_MouseKeyboard_WinHooks[4], $nCode, $wParam, $lParam)

    [/autoit] [autoit][/autoit] [autoit]

    Local $KBDLLHOOKSTRUCT = DllStructCreate("dword vkCode;dword scanCode;dword flags;dword time;ptr dwExtraInfo", $lParam)
    Local $vkCode = "0x" & Hex(DllStructGetData($KBDLLHOOKSTRUCT, "vkCode"), 2)

    [/autoit] [autoit][/autoit] [autoit]

    Local $sInclude = $ah_MouseKeyboard_WinHooks[4]
    Local $sExclude = $ah_MouseKeyboard_WinHooks[5]
    Local $hWnds = $ah_MouseKeyboard_WinHooks[6]

    [/autoit] [autoit][/autoit] [autoit]

    If $sInclude <> "||" Then ;Include proc
    If StringInStr($sInclude, "|" & $vkCode & "|") And _
    ($hWnds = "||" Or StringInStr($hWnds, "|" & WinGetHandle("[ACTIVE]") & "|")) Then Return 1 ;Block processing!
    Else ;Exclude proc
    If Not StringInStr($sExclude, "|" & $vkCode & "|") And _
    ($hWnds = "||" Or StringInStr($hWnds, "|" & WinGetHandle("[ACTIVE]") & "|")) Then Return 1 ;Block processing!
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    _WinAPI_CallNextHookEx($ah_MouseKeyboard_WinHooks[2], $nCode, $wParam, $lParam) ;Continue processing
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ;Mouse hook processing function
    Func _MouseHook_Proc($nCode, $wParam, $lParam)
    If $nCode < 0 Then Return _WinAPI_CallNextHookEx($ah_MouseKeyboard_WinHooks[3], $nCode, $wParam, $lParam) ;Continue processing

    [/autoit] [autoit][/autoit] [autoit]

    Local $iMouse_Event = BitAND($wParam, 0xFFFF)
    ;Add mouse exclude/include actions support...

    [/autoit] [autoit][/autoit] [autoit]

    Local $sInclude = $ah_MouseKeyboard_WinHooks[4]
    Local $sExclude = $ah_MouseKeyboard_WinHooks[5]
    Local $hWnds = $ah_MouseKeyboard_WinHooks[6]

    [/autoit] [autoit][/autoit] [autoit]

    If $sInclude <> "||" Then ;Include proc
    If StringInStr($sInclude, "|" & $iMouse_Event & "|") And _
    ($hWnds = "||" Or StringInStr($hWnds, "|" & WinGetHandle("[ACTIVE]") & "|")) Then Return 1 ;Block processing!
    Else ;Exclude proc
    If Not StringInStr($sExclude, "|" & $iMouse_Event & "|") And _
    ($hWnds = "||" Or StringInStr($hWnds, "|" & WinGetHandle("[ACTIVE]") & "|")) Then Return 1 ;Block processing!
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Return _WinAPI_CallNextHookEx($ah_MouseKeyboard_WinHooks[3], $nCode, $wParam, $lParam) ;Continue processing
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ;Releases callbacks and Unhook Windows hooks
    Func _BlockInput_UnhookWinHooks_Proc()
    ;Release KeyBoard callback function
    If $ah_MouseKeyboard_WinHooks[0] > 0 Then
    DllCallbackFree($ah_MouseKeyboard_WinHooks[0])
    $ah_MouseKeyboard_WinHooks[0] = 0
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    ;Release Mouse callback function
    If $ah_MouseKeyboard_WinHooks[1] > 0 Then
    DllCallbackFree($ah_MouseKeyboard_WinHooks[1])
    $ah_MouseKeyboard_WinHooks[1] = 0
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    ;Release KeyBoard Window hook
    If IsPtr($ah_MouseKeyboard_WinHooks[2]) Then
    _WinAPI_UnhookWindowsHookEx($ah_MouseKeyboard_WinHooks[2])
    $ah_MouseKeyboard_WinHooks[2] = 0
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    ;Release Mouse Window hook
    If IsPtr($ah_MouseKeyboard_WinHooks[3]) Then
    _WinAPI_UnhookWindowsHookEx($ah_MouseKeyboard_WinHooks[3])
    $ah_MouseKeyboard_WinHooks[3] = 0
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Return 1
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Parse_vkCodesList_CLASSes($sList)
    Local $a_vkCode_List = StringSplit($sList, "|")
    Local $sRet_Keys = ""

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 1 To $a_vkCode_List[0]
    Switch $a_vkCode_List[$i]
    Case "[:FUNC:]"
    $a_vkCode_List[$i] = "0x70|0x71|0x72|0x73|0x74|0x75|0x76|0x77|0x78|0x79|0x7A|0x7B|0x7C|" & _
    "0x7D|0x7E|0x7F|0x80H|0x81H|0x82H|0x83H|0x84H|0x85H|0x86H|0x87H"
    Case "[:ALPHA:]"
    $a_vkCode_List[$i] = "0x41|0x42|0x43|0x44|0x45|0x46|0x47|0x48|0x49|0x4A|0x4B|0x4C|0x4D|" & _
    "0x4E|0x4F|0x50|0x51|0x52|0x53|0x54|0x55|0x56|0x57|0x58|0x59|0x5A"
    Case "[:NUMBER:]"
    $a_vkCode_List[$i] = "0x30|0x31|0x32|0x33|0x34|0x35|0x36|0x37|0x38|0x39|0x60|0x61|0x62|" & _
    "0x63|0x64|0x65|0x66|0x67|0x68|0x69"
    Case "[:ARROWS:]"
    $a_vkCode_List[$i] = "0x25|0x26|0x27|0x28"
    Case "[:SPECIAL:]"
    $a_vkCode_List[$i] = "0x08|0x09|0x0C|0x0D|0x10|0x11|0x12|0x13|0x14|0x1B|0x20|0x21|0x22|" & _
    "0x23|0x24|0x29|0x2A|0x2B|0x2C|0x2D|0x2E|0x5B|0x5C|0x6A|0x6B|0x6C|" & _
    "0x6D|0x6E|0x6F|0x90|0x91|0xA0|0xA1|0xA2|0xA3|0xA4|0xA5|0xBA|0xBB|" & _
    "0xBC|0xBD|0xBE|0xBF|0xC0|0xDB|0xDC|0xDD"
    Case Else
    $a_vkCode_List[$i] = _KeyStr_To_vkCode($a_vkCode_List[$i])
    EndSwitch

    [/autoit] [autoit][/autoit] [autoit]

    $sRet_Keys &= $a_vkCode_List[$i] & "|"
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Return StringRegExpReplace($sRet_Keys, "\|+$", "")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Parse_vmCodesList_CLASSes($sList)
    Local Const $MOUSE_MOVE_EVENT = 512
    Local Const $MOUSE_PRIMARYDOWN_EVENT = 513
    Local Const $MOUSE_PRIMARYUP_EVENT = 514
    Local Const $MOUSE_SECONDARYDOWN_EVENT = 516
    Local Const $MOUSE_SECONDARYUP_EVENT = 517
    Local Const $MOUSE_WHELLDOWN_EVENT = 519
    Local Const $MOUSE_WHELLUP_EVENT = 520
    Local Const $MOUSE_WHELLSCROLL_EVENT = 522
    Local Const $MOUSE_SPECIALBUTTONDOWN_EVENT = 523
    Local Const $MOUSE_SPECIALBUTTONUP_EVENT = 524

    [/autoit] [autoit][/autoit] [autoit]

    Local $a_vmCode_List = StringSplit($sList, "|")
    Local $sRet_Keys = ""

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 1 To $a_vmCode_List[0]
    Switch $a_vmCode_List[$i]
    Case "{MMOVE}"
    $a_vmCode_List[$i] = $MOUSE_MOVE_EVENT
    Case "{MPDOWN}"
    $a_vmCode_List[$i] = $MOUSE_PRIMARYDOWN_EVENT
    Case "{MPUP}"
    $a_vmCode_List[$i] = $MOUSE_PRIMARYUP_EVENT
    Case "{MSDOWN}"
    $a_vmCode_List[$i] = $MOUSE_SECONDARYDOWN_EVENT
    Case "{MSUP}"
    $a_vmCode_List[$i] = $MOUSE_SECONDARYUP_EVENT
    Case "{MWDOWN}"
    $a_vmCode_List[$i] = $MOUSE_WHELLDOWN_EVENT
    Case "{MWUP}"
    $a_vmCode_List[$i] = $MOUSE_WHELLUP_EVENT
    Case "{MWSCROLL}"
    $a_vmCode_List[$i] = $MOUSE_WHELLSCROLL_EVENT
    Case "{MSPDOWN}"
    $a_vmCode_List[$i] = $MOUSE_SPECIALBUTTONDOWN_EVENT
    Case "{MSPUP}"
    $a_vmCode_List[$i] = $MOUSE_SPECIALBUTTONUP_EVENT
    EndSwitch

    [/autoit] [autoit][/autoit] [autoit]

    $sRet_Keys &= $a_vmCode_List[$i] & "|"
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Return StringRegExpReplace($sRet_Keys, "\|+$", "")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _KeyStr_To_vkCode($sKeyStr)
    Local $sRet_Keys = "", $aDelim_Keys[1]
    Local $aKeys = StringSplit("{LMouse}|{RMouse}|{}|(MMouse}|{}|{}|{}|{BACKSPACE}|{TAB}|{}|{}|{}|{ENTER}|{}|{}|{SHIFT}|{CTRL}|{ALT}|{PAUSE}|{CAPSLOCK}|{}|{}|{}|{}|{}|{}|{ESC}|{}|{}|{}|{]|{SPACE}|{PGUP}|{PGDN}|{END}|{HOME}|{LEFT}|{UP}|{RIGHT}|{DOWN}|{SELECT}|{PRINTSCREEN}|{}|{PRINTSCREEN}|{INSERT}|{DEL}|{}|0|1|2|3|4|5|6|7|8|9|{}|{}|{}|{}|{}|{}|{}|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|{LWIN}|{RWIN}|{APPSKEY}|{}|{SLEEP}|{numpad0}|{numpad1}|{numpad2}|{numpad3}|{numpad4}|{numpad5}|{numpad6}|{numpad7}|{numpad8}|{numpad9}|{NUMPADMULT}|{NUMPADADD}|{}|{NUMPADSUB}|{NUMPADDOT}|{NUMPADDIV}|{F1}|{F2}|{F3}|{F4}|{F5}|{F6}|{F7}|{F8}|{F9}|{F10}|{F11}|{F12}|{F13}|{F14}|{F15}|{F16}|{F17}|{F18}|{F19}|{F20}|{F21}|{F22}|{F23}|{F24}|{}|{}|{}|{}|{}|{}|{}|{}|{NUMLOCK}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{SHIFT}|{SHIFT}|{CTRL}|{CTRL}|{ALT}|{ALT}|{BROWSER_BACK}|{BROWSER_FORWARD}|{BROWSER_REFRESH}|{BROWSER_STOP}|{BROWSER_SEARCH}|{BROWSER_FAVORITES}|{BROWSER_HOME}|{VOLUME_MUTE}|{VOLUME_DOWN}|{VOLUME_UP}|{MEDIA_NEXT}|{MEDIA_PREV}|{MEDIA_STOP}|{MEDIA_PLAY_PAUSE}|{LAUNCH_MAIL}|{LAUNCH_MEDIA}|{LAUNCH_APP1}|{LAUNCH_APP2}|{}|{}|;|{+}|,|{-}|.|/|`|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|[|\|]|'", "|")

    [/autoit] [autoit][/autoit] [autoit]

    If StringRegExp($sKeyStr, "\A\[|\]\z") Then
    $sKeyStr = StringRegExpReplace($sKeyStr, "\A\[|\]\z", "")
    $sKeyStr = StringRegExpReplace($sKeyStr, "(.)", "\1|")
    $sKeyStr = StringRegExpReplace($sKeyStr, "\|+$", "")

    [/autoit] [autoit][/autoit] [autoit]

    $aDelim_Keys = StringSplit($sKeyStr, "")
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 1 To $aKeys[0]
    If $aDelim_Keys[0] > 1 Then
    For $j = 1 To $aDelim_Keys[0]
    If $aKeys[$i] = $aDelim_Keys[$j] Then $sRet_Keys &= "0x" & Hex($i, 2) & "|"
    Next
    Else
    If $aKeys[$i] = $sKeyStr Then Return "0x" & Hex($i, 2)
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If $sRet_Keys = "" Then Return $sKeyStr
    Return StringRegExpReplace($sRet_Keys, "\|+$", "")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ;Called when script exits to release resources.
    Func OnAutoItExit()
    _BlockInputEx(0)
    EndFunc

    [/autoit]

    [align=center]Meine Werke mit der Irrlicht Engine
    AutoIt Picture Viewer Dreidimensionaler Bildbetrachter
    Mr Bubble 3D Neue Interpretation des Flashklassikers Bubble trouble

    Einmal editiert, zuletzt von Twodollarbillie (22. Mai 2011 um 21:32)